This sample demonstrates how to host PostSharp in an application that performs runtime weaving. This would be the case for instance of an application server that weaves components before loading them in the server.
The sample explains how different application domain interact. It shows how to work with deep-first assembly processing and assembly name overwriting.
The characteristics of this sample host are:
.exe
) are woven.When you develop an aspect or a plug-in, your code is instantiated and invoked by PostSharp. However, when you develop a PostSharp Host, your code instantiate and invoke PostSharp.
PostSharp comes with two hosts: the MSBuild task and the
command-line utility. This sample illustrates how to develop a host
that weaves assemblies at runtime, that is, to be precise, just before
they are loaded in the application domain (AppDomain
).
Three application domains live in the host process:
An application that hosts PostSharp should implement the IPostSharpHost
interface. PostSharp calls back this interface when it needs to resolve
an assembly reference (ResolveAssemblyReference
method) or to know how an assembly should be processed (i.e. with which
PostSharp project and which parameters; GetProjectInvocationParameters
method), or when a message is emitted (OnMessage
method).
In our sample, the IPostSharpHost
interface is implemented by the Host
class.
Additionally, this class has an Execute
method that sets up the client application domain and invoke PostSharp.
PostSharpLocalHost
.
In this sample, we demonstrated how to override it. We defined the
class LocalHost
that registers to some event
and simply log to the console. Event handlers are registered in the Initialize
method.The ClientDomainManager
class
manages the client application domain. An instance of this object is
created by the Host
object just after it
creates the client application domain. The principal role of this
object is to handle the AppDomain.AssemblyResolve
event so that it provides the assembly transformed by PostSharp instead
of the unmodified assembly.
In order to achieve this, there is a close cooperation between
the Host
and the ClientDomainManager
objects. The ClientDomainManager
object
maintains a dictionary that associates an AssemblyName
to a location on the file system. This file could be transformed by
PostSharp or not; it is not relevant for ClientDomainManager
.
The Host
object should call the ClientDomainManager.SetAssemblyLocation
method after the assembly. Because
we chose to weave all assemblies present in the same directory as the
entry point assembly, and only them, it is sufficient to call the SetAssemblyLocation
method once we have ordered the transformation of the assembly, that
is, in the Host.GetInvocationParameters
method.
Another role of the ClientDomainManager
class it
to invoke the entry point of the method level. This is nearly trivial,
but one should not forget to set up the proper thread apartment. This
is done by the Execute
method.
Remember that we use the AppDomain.AssemblyResolve
event to tell the runtime engine to load our woven assembly instead of
the initial, unmodified assembly. However, this even is only a fallback
mechanism. That is, it is called only when the required assembly is not
found using the default probing mechanism. So we cannot know with
certitude that we will get the chance to have our event handler called.
In order to circumvent this issue, PostSharp renames woven
assemblies (this feature is named assembly name overwriting).
The result is that the default probing algorithm of the runtime engine
always fails, so the AssemblyResolve
event is
always raised and we get always the opportunity to load our woven
version of the assembly.
When PostSharp renames an assembly, it calls the RenameAssembly
method of the Host
object. This method, in
turn, propagates the renaming in the ClientDomainManager
object.