To wrap our target component for use by the client, we performed the following four steps:
We created the actual component using the CLSID of the component passed to the moniker earlier in the metadata XML file.
We created a DelegatorHook object for intercepting the QueryInterface calls to the object. The hook is responsible for routing method calls through each ASPect.
Next, we created the UD object and retrieved the IDelegatorFactory interface.
Using IDelegatorFactory, we called CreateDelegator, passing the interface of the actual object, the delegator hook, the IID of the interface the original caller requested (riidResult), and the pointer to the interface pointer (ppvResult). The delegator returns a pointer to the interceptor that calls our delegator hook on each call.
Figure 7 COM AOP Architecture
The result is shown in Figure 7. From this point on, the client can use the interceptor as the actual interface pointer from the target component. As calls are made, they are routed through the ASPects on the way to the target component.
ASPect Builder
To activate the component with all of the ASPects strung together properly, our AOP moniker relies on an XML file to describe the component and the associated ASPects. The simple format merely contains the CLSID of the component and the CLSIDs of the ASPect components. Figure 8 shows an example that wraps the Microsoft FlexGrid Control with two ASPects. In order to ease the task of creating instances of AOP metadata, we created ASPect Builder (as shown in Figure 9).
Figure 9 ASPect Builder
ASPect Builder enumerates all the ASPects registered on the machine and displays each one of them as cloud-shaped items in the list view on the left. The client area of the ASPect Builder contains a visual representation of the component. You can double-click on it (or use the corresponding menu item) and specify the component's ProgID. Once you've chosen a component, you can drag and drop the ASPects into the client area, adding ASPects to your component's AOP metadata.
To produce the XML format necessary to feed to the AOP moniker, choose Compile from the Tools menu and the metadata will be shown in the bottom pane. You can verify that the metadata is indeed correct by doing the actual scripting in the Verify ASPects pane. You can save these compiled XML instances to the disk and reload them with ASPect Builder as well.
ASPects in .NET
While the ASPect Builder makes things very pretty, storing ASPect metadata separately from the component makes programming AOP in COM less convenient than it could be. Unfortunately, COM's metadata leaves quite a bit to be desired when it comes to extensibility, which is why we felt the need to separate the metadata from the class in the first place. However, .NET, the heir apparent to COM, has no such issue. .NET's metadata is fully extensible and therefore has all the necessary underpinnings for associating ASPects directly with the class itself via attributes. For example, given a custom .NET attribute, we can readily associate a call-tracing attribute with a .NET method:
public class Bar {
[CallTracingAttribute("In Bar ctor")]
public Bar() {}
[CallTracingAttribute("In Bar.Calculate method")]
public int Calculate(int x, int y){ return x + y; }
}
Notice the square brackets containing the CallTracingAttribute and the string to be output when the method is accessed. This is the attribute syntax that associates the custom metadata with the two methods of Bar.
Like our AOP framework