Starting the CLR
Calling
CorBindToRuntimeEx
sets the CLR startup options and loads the
// Set the CLR startup options
HRESULT hr = CorBindToRuntimeEx(
L"v2.0.41013",
L"svr",
NULL,
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(PVOID*) &pCLR);
// Use ICLRRuntimeHost to start the CLR
hr = pCLR->Start();
assert(SUCCEEDED(hr));
|
Handling Failures from CorBindToRuntimeExRecall that only one copy of the CLR can be loaded in a process at a time. So calling CorBindToRuntimeEx multiple times has no effect. If you do so, S_FALSE is returned on all but the first call. Successful calls to the API return S_OK , as you'd expect.
If you pass in a version that is not present on the machine,
CLR_E_SHIM_RUNTIMELOAD
(defined in CorError.h in the .NET Framework software development kit) is returned. In addition, the dialog box shown in Figure 3-7 is displayed instructing the
Figure 3-7. Incorrect version dialog box
Clearly, scenarios exist,
SetErrorMode(SEM_FAILCRITICALERRORS); |
Deferring CLR Initialization and StartupIn 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
Fortunately,
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
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
|