2011-12-06 99 views
7

我试图从非托管C++代码中调用托管.NET代码的最佳方法。我在我的C++应用程序中发现了有关Hosting .NET的信息,并且我可以创建一个pRuntimeHost并无任何问题地启动它。从非托管代码调用托管.NET代码的最佳方式

ExecuteInDefaultAppDomain似乎非常有限,因为我真的想发送一些参数并让它返回一个信息结构。最明显的选择是使用COM方法,但目前的C#代码并没有真正设置为与方法的接口。

无论哪种方式我想返回整数,字符串(char *)s,双打和其他核心C++类型。双方都有太多的代码将C++转换为C#,并且使用Managed C++不是一个可接受的解决方案,因为使用此C++代码的其他组不希望为了性能原因而开始使用托管代码。

目标是尽可能少地修改现有的C++和C#代码,但仍然使用C++中特定点的C#代码中的方法,而不会对C++代码的速度产生重大影响。

基于互联网的启动和关闭序列来承载.NET上找到的代码为:

#include "stdafx.h" 
#include <metahost.h> 

#pragma comment(lib, "mscoree.lib") 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    ICLRMetaHost  *pMetaHost  = NULL; 
    ICLRMetaHostPolicy *pMetaHostPolicy = NULL; 
    ICLRDebugging  *pCLRDebugging = NULL; 

    HRESULT hr; 
    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost); 
    hr = CLRCreateInstance(CLSID_CLRMetaHostPolicy, IID_ICLRMetaHostPolicy, (LPVOID*)&pMetaHostPolicy); 
    hr = CLRCreateInstance(CLSID_CLRDebugging, IID_ICLRDebugging, (LPVOID*)&pCLRDebugging); 

    DWORD dwVersion = 0; 
    DWORD dwImageVersion = 0; 
    ICLRRuntimeInfo *pRuntimeInfo; 
    hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID *)&pRuntimeInfo); 

    ICLRRuntimeHost * pRuntimeHost = NULL; 
    hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&pRuntimeHost); 

    hr = pRuntimeHost->Start(); 

    DWORD dwRetCode = 0; 
    //hr = pRuntimeHost->ExecuteInDefaultAppDomain(argv[1], L"MyNamespace.MyClass", L"Message", L"Hello World!", &dwRetCode); 

    // Stop the CLR runtime and shutdown cleanly. 
    hr = pRuntimeHost->Stop(); 
    hr = pRuntimeHost->Release(); 
    hr = pRuntimeInfo->Release(); 
    hr = pCLRDebugging->Release(); 
    hr = pMetaHostPolicy->Release(); 
    hr = pMetaHost->Release(); 

    return 0; 
} 

回答

5

是的,我同意约翰。你并不是真的想创建一个新的运行时实例并明确地托管它。首先,这背后的管道并没有很好的文件记录,并可能在未来的版本中改变。其次,C++/CLI被设计为以最有效和最安全的方式完成这件事。

  1. 编写代表所需.Net funcionality的本地C++接口。

  2. 设置一个支持CLR的dll,它使用不受管理的类实现本机接口。在其实现的内部,您可以在gcroot<T>字段中创建和访问CLR类型并存储实例变量。使用clr interop funcionality在托管/非托管代码,谷歌或marshal_as之间来回编组。

  3. 提供一个(非托管)工厂函数,它创建该组件的一个实例。这个+非托管C++接口是您的本机代码将会看到的API。使用dll的方式与使用非托管dll的方式完全相同。

+0

之所以产生额外的AppDomain是,C++代码已经被使用了一些东西默认的AppDomain,我不希望有我的其他.NET程序集与当前的代码干扰,也避免有自己的东西与我的干扰。顺便说一下,我设法使CLI层完美工作,但我仍然试图弄清楚如何将整个CLI层置入单独的不是默认AppDomain的AppDomain。 –

+0

还没有试图自己做,但理论上这应该不成问题。我知道你的方案是从本地调用托管组件,对吧?有一个称为“线程升级”(google或bing)的功能,它会在本机线程首次尝试执行托管代码时,将原生线程提升为托管线程。由于CLR不知道在哪个AppDomain中执行该方式的托管代码,因此它会将其置入默认状态。所以你必须明确地处理这个转换,可能使用'msclr :: call_in_appdomain'函数家族。 –

+0

我已经证明我在这个位置最终解决方案:http://stackoverflow.com/questions/10301727/marshalling-c-pointer-interface-back-though-c-sharp-function-call-in-a-non-def –

3

如果它是可以接受的,最好的解决方案可以是创建托管C++ DLL,它位于在两者之间。托管C++代码是桥接托管代码和非托管代码的最佳/最有效的方式。

真的不想添加COM到混合中。这会让事情更加缓慢。

此外,这些“性能的原因”,以避免托管代码实际量化?这听起来有点像一个轶事,以避免他们不想要的东西。另外,你可以指出他们已经使用托管代码已经,因为C#处于混合状态。

+0

性能以微秒计算。所以,我已经将请求/响应的缓存添加为C++ std :: map。这允许快速查找先前请求的任何内容,而无需返回到C#图层。上面附加了其他信息。 –