Reprint: http://www.it1352.com/534235.html
Problem:
I am writing a Windows Form application in. Net to list all running instances of a third-party CAD/CAM software (in this C ASE CATIA) and let the user to choose one of the them to perform couple of automated tasks. For performing automated tasks, I need to get the specific instance of COM objects-compared to Getobject () which gives M e a non-specific COM instance. Is there a-specific COM instance using window handle or any other methods?
Update:as Raymond said there is no single solution for all COM objects; However I managed to get CATIA COM objects using following code (which uses ROT to fill a list with all CATIA com Instance s name):
<dllimport ("User32.dll", Charset:=charset.auto) > Private Shared Sub getclassname (ByVal hWnd as System.IntPtr, ByVal Lpclassname as System.Text.StringBuilder, ByVal nMaxCount as Integer) End sub<dllimport ("Ole32.dll", Exactspelling:=true, Preservesig:=false) > Private Shared Function getrunningobjecttable (ByVal reserved as Int32) as IRunningObjectTable End function<dllimport ("Ole32.dll", Charset:=charset.unicode, Exactspelling:=true, Preservesig:=false) > Private Shared Function createitemmoniker (ByVal Lpszdelim As String, ByVal Lpszitem as String) as IMoniker End function<dllimport ("Ole32.dll", Exactspelling:=true, Preservesig:=false) > Private Shared Function Createbindctx (ByVal reserved as Integer) as IBindCtx End Functiontry Dim rotobject As Object = Nothing Dim Runningob Jecttable as IRunningObjectTable Dim monikerenumerator As IEnumMoniker = Nothing Dim monikers (1) as IMoniker Runn ingobjecttable = getrunningobjecttable (0) RUNNINGOBJECTTABLE.ENumrunning (Monikerenumerator) monikerenumerator.reset () Dim numfetched as IntPtr = New IntPtr () while (Monikerenu Merator. Next (1, monikers, numfetched) = 0) Dim ctx As IBindCtx ctx = Createbindctx (0) Dim runningobjectname A s String = "" Monikers (0). GetDisplayName (CTX, Nothing, runningobjectname) Runningobjectname = Runningobjectname.toupper If (not Runnin Gobjectname.equals ("")) then Dim runningobjectins as Object = Nothing Runningobjecttable.getobject (m Onikers (0), runningobjectins) ' Check if object is a Catia object Try Dim catiains as INFITF. application = Nothing Catiains = DirectCast (Runningobjectins, INFITF. Application) ListCATIA.Items.Add (catiaIns.Windows.Count) Catch Exc as Exception MessageBox.Show (Exc.tostring ()) End try End If end Whilecatch Exc as Exception Throw excend Try
However, all CATIA instances refer to first CATIA application loaded. No idea why, anybody?
Solution Solutions
The "problem" in your, code is, calling GetObject always returns the first active server so it finds in the Running Object Table (ROT). Enumerating the ROT doesn ' t change that behavior and was a little frustrating because it does show that there was more than One server in the ROT. Note that some of the items returned in the enumeration is not actually be running:getobject returns the first Activ e -server-not necessarily the first one returned by the enumeration.
However, in the case of CATIA in particular it was possible to get a specific instance. I suspect it is possible with many applications if you can get the particular instance of interest to run some code, on de Mand, before you actually get a pointer to the COM instance.
For CATIA, which is a rough outline of the process I use:
1. Make a DLL with Functions:hresult __stdcall Comarshaltofile (iunknown* punk, const char* const filePath)/* u Ses ':: CreateStreamOnHGlobal ', ':: Comarshalinterface ', ':: Cogetmarshalsizemax ', and ':: Gethglobalfromstream ' to Marsh Al the IUnknown to the specified file. */HRESULT __stdcall Comarshalfromfile (iunknown** ppunk, const char* const FILEPATH)/* uses ':: Createstreamonhgloba L ' and ':: Counmarshalinterface ' to marshal from the file to an IUnknown pointer. */2. In CATIA:Note:this-only needs-to IS-do on the development computer. Make a new "VBA projects" macro library. Add "declare" statements for: "LoadLibrary" (Windows API) "Comarshaltofile" (DLL specified above) add a F Unction Public Function Marshalcatiatofile _ (Marshalinstancefilepath as String, _ Marsh Aldllfolder as String) as Long marshalcatiatofile calls "LoadLibrary" to load the C + + DLL and then calls Comarshalto File (in DLL) tO Marshal the CATIA instance to a file. Remove the macro library from CATIA ' s list of macro libraries.3. Create a file: "C:\Temp\CatiaOnTheFlyCatScripts\OnTheFlyCatScript.catvbs" The file can be empty.4. In CATIA:Note:this must is done for *each* user of CATIA on *each* computer used. It may be possible the available to the All users without individual setup required:it are saved in "frameuseral Iases. CATSettings "It may also is possible to reverse engineer the settings file and set up the needed data from Outsi De CATIA. Add "C:\Temp\CatiaOnTheFlyCatScripts\" as a new "directories" macro library. Make the added library ' current ' use ' Tools--Customize--Commands Macros ' to assign a ' User Alia S: "To the" ontheflycatscript.catvbs "script file. Name the alias "Executeontheflycatscript". Remove the macro library from CATIA ' s list of macro libraries. The changes to is Saved.5. Vb.net/c# Program:add the DLL (from step 1) and the CATVBA macro library (from step 2) as "Embedded Resource to the project. During program execution:extract the DLL and macro library to a appropriate location. The Load the DLL into session using "LoadLibrary". Create the file: "C:\Temp\CatiaOnTheFlyCatScripts\OnTheFlyCatScript.catvbs" the "Ontheflycatscript.catvbs" 'll is executed in CATIA. It uses CATIA. Systemservice.executescript to execute the ' marshalcatiatofile ' function in the CATVBA macro library. Add method of choice to the this file to indicate success/failure. I Use a dialog box with the appropriate title. To execute the ' ontheflycatscript.catvbs ': Using the Windows API functions, get the window handle for the "Power Input" box at the bottom right of the "desired" CATIA window. Using the Windows API functions (*not* "SendKeys") send "C:executeontheflycatscript" + {Enter} to the "Power Input". Wait for the ' completion ' signal from the script. If you used a dialog box, use the Windows API function to close it. Assuming the script succeeded in marshaling the CATIA instance to a file, call the DLL function comarshalfromfile To get the CATIA instance.
It's a lot of work with many "moving" parts but it does allow you to automate multiple CATIA sessions "simultaneously". Works well for my purposes:automated extraction of data from a set of CATIA models and automated creation of a set of CAT IA models using more than one CATIA session at a time. The bottleneck for my application are the individual CATIA session-not CPU resources (using a dual processor 4 or 6 core Per processor machine); Adding more sessions improves throughput.
[Reprint] Get a specific instance of a COM object in vb.net (Getting a specific instance of COM objects in vb.net)