Reputation: 638
I need to use a windows function like GetModuleHandle or GetModuleFileName to find out if a specific dll is loaded in the same process where my code is executing.
One module I'm looking for is System.Windows.Forms.dll, but even when it is loaded in process... (Here you can see it using Process Explorer)
GetModuleHandle still will not find it!
HMODULE modHandle = GetModuleHandle(L"System.Windows.Forms.dll");
GetLastError() returns ERROR_MOD_NOT_FOUND
If the function succeeds, the return value is a handle to the specified module. If the function fails, the return value is NULL.
I think it may be something to do with how the CLR loads these dlls. I see a note on LoadLibraryEx that if the LOAD_LIBRARY_AS_DATAFILE flag is used then:
If this value is used, the system maps the file into the calling process's virtual address space as if it were a data file. Nothing is done to execute or prepare to execute the mapped file. Therefore, you cannot call functions like GetModuleFileName, GetModuleHandle or GetProcAddress with this DLL.
Maybe this is my problem, but regardless of the cause - does anyone know a way to find a managed DotNet dll in a process using native / c++ code?
Thanks!
EDIT: Based on suggestions from Castorix in the comments I tried to use EnumProcessModules:
HMODULE modules[100];
void* hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, GetCurrentProcessId());
if (hProcess)
{
DWORD bytesNeeded;
BOOL rc = EnumProcessModules(hProcess, modules, sizeof(modules), &bytesNeeded);
if (rc)
{
int count = (int)(bytesNeeded / sizeof(HMODULE));
for (int i = 0; i < count; i++)
{
wchar_t moduleName[260];
GetModuleFileName(modules[i], moduleName, 260);
}
}
}
CloseHandle(hProcess);
This code finds a lot of the modules but not System.Windows.Forms.dll
Upvotes: 0
Views: 1559
Reputation: 638
To add to the above answer and provide relevant code; it was not possible to use a native function like EnumProcessModules
to detect the non-ngen'ed DotNet dlls and instead I had to use c++ interfaces to the CLR.
There is a lot more info here: https://blogs.msdn.microsoft.com/calvin_hsia/2013/12/05/use-reflection-from-native-c-code-to-run-managed-code/ The code most relevant to this particular question was:
HRESULT GetAssemblyFromAppDomain(_AppDomain* pAppDomain, LPCWSTR wszAssemblyName, _Deref_out_opt_ _Assembly **ppAssembly)
{
*ppAssembly = NULL;
// get the assemblies into a safearray
SAFEARRAY *pAssemblyArray = NULL;
HRESULT hr = pAppDomain->GetAssemblies(&pAssemblyArray);
if (FAILED(hr))
{
return hr;
}
// put the safearray into a smart ptr, so it gets released
CComSafeArray<IUnknown*> csaAssemblies;
csaAssemblies.Attach(pAssemblyArray);
size_t cchAssemblyName = wcslen(wszAssemblyName);
long cAssemblies = csaAssemblies.GetCount();
for (long i=0; i<cAssemblies; i++)
{
CComPtr<_Assembly> spAssembly;
spAssembly = csaAssemblies[i];
if (spAssembly == NULL)
continue;
CComBSTR cbstrAssemblyFullName;
hr = spAssembly->get_FullName(&cbstrAssemblyFullName);
if (FAILED(hr))
continue;
// is it the one we want?
if (cbstrAssemblyFullName != NULL &&
_wcsnicmp(cbstrAssemblyFullName,
wszAssemblyName,
cchAssemblyName) == 0)
{
*ppAssembly = spAssembly.Detach();
hr = S_OK;
break;
}
}
if (*ppAssembly == 0)
{
hr = E_FAIL;
}
return hr;
}
There's some information on the CLR interfaces here:
Upvotes: 0
Reputation: 49260
OK, this is an attempt to an answer (or really just a too long comment, sorry).
Personally, I have never seen managed .NET DLLs in the Process Explorer pane, but might not have been looking hard / often enough. However, what I can (and always could) see are the NGENed images (*.ni.dll
).
Note also the presence of System.Data.dll
here, which is not NGENed, but is a mixed mode assembly and contains native code as well as managed code.
So one could conclude, that you can only see NGENed and mixed mode "assemblies" here, because they are still loaded by LoadLibrary
or LoadLibraryEx
.
Also note my comment, which I reproduce here for easier access:
I think the CLR does not use LoadLibrary, which would explain why you cannot "see" them using the APIs you described. In fact, CLR 4 Does Not Use LoadLibrary to Load Assemblies is a blog entry that is relevant. You could always check the sources (CoreCLR, but shouldn't matter), about how it is done in particular. I have no really good place, but you could start here and then go from it. Use the ICorDebug interface instead.
Here are some relevant quotes from the blog entry linked above:
You may be asking yourself: …who cares? Well, first of all it’s good to know. I haven’t noticed a public service announcement to the above. It is an implementation detail, however—CLR assemblies are not even guaranteed to be implemented using files, not to mention DLL files in a specific format that are loaded using the LoadLibrary Win32 API.
However, there are several tools and scenarios which have come to rely on the fact the CLR loads assemblies using LoadLibrary. For example, up to CLR 4, if you wanted to know which .NET assemblies were loaded in your process, a fairly reliable heuristic would be to fire up Sysinternals Process Explorer and look at the DLLs view of a given process. This doesn’t work for CLR 4, as you can see here:
Frankly, I don't know how Process Explorer manages to show assemblies (not NGENed and not mixed mode) in your case - appart from you are watching a CLR2 process. However, mind you that PE does not only use Win32 APIs. It also uses WMI and probably also uses the CLR directly for more information. For example, the "Process Properties/.NET Assemblies" and "Process Properties/.NET Performance" tabs most likely use ICorDebug
/ICorProfile
and performance counters/ETW respectively.
You might need to use on of those interfaces as well, or something else from the unmanaged Debugging API or the unmanaged API in general.
Whatever it is, I don't think that EnumProcessModules
, etc. will get you there for reasons above.
Upvotes: 1