Enumerating CLR versions

The following is a sample from the developer who owns mscoree.dll. The sample prints out all the CLR versions installed in the machine.

The code will be shipped in .Net framework SDK as a sample.

 

// This is the function pointer definition for the shim API GetRequestedRuntimeInfoInfo.
// It has existed in mscoree.dll since v1.1, and in v2.0 it was modified to take "runtimeInfoFlags"
// which allow us to get even more information.
typedef HRESULT (STDAPICALLTYPE *PGetRRI)(LPCWSTR pExe,
                                          LPCWSTR pwszVersion,
                                          LPCWSTR pConfigurationFile,
                                          DWORD startupFlags,
                                          DWORD runtimeInfoFlags,
                                          LPWSTR pDirectory,
                                          DWORD dwDirectory,
                                          DWORD *dwDirectoryLength,
                                          LPWSTR pVersion,
                                          DWORD cchBuffer,
                                          DWORD* dwlength);
 
// This is the function pointer defintion for the shim API GetCorVersion.
// It has existed in mscoree.dll since v1.0, and will display the version of the runtime that is currently
// loaded into the process. If a CLR is not loaded into the process, it will load the latest version.
typedef HRESULT (STDAPICALLTYPE *PGetCV)(LPWSTR szBuffer,
                                         DWORD cchBuffer,
                                         DWORD* dwLength);
 
//-------------------------------------------------------------
// PrintAllRuntimes
//
// This prints all of the runtimes installed on the machine
//-------------------------------------------------------------
int PrintAllRuntimes()
{
    BOOL fV10installed = FALSE;     // Is v1.0 installed on the machine
    BOOL fV11installed = FALSE;     // Is v1.1 installed on the machine
    WCHAR wszLatestRuntime[30] = {0}; // Latest runtime on the machine
    DWORD cchLatestRuntime = 0;
    PGetRRI pfnGetRequestedRuntimeInfo = NULL;
    PGetCV pfnGetCorVersion = NULL;
    HMODULE hMscoree = NULL;
    HRESULT hr = S_OK;
 
    // First, if mscoree.dll is not found on the machine, then there aren't any CLRs on the machine
    hMscoree = LoadLibraryA("mscoree.dll");
    if (hMscoree == NULL)
        goto DoneFindingRuntimes;
 
 
    // There were certain OS's that shipped with a "placeholder" mscoree.dll. The existance of this DLL
    // doesn't mean there are CLRs installed on the box.
    //
    // If this mscoree doesn't have an implementation for GetCORVersion, then we know it's one of these
    // placeholder dlls.
    pfnGetCorVersion = (PGetCV)GetProcAddress(hMscoree, "GetCORVersion");
   
    if (pfnGetCorVersion == NULL)
        goto DoneFindingRuntimes;
 
    // Ok, so we now know that the CLR was, at one time, installed on this machine. Let's see what versions
    // of the runtime are on the box.
 
    // v1.0 and v1.1 had an annoying habit of popping up dialogs whenever you asked for runtimes that didn't exist.
    // We'll surpress those dialogs with this statement.
    SetErrorMode(SEM_FAILCRITICALERRORS);
 
    // v1.1 of mscoree shipped with the API GetRequestedRuntimeInfo(). This function will help us with identifying
    // runtimes.
 
    pfnGetRequestedRuntimeInfo = (PGetRRI)GetProcAddress(hMscoree, "GetRequestedRuntimeInfo");
 
    if (pfnGetRequestedRuntimeInfo == NULL)
    {
        // Ok, that API didn't exist. We've got the v1.0 mscoree.dll on the box. We're guaranteed that there isn't
        // a later version of the CLR on the machine, but we're not 100% guaranteed that v1.0 of the CLR is on the
        // box.
 
        // Unfortuately, the only way to verify that v1.0 is on the box is to try and spin up v1.0 of the CLR and
        // see if it works.
 
        WCHAR wszVersion[50];
        DWORD cchVersion = 0;
 
        hr = pfnGetCorVersion(wszVersion, NumItems(wszVersion), &cchVersion);
 
        // If this failed, then either the v1.0 CLR didn't exist on the machine, or, if the buffer wasn't
        // big enough to copy the version information, then something is messed up on the machine (v1.0.3705 should
        // fit in a 50 character buffer)
        if (FAILED(hr))
            goto DoneFindingRuntimes;
 
        // If the returned string is not v1.0.3705, then this machine is messed up
        if (wcscmp(wszVersion, L"v1.0.3705"))
        {
            printf("Installation error on this machine. v1.0 of mscoree.dll is running %S of the CLR.\n", wszVersion);
            goto DoneFindingRuntimes;
        }
 
        // Ok, we've verified that v1.0 is installed.
        fV10installed = TRUE;
        goto DoneFindingRuntimes;
    }
 
    // Ok, we know that, at a minimum, v1.1 of mscoree is installed on the machine. That makes this job much easier.
 
    // This function call will pop up a dialog if these runtimes don't exist on the machine, so make sure you call
    // SetErrorMode(SEM_FAILCRITICALERRORS); as we did up above
   
    WCHAR wszVersion[50]; // The version of the runtime that satisfies the runtime request
    DWORD cchVersion = 0;
    WCHAR wszDirectory[MAX_PATH]; // The top level directory where the runtime is located. Usually is %windir%\microsoft.net\framework
    DWORD cchDirectory = 0;
 
    // Check to see if v1.0 is installed on the machine
   
    hr = pfnGetRequestedRuntimeInfo(NULL, // pExe
                   L"v1.0.3705", // pwszVersion
                   NULL, // ConfigurationFile
                   0, // startupFlags
                   0, // v1.1, this is reserved, in v2.0, runtimeInfoFlags
                   wszDirectory, // pDirectory
                   NumItems(wszDirectory), // dwDirectory
                   &cchDirectory, // dwDirectoryLength
                   wszVersion, // pVersion
                   NumItems(wszVersion), // cchBuffer
                   &cchVersion); // dwlength
 
    if (SUCCEEDED(hr))
        fV10installed = TRUE;
 
 
    // Check to see if v1.1 is installed on the machine
    hr = pfnGetRequestedRuntimeInfo(NULL, // pExe
                   L"v1.1.4322", // pwszVersion
                   NULL, // ConfigurationFile
                   0, // startupFlags
                   0, // v1.1, this is reserved, in v2.0, runtimeInfoFlags
                   wszDirectory, // pDirectory
                   NumItems(wszDirectory), // dwDirectory
                   &cchDirectory, // dwDirectoryLength
                   wszVersion, // pVersion
                   NumItems(wszVersion), // cchBuffer
                   &cchVersion); // dwlength
 
    if (SUCCEEDED(hr))
        fV11installed = TRUE;
 
 
    // The same thing can be done for v2.0 when the final version number of Whidbey is decided upon.
 
 
    // The v2.0 shim allows us to use flags for this function that makes it easier to use. The v1.1 mscoree.dll will
    // not allow us to call this function with 3 NULLs. However, the v2.0 mscoree.dll, along with the RUNTIME_INFO_UPGRADE_VERSION
    // flag, will return us the latest version of the CLR on the machine.
 
    hr = pfnGetRequestedRuntimeInfo(NULL, // pExe
                   NULL, // pwszVersion
                   NULL, // ConfigurationFile
                   0, // startupFlags
                   RUNTIME_INFO_UPGRADE_VERSION|RUNTIME_INFO_DONT_RETURN_DIRECTORY|RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG, // runtimeInfoFlags,
                   NULL, // pDirectory
                   0, // dwDirectory
                   NULL, // dwDirectoryLength
                   wszLatestRuntime, // pVersion
                   NumItems(wszLatestRuntime), // cchBuffer
                   &cchLatestRuntime); // dwlength
 
    // If this fails, then v2.0 of mscoree.dll was not installed on the machine.
     
DoneFindingRuntimes:
 
    printf("Versions installed on the machine:\n");
 
    if (fV10installed)
        printf("v1.0.3705\n");
 
    if (fV11installed)
        printf("v1.1.4322\n");
 
    if (*wszLatestRuntime)
        printf("%S\n", wszLatestRuntime);
 
    // If we didn't find any runtimes
    if (!fV10installed && !fV11installed && !*wszLatestRuntime)
        printf("<none>\n");
 
    return 0;
}// PrintAllRuntimes