Deferring CLR Initialization and Startup


In some scenarios a host might elect to delay the initialization and loading of the CLR until it is actually needed rather than doing it upfront. Loading the CLR lazily can reduce both your startup time and startup working set. However, waiting until later to call CorBindToRuntimeEx opens a window in which a copy of the CLR might get loaded into the process without your knowledge and therefore without an opportunity for you to set the startup options you want.

The classic case in which this can occur is with COM interoperability. If your host doesn't initialize the CLR at startup and some unmanaged code in your process loads a managed type through COM, the activation of that type brings the CLR into the process. Because the host is out of the loop at this point, the default settings chosen by the default CLR host are used (refer to Chapter 4 to see how the default CLR host chooses these settings). This can result in loading a CLR that does not match what the host would have chosen. For example, a different version of the CLR might be loaded or the wrong build type might be chosen.

Fortunately, hosts can use a function on the shim called LockClrVersion to close this window. When you call LockClrVersion, you're essentially stating that you are the only one allowed to initialize the CLR in this process. When the shim receives the first request to run managed code, it will call a function that you provide to enable you to initialize the CLR the way you see fit. In the preceding scenario, the host's function would have been called when the managed type was accessed through the COM interoperability layer, thereby giving the host a chance to initialize and load the CLR.

Here's the definition of LockClrVersion as found in mscoree.h:

typedef HRESULT (__stdcall *FLockClrVersionCallback) (); STDAPI LockClrVersion(FLockClrVersionCallback hostCallback,                       FLockClrVersionCallback *pBeginHostSetup,                       FLockClrVersionCallback *pEndHostSetup);

The parameters to LockClrVersion are summarized in Table 3-4.

Table 3-4. Parameters to LockClrVersion

Parameter

Description

hostCallback

[in] A pointer to a host-supplied function the shim will call when the CLR needs to be initialized

pBeginHostSetup

[out] A pointer to a CLR-supplied function that the host must call just before it begins to initialize the CLR

pEndHostSetup

[out] A pointer to a CLR-supplied function that the host must call after it has initialized the CLR


At first glance, pBeginHostSetup and pEndHostSetup might seem unnecessary. After all, the shim can tell when the CLR has been initialized after the call to hostCallBack returns. The reason these additional parameters are needed is to notify the shim about which thread the initialization is happening on. The shim needs this information mostly for internal implementation reasons relating to the fact that only one thread in the process is allowed to initialize the CLR. The shim also uses the information to block other threads that might enter the process and wish to run managed code from proceeding until the initialization is done.

Listing 3-1 from the sample DeferredStartup.exe shows how to use LockClrVersion to delay the loading of the CLR. In this example, I force the CLR into the process by activating a managed type through the COM Interoperability layer as in the scenario described earlier. When the managed type is activated, the host-supplied function is called to initialize the CLR.

Listing 3-1. Using LockClrVersion to Load the CLR
#include "stdafx.h" #include <mscoree.h> // Declare globals to hold function pointers to call to notify the shim when the // initialization of the CLR is beginning and ending. FLockClrVersionCallback g_beginInit; FLockClrVersionCallback g_endInit; // This function is registered as the host callback provided to the CLR by LockClrVersion. //  The shim will call this function the first time it receives a request to run managed code. STDAPI InitializeCLR() {    // Notify the CLR that initialization is beginning    g_beginInit();    // Initialize the CLR.    ICLRRuntimeHost *pCLR = NULL;     HRESULT hr = CorBindToRuntimeEx(       L"v2.0.41013",       NULL,       NULL,       CLSID_CLRRuntimeHost,       IID_ICLRRuntimeHost,       (PVOID*) &pCLR);    assert(SUCCEEDED(hr));    // Start the CLR.    pCLR->Start(NULL, NULL);    // Notify the CLR that initialization has completed.    g_endInit();    return S_OK; } int main(int argc, char* argv[]) {    HRESULT hr = S_OK;    // Call LockClrVersion so the InitializeCLR always get called to set up the runtime.    LockClrVersion(InitializeCLR, &g_beginInit, &g_endInit);    // Initialize COM and create an instance of a managed type through COM Interop. This    // will require the CLR to be loadedInitializeCLR will be called.    CoInitialize(NULL);    CLSID clsid;    hr = CLSIDFromProgID(L"System.Collections.SortedList", &clsid);    assert(SUCCEEDED(hr));    IUnknown *pUnk = NULL;    hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (LPVOID *) &pUnk);    assert(SUCCEEDED(hr));    pUnk->Release();    CoUninitialize();    return 0; }

LockClrVersion can be used for another, less obvious purposeto prevent the CLR from ever being loaded in a process. The scenarios in which you'd want to do this are clearly limited, but if you had a requirement to prevent managed code from running in your process completely, LockClrVersion is the way to do it. As you've seen, LockClrVersion takes a callback function that gets invoked when the CLR needs to be initialized. If you return a failure HRESULT from this callback, the CLR would not be loaded. In this way, you can prevent the CLR from ever entering your process.

Note

There's another technique you can use to accomplish a subset of what LockClrVersion provides. Recall that CorBindToRuntimeEx has a startupFlags parameter. One of the flags you can pass is called STARTUP_LOADER_SETPREFERENCE. When you pass this flag to CorBindToRuntimeEx, the startup shim does not initialize the CLR, but rather just remembers the version number you've passed through the pwszVersion parameter. Later, when the CLR needs to be initializedeither implicitly (as happens when a managed type is created from COM) or explicitly by another call to CorBindToRuntimeEx that does not set STARTUP_LOADER_SETPREFERENCEthe shim loads the version of the CLR that it remembered earlier. In this way, you can use CorBindToRuntimeEx to indicate which version of the CLR to load in a delayed fashion.

Using STARTUP_LOADER_SETPREFERENCE with CorBindToRuntimeEx is more limited than LockClrVersion in three key ways:

  • You can specify only a version number STARTUP_LOADER_SETPREFERENCE remembers only the version number you passed to CorBindToRuntimeEx. It does not remember your preference for build type, your garbage collection settings, or your domain-neutral code settings. When the CLR is started later, defaults are used for these other settings.

  • You cannot provide a callback to do extra processing As you've seen, LockClrVersion takes a callback that is invoked when the CLR needs to be initialized. In addition to calling CorBindToRuntimeEx, hosts often use this callback to create their host control object and report it to the CLR so the appropriate hosting managers are set up properly. If you don't use LockClrVersion, there's no way to accomplish this, or other related startup tasks, in a delayed fashion.

  • You cannot prevent the CLR from entering the process You can use LockClrVersion to prevent the CLR from ever entering a process by returning a failure HRESULT from your callback. You can't do this using STARTUP_LOADER_SETPREFERENCE and CorBindToRuntimeEx.

Even with these limitations, passing STARTUP_LOADER_SETPREFERENCE to CorBindToRuntimeEx can still be useful. If all you need to do is ensure a specific version of the CLR is loaded in a lazy fashion, you can do this with much less code than it takes to use LockClrVersion properly.




    Customizing the Microsoft  .NET Framework Common Language Runtime
    Customizing the Microsoft .NET Framework Common Language Runtime
    ISBN: 735619883
    EAN: N/A
    Year: 2005
    Pages: 119

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net