Apply Now  | Knowledge Base  |  About Us | Locations |  Contact Us
  Knowledge Base

About .NET
LINQ
WPF
About SQL Server 2005
About Business Intelligence
Technical Articles
.NET Articles
SQL Server 2005 Articles
SharePoint 2007 Articles
Business Intelligence Articles
Online Resources





 Live chat by LivePerson



customizingclr

Customizing the .NET Common Language Runtime

Why Customize the .NET Common Language Runtime?
Dan Buskirk
May 1, 2007

kick it on DotNetKicks.com 

The user double-clicks on a .NET program. The CLR loads it and runs it. Works like a champ. Why would anyone need to customize the .NET Runtime?

In fact, there are several good reasons. Access to the debugging API is one. Low-level monitoring of things like garbage collection is another. But the most important reason for many developers is the creation of robust extensible applications. In addition to running their own code, extensible applications run code provided by the user. Any mishandling of memory or security could, in principle, damage the entire application. The user library which extends the application needs to be carefully managed. Experienced .NET programmers know that the unit of isolation for memory management and security within .NET is the AppDomain, much as the process is the unit of management within the operating system. The .NET 2.0 runtime provides new interfaces which permit developers to provide their own customized AppDomain management. While the CLR must be launched using unmanaged code, a custom AppDomain manager for version 2.0 can be written in C# or your favorite .NET language.

One example of an program which uses a customized CLR is Microsoft’s SQL Server 2005. If a stored procedure or function is implemented as .NET code, that code cannot be allowed to request memory from Windows. Any server expected to run 24/7 must manage its memory very carefully, and SQL Server cannot have memory being requested behind its back, so to speak. The customized CLR running .NET code within SQL Server routes all requests for memory through SQL Server rather than to the OS directly, thereby ensuring that SQL Server can manage its memory successfully. A service or application which loads the CLR into its own process to run managed code is said to host the CLR.

Of course, it is not only mission-critical server database systems which need to make use of such features. Imagine a small financial services firm which provides software for its customers’ use. Some of the customers are quite sophisticated and request special features. Of course, each customer requests a different special feature. One solution to this problem is to make the core application extensible. Special features can then be implemented as add-ins, vastly simplifying the management of the core application’s development.

This will be the scenario for our exploration of the .NET runtime and its customization. We will start by examining how the CLR loads and runs a simple program.

How Windows Loads a .NET Executable

All Windows executable files contain more than just compiled program code. The file start with a header, which provides the operating system with a great deal of information about the executable code. Headers can be viewed with the dumpin.exe tool that comes with Microsoft Visual Studio. (Dumpbin.exe may be found in the C:\Program Files\Microsoft Visual Studio 8\VC\bin directory.) To use this utility, you will first have to run the vcvars32.bat batch file that also comes with the Visual Studio and is found in the same directory. The batch file ensures that various environment settings are set. You can then run dumpbin /headers <filename> on the command line.

Towards the end of the output you will see an entry

RVA [size] of COM Descriptor Directory

In a native Windows executable file, this entry will have a value of 0. If the value is not 0, Windows knows that this is a managed file, and begins to load the Common Language Runtime. In Windows XP and later, the OS directly calls a function _CorExeMain, which is located in mscoree.dll. This function identifies the required CLR version, loads it, and executes the managed program file under the default settings.

One of our major motivations in developing hosting applications is to go beyond limitations imposed by those constraints.

Writing a Custom .NET Host

Writing a custom host is the first step to customizing the CLR. Writing a custom host even allows you to store your executable code in forms other than the classic "file", though we won't be exploring that possiblity in this series. As an example, SQL Server 2005 stores in managed code for functions, triggers and stored procedures in its system tables rather than access DLL files from the file system. If no dll file needs read when a managed stored procedure is called, SQL Server does not have to concern itselp with the security of such files.

To start with, we will create a simple host that merely runs a .NET executable file. Of course, such a program must be written in unmanaged code. Let's look at the key features of an extremely simple host. This sample is similar to that provided by Steven Pratschner in his MS Press book Customizing the Microsoft.NET Framework Common Language Runtime

    1 // SvrHost.cpp : Defines the entry point for the console application.

    2 //

    3 //

    4 

    5 #include "stdafx.h"

    6 #include <mscoree.h>

    7 

    8 // include the tlb for mscorlib for access to the default AppDomain through COM Interop

    9 #import <mscorlib.tlb> raw_interfaces_only high_property_prefixes("_get","_put","_putref")

   10 using namespace mscorlib;

   11 

   12 int _tmain(int argc, _TCHAR* argv[])

   13 {

   14     /* Use the hosting interfaces from .Net Framework 1.1

   15     even though we are loading version 2.0

   16     */

   17     // (1) Variable to hold our old-fashioned CLR reference

   18     ICorRuntimeHost *pCLR = NULL;

   19 

   20     // (2)

   21     // Initialize the CLR.  Specify the Server build.  Make sure

   22     // version 2.0 is loaded.

   23     HRESULT hr = CorBindToRuntimeEx(

   24       L"v2.0.50727",

   25       L"svr",    // "wks" is the default   

   26       NULL, // (3) Startup flags

   27       CLSID_CorRuntimeHost,

   28       IID_ICorRuntimeHost,

   29       (PVOID*) &pCLR);

   30 

   31     assert(SUCCEEDED(hr));

   32 

   33     // Start the CLR

   34     pCLR->Start();

   35 

   36     // (4) Get a pointer to the default AppDomain

   37     _AppDomain *pDefaultDomain = NULL;

   38     IUnknown   *pAppDomainPunk = NULL;

   39 

   40     hr = pCLR->GetDefaultDomain(&pAppDomainPunk);

   41     assert(pAppDomainPunk);

   42 

   43     hr = pAppDomainPunk->QueryInterface(__uuidof(_AppDomain), (PVOID*) &pDefaultDomain);

   44     assert(pDefaultDomain);

   45 

   46     // get the name of the exe to run

   47     long retCode = 0;

   48     BSTR asmName = SysAllocString(argv[1]);

   49 

   50 

   51 

   52     // (5) Run the managed exe in the default AppDomain

   53     hr = pDefaultDomain->ExecuteAssembly_3(asmName, NULL, NULL, &retCode);

   54     assert(SUCCEEDED(hr));

   55 

   56 

   57     SysFreeString(asmName);

   58     pAppDomainPunk->Release();

   59     pDefaultDomain->Release();

   60 

   61     _tprintf(L"\nReturn Code: %d\n", retCode);

   62     return retCode;

   63 }

   64 

In step (1) we are simply declaring declaring a variable to hold our CLR reference. While variable declaration is not usually very exciting, this declaration is very important. The variable type is ICorRuntimeHost. This is the older interface which was created for .NET version 1. The interface which provides newer and more powerful options is ICLRRuntime. However, more powerful often means more complex, and that is certainly the case here. We are using the older interface in this example to get a feel for how a host works; we shall soon move to the newer interface.

Step (2) is where the action is. We call the CorBindToRuntimeEx() function, which is defined in mscoree.dll. In this case, we insist on the .NET 2.0 runtime. We also want the server version, which is optimized to run server applications on symmetric multiprocessing machine. (If there is only one processor we'll always get the workstation CLR, even if we ask for server.) We supply classid values defined in the header files and we get back a reference to the CLR. CorBindToRuntimeEx() returns an HRESULT to indicate success or failure. That's right; this is all COM code. While COM is being banished from the toolbox of the application developer, it is alive and well at a lower level. The CLR itself is, in a very real sense, a COM application and we will use COM interfaces for all our interaction with it from the outside (i.e. unmanaged) world.

The parameter for CorBindToRuntimeEx() marked with (3) allows you to set flags which determine key features of the runtime at startup. For example, we can specify whether garbage collection should be concurrent or not. In this example, we are not using the flags at all; we accept the default by supplying NULL. Later, however, we shall explore how to load different code into different AppDomains, and this parameter will become very important.

In the code near markers (4) and (5) we ask the COM mechanisms to give us a reference to an AppDomain, and then use this pointer to launch our application. It is the code in this section which disappears in the newer .NET 2.0 ICLRRuntime interface. Let's take a quick peek at a simple use of the ICLRRuntime interface.

Next:  Page 2


   Email this pageprinter-friendly version   

Add the above content to:


 Google   Y! MyWeb   Furl   Simpy   Spurl   Blink  Del.icio.us   Digg  add to technorati Technorati


Microsoft Certification | Visual Studio 2005 | SQL Server 2005 |.NET Framework
HomeApply Now  | Master's Program℠  |  Corporate Services  |  .NET Courses  |  .NET Resources  |  Site Map  |  Trademarks