Scripting follow up on case #7 – can a script call a running EA AddIn

Calling a running AddIn from a script was has been a really interesting issue – read the original post for the background to this use case – and with some useful observations from my associated post on the Sparx Forum.

I have explored further, and not quiet managed a workaround, and as outlined in this post do not expect a viable practical solution, but in the spirit of sharing my findings and coming to this conclusion, here is an outline of my experiments.

A test DLL

To help work discover what goes on with an EA AddIn, I produced a small AddIn(DLL) – called DLLTester.  It’s main purpose was to be a very simple, normal EA AddIn with the added benefit that I could use it capture relevant system information that could help in investigating “the issue”.

EA and scripter do not share a DLL

Thanks to Geert who pointed out what appeared to be a very simple solution.  Based on windows documentation and various posts it looked like our AddIn DLL code would only be loaded a single time by the EA, process and if subsequent instances were required, they would share the same code. This would mean that static data would be shared across the different instances.  So if during the initial load we store a reference to our AddIn as a static variable, it could be retrieved by all subsequent instances as required.

In my initial test, I run EA and my AddIn loaded (the initialisation worked fine).  I then create a new object in the script (see code below).  And based on the premise that there is only a single copy of the DLL loaded, I would expect the code to be the same as the already loaded AddIn DLL, and hence the static data would already be initialised, and readily available as a reference to the required DLL.

Set myDLL= CreateObject("DLLTester.DLLTester")
Set myAddInRef = myDLL.getRef()

However this was not to be – I found that the static variable retrieved by my script was not set, and hence the was not the same code.  Upon further inspection, using Process Explorer  (see from the screen shot below), I found that EA and its Scripter engine exist in different processes (see PID) and hence their DLL’s are clearly different.

Both EA and SSProfile32 have a copy of the DLL

Both EA and SSProfile32 have a copy of the DLL

The fact that they are different processes not only prevents an elegant solution but dare I say a practical safe solution at all,  unless we start looking at using interprocess communication(?). Now even with my early background in writing operating system software (RSX, VMS – I am that old!) and that one of the key people behind both those operating systems and windows (Dave Cutler) which could suggest a common approach to the windows internals, I still wasn’t encourage to explore, perhaps another time.  So I think I’ll have to park the idea that we can implement case #7 “call an Addin from a script” for the moment.

However, before finishing, and purely out of interest I was keen to have a quick look at what I may be able to find. So here are a few things I did.

Looking for more information about the EA copy of the DLL.

Never one to give up I reckoned there was a means to get more information.  As we know that the parent of our Script control is EA, and the required DLL was a child of EA,  so we should be able to trace back and get some information. The steps I followed are:

  1. Get out process ID
  2. Get the process ID of our parent – EA
  3. Get information about the modules that EA had loaded

1. Get our process ID

To start with we need to know who we are, by which I mean what process are we.  Using some code I found stackoverflow (see below) I was able to get the process ID of the script control.  Basically, the method used creates a child process, we then inspect its parent id which is us, the script control.

Function MyProcessID ()
 MyProcessID = 0 ' Initially assume failure
 Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
 If objWMIService Is Nothing Then Exit Function ' for Guest and super-limited accounts
 Set objWshShell = CreateObject("WScript.Shell")
 Set objChildProcess = objWshShell.Exec ( """%ComSpec%"" /C pause" ) ' Fork a child process that just waits until its killed
 Set colPIDs= objWMIService.ExecQuery ( "Select * From Win32_Process Where ProcessId=" & objChildProcess.ProcessID,, 0 )
 For Each objPID In colPIDs 
 MyProcessID = objPID.ParentProcessId ' Return child's parent Process ID, i.e. my PID
 Next
 Call objChildProcess.Terminate() ' Terminate our temp child
 End Function MyProcessID

2. Get the process ID of EA

With the process ID of the script control we can easily get the process ID of the parent i.e.EA using the following code:

 Function getparentid(pid)
 Dim parentid 
 parentid = 0
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
 Set colItems = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessID = " & pid)
 For Each objItem in colItems
 if objItem.ProcessID = pid then
 parentid = objItem.ParentProcessID
 end if
 next
 getparentid = parentid
 end function

3. Find EA loaded module information

Using the process ID of EA we can get information about the EA process and specifically look for modules it has loaded; this should include our target AddIn DLL.

This is where life started to get a little more difficult, in that I was unable to work out how to do this using VBScript, so I had to resort to some real coding to access the required system information.  For simplicity of testing, I added the code to my DLLTester using functions that would return details for loaded modules as needed.

Below is a snippet from one test function.  Using the the process id (pid) of EA as the input it iterates through the modules, finding our target (“DLLTester”) and the outputs the required information.

 Process p = Process.GetProcessById(pid);
 MessageBox.Show("Process " + p.Id + " Module count = " + p.Modules.Count);
 foreach(ProcessModule module in p.Modules) {
 answer += "\r\n.." + module.FileName;
 al.Add("\r\n.." + module.FileName);
 if (module.FileName.Contains("DLLTester"))
  {
   myDLL = module;
   String moduleInformation = "Module information for " + module.ToString();
   moduleInformation += "\r\n Module name: " + module.ModuleName;
   moduleInformation += "\r\n Base address: " + module.BaseAddress.ToString();
   moduleInformation += "\r\n Entry point address: " + module.EntryPointAddress.ToString();
 moduleInformation += "\r\n Memory size: " + module.ModuleMemorySize;
 moduleInformation += "\r\n File name: " + module.FileName;
 MessageBox.Show(moduleInformation);

This worked fine.  If we ignore the cross process nature of our initial use case, and the real risk it could cause a crash or 2,  would it be possible to obtain more information, for example a reference to our module which we could use.  Well I explored a range of classes, for example:

But after an hour or so I had more information but nothing that provided me with a usable object. So I have to acknowledge that my lack of detailed knowledge of the windows internals probably means I won’t solve this and so for the time being I will gracefully going to step back from this challenge. However, if anybody does have a practical solution I’d be really interested but somehow I don’t expect one to exist (there’s a challenge!).

Before I end what I did discover during this experiment were a few useful items that may be of interest to others:

  • If you want to use get the list of DLL from the modules in a process there was a change at .Net 4 which means that managed DLL’s are no longer included in the list of modules – see stackoverflow for more information.
  • Then if you are using .Net 3.5 you may see that VS does not load your modules in debug mode. This is due to an assumption that the default framework is .Net 4.  To resolve this you need to place a configuration file name EA.exe.config in the EA.exe program directory (on my system C:\Program Files (x86)\Sparx Systems\EA) whose contents are:
<configuration>
 <startup useLegacyV2RuntimeActivationPolicy="true">
 <supportedRuntime version="v2.0.50727"/>
 <supportedRuntime version="v4.0"/>
 </startup>
 </configuration>

Useful tools include:

  • System internals – I’ve used these a lot over the years.  In this experiment I used – Process Explorer – worth checking through, as are  the other tools as there are a range of utilities that may be useful at some time.
  • OleView – an OLE/COM Object Viewer, which is provided in the Microsoft Windows Kits and could well have already installed on your system if you are running VS – also check through this directory for other useful tools (e.g. Orca for checking any msi’s you create)

Well I know this is not really about EA but more about windows programming, but then I’ve found that more than half the issues with producing an EA AddIn is about windows!

Until the next time.

Have fun EXploringEA.

Adrian

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s