This was inspired by Jim's IDispatch.Invoke interception @ https://forum.solidworks.com/thread/27197?tstart=60.
<p>swApplication.SetAddinCallbackInfo(0, targetObject, Cookie);</p>
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:
<p>[ComVisible(true)]<br> [Guid("00020400-0000-0000-c000-000000000046"),<br> InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]<br> public interface IDispatch3<br> {<br> }</p>Next, we need to generate an assembly, type and methods on the fly:
<p>[ComVisible(true)]<br> public class CodeGenerator<br> {<br> public object tbBakedInstance;<br><br> public CodeGenerator()<br> {<br> Guid g = Guid.NewGuid();<br> AssemblyName asmname = new AssemblyName();<br> asmname.Name = "temp" + g;<br> AssemblyBuilder asmbuild = System.Threading.Thread.GetDomain().DefineDynamicAssembly(asmname, AssemblyBuilderAccess.Run);<br> ModuleBuilder modbuild = asmbuild.DefineDynamicModule("test");<br><br> TypeBuilder tb = modbuild.DefineType("testType", TypeAttributes.Public, null, new Type[] { typeof(IDispatch3) });<br> MethodBuilder mb = tb.DefineMethod("testMethod", MethodAttributes.Public, typeof(void), null);<br> MethodInfo mi = typeof(System.Diagnostics.Debug).GetMethod("WriteLine", new Type[] { typeof(string) });<br> ILGenerator il = mb.GetILGenerator(256);<br> //il.Emit(OpCodes.Nop);<br> il.Emit(OpCodes.Ldstr, "hello_from_dynamic_testMethod");<br> il.EmitCall(OpCodes.Call, mi, null);<br> il.Emit(OpCodes.Ret);<br> tbBaked = tb.CreateType();<br><br> tbBakedInstance = Activator.CreateInstance(tbBaked);<br> }<br> }</p>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:
<p>myCode = new CodeGenerator();<br>callme = myCode.tbBakedInstance;<br>_swApplication.SetAddinCallbackInfo(0, callme, Cookie);</p>
Now our target object for SW callbacks is our tbBakedInstance.
And finally, we build our command tabs and menus:
<p>_CommandGroup.AddCommandItem2("Command3", -1, "Trigger Command 3", "ToolTip for Command3", 3, "testMethod", "Command2Enable", 103, (int)(swCommandItemType_e.swMenuItem | swCommandItemType_e.swToolbarItem));</p>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:
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.
SolidworksApi macros