Figure 2 COM Idioms Ripe for AOP
Exception and error handling.
|
Transaction management.
|
Logging of method calls.
|
Just-in-Time activation/swizzeling/lazy object instantiation.
|
Synchronization of method calls.
|
Security checks.
|
Extending a binary component's automation capabilities like expando objects.
|
Component instantiation based on memory gates.
|
Disabling COM pinging.
|
Design by contract like Eiffel.
|
Parameter validation.
|
Figure 3 IAspect Interface
interface IAspect : IUnknown {
HRESULT PreProcess( [in] IUnknown* pUnkDelegatee,
[in] BSTR riid,
[in] BSTR strMethodName,
[in] long nvtblSlot,
[in] IUnknown* pEnum);
HRESULT PostProcess([in]HRESULT hrOriginal,
[in] IUnknown* pUnkDelegatee,
[in] BSTR riid,
[in] BSTR strMethodName,
[in] long nvtblSlot,
[in] IUnknown* pEnum);
}
Figure 4 Call-tracing Aspect
class CCallTracingAspect : public IAspect, ... {
public:
BEGIN_CATEGORY_MAP(CCallTracingAspect)
IMPLEMENTED_CATEGORY(CATID_Aspects)
END_CATEGORY_MAP()
STDMETHODIMP PreProcess(...)
{ return DumpStack ( true, riid, strMethodName, pEnum ) ; }
STDMETHODIMP PostProcess(...)
{ return DumpStack ( false, riid, strMethodName, pEnum ) ; }
HRESULT DumpStack(bool preProcess, BSTR riid,
BSTR strMethodName, IUnknown *pEnum) {
if (preProcess) ATLTRACE("PreProcessing: %S(", strMethodName);
else ATLTRACE("PostProcessing: %S(", strMethodName);
CComPtr<IEnumVARIANT> spEnumVar;
pEnum->QueryInterface(&spEnumVar);
CComVariant v;
bool bNeedComma = false;
while (spEnumVar->Next(1, &v, 0) == S_OK) {
if (bNeedComma) ATLTRACE(", ");
else bNeedComma = true;
ATLTRACE("%S", ToString(v));
}
ATLTRACE(")\n");
return S_OK ;
}
•••
};
Figure 5 Creating the Object in BindToObject
STDMETHODIMP CAopFactory::BindToObject(
IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riidResult,
void** ppvResult) {
// ParseDisplayName has already pulled in the metadata from
// the XML file supplied by the client
// Create the object to be hosted in our AOP environment
CComPtr<IUnknown> spComp;
HRESULT hr = spComp.CoCreateInstance ( m_clsid ) ;
if (FAILED(hr)) return hr ;
// Create our interceptor and return it the client
... // Magic happens...
}
Figure 6 Rest of BindToObject Implementation
STDMETHODIMP CAopFactory::BindToObject(
IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riidResult,
void** ppvResult) {
•••
// Create our interceptor and return it the client
// Create and initialize our hook
CComObject<Chook>* pHook ;
hr = pHook->CreateInstance(&pHook) ;
if (FAILED(hr)) return hr ;
CComPtr<IDelegatorHookQI> spHook ;
hr = pHook->QueryInterface(&spHook) ;
if (FAILED(hr)) return hr ;
hr = pHook->SetAspects ( m_displayName, m_aspects.size(),
&m_aspects[0] ) ;
if (FAILED(hr)) return hr ;
// Create the UDFactory
CComPtr<IDelegatorFactory> spDel ;
hr = CoGetClassObject ( __uuidof(CoDelegator21), CLSCTX_INPROC, 0,
__uuidof(IDelegatorFactory), (void **)&spDel );
if (FAILED(hr)) return hr ;
// Create the interceptor
hr = spDel->CreateDelegator(0, spComp, 0, spHook, 0,
riidResult, ppvResult);
}
Figure 8 Wrapping the Microsoft FlexGrid Control
<AOPFramework>
<Component Name="Microsoft FlexGrid Control, version 6.0">
<CLSID>{6262D3A0-531B-11CF-91F6-C2863C385E30}</CLSID>
</Component>
<Aspects>
<Aspect Name="Call-Tracing Aspect">
<CLSID>{49EFA33A-FDB2-4AED-807E-4D447D096642}</CLSID>
</Aspect>
<Aspect Name="Synchronization Aspect">
<CLSID>{6DBA0579-8846-46A2-BEFF-382725A1022C}</CLSID>
</Aspect>
</Aspects>
</AOPFramework>
Figure 12 .NET Call-tracing Aspect
internal class CallTracingAspect : IMessageSink {
private IMessageSink m_next;
private String m_typeAndName ;
internal CallTracingAspect(IMessageSink next) {
// Cache the next sink in the chain
m_next = next;
}
public IMessage SyncProcessMessage(IMessage msg) {
Preprocess(msg);
IMessage returnMethod = m_next.SyncProcessMessage(msg);
PostProcess(msg, returnMethod);
return returnMethod;
}
private void Preprocess(IMessage msg) {
// We only want to process method calls
if (!(msg is IMethodMessage)) return;
IMethodMessage call = msg as IMethodMessage;
Type t = Type.GetType(call.TypeName) ;
m_typeAndName = t.Name + "." + call.MethodName ;
Console.Write("PreProcessing: " + m_typeAndName + "(");
// Loop through the [in] parameters
for (int i = 0; i < call.ArgCount; ++i) {
if (i > 0) Console.Write(", ");
Console.Write(call.GetArgName(i) + "= " + call.GetArg(i));
}
Console.WriteLine(")");
// set us up in the callContext
call.LogicalCallContext.SetData(ContextName, this);
}
private void PostProcess(IMessage msg, IMessage msgReturn)
{
// We only want to process method return calls
if (!(msg is IMethodMessage) ||
!(msgReturn is IMethodReturnMessage)) return;
IMethodReturnMessage retMsg = (IMethodReturnMessage)msgReturn;
Console.Write("PostProcessing: ");
Exception e = retMsg.Exception;
if (e != null) {
Console.WriteLine("Exception was thrown: " + e);
return;
}
// Loop through all the [out] parameters
Console.Write(m_typeAndName + "(");
if (retMsg.OutArgCount > 0) {
Console.Write("out parameters[");
for (int i = 0; i < retMsg.OutArgCount; ++i ) {
if (i > 0) Console.Write(", ");
Console.Write(retMsg.GetOutArgName(i) + "= " +
retMsg.GetOutArg(i));
}
Console.Write("]");
}
if (retMsg.ReturnValue.GetType() != typeof(void))
Console.Write("returned [" + retMsg.ReturnValue + "]");
Console.WriteLine(")");
}
•••
}
|