ds-blue-logo
Preview  |  SOLIDWORKS USER FORUM
Use your SOLIDWORKS ID or 3DEXPERIENCE ID to log in.
cmchris misztur29/06/2010

This was inspired by Jim's IDispatch.Invoke interception @ https://forum.solidworks.com/thread/27197?tstart=60.


swApplication.SetAddinCallbackInfo(0, targetObject, Cookie);

is very limiting, since all callback methods must be contained in targetObject.  I tried creating a class that implements IDispatch, hoping to intercept the Invoke method and dispatch my callbacks at runtime.  That did not work.

After some trial and error I finally got it to work with System.Reflection.Emit.

First, we need to define the interface that made all this possible:

[ComVisible(true)]
    [Guid("00020400-0000-0000-c000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IDispatch3
    {
    }

Next, we need to generate an assembly, type and methods on the fly:

[ComVisible(true)]
    public class CodeGenerator
    {
        public object tbBakedInstance;

        public CodeGenerator()
        {
            Guid g = Guid.NewGuid();
            AssemblyName asmname = new AssemblyName();
            asmname.Name = "temp" + g;
            AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(asmname, AssemblyBuilderAccess.Run);
            ModuleBuilder modbuild = asmbuild.DefineDynamicModule("test");

            TypeBuilder tb = modbuild.DefineType("testType", TypeAttributes.Public, null, new Type[] { typeof(IDispatch3) });
            MethodBuilder mb = tb.DefineMethod("testMethod", MethodAttributes.Public, typeof(void), null);
            MethodInfo mi = typeof(System.Diagnostics.Debug).GetMethod("WriteLine", new Type[] { typeof(string) });
            ILGenerator il = mb.GetILGenerator(256);
            //il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldstr, "hello_from_dynamic_testMethod");
            il.EmitCall(OpCodes.Call, mi, null);
            il.Emit(OpCodes.Ret);
            tbBaked = tb.CreateType();

            tbBakedInstance = Activator.CreateInstance(tbBaked);
        }
    }

What we just did is create an assembly at runtime, containing a testType Type.  This type contains one void method named testMethod.  This method just writes to our Debug console so that we know it was called.  Last we instantiate a public field tbBakedInstance that will be of our testType.

Next, we call set our callback info:

myCode = new CodeGenerator();
callme = myCode.tbBakedInstance;
_swApplication.SetAddinCallbackInfo(0, callme, Cookie);

Now our target object for SW callbacks is our tbBakedInstance.

And finally, we build our command tabs and menus:

_CommandGroup.AddCommandItem2("Command3", -1, "Trigger Command 3", "ToolTip for Command3", 3, "testMethod", "Command2Enable", 103, (int)(swCommandItemType_e.swMenuItem | swCommandItemType_e.swToolbarItem));

The above command item has our dynamic testMethod as our callback.

We run our addin, click our command button 3 times and we're all good:

dd.JPG

What does this do for me?

I can sew random public void methods all over my code to be my command/menu callbacks.  Then I tag them with an attribute.  When my addin loads I can then iterate my types, pull all those tagged methods out as MethodInfo.  Then, I can create a dynamic type.  In my dynamic type I can create a dynamic method for each one of the tagged methods.  The dynamic method's IL will basically invoke my tagged method, acting as a proxy between SW and my tagged methods that do the real work.