You may have multiple versions of the .NET framework installed and used on your machine. The framework has two components: the set of assemblies that provide functionalities for your application, and the common language runtime (CLR) that handles the execution of the application. These two components are versioned separately. If you what to see what versions of the framework are installed, you have to query the Windows Registry. If you want to know what versions of the CLR are installed you could either use clrver.exe or do it programmatically. In this article, we will look at this later option and how to do it in C++.
How to: Determine Which .NET Framework Versions Are Installed
To query the installed CLR versions from C++ we have to:
- Create an instance of the ICLRMetaHost COM interface, that provides functionality for enumerating installed and loaded runtimes, get a specific runtime and other runtime operations. For this purpose we have to use the CLRCreateInstance function.
- Call ICLRMetaHost::EnumerateInstalledRuntimes to get an object implementing the IEnumUnknown COM interface that enables enumerating a collection of ICLRRuntimeInfo interface pointers.
- Call ICLRRuntimeInfo::GetVersionString to get CLR version information.
#include <string> #include <iostream> #import <mscorlib.tlb> raw_interfaces_only rename( "value", "value2" ) rename( "ReportEvent", "InteropServices_ReportEvent" ) #include <metahost.h> #pragma comment( lib, "Mscoree" ) _COM_SMARTPTR_TYPEDEF(ICLRMetaHost, IID_ICLRMetaHost); _COM_SMARTPTR_TYPEDEF(ICLRRuntimeInfo, IID_ICLRRuntimeInfo); int main() { ICLRMetaHostPtr pMetaHost{ nullptr }; HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); if (FAILED(hr)) return -1; IEnumUnknownPtr enumptr{ nullptr }; hr = pMetaHost->EnumerateInstalledRuntimes(&enumptr); if (FAILED(hr)) return -1; while (true) { ULONG fetched{ 0 }; IUnknownPtr uptr{ nullptr }; hr = enumptr->Next(1, &uptr, &fetched); if (hr == S_OK) { ICLRRuntimeInfoPtr crlri{ uptr }; DWORD size{ 0 }; hr = crlri->GetVersionString(nullptr, &size); if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) && size > 0) { std::wstring version(size, 0); hr = crlri->GetVersionString(&version.front(), &size); if (SUCCEEDED(hr)) std::wcout << version << std::endl; } } else break; }; return 0; }
In order to call CLRCreateInstance we must include the metahost.h header and link with the Mscoree.lib static library.
To use the ICLRMetaHost and ICLRRuntimeInfo interfaces we must import the mscorlib.tlb type library. The _COM_SMARTPTR_TYPEDEF are used for defining COM smart pointers ICLRMetaHostPtr and ICLRRuntimeInfoPtr that automatically handle the reference counter of the underlying COM object.
The call to the EnumerateInstalledRuntimes method, when successful, returns a pointer to an IEnumUnknown interface. This enables enumerating through a component that contains multiple objects. Its method Next retrieves a specified number of items. In this implementation that number is 1. The return value is a pointer to the IUnknown interface, but what we are enumerating through are actually ICLRRuntimeInfo interfaces.
To retrieve the version info we must use the GetVersionString method of ICLRRuntimeInfo. The arguments are an array of wide characters that will receive the string and the size of the array. In order to retrieve the necessary size of the buffer, we have to first call the method with null for the first argument. In this case the function returns ERROR_INSUFFICIENT_BUFFER as a HRESULT (i.e. HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) and sets the second argument to the necessary size. After allocating the necessary buffer, we call the method again, providing the buffer and its size.
Running this program on my machine prints the following (which is the same as the output from clrver.exe).
v2.0.50727 v4.0.30319