About the author
For the past few days, I have been working on updating CCWSpy.NET to enable it to capture any event of any control. To that end, I have written a dynamic class in a dynamic assembly. So, in order to examine whether my IL code emission is correct or not, I had to save the assembly. Even though the assembly managed to get saved to disk, I am unable to view any types that I've created.
constructor EventSpy.Create;type TArray_Of_Type = array of &Type; TArray_Of_Object = array of TObject;var LAsm: AssemblyBuilder; name: AssemblyName; LModule: ModuleBuilder; fld: FieldBuilder; helperClass: TypeBuilder; ctor: ConstructorBuilder; il, ilctor: ILGenerator; basector: ConstructorInfo;
mytype, delgType, targType: &Type; whichEvents: BindingFlags; allEvents: array of EventInfo; miReportEvent2, miReportEvent: MethodInfo; ev: EventInfo; miInvoke: MethodInfo; prams: array of ParameterInfo; len: Integer; mthparams: array of &Type; mthname: string; mthd: MethodBuilder; spyimpl: &Object;
Types: array of &Type; I, J: Integer;begin inherited Create;
ThisSpy := Self; Name := AssemblyName.Create; Name.Name := 'EventSpy'; LAsm := AppDomain.CurrentDomain.DefineDynamicAssembly(Name, AssemblyBuilderAccess.RunAndSave);
// create dynamic module LModule := LAsm.DefineDynamicModule('EventSpyModule', true);
// EventSpyImpl = class // private // FSpy: EventSpy; // public // constructor Create(ASpy: EventSpy); // procedure OnEventXxx(sender: TObject; e:EventXxxArgs); // end; // // constructor EvSpyImpl.Create(ASpy: EventSpy); // begin // inherited Create; // FSpy := ASpy; // end; // // procedure EvSpyImpl.OnEventX; // begin // FSpy.ReportEvent('SomeEvent', sender, e); // end;
// First, create the class helperClass := LModule.DefineType('EventSpyImpl', TypeAttributes.Public);
// Add a field called FSpy of type EventSpy. fld := helperClass.DefineField('FSpy', typeof(EventSpy), FieldAttributes.Private);
ctor := helperClass.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, TArray_Of_Type.Create(typeof(EventSpy))); ilctor := ctor.GetILGenerator; SetLength(Types, 0); basector := typeof(&Object).GetConstructor(Types); // begin ilctor.Emit(OpCodes.Ldarg_0); ilctor.Emit(OpCodes.Call, basector); // inherited Create;
ilctor.Emit(OpCodes.Ldarg_0); ilctor.Emit(OpCodes.Ldarg_1); ilctor.Emit(OpCodes.Stfld, fld); // FSpy := spy; ilctor.Emit(OpCodes.Ret); // end;
// Now create an OnEventX method for every event X exposed by // the target object (Sender). Each method looks like this: // // procedure OnEventX(Sender: TObject; e: EventXxxArgs) // begin // FSpy.ReportEvent("EventX", Sender, e); // end; // // // The MSIL for this from Borland Reflector: // // ldarg.0 // ldfld class SpyTestForm::FSpy // ldstr "EventXxx" // ldarg.1 // ldarg.2 // callvirt void SpyTestForm::ReportEvent(string, // object, class [mscorlib]System.EventArgs) // ret // targType := SpyObject.GetType; whichEvents := BindingFlags.Instance or BindingFlags.Public; allEvents := targType.GetEvents(whichEvents);
miReportEvent := Self.GetType.GetMethod('ReportEvent');
// loop over all events to create each handler for I := Low(allEvents) to High(allEvents) do begin ev := allEvents[I]; // get event handler (delegate) Type delgType := ev.EventHandlerType;
// To get parameter types (eg., FooEventArgs), need to get // parameter types of the delegate's Invoke method. miInvoke := delgType.GetMethod('Invoke'); prams := miInvoke.GetParameters; len := Length(prams);
SetLength(mthparams, len); // Copy parameter types into an array for J := Low(prams) to High(prams) do mthparams[J] := prams[J].ParameterType;
// name of method is "On" + eventName, eg., "OnSomeEvent" mthname := 'On' + ev.Name;
// create the method with proper signature mthd := helperClass.DefineMethod(mthname, MethodAttributes.Public, typeof(void), mthparams);
// Now generate the MSIL as described in comment above. il := mthd.GetILGenerator;
il.Emit(OpCodes.Ldarg_0); // Self
il.Emit(OpCodes.Ldfld, fld); // FSpy il.Emit(OpCodes.Ldarg_1); // Sender il.Emit(OpCodes.Ldarg_2); // e il.Emit(OpCodes.Call, miReportEvent); // Self.ReportEvent(Sender, e);
il.Emit(OpCodes.Ret); end;
// Once all fields and methods are defined, finally create the Type. mytype := helperClass.CreateType;
LAsm.Save('SpyTest.dll');end;
Continued discussion of undocumented Delphi 8 Property Access Specifiers, and other ways of adding and removing delegates / events handlers, including clearing the list of all the delegates / event handlers.
This article discusses the new Delphi 8 property access specifiers.
A method pointer is now the same as a global procedure, ie, procedure of object = procedure.