Chapter 4. Using the Default CLR Host

The CLR ships with a default host that you can use to run managed code in any process without having to write your own CLR host. That is, you don’t have to include code in your application to call CorBindToRuntimeEx, interact with the CLR through the ICLRRuntimeHost interface, and so on. The goal of this chapter is to provide enough details about the default CLR host to help you determine whether it meets the requirements of your scenario. If the capabilities provided by the default host meet your needs and if you don’t require the additional functionality provided by the CLR hosting APIs, using the default host can save you time and effort.

The default host is invoked in one of two ways. First, the default host is used to run managed executables launched from the command line or from the shell. The second use of the default host is to run managed types that have been introduced to a process through the CLR’s COM interoperability layer. This second method of invoking the default host can fit with a wide range of scenarios. Any application that exposes an extensibility model based on COM (or creates COM components as part of the application itself) can use the default host to extend the application with the capabilities of managed code.

Using the default host doesn’t provide all of the flexibility you get by writing a host yourself, but you can configure many of the CLR startup options through application configuration files (see Chapter 3 for details on what the CLR startup options are). Understanding how the default host gets activated, what its default values are for the CLR startup options, and the degree to which these defaults can be customized helps you determine whether using the default host is appropriate in your application.

Invoking the Default Host: Running Managed Executables

The default host for running managed executables is implemented in the function _CorExeMain in the CLR startup shim (mscoree.dll). Control is passed to _CorExeMain differently depending on the operating system on which you’re running. On computers running Microsoft Windows XP and later, the operating system loader has explicit knowledge of managed code executables and calls _CorExeMain directly when a managed executable is launched.

For earlier versions of the Windows operating system, the process for getting to _CorExeMain isn’t as direct. When a compiler emits a managed executable, it sets the main routine (in the Portable Executable [PE] header) to a small stub that calls _CorExeMain instead of the executable’s "real" entry point. Launching the executable thereby passes control to the CLR startup shim, where the process of running the program begins.

Invoking the Default Host: Activating Managed Types Through COM Interop

The Microsoft .NET Framework software development kit (SDK) includes a tool (regasm.exe) you can use to make managed types available through COM. In this way, a managed type can be brought into a process using any of the existing COM APIs. regasm.exe works by creating the registry entries necessary to make the type visible to COM. The default host for managed types activated in this way is also implemented in the CLR startup shim (mscoree.dll). In this scenario, the default host is activated because regasm.exe sets the CLR startup shim as the InProcServer32 key under the COM registry entries for all managed types. Figure 4-1 shows the registry entries that regasm.exe would make to allow the managed type System.Collections.SortedList to be activated through COM.

Registry entries that expose a managed type to COM

Figure 4-1. Registry entries that expose a managed type to COM

When an instance of System.Collections.SortedList is created through COM, the shim loads the CLR in the process and returns a class factory through which COM can create the type. The CLR determines which type it needs to create by looking back in the registry for the globally unique identifier (GUID)/ProgID requested by COM. Notice in Figure 4-1 that regasm.exe adds some custom registry values under the InProcServer32 key for a managed type. These values include the name of the type to create and the assembly in which the type is implemented.

Defaults for the CLR Startup Options

Once control reaches the shim through either of the activation paths described previously, the default host determines which values to use for the four CLR startup options (version, build type, garbage collection mode, and the domain-neutral settings). Determining the build type, the garbage collection mode, and the domain-neutral settings is easy: the default values are hard-coded into the host. The default values for these settings are as follows:

  • Build type. The workstation build is always loaded, regardless of the number of processors on the machine.

  • Garbage collection mode. The garbage collection mode is always set to concurrent.

  • Domain-neutral code. No assemblies (except mscorlib) are loaded domain neutral by default.

Selecting a CLR Version

The process for selecting the version of the CLR to load into the process is more involved and is different depending on whether the default host is activated to run a managed executable or to load a managed type through COM. The version to load is not hard-coded into the host as the other settings are, but rather varies based on which version of the CLR was used to build the assembly about to be run and which versions of the CLR are present on the machine.

Running Managed Executables

Embedded in every assembly is the version of the CLR used when the assembly was built. For example, if you build an assembly with the .NET Framework SDK 1.1 (included in Microsoft Visual Studio .NET 2003), your assembly will contain the version number 1.1.4322—the version of the CLR included in .NET Framework 1.1. Keep in mind that the version number I am discussing here is not related to the version you give to the assembly. The recording of the CLR version in your assembly happens completely under the covers when your assembly is compiled.

The samples for this book include a utility called clrversion.exe that can be used to display the version of the CLR used to build a particular assembly. Just invoke clrversion.exe from the command line with the name of the file containing the assembly you’re interested in to display the version of the CLR used to build that assembly. Here’s sample output from running clrversion.exe on a file called simpleexe.exe.

C:>clrversion SimpleExe.exe
SimpleExe.exe was built with v1.1.4322 of the CLR

The tool clrversion.exe is useful for helping you determine why the shim is choosing a particular version of the CLR when many are installed on the machine.

When running a managed code executable, the default host begins by looking in the assembly to determine which version of the CLR was used to build the file. It then looks in the registry to determine whether that version of the CLR is installed on the machine. It also determines whether any upgrades to that version should be applied (more on this later in the chapter). If the version of the CLR used to build the executable is installed, the startup shim loads that version of the CLR to run the program. For more details on what these registry keys look like and how the shim maps a version number to a given CLR, see Chapter 3.

Upgrades

If the version of the CLR used to build the executable is not installed on the machine, the shim might choose to run the program with a later version of the CLR. It’s important to remember that an upgrade is not applied in all circumstances. The shim chooses to run an application with a later version of the CLR only if the following two conditions are true:

  • The version of the CLR used to build the application has not been installed.

  • The application does not have a configuration file that explicitly states a preference for a specific CLR version(s).

If these two conditions are met, the shim looks to see whether a newer version of the CLR is present on the machine that is compatible with the version used to build the application. Information about whether a given CLR release is intended to be compatible with other releases is stored in the registry under the Upgrades key:

HKEY_LOCAL_MACHINESOFTWAREMicrosoft.NETFrameworkPolicyUpgrades

For example, the Upgrades registry key shown in Figure 4-2 indicates that version 1.1.4322 of the CLR is compatible with version 1.0.3705. Because 1.1.4322 is intended to be compatible with 1.0.3705, the shim will use version 1.1.4322 of the CLR to run an application built with 1.0.3705 (but remember, this happens only if 1.0.3705 isn’t present on the machine and the application doesn’t have a configuration file that states a version preference).

The Upgrades registry key

Figure 4-2. The Upgrades registry key

Let’s look at a few examples to see how the shim behaves when the conditions for an upgrade are met. Say you have a program myapp.exe that was built with CLR version 1.0.3705. Regardless of whether a newer, compatible version of the CLR is present, myapp.exe will always be run with 1.0.3705 if that version is installed on the machine. Figure 4-3 shows the application running with the version it was built with, even though a newer, compatible version is present.

Applications run with the CLR version they are built with by default.

Figure 4-3. Applications run with the CLR version they are built with by default.

However, if CLR version 1.0.3705 is not installed, but version 1.1.4322 is, the application is run with 1.1.4322 because the Upgrades key identifies it as a suitable upgrade. This scenario is shown in Figure 4-4.

Applications can be run with a newer, compatible version of the CLR.

Figure 4-4. Applications can be run with a newer, compatible version of the CLR.

Remember that this behavior occurs only when the default host has not been customized using a configuration file. I explore the details of how to specify a CLR version using an application configuration file later in the chapter.

Activating Managed Types Through COM Interop

The shim’s algorithm for picking a version in the COM interoperability scenario is much more straightforward—the latest version installed on the machine is always used. This "use latest" behavior might sound like taking the easy way out, but in fact the alternatives are no more desirable. In the interoperability scenario discussed previously, the executable that started the process clearly contains no information about CLR versions because it is not a managed code file. So no information is available when the process starts to hint at which version to use.

The managed assemblies that get loaded through COM interoperability do contain the version of the CLR they were built with just like managed executables do. It’s tempting to think that the version of the CLR to load can be determined by looking at the assembly the shim has been asked to load through COM. This approach might work fine if the application were to load only one assembly, but it can fall apart if multiple assemblies are to be loaded. In this case, each assembly might have been built with a different version of the CLR. Therefore, the first one to be loaded determines the version of the CLR that would run all assemblies in the process (recall that only one version of the CLR can exist in a process). In many scenarios, the order in which multiple assemblies are loaded is not guaranteed. So basing the versioning decision on the first loaded assembly introduces unpredictability when multiple assemblies are involved.

For example, consider an application that supports an add-in model based on COM. The addins for this application can be written in managed code as long as the appropriate COM interfaces are provided to hook into the application. If the user selects which add-ins to load based on choices in a menu, the order in which the add-ins are selected determines which version of the CLR is used, which in turn influences how the application behaves.

Scenarios like these are the primary reason the shim always selects the latest CLR in situations in which managed code is first brought into the process through COM. Although choosing the latest version relies on a high degree of compatibility between releases of the CLR, it at least keeps the behavior of this type of application consistent.

This behavior of using the latest CLR will likely remain true as long as Microsoft continues to release versions of the CLR that are backward compatible (at least by design). If Microsoft decides to ship a version of the CLR that is intentionally not backward compatible, the logic for determining which CLR to load in these types of interoperability scenarios will likely have to change.

Customizing the Default Host Using Configuration Files

The default behavior described previously can be customized for the version, build type, and garbage collection settings using application configuration files. The only setting you cannot change using a configuration file is the specification for which assemblies should be loaded domain neutral. The domain-neutral settings can be set only by using the CLR hosting interfaces or through a custom attribute, as discussed in Chapter 9.

Application configuration files are Extensible Markup Language (XML) files that are associated with an application by naming convention and directory. When using the default host, configuration files must be placed in the same directory as the application and are named by simply appending .config to the name of the executable file for the application. For example, the configuration file for myapp.exe must be called myapp.exe.config.

Configuration files can be used to customize the settings for both the managed executable scenario and the COM interoperability scenario described earlier. It might seem odd that a configuration file can be used to customize the CLR in the COM interoperability scenario. After all, the configuration file has settings that are relevant only to managed programs, yet the main executable for the application in this scenario is unmanaged. This works because the shim looks for the configuration file based on the name of the executable that started the process (as obtained by calling the Microsoft Win32 API GetModuleFileName). The fact that the primary executable does not consist of managed code is irrelevant.

There are two ways to edit configuration files: by hand or with the .NET Framework configuration tool. For purposes of this discussion, the configuration tool is somewhat limited in that it doesn’t enable you to set the CLR version that should be used to run the application. For this reason, and for a better understanding of what’s going on under the covers, I stick to showing the XML itself. However, I point out the scenarios in which the tool can be used instead.

In the following sections, I describe how to use configuration files to customize the garbage collection, build type, and version settings.

Concurrent Garbage Collection

Recall that the default setting for concurrent garbage collection is on. That is, collections will happen on background threads while the application is running if the application is running on a multiprocessor machine. As described, the default for concurrent garbage collection and that of the build type (workstation) work nicely together. It is possible, however, to disable concurrent garbage collection through the configuration file.

The value for concurrent garbage collection is set using the <gcConcurrent> element in the configuration file. This element must always be nested inside the <runtime> element. (See the .NET Framework SDK documentation for a description of the complete configuration file schema.) The element <gcConcurrent> has a single attribute called enabled whose value is either the string "true" (the default) or "false". The following configuration file shows how to disable concurrent garbage collection for a given application:

<configuration>
  <runtime>
    <gcConcurrent enabled="false" />
  </runtime>
</configuration

Concurrent garbage collection is one of the settings that can be changed using the .NET Framework configuration tool as well. You can find a shortcut to the tool in Control Panel under Administrative Tools. I don’t go into detail on using the tool here, but if you’ve never used the tool, an overview can be found in the SDK guide.

The options for concurrent garbage collection are shown in the Properties dialog box for an application, as shown in Figure 4-5.

Setting concurrent garbage collection using the .NET Framework configuration tool

Figure 4-5. Setting concurrent garbage collection using the .NET Framework configuration tool

The Properties dialog box presents the options as a set of radio buttons under Garbage Collection Mode. The wording of the options conveys whether collections are done in the background or not. The option Run In Background For User Applications corresponds to the default of enabled, whereas the Run In Foreground For Server Applications option corresponds to not enabled.

Be careful not to confuse the term server applications in this dialog box with the server build of the CLR. By choosing this option, you are configuring the workstation build to run without concurrent garbage collection—you are not directing the shim to load the server build. You can specify that the server build should be used either by calling CorBindToRuntimeEx as described in Chapter 3 or by using a configuration file as described in the next section.

Build Type

You can change the default build type of workstation to server using the gcServer element in an application configuration file. The gcServer element has an attribute called enabled that you set to true if you’d like to run the server build. Keep in mind that the server build is loaded only on multiprocessor machines, however. Even if you set gcServer to true on a single-processor machine, the workstation build will be loaded. The following configuration file uses the gcServer element to specify that the server build of the CLR should be used:

<configuration>
  <runtime>
    <gcServer enabled="true" />
  </runtime>
</configuration

Unlike the settings for concurrent garbage collection, there is no support in the .NET configuration tool for setting the build type—you must edit the XML directly.

As I explained in Chapter 3, you can also specify a build type using the pwszBuildFlavor parameter to CorBindToRuntimeEx. If you specify a build type using both CorBindToRuntimeEx and the gcServer element in the application’s configuration file, the setting passed to CorBindToRuntimeEx takes precedence.

Changing the Build Type on Older Versions of the CLR

The ability to specify a build type using an application configuration file is new in .NET Framework 2.0. If you want to load the server build for executables running with either .NET Framework 1.0 or .NET Framework 1.1, you must write a small CLR host that uses CorBindToRuntimeEx to specify the build type. Fortunately, it’s very easy to write an application launcher that calls CorBindToRuntimeEx to set the server build and then runs the original application.

The following sample does just that. svrhost.exe is a CLR host that can be used to run managed executables from the command line using the server build of the CLR. svrhost.exe takes the managed executable to run as a command-line parameter. In addition to the executable, you can also pass any command-line arguments for the managed executable to svrhost.exe as well. For example, say you have an application called paystub.exe that you normally invoke as follows:

C:> paystub stevenpr 013103

To run paystub.exe with the server build of the CLR, you’d invoke it using svrhost.exe as follows:

C:> svrHost paystub stevenpr 013103

The code for svrhost.exe is very straightforward, as shown in Example 4-1. svrhost.exe uses CorBindToRuntimeEx to load the server build of the CLR. After the CLR has started, svrhost.exe gathers the name of the program to execute along with any arguments from the command line. It then uses the ExecuteAssembly method on the System.AppDomain class to run the specified program in the default application domain. Notice that svrhost.exe uses an interface called ICorRuntimeHost instead of the ICLRRuntimeHost interface I introduced in Chapter 2. ICorRuntimeHost is the primary hosting interface in .NET Framework 1.0 and .NET Framework 1.1. In .NET Framework 2.0, that interface is replaced with ICLRRuntimeHost. Because you want svrhost.exe to run on these older versions of the CLR, the sample sticks with ICorRuntimeHost.

Example 4-1. svrhost.cpp

#include "stdafx.h"

// needed for CorBindToRuntimeEx
#include <mscoree.h>

// include the typelib for mscorlib for access to the default AppDomain through COM Interop
#import <mscorlib.tlb> raw_interfaces_only high_property_prefixes("_get","_put","_putref")

using namespace mscorlib;

int _tmain(int argc, _TCHAR* argv[])
{
   if (argc < 2)
   {
      printf("Usage: SvrHost <Managed Exe> [Arguments for Managed Exe]
");
      return 0;
   }

   ICorRuntimeHost *pCLR = NULL;

   // Initialize the CLR.  Specify the server build. Note that I'm
   // loading the CLR from .NET Framework 1.1.
   HRESULT hr = CorBindToRuntimeEx(
      L"v1.1.4322",
      L"svr",
      NULL,
      CLSID_CorRuntimeHost,
      IID_ICorRuntimeHost,
      (PVOID*) &pCLR);

   assert(SUCCEEDED(hr));

   // Start the CLR
   pCLR->Start();

   // Get a pointer to the default AppDomain
    _AppDomain *pDefaultDomain = NULL;
    IUnknown   *pAppDomainPunk = NULL;

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

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

   // get the name of the exe to run
   long retCode = 0;
   BSTR asmName = SysAllocString(argv[1]);

   // Collect the command-line arguments to the managed exe. These must be
   // packaged as a SAFEARRAY to pass to ExecuteAssembly.
   SAFEARRAY *psa = NULL;
   SAFEARRAYBOUND rgsabound[1];

   rgsabound[0].lLbound = 0;
   rgsabound[0].cElements = (argc - 2);
   psa = SafeArrayCreate(VT_BSTR, 1, rgsabound);
   assert(psa);

   for (int i = 2; i < argc; i++)
   {
      long idx[1];
      idx[0] = i-2;
      SafeArrayPutElement(psa, idx, SysAllocString(argv[i]));
   }

   // Run the managed exe in the default AppDomain
   hr = pDefaultDomain->ExecuteAssembly_3(asmName, NULL, psa, &retCode);
   assert(SUCCEEDED(hr));

   // clean up
   SafeArrayDestroy(psa);
   SysFreeString(asmName);
   pAppDomainPunk->Release();
   pDefaultDomain->Release();

   _tprintf(L"
Return Code: %d
", retCode);
   return retCode; }

Version

Microsoft’s goal is to keep each release of the CLR as backward compatible as possible with all previous releases. (I say as possible because we all know that complete compatibility can never be guaranteed.) This is the approach taken for years by other Microsoft products, including the Windows operating system, Microsoft Office, and Microsoft SQL Server. Because of the focus on backward compatibility, it is quite likely that an application built with one version of the CLR will run just fine on a later version of the CLR.

The default behavior of the shim is to run an application with the version of the CLR used to build it (minus the upgrades scenarios I discussed). However, it might be the case that you as an application author want to be able to take advantage of a new CLR when it is released without having to rebuild and re-release your application. Each new version of the CLR will undoubtedly contain numerous bug fixes and performance enhancements, and because backward compatibility is likely, you can choose to have the shim run your application with a new version when it becomes available.

The <supportedRuntime> Element

The default host enables you to state your desire to run with different versions of the CLR using the <supportedRuntime> element in your application configuration file. The <supportedRuntime> element has a single attribute called version that indicates the version of the CLR you support. By including a <supportedRuntime> element for a given version, you are telling the shim that you are willing to have your application run with that version of the CLR.

You might also specify more than one <supportedRuntime> element. This enables your application to run on any machine that has at least one of your supported versions. If, through testing, you determine that several different versions of the CLR are acceptable, supplying the full list can dramatically broaden the number of machines your application can run on without you having to worry about redistributing a given version of the .NET Framework.

Clearly, the safest way to determine which CLR versions your application can run on is through testing. As discussed earlier, backward compatibility cannot be universally guaranteed and can really be defined only within the context of a given application. Be aware of the potential for compatibility issues when specifying a <supportedRuntime> element for a version of the CLR you haven’t tested with.

If you’ve specified multiple <supportedRuntime> elements, the order in which the elements appear in the configuration file is the order in which the shim will try to load those versions of the CLR. If none of the versions you specify can be found, the shim will display an error and your application won’t run.

All <supportedRuntime> elements must be nested inside the <startup> tag of the runtime section of your configuration file. The value of the version attribute is a string in the following format:

"v + <major number> + <minor number> + <build number>"

For example, setting version to "v1.1.4322" indicates that your application can be run with the version of the CLR that shipped with .NET Framework 1.1. Recall from the previous discussion in Chapter 3 of how the shim locates a CLR that this string maps to the name of the subdirectory (under the main CLR installation root directory) in which the desired version of the CLR is installed.

Here’s an example configuration file that causes the shim to attempt to run the application with the last two publicly released versions of the CLR. Note that the versions are specified from newest to oldest. In this way, the version with the latest bug fixes and performance enhancements is tried first.

<configuration>
  <startup>
    <supportedRuntime version="v2.0.41013" />
    <supportedRuntime version="v1.1.4322" />
  </startup>
</configuration

By design, the <supportedRuntime> element enables you to run your application with a version of the CLR that is different from the one used to develop the application. So it’s important to use this element with caution. The whole premise behind the side-by-side architecture of the .NET Framework is to provide a platform where applications can remain isolated from changes made to the system without their knowledge. The <supportedRuntime> element is a handy tool to use when you have completed testing and are ready to make an explicit decision to allow your application to be upgraded. However, if you allow your application to be upgraded without explicit testing, you’re opening yourself and your customers to potential unwanted compatibility problems.

The <requiredRuntime> Element and .NET Framework 1.0

The preceding section describes how the <supportedRuntime> element can make it easier to deploy your application when you’re not sure which version of the CLR is installed on a given target machine. This statement is true with one exception: you cannot use the < supportedRuntime> element on a machine that has only the version of the CLR that shipped with .NET Framework 1.0. That original version of the CLR does not include support for this element. Instead, the design at the time used a different element that is now deprecated called < requiredRuntime>. This element is similar in spirit to <supportedRuntime> in that it is used to indicate which version of the CLR an application should run with. However, because <requiredRuntime> has been retired in favor of <supportedRuntime>, it really has use only when you have an application you’ve built with .NET Framework 1.1, but you want to make sure it runs on machines with only .NET Framework 1.0 installed. (Of course, this scenario also requires that you haven’t used any features available only in the new releases, but I’m ignoring that fact for now.)

To support this scenario, you need a configuration file that contains a <requiredRuntime> element for the original version of the CLR and a <supportedRuntime> element for .NET Framework 1.1. Here’s an example:

<configuration>
  <startup>
    <supportedRuntime version="v1.1.4322" />
    <requiredRuntime  version="v1.0.3705" />
  </startup>
</configuration>

This configuration file enables an application built with either .NET Framework 1.0 or .NET Framework 1.1 regardless of which version is installed on the machine.

Note

Note

Applications built with .NET Framework 2.0 cannot be run on older versions of the CLR. The .NET Framework 2.0 release contains a few new features, such as support for generics, that required the format of the CLR metadata to change. Older versions of the CLR won’t be able to read this new metadata and therefore can’t run applications built with the .NET Framework 2.0.

As described earlier, the order of these elements in the configuration file determines their priority. Hence, if both versions 1.1.4322 and 1.0.3705 are installed, 1.1.4322 will be used. If neither of those versions is installed, the shim will run the application with v1.0.3705 (that is, .NET Framework 1.0) if it is present.

Unfortunately, your configuration file needs more work if you want to run an application built with .NET Framework 1.1 on .NET Framework 1.0. (Keep in mind, too, that these scenarios are restricted to the cases in which you’re sure you don’t rely on any features that aren’t available in .NET Framework 1.0—and that you’ve tested your application on that version.) Recall that in Chapter 3 I said that when you pick a version of the CLR to run your application, you also get the matching set of class library assemblies by default. Recall also that this behavior occurs only when either .NET Framework 1.1 or .NET Framework 2.0 is installed on the machine.

If only .NET Framework 1.0 is available, you must include additional statements in the configuration file to redirect all assembly references back down to the versions that shipped with .NET Framework 1.0. You do this using the <bindingRedirect> tag in your application configuration file. You must include one <bindingRedirect> tag for each assembly that ships in the .NET Framework redistributable (unless you’re sure there are assemblies you don’t use). I know this sounds cumbersome, but fortunately, the same configuration file works for all applications, so once you get it right, you’re set.

Here’s the configuration file used earlier with <bindingRedirect> entries for the .NET Framework assemblies. (I haven’t included them all for the sake of brevity but the entire configuration file is available from the Microsoft Press download site for this book. See the Introduction of this book for the URL.)

<configuration>
   <startup>
      <supportedRuntime version="v1.1.4322" />
      <requiredRuntime  version="v1.0.3705" />
   </startup>

   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="System.Security"
               publicKeyToken="b03f5f7f11d50a3a" culture=""/>
         <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
             newVersion="1.0.3300.0"/>
          </dependentAssembly>
          <dependentAssembly>
             <assemblyIdentity name="System.Runtime.Remoting"
                publicKeyToken="b77a5c561934e089" culture=""/>
             <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
                newVersion="1.0.3300.0"/>
         </dependentAssembly>
         <--! Lots of others need to be included here.....
      </assemblyBinding>
   </runtime>
</configuration>

If you’ve been following along closely and are familiar with how <bindingRedirect> statements work, you’re probably thinking that the configuration file I’ve just shown works great if my application runs with .NET Framework 1.0, but what if my application runs with .NET Framework 1.1 or 2.0? Won’t all my assembly references get redirected down to the versions of the class library assemblies that shipped with .NET Framework 1.0? Well, the answer is yes—the configuration file as I’ve defined it doesn’t work with all versions of the .NET Framework. What I’d really like is for the <bindingRedirect> statements to apply as specified if my application runs with .NET Framework 1.0 but to be ignored if my application runs with a later version (because later versions will ensure that my references to system assemblies automatically get redirected to the version that matches the CLR I’ve chosen).

To accommodate this scenario, you must add the appliesTo attribute to the <assemblyBinding> element (under which all of your <bindingRedirect> statements lie) in your application configuration file. The value of the appliesTo attribute is the version of the CLR to which the statements in the <assemblyBinding> section of the configuration file apply. In other words, the <bindingRedirect> statements take effect only if the version of the CLR identified by the appliesTo attribute is loaded. In this case, I clearly want the <bindingRedirect> statements to apply only if version 1.0.3705 (also known as .NET Framework 1.0) is loaded. As a result, the final configuration file looks like this:

<configuration>
   <startup>
      <supportedRuntime version="v2.0.1111" />
      <supportedRuntime version="v1.1.4322" />
      <requiredRuntime  version="v1.0.3705" />
   </startup>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"
            appliesTo="v1.0.3705">
         <dependentAssembly>
            <assemblyIdentity name="System.Security"
               publicKeyToken="b03f5f7f11d50a3a" culture=""/>
            <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
                             newVersion="1.0.3300.0"/>
         </dependentAssembly>
         <dependentAssembly>
            <assemblyIdentity name="System.Runtime.Remoting"
                    publicKeyToken="b77a5c561934e089" culture=""/>
            <bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
                             newVersion="1.0.3300.0"/>
         </dependentAssembly>
         <--! Lots of others need to be included here...
      </assemblyBinding>
   </runtime>
</configuration>

Summary

The CLR includes a default host that enables you to use application configuration files to specify which version and build type of the CLR to use and in which garbage collection mode to run. If you don’t need to customize the settings for domain-neutral code and if you don’t require any other features of the CLR hosting API, using the default host can save you time.

The configuration file used to customize the default CLR host enables you to list more than one version of the CLR with which to run the application. If multiple versions are listed, the order of the versions in the list determines their priority. However, it’s always safest to list only the version of the CLR your application was built with because that’s the version that offers the most predictable behavior at run time. If you choose to list additional versions, make sure you’ve completed sufficient testing to ensure that the other versions are backward compatible from the perspective of your application.

Finally, if you’ve determined that your application can run on a machine that has only .NET Framework 1.0 installed on it, you must include extra statements in your configuration file to upgrade these references manually because the CLR that shipped with .NET Framework 1.0 does not automatically upgrade all references to the .NET Framework assemblies to match the CLR.



[1] A nonzero value identifies a managed code file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.74.25