2011-12-06 11 views
7

Sto cercando di trovare il metodo di chiamata più performante nel codice Managed .NET dal codice C++ non gestito. Ho trovato informazioni su Hosting .NET all'interno della mia applicazione C++ e sono in grado di creare un pRuntimeHost e avviarlo senza problemi.Il modo migliore per chiamare il codice Managed .NET dal codice Unmanaged

ExecuteInDefaultAppDomain sembra molto limitato poiché desidero davvero inviarlo alcuni parametri e restituire una struttura di informazioni. L'alternativa più ovvia è usare i metodi COM ma l'attuale codice C# non è realmente configurato come interfaccia con i metodi.

In entrambi i casi voglio restituire interi, stringhe (char *) s, doppi e altri tipi di C++ di base. C'è troppo codice su entrambi i lati per convertire il C++ in C# e l'utilizzo di Managed C++ non è una soluzione accettabile, dal momento che gli altri gruppi che utilizzano questo codice C++ non vogliono iniziare a utilizzare il codice gestito per motivi di prestazioni.

L'obiettivo è modificare il codice C++ e C# esistente il meno possibile, ma utilizzare ancora metodi all'interno del codice C# in punti specifici all'interno del C++ senza influire in modo significativo sulla velocità del codice C++.

Basato sul codice trovato su internet l'avvio e l'arresto di sequenza per ospitare 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; 
} 

risposta

5

Sì, sono d'accordo con John. Non si desidera realmente creare una nuova istanza del runtime e ospitarla in modo esplicito. In primo luogo, l'impianto idraulico dietro questo non è ben documentato e potrebbe cambiare nelle versioni future. In secondo luogo, C++/CLI è stato progettato per fare esattamente questo nel modo più efficiente e sicuro.

  1. Scrivere interfacce C++ native che rappresentano la funzionalità .Net richiesta.

  2. Impostare una DLL con supporto CLR che implementa le interfacce native utilizzando le classi umanizzate. All'interno della loro implementazione è possibile creare e accedere ai tipi CLR e memorizzare le variabili di istanza nei campi gcroot<T>. Utilizzare la funzionalità di interoperabilità clr per eseguire il marshalling avanti e indietro tra codice gestito/non gestito, google o bing per marshal_as.

  3. Fornire una funzione di fabbrica (non gestita), che crea un'istanza di questo componente. Questa + l'interfaccia C++ non gestita è l'API che il tuo codice nativo vedrà. Usa la dll esattamente come faresti con una dll non gestita.

+0

La ragione per la creazione di un ulteriore AppDomain è che il codice C++ utilizza già l'AppDomain predefinito per alcune cose e non voglio che i miei assembly .NET aggiuntivi interferiscano con il codice corrente e anche per evitare che la loro roba interferisca con il mio . BTW Sono riuscito a far funzionare perfettamente lo strato CLI, ma sto ancora cercando di capire come ottenere l'intero livello CLI in un AppDomain separato che non è l'AppDomain predefinito. –

+0

Non ho ancora provato a farlo, ma teoricamente questo non dovrebbe essere un problema. Capisco il tuo scenario è quello di chiamare in un componente gestito da uno nativo, giusto? Esiste una funzionalità chiamata "promozione dei thread" (google o bing) che fa sì che un thread nativo venga promosso a uno gestito, ogni volta che tenta di eseguire il codice gestito. Poiché CLR non ha idea in quale appDomain debba essere eseguito il codice gestito chiamato in quel modo, lo inserisce in quello predefinito. Quindi dovrai gestire esplicitamente questa transizione, probabilmente usando la famiglia di funzioni 'msclr :: call_in_appdomain'. –

+0

Ho documentato la mia soluzione finale in questa posizione: http://stackoverflow.com/questions/10301727/marshalling-c-pointer-interface-back-though-c-sharp-function-call-in-a-non-def –

3

Se è accettabile, la soluzione migliore potrebbe essere quella di creare una DLL gestita C++ che si trova nel mezzo . Il codice C++ gestito è il modo migliore/più efficiente per eseguire il bridging del codice gestito e non gestito.

Si proprio non si desidera aggiungere COM nel mix. Ciò rallenterà molto di più.

Inoltre, sono questi "motivi di prestazioni" per evitare che il codice gestito venga effettivamente quantificato? Sembra un po 'come un aneddoto buttato fuori per evitare qualcosa che semplicemente non vogliono. Inoltre, è possibile indicare che sono già utilizzando il codice gestito, poiché C# è nel mix.

+0

La prestazione viene conteggiata in microsecondi. Quindi, dopo tutto ciò ho aggiunto il caching della richiesta/risposta come una C++ std :: map. Ciò consente una rapida ricerca di qualsiasi cosa precedentemente richiesta senza tornare al livello C#. Ho allegato ulteriori informazioni sopra. –

Problemi correlati