.Net Runtime Library for Delphi
Close
Resolving Assembly Loads

The Framework Library provides the TClrAppDomain.AssemblyResolve event for applications that require greater control over assembly loading. By handling this event, your application can load an assembly into the load context from outside the normal probing paths, select which of several assembly versions to load, emit a dynamic assembly and return it, and so on. This topic provides guidance for handling the AssemblyResolve event.

System_CAPS_noteNote

For resolving assembly loads in the reflection-only context, use the TClrAppDomain.ReflectionOnlyAssemblyResolve event instead.

When you register a handler for the AssemblyResolve event, the handler is invoked whenever the runtime fails to bind to an assembly by name. For example, calling the following methods from user code can cause the AssemblyResolve event to be raised:

The handler for the AssemblyResolve event receives the display name of the assembly to be loaded, in the ResolveEventArgs.Name property. If the handler does not recognize the assembly name, it returns null.

If the handler recognizes the assembly name, it can load and return an assembly that satisfies the request. The following list describes some sample scenarios.

  • If the handler knows the location of a version of the assembly, it can load the assembly by using the TClrAssembly.LoadFrom or TClrAssembly.LoadFile method, and can return the loaded assembly if successful.

  • If the handler has access to a database of assemblies stored as byte arrays, it can load a byte array by using one of the TClrAssembly.Load method overloads that take a byte array.

  • The handler can generate a dynamic assembly and return it.

It is the responsibility of the event handler to return a suitable assembly. The handler can parse the display name of the requested assembly by passing the ResolveEventArgs.Nameproperty value to the CoAssemblyName.CreateInstance(WideString) . The event handler can return a different version of the assembly than the version that was requested.

In most cases, the assembly that is returned by the handler appears in the load context, regardless of the context the handler loads it into. For example, if the handler uses the TClrAssembly.LoadFrom method to load an assembly into the load-from context, the assembly appears in the load context when the handler returns it. However, in the following case the assembly appears without context when the handler returns it:

  • The handler loads an assembly without context.

  • The ResolveEventArgs.RequestingAssembly property is not null.

  • The requesting assembly (that is, the assembly that is returned by the ResolveEventArgs.RequestingAssembly property) was loaded without context.

Multiple versions of the same assembly can be loaded into the same application domain. This practice is not recommended, because it can lead to type assignment problems.

The primary rule for handling the AssemblyResolve event is that you should not try to return an assembly you do not recognize. When you write the handler, you should know which assemblies might cause the event to be raised. Your handler should return null for other assemblies.

When loading an assembly, the event handler must not use any of the TClrAppDomain.Load or TClrAssembly.Load method overloads that can cause the AssemblyResolve event to be raised recursively, because this can lead to a stack overflow. This happens even if you provide exception handling for the load request, because no exception is thrown until all event handlers have returned. Thus, the following code results in a stack overflow if MyAssembly is not found:
Delphi
program BadExample; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Core, CNClrLib.Core.Intf; var Console: _Console; AssemblyResolve: TClrEventHandlerR; procedure MyHandler(ASender: _ClrObject; AEventArgs: _ClrEventArgs; out ReturnValue: _ClrObject); stdcall; var AResolveEventArgs: _ResolveEventArgs; begin AResolveEventArgs := CoResolveEventArgs.Wrap(AEventArgs.EventArgs); Console.WriteLine_15('Resolving {0}', AResolveEventArgs.Name); ReturnValue := TClrAssembly.Load(AResolveEventArgs.Name).AsClrObject; end; begin Console := CoConsole.CreateInstance; AssemblyResolve := MyHandler; TClrAppDomain.GetCurrentDomain.AddAssemblyResolve(nil, @AssemblyResolve); try TClrAppDomain.GetCurrentDomain.DefaultInterface.CreateInstanceAndUnwrap('MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null', 'MyType'); except on E: Exception do Console.WriteLine_14(E.Message); end; Console.ReadKey; end. (* This example produces output similar to the following: Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null ... Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null Process is terminated due to StackOverflowException. *)