Цель заключается в следующем: вызов методов библиотеки .NET из неизменного c ++ с использованием позднего связывания без использования COM и импорта библиотеки tlb.
У меня есть тривиальный класс, написанный на c # (только для теста), помещенный в библиотеку классов (CSLib), скомпилированную с фреймворком 4.0
открытый класс MyCSObject
{
string _str = «Test»;
public int GetString(out string str)
{
str = _str;
return 0;
}
public int PutString(string str)
{
_str = str;
return 0;
}
}
Я создал тривиальное приложение cpp, которое я перечислил здесь:
#include "stdafx.h"#include <stdio.h>
#include <windows.h>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
// Import mscorlib.tlb (Microsoft Common Language Runtime Class Library).
#import "mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent")
using namespace mscorlib;
#pragma endregion
int main()
{
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
// 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.
ICorRuntimeHost *pCorRuntimeHost = NULL;
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// The .NET assembly to load.
bstr_t bstrAssemblyName(L"CSLib");
_AssemblyPtr spAssembly = NULL;
// The .NET class to instantiate.
bstr_t bstrClassName(L"CSLib.MyCSObject" );
_TypePtr spType = NULL;
variant_t vtObject;
variant_t vtEmpty;
// The instance method in the .NET class to invoke.
bstr_t bstrMethodNameGet(L"GetString");
bstr_t bstrMethodNamePut(L"PutString");
bstr_t bstrparam(L"Helloworld");
variant_t vtRet;
SAFEARRAY *psaMethodArgs = NULL;
variant_t vtStringRet;
//
// Load and start the .NET runtime.
//
wprintf(L"Load and start the .NET runtime %s \n", L"v4.0.30319");
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost,
IID_PPV_ARGS(&pCorRuntimeHost));
hr = pCorRuntimeHost->Start();
// Get a pointer to the default AppDomain in the CLR.
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));// Load the .NET assembly.
hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
// Get the Type of CSSimpleObject.
hr = spAssembly->GetType_2(bstrClassName, &spType);
// Instantiate the class.
hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
// Call the instance method of the class.
// public string ToString();
// Create a safe array to contain the arguments of the method.
psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
VARIANT vtpar;
vtpar.vt = VT_BSTR;
vtpar.bstrVal = bstrparam;
long r = 0;
hr = SafeArrayPutElement(psaMethodArgs, &r, &vtpar);
// Invoke the "ToString" method from the Type interface.
hr = spType->InvokeMember(bstrMethodNamePut,
static_cast<BindingFlags>( BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
NULL,
vtObject,
psaMethodArgs,
NULL,
NULL,
NULL,
&vtRet);BSTR bstr(_T(""));
SAFEARRAY* psaMethodArgsByRef = SafeArrayCreateVector(VT_VARIANT , 0, 1);
VARIANT vtbstr;
vtbstr.vt = VT_BSTR | VT_BYREF;
vtbstr.pbstrVal = &bstr;
r = 0;
hr = SafeArrayPutElement(psaMethodArgsByRef, 0, &vtbstr);
SAFEARRAY* modifiers = NULL;
// VARIANT WITH INSDE VARIANT_BOOL
/*modifiers = SafeArrayCreateVector(VT_VARIANT, 0, 1);
VARIANT vtmod;
vtmod.vt = VT_BOOL;
vtmod.boolVal = VARIANT_TRUE;
hr = SafeArrayPutElement(psaMethodArgsByRef, 0, &vtmod);*/
// VARIANT_BOOL
/*modifiers = SafeArrayCreateVector(VT_BOOL, 0, 1);
VARIANT_BOOL bmod = VARIANT_TRUE;
hr = SafeArrayPutElement(psaMethodArgsByRef, 0, &bmod);*/
hr = spType->InvokeMember(bstrMethodNameGet,
static_cast<BindingFlags>(BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
NULL,
vtObject,
psaMethodArgsByRef,
modifiers,
NULL,
NULL,
&vtRet);return 0;
}
Все работает нормально, если приложение C ++ вызывает методы, которые имеют только входные параметры, такие как «PutString», но не работает, когда оно вызывает методы, которые имеют или ссылаются на параметры, такие как «GetString», из моей библиотеки.
Я думаю, что проблема заключается в методе mscorelibrary InvokeMethod
virtual HRESULT __stdcall InvokeMember (
/*[in]*/ BSTR name,
/*[in]*/ enum BindingFlags invokeAttr,
/*[in]*/ struct _Binder * Binder,
/*[in]*/ VARIANT Target,
/*[in]*/ SAFEARRAY * args,
/*[in]*/ SAFEARRAY * modifiers,
/*[in]*/ struct _CultureInfo * culture,
/*[in]*/ SAFEARRAY * namedParameters,
/*[out,retval]*/ VARIANT * pRetVal ) = 0;
и, в частности, в параметре модификаторов SAFEARRAY: я понятия не имею, чтобы установить этот параметр, я пробую другое решение (например, которое я прокомментировал в своем коде), и я не нашел никакой документации по этому поводу, когда я вызываю этот метод из неуправляемого c ++.
Задача ещё не решена.
Других решений пока нет …