【问题标题】:Best way to call Managed .NET code from Unmanaged code从非托管代码调用托管 .NET 代码的最佳方式
【发布时间】:2012-01-14 10:30:48
【问题描述】:

我正在尝试找到从非托管 C++ 代码调用托管 .NET 代码的最佳执行方法。我在我的 C++ 应用程序中找到了有关 Hosting .NET 的信息,我能够创建一个 pRuntimeHost 并毫无问题地启动它。

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

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

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

根据在 Internet 上找到的代码,托管 .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;
}

【问题讨论】:

    标签: c# c++ unmanaged managed


    【解决方案1】:

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

    真的不想将 COM 添加到组合中。这会让事情变得更慢。

    另外,这些避免托管代码的“性能原因”是否实际量化了?这听起来有点像是为了避免他们不想要的事情而抛出的轶事。此外,您可以指出他们已经使用托管代码,因为 C# 也在其中。

    【讨论】:

    • 性能以微秒计。因此,在这一切之后,我将请求/响应的缓存添加为 C++ std::map。这允许快速查找以前请求的任何内容,而无需返回 C# 层。我在上面附上了其他信息。
    【解决方案2】:

    是的,我同意约翰的观点。您真的不想创建运行时的新实例并明确托管它。首先,这背后的管道没有很好的记录,并且可能在未来的版本中发生变化。其次,C++/CLI 旨在以最有效和最安全的方式完成此任务。

    1. 编写代表所需 .Net 功能的本机 C++ 接口。

    2. 设置一个支持 CLR 的 dll,它使用非托管类实现本机接口。在它们的实现中,您可以创建和访问 CLR 类型并将实例变量存储在 gcroot&lt;T&gt; 字段中。使用 clr 互操作功能在托管/非托管代码、google 或 bing 之间来回编组marshal_as

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

    【讨论】:

    • 创建附加 AppDomain 的原因是 C++ 代码已经在使用默认 AppDomain 来处理某些内容,我不想让我的附加 .NET 程序集干扰当前代码,并且避免让他们的东西干扰我的。顺便说一句,我设法使 CLI 层完美地工作,但我仍在试图弄清楚如何将整个 CLI 层放入一个单独的 AppDomain 中,该 AppDomain 不是默认的 AppDomain。
    • 我自己还没有尝试过这样做,但理论上这应该不是问题。我了解您的情况是从本机调用托管组件,对吗?有一种称为“线程提升”(google 或 bing)的功能,它会在本机线程第一次尝试执行托管代码时将其提升为托管线程。由于 CLR 不知道以这种方式调用的托管代码应该在哪个 AppDomain 中执行,因此它将其放入默认的 AppDomain 中。因此,您必须明确处理该转换,可能使用msclr::call_in_appdomain 系列函数。
    • 我在这个位置记录了我的最终解决方案:stackoverflow.com/questions/10301727/…
    猜你喜欢
    • 2011-02-28
    • 2010-12-25
    • 2013-08-24
    • 2011-02-05
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 2015-02-24
    相关资源
    最近更新 更多