Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
This article presents an overview of the motivation behind new techniques that decompose problems into independent pieces for optimal use of parallel programming.

By David Callahan (October 2008)
We take a look at planned support for parallel programming for both managed and native code in the next version of Visual Studio.

By Stephen Toub and Hazim Shafi (October 2008)
Here we describe some of the more common challenges to concurrent programming and present advice for coping with them in your software.

By Joe Duffy (October 2008)
Here is an ASP.NET AJAX data-driven Web application that takes the best features from server- and client-side programming to deliver an efficient, user-friendly experience.

By Bertrand Le Roy (October 2008)
More ...
Articles by this Author
Here's a look at how code fails and techniques for writing more reliable and resilient managed code.

By Alessandro Catorcini and Brian Grunkemeyer (December 2007)
More ...
Popular Articles
We take a look at planned support for parallel programming for both managed and native code in the next version of Visual Studio.

By Stephen Toub and Hazim Shafi (October 2008)
Microsoft Robotics Studio is not just for playing with robots. It also allows you to build service-based applications for a wide range of hardware devices.

By Sara Morgan (June 2008)
We build a Silverlight 2.0 application using the InkPresenter to let users annotate a pre-defined collection of images, perform handwriting recognition, and save the annotations and recognized text into a server-side database.

By Julia Lerman (August 2008)
In this excerpt from his upcoming book, Laurence Moroney explains the basics of Silverlight animation and the animation tools available in Expression Blend.

By Lawrence Moroney (August 2008)
More ...
Read the Blog
Well designed code keeps things that have to change together as close together in the code as possible and allows unrelated things in the code to change independently, while minimizing duplication in the code. In the October 2008 issue of MSDN Magazine, Jeremy Miller shows you some design ...
Read more!
The process for ink capture and analysis on the Tablet PC is straightforward in managed code. To the uninitiated developer, however, creating unmanaged Tablet PC applications can be rather daunting. In the October 2008 issue of MSDN Magazine, Gus Class a quick introduction to the Tablet PC ...
Read more!
Multicore systems are becoming increasingly prevalent, but the majority of software today will not automatically take advantage of this additional processing ability. And multithreaded programming, for anything but the most trivial of systems, is incredibly difficult and error prone today. In the October 2008 issue of MSDN ...
Read more!
Concurrent programming is notoriously difficult, even for experts. You have all of the correctness and security challenges of sequential programs plus all of the difficulties of parallelism and concurrent access to shared resources. In the October 2008 issue of MSDN Magazine, David Callahan describes ...
Read more!
A major advantage of AJAX and Silverlight applications is that they can transparently and continuously interact with a back-end service. The problem is that they run over HTTP, which wasn't designed with security in mind. In the September 2008 issue of MSDN Magazine, Dino Esposito shows you ...
Read more!
Unhandled exception processing shouldn't be a mystery. It's actually quite useful since it gives a crashing application an opportunity to perform last-minute diagnostic logging about what went wrong. In the September 2008 issue of MSDN Magazine, Gaurav Khanna discusses how ...
Read more!
More ...
CLR Inside Out
CLR Hosting APIs
Alessandro Catorcini and Piotr Puszkiewicz

Suppose you are developing a large application in native C++ and you want to allow your customers to extend this app so they can mold it to their needs. Allowing your customers to write the extensions in managed code inside the Microsoft® .NET Framework would make their development experiences much smoother than if they had to work with native code, but loading the common language runtime (CLR) into your application's process can be worrisome. What if an uncaught CLR exception ends up killing the application's process? What if the managed extensions used more memory than your application was willing to give up? You could solve these problems by launching the runtime in another process, but moving large amounts of data across process boundaries could soon become prohibitively expensive. Is there any way to take advantage of the benefits of managed code without such expenses?
These issues quite frequently arise in the development of extensible applications. The CLR provides a variety of functionality, resources, and libraries to reduce the cost of building extensions. However, developers of such extensible applications are understandably wary of allowing potentially unreliable and resource-hungry third-party code to execute inside of their applications. Fortunately, the CLR 2.0 hosting APIs allow application developers to control the CLR's resource consumption and to guarantee a specific level of reliability for code running within the CLR. By using the hosting APIs, developers of native hosts can execute managed code in-process with complete knowledge and control over how the CLR behavior can affect their application.
The CLR has always allowed a level of integration between itself and a host. In the .NET Framework 1.x, native applications were able to load a specific version of the CLR, to start and stop its execution, and to configure a limited number of settings. In version 2.0 of the Framework, the CLR allows for a much deeper integration. The updated hosting APIs provide layers of abstraction that let the host manage many of the resources currently provided by Win32®. Furthermore, the updated APIs extend the set of CLR functionality that is configurable by the host.

Hosting Managers
The CLR 2.0 hosting APIs are split into two primary sets: host managers and CLR managers. Host manager interfaces have names prefixed with IHost, and follow with a function description (as in IHostMemoryManager and IHostSecurityManager). The implementations of the host manager interfaces are provided by the developer of the host and are registered with the CLR, which uses these interfaces to make callbacks against the host for the duration of the process lifetime.
The CLR manager interfaces are prefixed with ICLR, and what follows ICLR in the name describes the function of the manager (as in ICLRTaskManager and ICLRSyncManager). CLR managers are handed from the CLR to the host and are implemented by the CLR. The host uses these interfaces to request that the CLR perform some specific action (for example, a host can use the provided ICLRGCManager to force a garbage collection).
For the purposes of this article, we'll group the CLR managers and host managers according to their intended uses, which will help clarify the key benefits of using the hosting APIs. We will include a list of the associated ICLR* and IHost* interfaces and their descriptions when covering an area of functionality.

Memory Manager
The memory manager lets the host provide an interface through which the CLR will request all memory allocations. It replaces both the Windows® memory APIs and the standard C CLR allocation routines. Moreover, the interface allows the CLR to inform the host of the consequences of failing a particular allocation (for example, failing a memory allocation from a thread holding a lock may have certain reliability consequences). It also permits the host to customize the CLR's response to a failed allocation, ranging from an OutOfMemoryException being thrown all the way up through the process being torn down. The host can also use this manager to recapture memory from the CLR by unloading unused app domains and forcing garbage collection. The memory manager interfaces are listed in Figure 1.
SQL Server 2005 demonstrates the usefulness of the memory manager hosting APIs. SQL Server 2005 operates within a configurable amount of memory and is often set to use nearly all of the physical memory on the machine. To maximize performance, SQL Server 2005 tracks all memory allocations in an attempt to ensure that paging never occurs. The server would rather fail a memory allocation than page to disk. To accurately track all allocations, SQL Server uses the memory manager hosting APIs such that any memory allocation requests by the CLR are made through SQL Server rather than directly to Windows. This gives SQL Server the ability to fail CLR allocation requests before paging occurs.

Threading Manager
The new hosting APIs abstract the notion of a Win32 thread and essentially let the host define the unit of scheduling and execution. The hosting APIs use the term "Task" to define this abstraction. As part of abstracting Win32 threads, tasks provide a variety of methods for modifying the CLR.
The threading manager can:
  • Allow the host to provide an interface that the CLR will use to create and start new tasks.
  • Provide the host with a mechanism to "reuse" or pool the CLR-implemented portion of a task.
  • Support standard operations like start, abort, join, and alert.
  • Implement a callback to notify the CLR when a task has been moved to or from a runnable state. When a call is moved from a runnable state, the CLR must be able to specify that the task should be rescheduled as soon as possible.
  • Provide a way for the CLR to notify the host that a given task cannot be moved to a different physical OS thread and cannot have its execution blocked during a specified window.
  • Allow the host to provide an implementation of the thread pool. The CLR must be able to queue work items, set and query the size of the thread pool, and so on.
  • Provide notifications on both the host and CLR sides that the locale has been changed on a given task.
  • Provide a means for the CLR (and user code) to adjust the priority of a task.
Figure 2 shows the threading manager interfaces.

Synchronization Manager
While letting the host create tasks is sufficient for many multithreaded applications, some hosts also require the ability to override the CLR's synchronization primitives. This ensures that locks are not taken on an operating system thread without the host's knowledge, and it allows CLR tasks to further integrate with the host's scheduling mechanism as well as permitting the host to perform deadlock detection.
Using the synchronization manager allows the host to provide implementations for several synchronization primitives to the CLR, including critical sections, events (both manual and auto-reset), semaphores, monitors, and reader/writer locks. See the synchronization manager interfaces in Figure 3.

I/O Completion Manager
The I/O completion manager lets the host provide a custom port implementation for the CLR to use in place of the default Windows I/O completion port functionality. The host provides a way for the CLR to bind a handle to an I/O completion port. In return, the CLR supplies a callback to be invoked by the host when an asynchronous I/O operation completes. In addition, this manager also allows the host to insert custom data at the end of the OVERLAPPED structure passed to the I/O routines. Figure 4 shows the I/O completion manager interfaces.

Assembly Load Manager
Hosts relying on the old hosting APIs customized assembly-loading behavior by catching events on System.AppDomain that are thrown when an assembly, resource, or type cannot be found (AssemblyResolveEvent, ResourceResolveEvent, and TypeResolveEvent). The requested assembly was then loaded by the host using the overload of Assembly.Load that accepts a byte array and is passed back from the event. This approach has proven insufficient for a few reasons, including performance. Loading an assembly by specifying a managed byte array involves copying the bytes multiple times between managed and unmanaged memory before the assembly can be run by the host.
In version 2.0 of the hosting APIs, the host specifies which assemblies are to be loaded from the Global Assembly Cache (GAC) and fulfills all other requests directly. When binding to an assembly, the host can return the assembly as a pointer to unmanaged memory (an unmanaged IStream *). In effect, this allows a host to implement a custom assembly store. In fact, this is the primary reason that SQL Server 2005 implements its own custom assembly load manager. The assemblies that make up a database application in SQL Server 2005 are physically stored in the SQL Server database rather than as separate files on disk. SQL Server 2005 uses the assembly load manager APIs to efficiently load application assemblies out of the database while continuing to rely on the usual .NET Framework mechanisms to load the assemblies. Figure 5 shows the assembly load manager interfaces.

CLR Configuration Manager
The CLR configuration manager gives access to interfaces (see Figure 6) that allow the host to configure several aspects of the CLR, including logically grouping tasks to simplify debugging, setting a custom heap dump configuration, registering callbacks for important CLR events, blocking certain functionality from being loaded into the CLR, and setting a critical failure escalation policy.
The concept of an escalation policy deserves some explanation. Critical failures include resource allocation failures (such as out-of-memory conditions) and asynchronous failures (such as stack overflows), and every type of failure has a default behavior associated with it, ranging from throwing an exception through process termination. In version 2.0, the CLR enables a host to override the default behavior when a critical failure occurs. Advanced hosts can define a chain of events that will be tried in a sequence determined by the host to respond to failures. For example, a host may decide that every out-of-memory failure will translate into aborting the thread or unloading the AppDomain.
SQL Server 2005 uses escalation policy in part to ensure process stability by escalating thread aborts to AppDomain unloads when resource failures occur in areas of code that could affect multiple tasks. For example, suppose a task that's holding a lock receives a failure when trying to allocate memory. In this scenario, aborting just the current task is not sufficient to ensure stability of the AppDomain because there may be other tasks in the domain waiting on the same lock or expecting the data that was being manipulated to be in a consistent state. For more information on escalation policies and reliability, see Stephen Toub's article "High Availability: Keep Your Code Running with the Reliability Features of the .NET Framework" in the October 2005 issue of MSDN®Magazine.

Using the Hosting APIs
In his article "No More Hangs: Advanced Techniques to Avoid and Detect Deadlocks in .NET Apps" in the April 2006 issue of MSDN Magazine, Joe Duffy developed a thin host that intercepted thread and synchronization primitive operations to perform deadlock detection (the source code is available at msdn.microsoft.com/msdnmag/issues/06/04/Deadlocks). The code shows how to use the hosting APIs.
The code in Figure 7 is similar to what you'll need in every host you write. It loads the CLR into the process, starts it, and loads an assembly into the default AppDomain, invoking a method on it. (Error checking code is omitted for clarity.)
The first task in hosting the CLR is getting a pointer to the ICLRRuntimeHost interface from the CLR:
ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
    NULL, // Load the latest CLR version available
    L"wks", // Workstation GC ("wks" or "svr" overrides)
    0, // No flags needed
    CLSID_CLRCLRHost,
    IID_ICLRRuntimeHost,
    (PVOID*)&pClrHost);
In the .NET Framework 1.x you'd use ICorCLRHost to communicate between the CLR and the host; in version 2.0, ICLRRuntimeHost is the recommended interface, as it allows you to access all the new opt-in functionality described here. Among other functions, ICLRRuntimeHost facilitates the exchange of hosting interface implementations between the CLR and the host. ICLRRuntimeHost::SetCLRControl allows the host to notify the CLR that some host-defined services need to be hooked up to override the CLR's default behavior. Our example uses the DHHostControl class, which implements IHostControl, to override some of the CLR's default managers:
DHHostControl *pHostControl = new DHHostControl(pClrHost);
pClrHost->SetHostControl(pHostControl);
Once the host successfully calls SetHostControl, the CLR will use any interfaces defined in the host's IHostControl implementation in place of the default actions. In this example, DHHostControl overrides IHostTaskManager and IHostSyncManager by providing two classes in DHHostControl that inherit from these interfaces.
ICLRRuntimeHost also allows the host to get pointers to the CLR's implementation of the ICLR* interfaces. This is done by calling the ICLRRuntimeHost::GetCLRControl method to get a pointer to an ICLRControl object. ICLRControl lets the host access all other ICLR* interfaces through which the host can request specific actions from the CLR. Since the sample application is monitoring the CLR, it does not use the ICLR* interfaces.
Once you've loaded and started the CLR, the easiest way to load and run your managed code is to use the ExecuteInDefaultApp-Domain method on ICLRRuntimeHost. This will simply load a managed assembly and execute a method on it:
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
    pwzAssemblyPath, pwzAssemblyName, 
    pwzMethodName, pwzMethodArgs,
    &retVal);

Conclusion
Unmanaged hosts are able to control very fine-grained details of the internal workings of the CLR. Using the new .NET Framework 2.0 hosting APIs, a host can, in fact, place itself between the CLR and the operating system and broker any request from the CLR. You can find more information in the online documentation of the hosting APIs and in the book Customizing the Microsoft .NET Framework Common Language Runtime by Steven Pratschner (Microsoft Press®, 2005).

Send your questions and comments to  clrinout@microsoft.com.


Alessandro Catorcini is a lead program manager in the CLR team. In Visual Studio 2005 he was responsible for the hosting API layer and CLR integration into SQL Server 2005.

Piotr Puszkiewiczis a program manager in the CLR team. He is currently responsible for the hosting API, the CLR exception system, and Managed Debugging Assistants.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker