Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining when .NET is about to be loaded in (unmanaged) C++

Tags:

c++

.net

[ this is getting TLDR...sorry... ]

I work on a huge (mostly) C++/MFC application with hundreds of DLLs; it supports a dynamically-loaded "add in" mechanism via COM, thus add-ins can be developed in .NET by using COM interop. Some limited new functionality is developed in .NET w/o using this "add-in" mechanism (although still dynamically loaded); however, the end user can decide to not use this feature. Thus, .NET might not be loaded at startup.

But, when .NET is loaded, I need to do some .NET-specific initialzation (specifically setting CurrentUICulture to match the native/unmanaged UI).

One solution is to simply punt and do this .NET initialization when the code gets around to loading either the new .NET functionaliy or COM add-ins. Given the nature of this applicaton, it is probably a 95+% solution (most users will be using the new functionality).

But it's not foolproof. Somebody could "readily" add new .NET functionality at any time by building a module with the /clr flag (remember, this is a huge application).

One more robust (and obvious) solution is simply cause .NET to be loaded at startup via C++/CLI. But some of the die-hard C++ developers to whom every byte and clock cycle matter don't want to do this; somewhat understandable as there's no need to set CurrentUICulture unless/until .NET is loaded.

Another possibility I thought of is to hook LoadLibrary and look for mscorlib. Now I know .NET is about to be loaded for some reason, load it in the normal manner and do the initialization before the other code does anything. But hooking LoadLibrary (or anything else, for that matter) really isn't something I want to do.

So, is there an easy(ier)/better way to tell if .NET about to be loaded?

Edit: Reed's answer of LockClrVersion is pretty darn close. The only hiccup is that it won't work if you link in a mixed-mode DLL/assembly.

// ClrAboutToLoad.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <MSCorEE.h>

// http://community.bartdesmet.net/blogs/bart/archive/2005/07/22/2882.aspx
FLockClrVersionCallback begin_init, end_init;
STDAPI hostCallback()
{
    printf("hostCallback()\n");

    // we're in control; notify the shim to grant us the exclusive initialization right
    begin_init();

    ICLRRuntimeHost *pHost = NULL;
    HRESULT hr = CorBindToRuntimeEx(NULL, L"wks", STARTUP_SERVER_GC, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*) &pHost);
    hr = pHost->Start();

    // mission completed; tell the shim we're ready
    end_init();

    return S_OK;
}

int _tmain(int argc, _TCHAR* argv[])
{
    LockClrVersion(&hostCallback, &begin_init, &end_init);

    //fnTheDLL();
    HMODULE hModule = LoadLibrary(L"TheDLL");
    FARPROC fp = GetProcAddress(hModule, "fnTheDLL");
    typedef void (*fnTheDLL_t)();
    fnTheDLL_t fnTheDLL = reinterpret_cast<fnTheDLL_t>(fp);
    fnTheDLL();
    FreeLibrary(hModule);

    return 0;
}
like image 245
Ðаn Avatar asked Oct 28 '09 21:10

Ðаn


3 Answers

I believe you can do this by having your process call the unmanaged LockClrVersion function prior to using any managed APIs. This function allows you to specify two FLockClrVersion callback methods.

The first method (pBeginHostSetup), is called prior to the CLR being initialized the first time for the hosting process. The second method (pEndHostSetup) is called when initialization of the CLR is complete.

This should allow you to specify unmanaged code that is run just prior and just after CLR initialization. In your case, you probably need to hook into the pEndHostSetup to call your managed API setup routines (you'll need to wait until the CLR is hosted successfully).

like image 185
Reed Copsey Avatar answered Oct 20 '22 11:10

Reed Copsey


You can run your own CLR host to load assemblies. Then you can make more specific decisions about which version of the runtime (1.1?2.0?4.0?) to load, and separate managed code from your code so when a plugin crash, your code won't go down with it.

like image 1
Sheng Jiang 蒋晟 Avatar answered Oct 20 '22 10:10

Sheng Jiang 蒋晟


There are two things that might help you: Performance Counters and WMI. The .NET CLR integrates with both, and you should be able to interact with either (WMI would probably be best) to watch for AppDomain startup. However, neither of these tools is particularly ideal, and in terms of overhead, doing what Sheng Jiang suggested, hosting the CLR on your own, would probably be more efficient (and thus more pleasing to your "byte counters".)

On a slightly different note...if you have any measure of influence and control over the development team, I would reign in the "byte counters" a bit. One of the biggest misconceptions about .NET is that it is less efficient than C++. That old fallacy needs to be buried, as .NET can be amazingly efficient, and when it is used correctly, more efficient than C++ at times. Beyond the baseline efficiency of either platform, you have to ask the question: how much efficiency will I really gain by spending unknown volumes of hours optimizing the finest grain, when statistically, its larger scale things that usually kill performance: cross-process calls (i.e. Calling COM objects from C++ vs. calling .NET objects from .NET), invoking remote processes (i.e. Web services, RPC, etc.), calling into a database, etc.

You can try to solve your .NET AppDomain startup problem to appease the byte-counters misconceptions, or you can implement a .NET system properly, and avoid the marshaling and inter-op hit that is going to be massively more draining than any amount of byte-level performance tuning could counteract.

like image 1
jrista Avatar answered Oct 20 '22 10:10

jrista