Ask a questionAsk a question
 

Answer.NET 4 process doesn't show up in enumeration

  • Friday, September 18, 2009 1:45 AMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Monday, September 21, 2009 6:44 PMJon LangdonMSFT, OwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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:
    1. Load mscoree.dll
      • If this fails, no runtime is installed
    2. GetProcAddress on GetCLRMetaHost (in Beta2, this API name will change to CLRCreateInstance)
      1. If that succeeds, use the API to get an ICLRMetaHost interface
      2. Loop through the processes on the machine calling ICLRMetaHost::EnumerateLoadedRuntimes for each
      3. 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
    3. If GetProcAddress for GetCLRMetaHost fails, V4 is not installed. Call GetProcAddress on GetVersionFromProcess
      1. If that succeeds, loop through the processes on the machine calling GetVersionFromProcess for each
      2. If one is loaded, it will return the version string (e.g. v1.1.4322 or V2.0.50727)
    4. 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

  • Thursday, November 12, 2009 2:33 AMKarel ZikmundMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code
    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();
    }
    

All Replies

  • Monday, September 21, 2009 6:44 PMJon LangdonMSFT, OwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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:
    1. Load mscoree.dll
      • If this fails, no runtime is installed
    2. GetProcAddress on GetCLRMetaHost (in Beta2, this API name will change to CLRCreateInstance)
      1. If that succeeds, use the API to get an ICLRMetaHost interface
      2. Loop through the processes on the machine calling ICLRMetaHost::EnumerateLoadedRuntimes for each
      3. 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
    3. If GetProcAddress for GetCLRMetaHost fails, V4 is not installed. Call GetProcAddress on GetVersionFromProcess
      1. If that succeeds, loop through the processes on the machine calling GetVersionFromProcess for each
      2. If one is loaded, it will return the version string (e.g. v1.1.4322 or V2.0.50727)
    4. 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

  • Monday, September 21, 2009 11:20 PMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Saturday, October 31, 2009 11:30 PMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Tuesday, November 10, 2009 12:57 AMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Wednesday, November 11, 2009 10:37 PMKarel ZikmundMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Thursday, November 12, 2009 2:33 AMKarel ZikmundMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     AnswerHas Code
    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();
    }
    
  • Thursday, November 12, 2009 4:45 PMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Thursday, November 12, 2009 8:00 PMKarel ZikmundMSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Thursday, November 12, 2009 11:37 PMlpszDan Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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