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;
Learn why the map is cool in Go!
A method to design records so that they're allocated on a specific byte boundary, such as 16 bytes, 512 bytes, 4096 bytes, etc.
Learn the command line used to compile System.pas in Delphi
How to free more space on your home drive by redirecting the location for SDKs in RAD Studio