Customizing the CLR, Page 2
Back to page 1
Using the ICLRRuntime interface
In this extremely simple example, we won't be launching an application, we will
be invoking a method using a function called ExecuteInDefaultAppDomain(). We can
certainly invoke a method from an exe file. However, that method cannot be Main().
ExecuteInDefaultAppDomain() requires that the method we invoke accept a string argument
and return an integer. As a program entry point, Main() can receive no arguments,
or it can receive a string array - it is therefore out-of-the-running as a candidate
for ExecuteInDefaultAppDomain(). Similar code is discussed in the excellent Richter
book CLR Via C#: Applied Microsoft .Net Framework 2.0 Programming, also from
MS Press.
Let's look at the code:
1 #include
<Windows.h>
2 #include
"assert.h"
3 #include
<MSCorEE.h>
4 #include
<stdio.h>
5 #include
<CorError.h>
6
7 void
main(int argc, WCHAR **argv) {
8
// Load the CLR
9
//(1)****************************
10 ICLRRuntimeHost
*pClrHost;
11 HRESULT
hr = CorBindToRuntimeEx(
12
L"v2.0.50727",
// desired CLR version (NULL=latest)
13
NULL,
// desired CLR Optimization (NULL=workstation)
14
STARTUP_CONCURRENT_GC, // desired startup flags
15
CLSID_CLRRuntimeHost, // CLSID of CLR
16
IID_ICLRRuntimeHost, // IID of ICLRRuntimeHost
17
(PVOID*) &pClrHost); // returned COM interface
18
19 //(2) ****************************
20 // (This is where you would set Host managers)
21 // (This is where you could get CLR managers)
22
ICLRControl *pCLRControl;
23
hr = pClrHost->GetCLRControl(&pCLRControl);
24
25 assert(SUCCEEDED(hr));
26 //
must get this stuff before the CLR is started
27 ICLRGCManager
*ppObject;
28 hr =
pCLRControl->GetCLRManager(
29
IID_ICLRGCManager,
30
(VOID **) &ppObject);
31
32
//wprintf(L"\n%d\n",hr);
33
assert(SUCCEEDED(hr));
34
35
//(3) *****************************
36
// Initialize and start the CLR
37 pClrHost->Start();
38
39 // Load an assembly and call a static method that
40 // takes a String and returns an Int32
41 DWORD
retVal;
42
43 hr =
pClrHost->ExecuteInDefaultAppDomain(
44
L"HelloWorld.exe",
45
L"HelloWorld.Program", L"Hola",
L"mundo", &retVal);
46
47 assert(SUCCEEDED(hr));
48 // Show the result returned from managed code
49 wprintf(L"\nManaged code returned %d", retVal);
50
51
// (4) *****************************
52
//hr=ppObject->Collect(0L); //Force a garbage collection
53
//assert(SUCCEEDED(hr));
54
55
COR_GC_STATS pStats;
56
pStats.CommittedKBytes=0L;
57
pStats.GenCollectionsTaken[0]=0L;
58
pStats.LargeObjectHeapSizeKBytes=0;
59
pStats.Gen0HeapSizeKBytes=0;
60
pStats.KBytesPromotedFromGen0=0;
61
hr= ppObject->GetStats (&pStats);
62 assert(SUCCEEDED(hr));
63
64
wprintf(L"\nGenCollectionsTaken: %lu",pStats.GenCollectionsTaken[0]);
65 wprintf(L"\nLargeObjectHeapSizeKBytes: %lu",pStats.LargeObjectHeapSizeKBytes);
66 wprintf(L"\nCommittedKBytes: %lu",pStats.CommittedKBytes);
67 wprintf(L"\nGen0HeapSizeKBytes: %lu",pStats.Gen0HeapSizeKBytes);
68 wprintf(L"\nKBytesPromotedFromGen0: %lu",pStats.KBytesPromotedFromGen0);
69
70 }
Code section (1) is similar to that in the previous example, but the interface is
the new ICLRRuntimeHost interface. This new interface bears little resemblance to
the interface used in the 1.0 and 1.1 versions of the CLR. In particular, the methods
for launching an executable assembly are quite distinct. These will be discussed
in the next installment of this column. Section (2) of the sample code is not necessary
for this example, but we have included it to give you the flavor of customization.
In this case, we are obtaining a pointer to the ICLRControl interface, which will
then provide us with a pointer to the Garbage Collection Manager. Obtaining these
pointers, and other comparable pointers used in CLR customization, must be done
before the CLR is started.
In code section (3), we start the CLR and ask it to run a static method for us.
As we have noted, ExecuteInDefaultAppDomain() requires that this method receive
a string argument and return an Int32. In this example, we assume that the executable
file HelloWorld.exe is in the same directory as the host program. If it is not,
then the first argument of ExecuteInDefaultAppDomain() must be a fully qualified
path. The source for the executable used in this example is included in the appendix,
but you should have no difficulty experimenting with your own.
Code section (4) is not necessary for execution; it simply illustrates how to use
the pointer we obtained earlier to get detailed information on the status of the
Garbage Collector. We create and initialize a COR_GC_STATS structure and pass it
to the GetStats method.
Next Month: Creating a Custom AppDomain Manager
Next month we shall build on this example to create a custom AppDomain manager using
C#, and lay the foundation for an extensible application.
Appendix
As if the world needs another HelloWorld program:
1 using
System;
2 using
System.Collections.Generic;
3 using
System.Text;
4
5 namespace
HelloWorld
6 {
7
class Program
8
{
9
static
void Main()
10
{
11
Hola("mundo");
12
13
}
14
static
int Hola(string jrichter)
15
{
16
Console.WriteLine("Hola, {0}!", jrichter);
17
return 0;
18
}
19
}
20 }
Source Code
The source code for all 3 examples (Simple Command Host Line, Simple Command Host
Line 2, and Hello World)
can be found here.
About the author:
Dan Buskirk had been a research scientist for many years when he left the university
labs to participate in a startup venture to design and build databases for medical
science. Since that time he has managed a consulting practice specializing in database
design and development. Dan balances this with training on Microsoft SQL Server,
Microsoft Analysis Services, and .NET programming. His interests include mathematical
methods for data mining and computing methods for advanced data categorization on
Windows clusters.