native C++ 动态调用.NET DLL
关于这个问题找了好多地方,都只有第二种解决办法,可是我要返回一个字符串,没办法,继续找,最后还是在http://blogs.msdn.com/b/msdnforum/archive/2010/07/09/use-clr4-hosting-api-to-invoke-net-assembly-from-native-c.aspx找到了解决的办法,这里记下来备忘。
转发下范例包:https://files.cnblogs.com/files/maintell/Cpp_app_hosts_CLR_4_and_invokes_.NET_assembly_%28CppHostCLR%29.zip
第一种可以返回一个任意变量或者对象,第二种简单点,只能返回整型,具体看代码:
1 // 2 // FUNCTION: RuntimeHostV4Demo1(PCWSTR, PCWSTR) 3 // 4 // PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting 5 // Interfaces to host a .NET runtime, and use the ICorRuntimeHost interface 6 // that was provided in .NET v1.x to load a .NET assembly and invoke its 7 // type. 8 // 9 // If the .NET runtime specified by the pszVersion parameter cannot be 10 // loaded into the current process, the function prints ".NET runtime <the 11 // runtime version> cannot be loaded", and return. 12 // 13 // If the .NET runtime is successfully loaded, the function loads the 14 // assembly identified by the pszAssemblyName parameter. Next, the function 15 // instantiates the class (pszClassName) in the assembly, calls its 16 // ToString() member method, and print the result. Last, the demo invokes 17 // the public static function 'int GetStringLength(string str)' of the class 18 // and print the result too. 19 // 20 // PARAMETERS: 21 // * pszVersion - The desired DOTNETFX version, in the format “vX.X.XXXXX”. 22 // The parameter must not be NULL. It’s important to note that this 23 // parameter should match exactly the directory names for each version of 24 // the framework, under C:\Windows\Microsoft.NET\Framework[64]. The 25 // current possible values are "v1.0.3705", "v1.1.4322", "v2.0.50727" and 26 // "v4.0.30319". Also, note that the “v” prefix is mandatory. 27 // * pszAssemblyName - The display name of the assembly to be loaded, such 28 // as "CSClassLibrary". The ".DLL" file extension is not appended. 29 // * pszClassName - The name of the Type that defines the method to invoke. 30 // 31 // RETURN VALUE: HRESULT of the demo. 32 // 33 HRESULT RuntimeHostV4Demo1(PCWSTR pszVersion, PCWSTR pszAssemblyName, 34 PCWSTR pszClassName) 35 { 36 HRESULT hr; 37 38 ICLRMetaHost *pMetaHost = NULL; 39 ICLRRuntimeInfo *pRuntimeInfo = NULL; 40 41 // ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces 42 // supported by CLR 4.0. Here we demo the ICorRuntimeHost interface that 43 // was provided in .NET v1.x, and is compatible with all .NET Frameworks. 44 ICorRuntimeHost *pCorRuntimeHost = NULL; 45 46 IUnknownPtr spAppDomainThunk = NULL; 47 _AppDomainPtr spDefaultAppDomain = NULL; 48 49 // The .NET assembly to load. 50 bstr_t bstrAssemblyName(pszAssemblyName); 51 _AssemblyPtr spAssembly = NULL; 52 53 // The .NET class to instantiate. 54 bstr_t bstrClassName(pszClassName); 55 _TypePtr spType = NULL; 56 variant_t vtObject; 57 variant_t vtEmpty; 58 59 // The static method in the .NET class to invoke. 60 bstr_t bstrStaticMethodName(L"GetStringLength"); 61 SAFEARRAY *psaStaticMethodArgs = NULL; 62 variant_t vtStringArg(L"HelloWorld"); 63 variant_t vtLengthRet; 64 65 // The instance method in the .NET class to invoke. 66 bstr_t bstrMethodName(L"ToString"); 67 SAFEARRAY *psaMethodArgs = NULL; 68 variant_t vtStringRet; 69 70 // 71 // Load and start the .NET runtime. 72 // 73 74 wprintf(L"Load and start the .NET runtime %s \n", pszVersion); 75 76 hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); 77 if (FAILED(hr)) 78 { 79 wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr); 80 goto Cleanup; 81 } 82 83 // Get the ICLRRuntimeInfo corresponding to a particular CLR version. It 84 // supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE. 85 hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo)); 86 if (FAILED(hr)) 87 { 88 wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr); 89 goto Cleanup; 90 } 91 92 // Check if the specified runtime can be loaded into the process. This 93 // method will take into account other runtimes that may already be 94 // loaded into the process and set pbLoadable to TRUE if this runtime can 95 // be loaded in an in-process side-by-side fashion. 96 BOOL fLoadable; 97 hr = pRuntimeInfo->IsLoadable(&fLoadable); 98 if (FAILED(hr)) 99 { 100 wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr); 101 goto Cleanup; 102 } 103 104 if (!fLoadable) 105 { 106 wprintf(L".NET runtime %s cannot be loaded\n", pszVersion); 107 goto Cleanup; 108 } 109 110 // Load the CLR into the current process and return a runtime interface 111 // pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting 112 // interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost 113 // interface that was provided in .NET v1.x, and is compatible with all 114 // .NET Frameworks. 115 hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, 116 IID_PPV_ARGS(&pCorRuntimeHost)); 117 if (FAILED(hr)) 118 { 119 wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr); 120 goto Cleanup; 121 } 122 123 // Start the CLR. 124 hr = pCorRuntimeHost->Start(); 125 if (FAILED(hr)) 126 { 127 wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr); 128 goto Cleanup; 129 } 130 131 // 132 // Load the NET assembly. Call the static method GetStringLength of the 133 // class CSSimpleObject. Instantiate the class CSSimpleObject and call 134 // its instance method ToString. 135 // 136 137 // The following C++ code does the same thing as this C# code: 138 // 139 // Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName); 140 // object length = type.InvokeMember("GetStringLength", 141 // BindingFlags.InvokeMethod | BindingFlags.Static | 142 // BindingFlags.Public, null, null, new object[] { "HelloWorld" }); 143 // object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject"); 144 // object str = type.InvokeMember("ToString", 145 // BindingFlags.InvokeMethod | BindingFlags.Instance | 146 // BindingFlags.Public, null, obj, new object[] { }); 147 148 // Get a pointer to the default AppDomain in the CLR. 149 hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk); 150 if (FAILED(hr)) 151 { 152 wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr); 153 goto Cleanup; 154 } 155 156 hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain)); 157 if (FAILED(hr)) 158 { 159 wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr); 160 goto Cleanup; 161 } 162 163 // Load the .NET assembly. 164 wprintf(L"Load the assembly %s\n", pszAssemblyName); 165 hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly); 166 if (FAILED(hr)) 167 { 168 wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr); 169 goto Cleanup; 170 } 171 172 // Get the Type of CSSimpleObject. 173 hr = spAssembly->GetType_2(bstrClassName, &spType); 174 if (FAILED(hr)) 175 { 176 wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr); 177 goto Cleanup; 178 } 179 180 // Call the static method of the class: 181 // public static int GetStringLength(string str); 182 183 // Create a safe array to contain the arguments of the method. The safe 184 // array must be created with vt = VT_VARIANT because .NET reflection 185 // expects an array of Object - VT_VARIANT. There is only one argument, 186 // so cElements = 1. 187 psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1); 188 LONG index = 0; 189 hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg); 190 if (FAILED(hr)) 191 { 192 wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr); 193 goto Cleanup; 194 } 195 196 // Invoke the "GetStringLength" method from the Type interface. 197 hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>( 198 BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public), 199 NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet); 200 if (FAILED(hr)) 201 { 202 wprintf(L"Failed to invoke GetStringLength w/hr 0x%08lx\n", hr); 203 goto Cleanup; 204 } 205 206 // Print the call result of the static method. 207 wprintf(L"Call %s.%s(\"%s\") => %s\n", 208 static_cast<PCWSTR>(bstrClassName), 209 static_cast<PCWSTR>(bstrStaticMethodName), 210 static_cast<PCWSTR>(vtStringArg.bstrVal), 211 vtLengthRet.lVal); 212 213 // Instantiate the class. 214 hr = spAssembly->CreateInstance(bstrClassName, &vtObject); 215 if (FAILED(hr)) 216 { 217 wprintf(L"Assembly::CreateInstance failed w/hr 0x%08lx\n", hr); 218 goto Cleanup; 219 } 220 221 // Call the instance method of the class. 222 // public string ToString(); 223 224 // Create a safe array to contain the arguments of the method. 225 psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0); 226 227 // Invoke the "ToString" method from the Type interface. 228 hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>( 229 BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public), 230 NULL, vtObject, psaMethodArgs, &vtStringRet); 231 if (FAILED(hr)) 232 { 233 wprintf(L"Failed to invoke ToString w/hr 0x%08lx\n", hr); 234 goto Cleanup; 235 } 236 237 // Print the call result of the method. 238 wprintf(L"Call %s.%s() => %s\n", 239 static_cast<PCWSTR>(bstrClassName), 240 static_cast<PCWSTR>(bstrMethodName), 241 static_cast<PCWSTR>(vtStringRet.bstrVal)); 242 243 Cleanup: 244 245 if (pMetaHost) 246 { 247 pMetaHost->Release(); 248 pMetaHost = NULL; 249 } 250 if (pRuntimeInfo) 251 { 252 pRuntimeInfo->Release(); 253 pRuntimeInfo = NULL; 254 } 255 if (pCorRuntimeHost) 256 { 257 // Please note that after a call to Stop, the CLR cannot be 258 // reinitialized into the same process. This step is usually not 259 // necessary. You can leave the .NET runtime loaded in your process. 260 //wprintf(L"Stop the .NET runtime\n"); 261 //pCorRuntimeHost->Stop(); 262 263 pCorRuntimeHost->Release(); 264 pCorRuntimeHost = NULL; 265 } 266 267 if (psaStaticMethodArgs) 268 { 269 SafeArrayDestroy(psaStaticMethodArgs); 270 psaStaticMethodArgs = NULL; 271 } 272 if (psaMethodArgs) 273 { 274 SafeArrayDestroy(psaMethodArgs); 275 psaMethodArgs = NULL; 276 } 277 278 return hr; 279 }
第二种比较简单:
1 // 2 // FUNCTION: RuntimeHostV4Demo2(PCWSTR, PCWSTR) 3 // 4 // PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting 5 // Interfaces to host a .NET runtime, and use the ICLRRuntimeHost interface 6 // that was provided in .NET v2.0 to load a .NET assembly and invoke its 7 // type. Because ICLRRuntimeHost is not compatible with .NET runtime v1.x, 8 // the requested runtime must not be v1.x. 9 // 10 // If the .NET runtime specified by the pszVersion parameter cannot be 11 // loaded into the current process, the function prints ".NET runtime <the 12 // runtime version> cannot be loaded", and return. 13 // 14 // If the .NET runtime is successfully loaded, the function loads the 15 // assembly identified by the pszAssemblyName parameter. Next, the function 16 // invokes the public static function 'int GetStringLength(string str)' of 17 // the class and print the result. 18 // 19 // PARAMETERS: 20 // * pszVersion - The desired DOTNETFX version, in the format “vX.X.XXXXX”. 21 // The parameter must not be NULL. It’s important to note that this 22 // parameter should match exactly the directory names for each version of 23 // the framework, under C:\Windows\Microsoft.NET\Framework[64]. Because 24 // the ICLRRuntimeHost interface does not support the .NET v1.x runtimes, 25 // the current possible values of the parameter are "v2.0.50727" and 26 // "v4.0.30319". Also, note that the “v” prefix is mandatory. 27 // * pszAssemblyPath - The path to the Assembly to be loaded. 28 // * pszClassName - The name of the Type that defines the method to invoke. 29 // 30 // RETURN VALUE: HRESULT of the demo. 31 // 32 HRESULT RuntimeHostV4Demo2(PCWSTR pszVersion, PCWSTR pszAssemblyPath, 33 PCWSTR pszClassName) 34 { 35 HRESULT hr; 36 37 ICLRMetaHost *pMetaHost = NULL; 38 ICLRRuntimeInfo *pRuntimeInfo = NULL; 39 40 // ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces 41 // supported by CLR 4.0. Here we demo the ICLRRuntimeHost interface that 42 // was provided in .NET v2.0 to support CLR 2.0 new features. 43 // ICLRRuntimeHost does not support loading the .NET v1.x runtimes. 44 ICLRRuntimeHost *pClrRuntimeHost = NULL; 45 46 // The static method in the .NET class to invoke. 47 PCWSTR pszStaticMethodName = L"GetStringLength"; 48 PCWSTR pszStringArg = L"HelloWorld"; 49 DWORD dwLengthRet; 50 51 // 52 // Load and start the .NET runtime. 53 // 54 55 wprintf(L"Load and start the .NET runtime %s \n", pszVersion); 56 57 hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); 58 if (FAILED(hr)) 59 { 60 wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr); 61 goto Cleanup; 62 } 63 64 // Get the ICLRRuntimeInfo corresponding to a particular CLR version. It 65 // supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE. 66 hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo)); 67 if (FAILED(hr)) 68 { 69 wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr); 70 goto Cleanup; 71 } 72 73 // Check if the specified runtime can be loaded into the process. This 74 // method will take into account other runtimes that may already be 75 // loaded into the process and set pbLoadable to TRUE if this runtime can 76 // be loaded in an in-process side-by-side fashion. 77 BOOL fLoadable; 78 hr = pRuntimeInfo->IsLoadable(&fLoadable); 79 if (FAILED(hr)) 80 { 81 wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr); 82 goto Cleanup; 83 } 84 85 if (!fLoadable) 86 { 87 wprintf(L".NET runtime %s cannot be loaded\n", pszVersion); 88 goto Cleanup; 89 } 90 91 // Load the CLR into the current process and return a runtime interface 92 // pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting 93 // interfaces supported by CLR 4.0. Here we demo the ICLRRuntimeHost 94 // interface that was provided in .NET v2.0 to support CLR 2.0 new 95 // features. ICLRRuntimeHost does not support loading the .NET v1.x 96 // runtimes. 97 hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, 98 IID_PPV_ARGS(&pClrRuntimeHost)); 99 if (FAILED(hr)) 100 { 101 wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr); 102 goto Cleanup; 103 } 104 105 // Start the CLR. 106 hr = pClrRuntimeHost->Start(); 107 if (FAILED(hr)) 108 { 109 wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr); 110 goto Cleanup; 111 } 112 113 // 114 // Load the NET assembly and call the static method GetStringLength of 115 // the type CSSimpleObject in the assembly. 116 // 117 118 wprintf(L"Load the assembly %s\n", pszAssemblyPath); 119 120 // The invoked method of ExecuteInDefaultAppDomain must have the 121 // following signature: static int pwzMethodName (String pwzArgument) 122 // where pwzMethodName represents the name of the invoked method, and 123 // pwzArgument represents the string value passed as a parameter to that 124 // method. If the HRESULT return value of ExecuteInDefaultAppDomain is 125 // set to S_OK, pReturnValue is set to the integer value returned by the 126 // invoked method. Otherwise, pReturnValue is not set. 127 hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath, 128 pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet); 129 if (FAILED(hr)) 130 { 131 wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr); 132 goto Cleanup; 133 } 134 135 // Print the call result of the static method. 136 wprintf(L"Call %s.%s(\"%s\") => %d\n", pszClassName, pszStaticMethodName, 137 pszStringArg, dwLengthRet); 138 139 Cleanup: 140 141 if (pMetaHost) 142 { 143 pMetaHost->Release(); 144 pMetaHost = NULL; 145 } 146 if (pRuntimeInfo) 147 { 148 pRuntimeInfo->Release(); 149 pRuntimeInfo = NULL; 150 } 151 if (pClrRuntimeHost) 152 { 153 // Please note that after a call to Stop, the CLR cannot be 154 // reinitialized into the same process. This step is usually not 155 // necessary. You can leave the .NET runtime loaded in your process. 156 //wprintf(L"Stop the .NET runtime\n"); 157 //pClrRuntimeHost->Stop(); 158 159 pClrRuntimeHost->Release(); 160 pClrRuntimeHost = NULL; 161 } 162 163 return hr; 164 }