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.
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 we shall build on this example to create a custom AppDomain manager
using C#, and lay the foundation for an extensible application.
The source code for all 3 examples
(Simple Command Host Line, Simple Command Host Line 2, and Hello World)
can be found here.
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.