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;