2011-12-06 54 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#圖層。上面附加了其他信息。 –