.NET 4 process doesn't show up in enumeration
Hi Guys and Gals!
So I enumerate all managed processes this way and it shows me asp.net process aspnet_wp.exe for .net 2, but it doesn't show for .net 4. Should I enumberate them differently?
//30. Create Cor Publisher
hr = CoCreateInstance( __uuidof( CorpubPublish ),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof( ICorPublish ),
reinterpret_cast< LPVOID * >( &corDebuggerPublisher )
);//40. Enumerate managed processes
hr = corDebuggerPublisher->EnumProcesses( COR_PUB_MANAGEDONLY,
&activeProcesses
);
Looks like .net 4 creates it's own aspnet_wp.exe, so I see 2 of them in task manager, 1 for v4 and 1 for v2.
Thanks!
Dan
Answers
- Dan,
This interface can be made to work for V1, V2 and V4, but it's not the API I'd recommend using if V2 and/or V4 are installed on the machine. For any scenario requiring you to know specifically what version of the runtime is in a target process, the API is insufficient. We actually plan to remove the API at some point. In light of all that, here's my recommendation:- If V4 is the latest runtime on the machine, use the new MetaHost APIs (the key one being EnumerateLoadedRuntimes). This will tell you which runtimes, if any, are loaded in a target process: V1 or V2 and/or V4 (Yes, there can be more than one as of V4. We call that in-process side-by-side).
- If V2 is the latest installed on the machine, you can use GetVersionFromProcess. This will tell you what version (V1x or V2), if any, is loaded in a target process.
- If V1 is the latest, use ICorPublish (as you are above).
One key difference between the first two bullets and the last is in the former two, you're looping the target processes, calling either EnumerateLoadedRuntimes or GetVersionFromProcess for each. In the latter case, it just returns you an enumeration of all the "managed" processes on the machine. While looping through sounds like more work, either you, or the API implementation are going to have to do it. The benefit of your doing it, using the newer APIs, is that you can get more information about what runtime(s) is/are running in the target as opposed to knowing only that some version is.
Your workflow should look like this:- Load mscoree.dll
- If this fails, no runtime is installed
- GetProcAddress on GetCLRMetaHost (in Beta2, this API name will change to CLRCreateInstance)
- If that succeeds, use the API to get an ICLRMetaHost interface
- Loop through the processes on the machine calling ICLRMetaHost::EnumerateLoadedRuntimes for each
- If one or more are loaded, you'll get an ICLRRuntimeInfo for each, which you can use to reason about the runtime(s) in the target
- If GetProcAddress for GetCLRMetaHost fails, V4 is not installed. Call GetProcAddress on GetVersionFromProcess
- If that succeeds, loop through the processes on the machine calling GetVersionFromProcess for each
- If one is loaded, it will return the version string (e.g. v1.1.4322 or V2.0.50727)
- If GetProcAddress for GetVersionFromProcess fails, V2 is not installed. CoCreateInstance CorPubPublish and use your existing code to enumerate. If any are returned, assume they're v1.x
If you're curious about how to make an ICorPublish solution work, let me know and I'll either post details in the forum or write a blog entry and point you to it (it's a much more confusing solution). For some background information, I'd recommend watching our Channel9 video "CLR 4 - Side-by-Side In-Process - What. How. Why." It should shed light on some topics I didn't explain in detail in this response; specifically, in-process side-by-side and our V4 compatibility policy (e.g. why doesn't CoCreateInstance get me a V4 version of ICorPublish).
If you don't think the proposed solution will meet your requirements, please help us understand your high-level scenario and we can offer other suggestions for a solution.
Regards,
Jon- Marked As Answer bylpszDan Saturday, September 26, 2009 1:56 AM
- Marked As Answer byJon LangdonMSFT, OwnerMonday, September 21, 2009 6:45 PM
- Unmarked As Answer byJon LangdonMSFT, OwnerMonday, September 21, 2009 6:51 PM
- This code works for me:
#include <stdio.h> #include <windows.h> #include <metahost.h> #using <System.dll> using namespace System; using namespace System::Diagnostics; int main() { ICLRMetaHost * pMetaHost = NULL; HRESULT hr = CLRCreateInstance( CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&pMetaHost); if (hr != S_OK) { System::Console::WriteLine("CLRCreateInstance failed: 0x{0:x}", hr); return -1; } // Get all processes running on the local computer. array<Process ^> ^ processes = Process::GetProcesses(); for (int i = 0; i < processes->Length; i++) { Process ^ process = processes[i]; Console::Write("{0}", process->ProcessName); HANDLE hProcess; try { hProcess = (HANDLE)process->Handle; } catch (System::ComponentModel::Win32Exception ^ e) { Console::WriteLine(" ... Win32 Exception (probably no permission)"); continue; } IEnumUnknown * pEnum; hr = pMetaHost->EnumerateLoadedRuntimes( hProcess, &pEnum); if (hr == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) { Console::WriteLine(" ... no CLR"); continue; } if (hr != S_OK) { Console::WriteLine(" ... EnumerateLoadedRuntimes(0x{0:x}) failed: 0x{1:x}", (int)hProcess, hr); continue; } Console::WriteLine(); for (;;) { IUnknown * pRuntime; ULONG cRuntimes; hr = pEnum->Next(1, &pRuntime, &cRuntimes); if (hr == S_FALSE) { // No more runtimes anymore Console::WriteLine(" Next() returned S_FALSE"); break; } if (hr != S_OK) { Console::WriteLine(" Next() failed: 0x{0:x}", hr); break; } if (cRuntimes != 1) { Console::WriteLine(" Next() returned {0} runtimes", cRuntimes); continue; } ICLRRuntimeInfo * pRuntimeInfo; hr = pRuntime->QueryInterface(IID_ICLRRuntimeInfo, (LPVOID *)&pRuntimeInfo); if (hr != S_OK) { Console::WriteLine(" QI(IID_ICLRRuntimeInfo) failed: 0x{0:x}", hr); continue; } pRuntime->Release(); WCHAR wszVersion[500]; DWORD cchVersion = _countof(wszVersion); pRuntimeInfo->GetVersionString(wszVersion, &cchVersion); printf(" %S\n", wszVersion); pRuntimeInfo->Release(); } } Console::WriteLine(); }
- Marked As Answer byKarel ZikmundMSFT, ModeratorThursday, November 12, 2009 7:52 PM
All Replies
- Dan,
This interface can be made to work for V1, V2 and V4, but it's not the API I'd recommend using if V2 and/or V4 are installed on the machine. For any scenario requiring you to know specifically what version of the runtime is in a target process, the API is insufficient. We actually plan to remove the API at some point. In light of all that, here's my recommendation:- If V4 is the latest runtime on the machine, use the new MetaHost APIs (the key one being EnumerateLoadedRuntimes). This will tell you which runtimes, if any, are loaded in a target process: V1 or V2 and/or V4 (Yes, there can be more than one as of V4. We call that in-process side-by-side).
- If V2 is the latest installed on the machine, you can use GetVersionFromProcess. This will tell you what version (V1x or V2), if any, is loaded in a target process.
- If V1 is the latest, use ICorPublish (as you are above).
One key difference between the first two bullets and the last is in the former two, you're looping the target processes, calling either EnumerateLoadedRuntimes or GetVersionFromProcess for each. In the latter case, it just returns you an enumeration of all the "managed" processes on the machine. While looping through sounds like more work, either you, or the API implementation are going to have to do it. The benefit of your doing it, using the newer APIs, is that you can get more information about what runtime(s) is/are running in the target as opposed to knowing only that some version is.
Your workflow should look like this:- Load mscoree.dll
- If this fails, no runtime is installed
- GetProcAddress on GetCLRMetaHost (in Beta2, this API name will change to CLRCreateInstance)
- If that succeeds, use the API to get an ICLRMetaHost interface
- Loop through the processes on the machine calling ICLRMetaHost::EnumerateLoadedRuntimes for each
- If one or more are loaded, you'll get an ICLRRuntimeInfo for each, which you can use to reason about the runtime(s) in the target
- If GetProcAddress for GetCLRMetaHost fails, V4 is not installed. Call GetProcAddress on GetVersionFromProcess
- If that succeeds, loop through the processes on the machine calling GetVersionFromProcess for each
- If one is loaded, it will return the version string (e.g. v1.1.4322 or V2.0.50727)
- If GetProcAddress for GetVersionFromProcess fails, V2 is not installed. CoCreateInstance CorPubPublish and use your existing code to enumerate. If any are returned, assume they're v1.x
If you're curious about how to make an ICorPublish solution work, let me know and I'll either post details in the forum or write a blog entry and point you to it (it's a much more confusing solution). For some background information, I'd recommend watching our Channel9 video "CLR 4 - Side-by-Side In-Process - What. How. Why." It should shed light on some topics I didn't explain in detail in this response; specifically, in-process side-by-side and our V4 compatibility policy (e.g. why doesn't CoCreateInstance get me a V4 version of ICorPublish).
If you don't think the proposed solution will meet your requirements, please help us understand your high-level scenario and we can offer other suggestions for a solution.
Regards,
Jon- Marked As Answer bylpszDan Saturday, September 26, 2009 1:56 AM
- Marked As Answer byJon LangdonMSFT, OwnerMonday, September 21, 2009 6:45 PM
- Unmarked As Answer byJon LangdonMSFT, OwnerMonday, September 21, 2009 6:51 PM
- Thanks.
I would have never guessed... My goal is very simple, it's to enumerate the running processes and attach to the one I select. I am attaching to asp.net processes cause that's the debugger I am writing primarily. Other than this simple task I also want to be able to attach to asp.net process before it actually executes anything, right when it's created. And I have no idea how to do it other than scan for processes repeadely and attach right away, hoping I cought it in time.
I appreciate it,
Dan - How can I enumerate all processes.
I use this one and
hr = corDebuggerPublisher->EnumProcesses( COR_PUB_MANAGEDONLY,
&activeProcesses
);
and it looks like with your algorithm this can't be used, should I enumerate them all natively? And specifically how to enumerate 32 and 64 bit processes?
I tried enumerating them natively but some processes return they don't have any loaded runtimes when I call EnumerateLoadedRuntimes(handle,...)
Surprisingly for example just a windows application (wfa1.exe) compiled in VS2010 beta 2 in C# returns empty enumeration, but the same app wfa1.vshost.exe returns 2 runtimes v4... and v2.. I tried x64 and x86 - same thing
So it would be very helpful if you could provide a sample for that.
Dan - It still doesn't show up in enumeration. For example, w3wp.exe doesn't show up, what I do is:
I get a list of all processses from c++ through .net's function, (cause I didn't want to figure out the native way):
array<System::Diagnostics::Process^>^ processes = System::Diagnostics::Process::GetProcesses();
And it returns all processes just fine.
Then for each process I do:
HANDLE h = static_cast<HANDLE>(p->Handle);
I am not sure about static_cast, cause I never fully understood the difference in different casts.
And then I do:
hr = mh->EnumerateLoadedRuntimes( h, &e_ri );
And it returns 0 runtimes for many apps that I know have run-times loaded in them. And for some it returns several runtimes.
Clearly I am doing something wrong, so may be you guys can tell me what to do.
The OS I run this on is W7 x64 and the code is in x64. so may be this is the cause, I don't know.
Dan Your code looks fine to me.
Note that you cannot enumerate x86 processes from x64 process and vice versa. That's probably reason why w3wp.exe is not showing up in your enumeration.
If you see a process where the EnumerateLoadedRuntimes API doesn't work - print out the process name and check manually (e.g. using ProcessExplorer) that the process has laoded mscoree.dll and either of mscorkws.dll and clr.dll. Also make sure that the process you are enumerating an the process form which you do the enumeration have the same x86/x64 bitness. If you find such process where it doesn't work, please post more info about it here (process name, how it was created, etc.).
Thanks,
-Karel- This code works for me:
#include <stdio.h> #include <windows.h> #include <metahost.h> #using <System.dll> using namespace System; using namespace System::Diagnostics; int main() { ICLRMetaHost * pMetaHost = NULL; HRESULT hr = CLRCreateInstance( CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&pMetaHost); if (hr != S_OK) { System::Console::WriteLine("CLRCreateInstance failed: 0x{0:x}", hr); return -1; } // Get all processes running on the local computer. array<Process ^> ^ processes = Process::GetProcesses(); for (int i = 0; i < processes->Length; i++) { Process ^ process = processes[i]; Console::Write("{0}", process->ProcessName); HANDLE hProcess; try { hProcess = (HANDLE)process->Handle; } catch (System::ComponentModel::Win32Exception ^ e) { Console::WriteLine(" ... Win32 Exception (probably no permission)"); continue; } IEnumUnknown * pEnum; hr = pMetaHost->EnumerateLoadedRuntimes( hProcess, &pEnum); if (hr == HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) { Console::WriteLine(" ... no CLR"); continue; } if (hr != S_OK) { Console::WriteLine(" ... EnumerateLoadedRuntimes(0x{0:x}) failed: 0x{1:x}", (int)hProcess, hr); continue; } Console::WriteLine(); for (;;) { IUnknown * pRuntime; ULONG cRuntimes; hr = pEnum->Next(1, &pRuntime, &cRuntimes); if (hr == S_FALSE) { // No more runtimes anymore Console::WriteLine(" Next() returned S_FALSE"); break; } if (hr != S_OK) { Console::WriteLine(" Next() failed: 0x{0:x}", hr); break; } if (cRuntimes != 1) { Console::WriteLine(" Next() returned {0} runtimes", cRuntimes); continue; } ICLRRuntimeInfo * pRuntimeInfo; hr = pRuntime->QueryInterface(IID_ICLRRuntimeInfo, (LPVOID *)&pRuntimeInfo); if (hr != S_OK) { Console::WriteLine(" QI(IID_ICLRRuntimeInfo) failed: 0x{0:x}", hr); continue; } pRuntime->Release(); WCHAR wszVersion[500]; DWORD cchVersion = _countof(wszVersion); pRuntimeInfo->GetVersionString(wszVersion, &cchVersion); printf(" %S\n", wszVersion); pRuntimeInfo->Release(); } } Console::WriteLine(); }
- Marked As Answer byKarel ZikmundMSFT, ModeratorThursday, November 12, 2009 7:52 PM
- Thank you very much for the code sample, Karel.
I find some of the pieces mew to me, for example, the try,catch(System::..) I didn't know we can do that:-)
So thanks, I'll check it out and let you know.
Dan - If exception handling is a new concept for you, I would strongly suggest you to read a book or tutorial on C# and C++ programming.
If you have questions about C#/C++ concepts like exception handling, please use general programming forums. This forum is about more advanced topics - APIs to develop new tools for .NET like compilers, debuggers and profilers - see the sticky post.
Thanks,
-Karel - Exception handling is not a new concept for me, but I didn't realise I could use .net's objects in it. But I understand what you're saying.
Dan


