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
Comments
Anonymous
July 08, 2005
Great post! Thanks for making this very useful information available. The #defines for RUNTIME_INFO_UPGRADE_VERSION, RUNTIME_INFO_UPGRADE_VERSION and RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG that are passed as the runtimeInfoFlags parameter don't seem to be defined in any of the headers in DevStudio 2003, DevStudio 2005 Beta 2 or current SDKs.Anonymous
July 08, 2005
The comment has been removedAnonymous
July 09, 2005
Great information.
How about an API in next versions of .NET about those version ( reflection, any other code)? Why MUST all this code be written in C++ and not in C# ? ( or any other .NET language )?Anonymous
July 11, 2005
John - I just did a search for RUNTIME_INFO_UPGRADE_VERSION in my Visual Studio 2005 Beta 2 folder, and I found it in SDKv2.0includemscoree.h. Maybe you should look in the .NET SDK folder instead of the Platform SDK folder?
Andrei - personally, I find these functions much more useful in the unmanaged C++ case than in the C# case. If you're in C# already, then the CLR is already loaded and you can't really do anything about that. From C++, you can make an intelligent decision based on the versions that are installed. I have to do just that, although I'm not sure if I want to rely on the above code though since I'll need to do the same sort of thing even if .NET v2.0 isn't installed, which doesn't have this ability.
You can get the current version of the CLR that is running from C#, just call System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion(). If Microsoft is going to expose the above functionality from within .NET, it would probably be in the RuntimeEnvironment class, which appears to be a wrapper for the unmanaged API exposed by mscoree.dll.Anonymous
July 18, 2005
How about also expanding this code to include displaying information on service packs and hotfixes. The lack of a "CLR status utility" is disappointing and we had to write our own using variations of the above code.Anonymous
July 30, 2005
I found an interesting post that lists some sample code to enumerate the installed versions of the .NET...Anonymous
January 21, 2009
PingBack from http://www.keyongtech.com/426224-determining-framework-versionAnonymous
May 29, 2009
I previously posted some sample code to detect the version(s) and service pack levels of the .NET Framework