As part of this personal project I am currently working on, I need to reuse a C# component I wrote a few years back. I think I have blogged about this topic in the past on my old blog, but here I will show you how to check whether the CLR requires to be loaded beforehand, as well as a convenient way to “warm up” .NET code for execution.
////////////////////////////
// Check if CLR is loaded //
////////////////////////////
BOOL MyClass::CheckIfClrIsLoaded(const CComPtr<IEnumUnknown>& pEnumerator) {
ULONG fetched = 0;
DWORD bufferSize;
auto retval = FALSE;
wchar_t buffer[MAX_PATH];
CComPtr<ICLRRuntimeInfo> pRuntimeInfo;
while (SUCCEEDED(pEnumerator->Next(1, (IUnknown **)&pRuntimeInfo, &fetched)) && fetched > 0) {
if ((SUCCEEDED(pRuntimeInfo->GetVersionString(buffer, &bufferSize))))
if (wcscmp(buffer, L"v4.0.30319") == 0) {
retval = TRUE;
break;
}
}
return retval;
}
///////////////////////////////////////////////////////////////////////
// Load, Initialize CLR and load dependant assemblies into AppDomain //
///////////////////////////////////////////////////////////////////////
void MyClass::InitializeClr() {
CoInitialize(NULL);
HANDLE hProcess;
ExecuteArgs args;
auto isLoaded = FALSE;
CComPtr<ICLRMetaHost> pMetaHost;
CComPtr<ICLRRuntimeInfo> pRuntimeInfo;
CComPtr<ICLRRuntimeHost> pRuntimeHost;
CComPtr<IEnumUnknown> pEnumerator;
args.pwzAssemblyPath = L"MyAssembly.dll";
args.pwzTypeName = L"MyAssembly.MyClass.BusinessRules";
args.pwzMethodName = L"PreLoadData";
args.pwzArgument = L"";
args.pReturnValue = 0;
if ((hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId())) != NULL) {
if (SUCCEEDED(CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost))) {
isLoaded = SUCCEEDED(pMetaHost->EnumerateLoadedRuntimes(hProcess, &pEnumerator)) && CheckIfClrIsLoaded(pEnumerator);
if (!isLoaded && SUCCEEDED(pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimeInfo))) {
if (SUCCEEDED(pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pRuntimeHost))) {
pRuntimeHost->Start();
pRuntimeHost->ExecuteInDefaultAppDomain(args.pwzAssemblyPath, args.pwzTypeName,
args.pwzMethodName, args.pwzArgument, &args.pReturnValue);
}
}
}
}
CloseHandle(hProcess);
CoUninitialize();
}
.NET provides a few interfaces that enable Visual C++ developer to host and use the CLR from native code applications. In order to improve performance and minimize the load time/first execution of .NET code it’s a good practice to load all of the dependencies (assemblies) required by the target library. As you can see I use smart pointers to do all of this because .NET is COM based and when it’s not done properly one can incur in memory leakage. It’s a good thing we got smart pointers
Like I’ve previously mentioned, I needed to reuse an existing .NET assembly, well, it’s a bit of a shocker to find out the number of reference it loads even when I am not using them