|
Tarn GURU
Joined: 10 Oct 2000 Posts: 867 Location: USA
|
Posted: Wed Apr 10, 2013 5:17 am
Notes on Building a COM Component for Use by CMUD |
A few points on building COM components for use with CMUD:
Visual Studio 2012 does not seem to be very automatic about building dual interface COM objects using C++ via ATL. It's easy to build one which is recognized when early bound by another program. Getting them to work with scripting languages is another matter even if you ask for a dual interface (that applies within Visual Studio as well as by CMUD). It turned out to be easier for me to build a skeleton COM library in Visual Studio (C++) 6 and then import it into Visual Studio 2012 than to get VS 2012 to just produce the library by itself. Maybe I was doing something wrong but the steps seemed quite similar. Compiling to 32 bit XP-compatible code didn't help.
If you don't mind dragging around the .NET framework, producing a COM component in VB.net does work (and I assume C#; I used VB.NET so the test code would be vbscript equivalent). That .NET component is able to early bind or late bind to whatever other components you need.
When testing, even after #UNVAR'ing the reference to the last COM object in CMUD does not unload/release the library (which means that you can't recompile it without closing CMUD). Reference:
http://forums.zuggsoft.com/forums/viewtopic.php?t=17226
I do not believe that this is a problem with CMUD; I see the same behavior within Visual Studio itself (though when running a calling application in debug mode I think VS "cheats" by actually using the equivalent of another process, which does end and release the library after the debugging session is stopped).
The CMUD %comget doesn't just work with properties; it also works with functions/methods.
One other comment- I had a bit of trouble accessing an existing CMud application object: creating a new one returns a reference but the call for an existing one fails even if CMUD is already running. |
|
|
|
Tarn GURU
Joined: 10 Oct 2000 Posts: 867 Location: USA
|
Posted: Mon Apr 15, 2013 4:57 am |
Followup:
Creating the COM object works as expected:
#VAR testinstance {%comcreate("TestLib.CTestClass")}
One more argument than you might expect must be passed in the case of a function (CMUD has %comget() but not a function specifically to invoke a method which returns a value, so the extra arg is probably a side effect of that).
#var tmp %comget(testinstance,"aaa","bbb","ccc","ddd","???")
works in response to the compiled call
STDMETHODIMP CTestClass::execcmd(BSTR dat1, BSTR dat2, BSTR Arg1, BSTR Arg2, BSTR* Arg3)
where Arg3 is really the return value of the COM call as seen in the scripting language (the C style return being the success/failure of the function at the COM API level).
The return value works properly and is stored in tmp.
I did have a problem with passing variable arguments into COM calls, for example
#var tmp %comget(delmea,"aaa","bbb","ccc",%concat(@tmpinput,":"),"???")
Having located the other calls, triggers, and variables related to the DLL in a class it silently failed when tmpinput was a top level variable but succeeded if it was in the same class even though the variable storing the COM class instance was top level.
So, data in and data out. I could put together a demo package for C++ if anyone's interested (the normal .NET languages like VB/C# being much easier- just creating the project and adding calls as normal seems to work). |
|
|
|
Anaristos Sorcerer
Joined: 17 Jul 2007 Posts: 821 Location: California
|
Posted: Thu Jun 06, 2013 4:36 am |
I got around the release problem by making a call to my COM object manager in the OnExit event:
The C#-side code looks like this:
Code: |
public void stop()
{
MessageBox.Show(Properties.Settings.Default.ExitMessage);
sess = null;
cmud = null;
fr = null;
ms = null;
mw = null;
rl = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
|
You have to call the garbage collector once for each COM variable. In the case above I have 6, so I call GC 6 times. |
|
_________________ Sic itur ad astra. |
|
|
|
Tarn GURU
Joined: 10 Oct 2000 Posts: 867 Location: USA
|
Posted: Fri Jun 07, 2013 1:55 am |
Anaristos wrote: |
You have to call the garbage collector once for each COM variable. In the case above I have 6, so I call GC 6 times. |
Thanks for the pointer. I'll look and see if the cleanup is being ignored at my end. |
|
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|