/* ====== x86 (32 bits) EXE: 1/ Run the following bat file: "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 2/ Then compile it using c:\> cl.exe loadAssemblyFromMemory.cpp /o loadAssemblyFromMemory.exe ====== x64 (64 bits) EXE: 1/ Run the following bat file: "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" 2/ Then compile it using c:\> cl.exe loadAssemblyFromMemory.cpp /o loadAssemblyFromMemory.exe */ #pragma region Includes and Imports #include #pragma comment(lib, "mscoree.lib") // Import mscorlib.tlb (Microsoft Common Language Runtime Class Library). #import raw_interfaces_only \ high_property_prefixes("_get","_put","_putref") \ rename("ReportEvent", "InteropServices_ReportEvent") \ rename("or", "InteropServices_or") using namespace mscorlib; #pragma endregion // This is the TestAssembly.dll managed DLL represented as an array of bytes. // Use the following PowerShell snippet to convert the dll file to the expect hex array and get the assembly size: /* $Bytes = Get-Content .\TestAssembly.dll -Encoding Byte $Bytes.Length $HexString = [System.Text.StringBuilder]::new($Bytes.Length * 4) ForEach($byte in $Bytes) { $HexString.AppendFormat("\x{0:x2}", $byte) | Out-Null } $HexString.ToString() */ unsigned char assemblyDLL[] = "\x4d\x5a[...]"; const unsigned int assemblyDLL_len = 3072; int main() { //------------------------------------ // Code from https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0 //------------------------------------ HRESULT hr; ICLRRuntimeInfo* pRuntimeInfo = NULL; ICorRuntimeHost* pCorRuntimeHost = NULL; //---------------------------------------------------------------------- // Load the CLR runtime ICLRMetaHost* pMetaHost = NULL; hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); if (FAILED(hr)) { return -1; } // Get the ICLRRuntimeInfo corresponding to a particular CLR version. It // supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE. hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo)); if (FAILED(hr)) { return -1; } // Check if the specified runtime can be loaded into the process. This // method will take into account other runtimes that may already be // loaded into the process and set pbLoadable to TRUE if this runtime can // be loaded in an in-process side-by-side fashion. BOOL fLoadable; hr = pRuntimeInfo->IsLoadable(&fLoadable); if (FAILED(hr)) { return -1; } if (!fLoadable) { return -1; } // Load the CLR into the current process and return a runtime interface // pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting // interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost // interface that was provided in .NET v1.x, and is compatible with all // .NET Frameworks. hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(&pCorRuntimeHost)); if (FAILED(hr)) { return -1; } //---------------------------------------------------------------------- // Start the CLR hr = pCorRuntimeHost->Start(); if (FAILED(hr)) { return -1; } //---------------------------------------------------------------------- // Get the default AppDomain for this Runtime host IUnknownPtr spAppDomainThunk = NULL; _AppDomainPtr spDefaultAppDomain = NULL; // Get a pointer to the default AppDomain in the CLR. hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk); if (FAILED(hr)) { return -1; } hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain)); if (FAILED(hr)) { return -1; } //---------------------------------------------------------------------- // Load the assembly from memory, declared as an unsigned char array SAFEARRAYBOUND bounds[1]; bounds[0].cElements = assemblyDLL_len; bounds[0].lLbound = 0; SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds); SafeArrayLock(arr); memcpy(arr->pvData, assemblyDLL, assemblyDLL_len); SafeArrayUnlock(arr); _AssemblyPtr spAssembly = NULL; hr = spDefaultAppDomain->Load_3(arr, &spAssembly); if (FAILED(hr)) { return -1; } // Get the Type (ie: Namespace and Class type) to be instanciated from the assembly bstr_t bstrClassName("TestNamespace.TestClass"); _TypePtr spType = NULL; hr = spAssembly->GetType_2(bstrClassName, &spType); if (FAILED(hr)) { return -1; } //---------------------------------------------------------------------- // Finally, invoke the method passing it some arguments as a single string bstr_t bstrStaticMethodName(L"EntryPoint"); SAFEARRAY* psaStaticMethodArgs = NULL; variant_t vtStringArg(L"these|are|arguments|passed|to|the|dotNet|method"); variant_t vtPSEntryPointReturnVal; variant_t vtEmpty; psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1); LONG index = 0; hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg); if (FAILED(hr)) { return -1; } // Invoke the method from the Type interface. hr = spType->InvokeMember_3( bstrStaticMethodName, static_cast(BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public), NULL, vtEmpty, psaStaticMethodArgs, &vtPSEntryPointReturnVal); if (FAILED(hr)) { return -1; } SafeArrayDestroy(psaStaticMethodArgs); psaStaticMethodArgs = NULL; }