De beste manier om Beheerde .NET-code te bellen vanuit Unmanaged-code

Ik probeer de best presterende methode te vinden om in de Managed .NET-code te bellen vanuit Unmanaged C ++ -code. Ik heb informatie over Hosting .NET gevonden in mijn C ++ -toepassing en ik kan een pRuntimeHost maken en zonder problemen starten.

De ExecuteInDefaultAppDomain lijkt erg beperkt omdat ik het echt een paar parameters wil sturen en laat een structuur van informatie retourneren. Het meest voor de hand liggende alternatief is om COM-methoden te gebruiken, maar de huidige C# -code is niet echt ingesteld als interface met methoden.

Hoe dan ook, ik wil gehele getallen, strings (char *) s, doubles en andere C ++ kerntypen retourneren. Er is te veel code aan beide zijden om de C ++ naar C# te converteren en het gebruik van Managed C ++ is geen acceptabele oplossing, omdat de andere groepen die deze C ++ code gebruiken, Managed code om prestatieredenen niet willen gaan gebruiken.

Het doel is de bestaande C ++ - en C# -code zo weinig mogelijk te wijzigen, maar nog steeds methoden in de C# -code te gebruiken op specifieke punten in C ++ zonder de snelheid van de C ++ -code aanzienlijk te beïnvloeden.

Op basis van de code die op internet is gevonden, is de opstart- en afsluitprocedure voor host .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;
}
7

2 antwoord

Ja, ik ben het met John eens. U wilt niet echt een nieuw exemplaar van de runtime maken en dit expliciet hosten. Ten eerste is het sanitair erachter niet goed gedocumenteerd en kan het in toekomstige versies veranderen. Ten tweede is C ++/CLI ontworpen om precies dit op de meest efficiënte en veilige manier te doen.

  1. Write native C++ interfaces which represents the required .Net funcionality.

  2. Set up a dll with CLR support which implements the native interfaces using umanaged classes. Inside of their implementation you can create and access CLR types and store instance variables in gcroot fields. Use the clr interop funcionality to marshal back and forth between managed/unmanaged code, Google or bing for marshal_as.

  3. Provide an (unmanaged) factory function, which creates an instance of this component. This + the unmanaged C++ interface is the API your native code will see. Use the dll exactly the same way you'd use an unmanaged dll.

5
toegevoegd
Ik heb dit nog niet zelf geprobeerd, maar in theorie zou dit geen probleem moeten zijn. Ik begrijp dat je scenario is om een ​​beheerde component van een native component op te roepen, toch? Er is een functie genaamd 'threadpromotie' (google of bing) die ervoor zorgt dat een native thread wordt gepromoveerd naar een beheerde, telkens wanneer deze eerst probeert beheerde code uit te voeren. Omdat de CLR geen idee heeft waarin AppDomain de beheerde code die op die manier wordt aangeroepen, moet uitvoeren, wordt deze in de standaardcode geplaatst. U moet dus expliciet die overgang afhandelen, waarschijnlijk met behulp van de familie van msclr :: call_in_appdomain .
toegevoegd de auteur Paul Michalik, de bron
De reden voor het maken van een extra AppDomain is dat de C ++-code al het standaard AppDomain gebruikt voor sommige dingen en ik wil niet dat mijn extra .NET-assembly's de huidige code verstoren en ook voorkomen dat hun spullen de mijne verstoren. Ik slaagde erin om de CLI-laag perfect te laten werken, maar ik probeer nog steeds uit te zoeken hoe de hele CLI-laag in een afzonderlijke AppDomain kan worden geplaatst die niet de standaard AppDomain is.
toegevoegd de auteur Andrew Stern, de bron
Ik heb mijn definitieve oplossing op deze locatie gedocumenteerd: stackoverflow.com/questions/10301727/…
toegevoegd de auteur Andrew Stern, de bron

Als dit acceptabel is, is de beste oplossing mogelijk om een ​​beheerde C ++ dll te maken die er tussenin zit. Managed C ++ -code is de beste/meest efficiënte manier om beheerde en niet-beheerde code te overbruggen.

Je echt wil COM niet aan de mix toevoegen. Dat zal de dingen veel meer vertragen.

Zijn deze "prestatieredenen" ook om te voorkomen dat beheerde code daadwerkelijk wordt gekwantificeerd? Het klinkt een beetje als een anekdote die wordt uitgegooid om iets te vermijden dat ze gewoon niet willen. U kunt er ook op wijzen dat ze al zijn met beheerde code, omdat C# in de mix zit.

3
toegevoegd
De prestaties worden geteld in microseconden. Dus na dit alles heb ik caching van het verzoek/antwoord toegevoegd als een C ++ std :: map. Hiermee kunt u snel iets opzoeken wat eerder is aangevraagd zonder terug te gaan naar de C# -laag. Ik heb hierboven aanvullende informatie bijgevoegd.
toegevoegd de auteur Andrew Stern, de bron