3

A running instance of EA can be accessed from a Python script with something like:

from win32com import client
eaApp = client.GetActiveObject("EA.App")
eaRepo = eaApp.Repository

However, this seems to always returns the COM object of the instance first started.

Let's say we have a script that needs to starts a new instance of EA. It does so by calling os.startfile(eapxFile) with eapxFile being the path to an empty EA file. Then it should import some XMI file, for which it needs access to the COM object.

At the same time, a couple of other (older) instances of EA are open. How can the COM object of the new instance of EA be retrieved?

Note: An alternative way of starting a new instance of EA, if immediate access to its COM object is possible, would of course be feasible as well. Maybe even using the COM interface directly. Most importantly, other running instances of EA can not be used.

Benjamin4991
  • 123
  • 7
  • I don't get it. If you start a new instance, then you already have the COM object don't you? There would not be a need to get a specific running instance. – Geert Bellekens Nov 09 '21 at 17:49
  • @GeertBellekens He wants to pick a certain instance from multiple running ones I guess. Personally I only deal with a single EA instance in all cases. Simple requirement for my scripts. – qwerty_so Nov 12 '21 at 11:03
  • @GeertBellekens I started the new instance by calling the EA executable. – Benjamin4991 Nov 12 '21 at 12:49
  • Basically if there is a way to get a COM object for a newly created instance of EA, that would be awesome. I tried using `DispatchEx` to get a 'new' COM object as mentioned here: https://stackoverflow.com/questions/18648933/using-pywin32-what-is-the-difference-between-dispatch-and-dispatchex but that just seems to freeze my script. – Benjamin4991 Nov 12 '21 at 13:04
  • Seems you are right and Dispatch also accesses the current runnng instance. – qwerty_so Nov 12 '21 at 13:49
  • DispatchEx has a different signature: `def DispatchEx(clsid, machine=None, userName=None, resultCLSID=None, typeinfo=None, UnicodeToString=None, clsctx=None) ` rather than `def Dispatch(dispatch, userName=None, resultCLSID=None, typeinfo=None, UnicodeToString=None, clsctx=21) ` – qwerty_so Nov 12 '21 at 13:51
  • A bit of googling was very unpromising. Even the author of win32api has no example of that Ex. I'm no windoze specialist. Would well be interested in an answer. – qwerty_so Nov 12 '21 at 13:56
  • Just an abstruse idea: if you fliddle with the registry and place EAx.app as a doppleganger you might trick windoze. – qwerty_so Nov 12 '21 at 16:13
  • this answers seems to indicate how to create a new instance of a com object in python: https://stackoverflow.com/a/36711595/2018133 – Geert Bellekens Nov 12 '21 at 18:20
  • @GeertBellekens On trying the suggested code, I saw it manages to return a 'different' COM object - that is, comparing `app1 == app2` returns `False` - but they both still seem to be pointing to the same EA instance. – Benjamin4991 Nov 13 '21 at 15:46
  • there is no defined way to get a specific instance of an out-of-process COM object[i.e. apps like Excel, word, ], unless that application specifically provides any way to enumerate all its running instances. – Anand Sowmithiran Nov 18 '21 at 16:54
  • @AnandSowmithiran: You mean by adding the EA instance to the Running Object Table? Perhaps EA does this then you could get your hands on it. – Alois Kraus Nov 19 '21 at 12:44
  • The application(in this case EA) has to register with ROT or provide any other ways to enumerate all its active running instances. I am not sure if EA does it though. – Anand Sowmithiran Nov 19 '21 at 15:20
  • @Benjamin4991 have you tried using the psutil python module, it provides ways to enumerate all the runninc processess. You can iterate and get the active instances of EA and determine somehow[using process.create_time() !] the one launched by your script. Refer this documentation for (psutil](https://psutil.readthedocs.io/en/latest/#processes). – Anand Sowmithiran Nov 19 '21 at 15:34
  • @AnandSowmithiran I can confirm that "monikers" of different instances of EA are visible in the Running Object Table. Through `moniker.GetDisplayName(context, moniker)` a display name like `!Sparx.EA.App:14432` is returned. Starting with a moniker object, or to be precise, a `PyIMoniker` object like that, how do I get to the COM object so that I can access the EA API? – Benjamin4991 Nov 20 '21 at 13:51
  • When you iterate the monikers by calling pythoncom.GetRunningObjectTable, the PyIMoniker exposes a BindToObject method, that may need to be used, or if somehow you get the `Dispatch` interface, then you could call methods exposed by EA app. see this youtube [video](https://www.youtube.com/watch?v=tUthExQRzOM), it may give some clues. – Anand Sowmithiran Nov 20 '21 at 17:15
  • As the EA guru @GeertBellekens asks above, can you post your script. if your script is creating new instance of EA, it does not matter there were other running EA instances, your script would have got the pointer to the instance that was fired by your script, so you could get its Repository and do your automation, is it not? – Anand Sowmithiran Nov 20 '21 at 17:33
  • 1
    @AnandSowmithiran That's exactly what _not_ happens. Dispatch will eventually create an instance if there is none. But else it will return the already running one and _not_ create a new one. – qwerty_so Nov 21 '21 at 23:20
  • @AnandSowmithiran Just edited the question, hopefully clarifiying how the instance of EA was launched that I am trying to access through COM. – Benjamin4991 Nov 22 '21 at 11:32
  • As an alternative you could start by accessing the (new or existing) EA instance using Dispatch, and then use Repository.OpenFile(eapxfile) to open the new model. Downside to this approach is that it will highjack your existing EA instance if you have one running. – Geert Bellekens Nov 22 '21 at 11:46
  • Yes @GeertBellekens, that is unfortunately exactly what I need to avoid. Other instances of EA must be left untouched. – Benjamin4991 Nov 22 '21 at 11:59
  • That's too bad. I'm pretty sure there must be a solution. I can do it in VBScript using `CreateObject("EA.Repository")` so there must be an equivalent to do the same in python – Geert Bellekens Nov 22 '21 at 12:28

1 Answers1

1

In VBScript you can create an EA.Repository object in a brand new EA instance using

dim repository
set repository = CreateObject("EA.Repository")

The Python equivalent seems to be

from comtypes.client import CreateObject
repository = CreateObject("EA.Repository")

then open a model using

repository.OpenFile(myEapFile)

close the model using

repository.CloseFile()

followed by

repository.Exit()

to make sure the instance of the ea.exe process is closed.

Geert Bellekens
  • 12,788
  • 2
  • 23
  • 50
  • Cool stuff, I didn't know about the comtypes library. On playing around with that in the Python Console, I see `CreateObject` reliably gives me _one_ new EA instance no matter how many other _manually started_ EAs are already running. But once I start one EA instance with `CreateObject`, if I try to do it again I either get the same old one I started previously or the console/script simply freezes. Guessing this might be an issue on EAs side. Unfortunately, starting up several instances of EA by script is a use case for us... – Benjamin4991 Nov 22 '21 at 15:00
  • I added another question to ask specifically for how to start a new EA through COM: https://stackoverflow.com/questions/70068010/get-a-com-object-of-a-new-enterprise-architect-instance-in-python - can't really accept this as an answer for the question I posed. Feel free to put the answer there as well. – Benjamin4991 Nov 22 '21 at 15:13
  • Verified that this is working :-) – qwerty_so Nov 22 '21 at 15:23
  • @Benjamin4991 this new question is just the same as this one and Geert answered it well! – qwerty_so Nov 22 '21 at 15:25
  • Actually, you're right. – Benjamin4991 Nov 22 '21 at 15:39