Category Archives: Automation

Information relating specifically to EA Automation – this could include scripts, VBA, API

Automatic testing of EA AddIns

This post was inspired by a post on the Sparx forum a couple of weeks ago which was asking about automated testing for AddIns. This got me thinking about what can and can’t be done in EA using scripts and/or code, and what else may needed to perform the task.

The problem with testing is often that you need to emulate the actions of the user through a windows UI, and this is the case that was highlighted.  Without interaction through the EA UI AddIn Broadcasts events are not fired, and hence our AddIn would be outside of the processing  loop.

If we look at an example.  Say we have an AddIn which adds an attribute to every new class that is created.  To test this functionality we need to manually add our new class element through the EA UI.

So any solution we develop requires us to be able to replicate the manual interaction with the EA UI, perform the required sequence of tests, and then check the results to verify that our AddIn worked correctly.

Fortunately there are test tools that will emulate user inputs for windows application. So keen to validate my proposed solution I set to work:

  1. Create a small test AddIn – that simply added an attribute to each new class added (probably not a real use case but at least it’s easy to do an very visible}
  2. Use a test tool to emulate the manual process of adding  a new class element (and hopefully ensure our AddIn received the required events!)
  3. Run a script that could verify our new class element had its attribute added. I was planning on using EA scripts to do this, but as I’ll detail below I ended up using an external script to access the automation API to access the model and perform the checks.

So now for the detail and results of my experiment.

1. Create an AddIn

To check the process I needed a test AddIn – which I called TestingChecker, that responds to the OnPostNewElement event, which is fired by EA when a user adds a new element. Here is the code stripped of all error handling code

 
Function EA_OnPostNewElement(Repository As EA.Repository, Info As EA.EventProperties) As Boolean
Dim myElementID As Long = Info.Get(0).Value
Dim myElement As EA.Element = Repository.GetElementByID(myElementID)
    myElementID = Info.Get(0).Value
    myElement = Repository.GetElementByID(myElementID)
    If myElement.Type = "Class" Then
       Dim et As String = myElement.Type
       ' add an attribute
        Dim a As EA.Attribute = Nothing
        a = myElement.Attributes.AddNew("NewAttribute", "")
        a.Update()
        a.Name = "New Attribute"
        a.Update()
     End If
   Return True
    End Function
End Class

Nothing special here, so I assume you are happy with this and it’s operation so no further comment.

2. Testing the UI

I hadn’t used windows test tools for decades so had to reacquaint myself with what tools were available.  A quick trawl around the Internet and there were plenty to chose from, but which? Many required the writing of scripts, whilst others promised more features including the power of recording a users input which could be replayed.

So with the aim of minimising my effort, I downloaded a trial copy of one of the more powerful products and set about “recording”  my inputs to EA.  However, I found it very frustrating with the tools ability to select the correct UI items.  When the recorded scripts were replayed I found that they weren’t as good as I had hoped, and failed to select items correctly.  Hence the tool failed to perform the required tasks. I suspect the recording process relies on mouse location rather than correctly identifying controls, or the challenge with identifying the EA controls..  So rather than spend more time trying to work around these issues I decided to jump in and write the scripts myself.

Based on that decision I downloaded AutoIt (a freeware toolset).  The information on this tool stated it could perform the required control selection and provide the required user inputs.

Tools needed:

A few notes relating to the AutoIt tools.

The AutoIt Full Installation download includes all the tools required although it only contains a lite version of SciTE and I read it is recommended that you download the full version of SciTE.

You use SciTE that you use to create your scripts with AutoIt Window Info tool to get information that you need from your application under test e.g. control details. You can compile, build, run you scripts from the SciTE tools options, and if you needed can access the Koda GUI Form designer from the same menu.

In the SciTE help file, amongst other useful information, is a simple Notepad automation tutorial which was sufficient to get me started – I would suggest you work through the tutorials to gain familiarity with the tools.  Also for those interested there are several more tutorials.

Following the inevitable trials and errors I was able to write scripts that could select controls and perform the required actions.

To make it easier for testing I set up EA to use a default model (so didn’t need to specify when running EA) whose initial content was:

Initial Model Content

Initial contents of test EA model

Using the AutoIt script I produced a simple function that will:

  • run EA – opening default model
  • add a single class element
  • close the EA file
  • close EA
Func AddStuff2EA()
$PID = Run("C:\Program Files (x86)\Sparx Systems\EA\EA.exe")
WinWaitActive("WinTextCheck - Enterprise Architect")
; get focus on EA
$Res = ControlFocus("WinTextCheck - Enterprise Architect","",2)
;Send("^M") - want to send cntrl+M but there seems to be an issues - the following code found on wbe
; http://www.autoitscript.com/forum/topic/125359-solved-send-ctrlm-leaves-ctrl-pressed-in-some-way/
Send("{LCTRL down}")
Send("{m down}")
Sleep(500)
Send("{m up}")
Send("{LCTRL up}")
WinWaitActive("New Element","") ; wait for dialog to present
Send("Class 1") ; enter name of new class
Sleep(500)
ControlClick("New Element","Save",1) ; save the class dialog
; close EA
$Closed= ProcessClose($PID)
EndFunc

This code could be improved and clearly if you were in production mode, where you needed to add lots of elements and other stuff, you would restructure with a proper design!

Running this script and inspecting the model we can see that the class element has been added together with the attribute added by my AddIn.

Model Contents after adding New Class

Model Contents after adding New Class

3. Checking the results

Now I want to have a means to check the results automatically, checking that the model has the required contents.

My initial approach was to use EA scripts which could be initiated by through the UI, however a combination of issues meant that I changed my approach. The issues were:

  1. Accessing the relevant scripts within the EA UI – one of the issues I did find using AutoIt was the ability to access a specific script; I guess that is more I need to explore in the the tool to do this accurately.
  2. Ensure that the scripts were present in the test model
  3. Wanting any checking to be independent of the creation scripts.
  4. AutoIt scripts could interact with COM and hence allow me to access the automation API

So with that decision made I created a simple script that would interact with EA, using the normal automation API, to open the test EA model and check that both the new element and its attribute had been added to our model. The code for this function below:

  • Opens an instance of EA
  • Looks for the element I have added
  • Checks that the AddIn has created the attribute for this element
Func CheckCreated()
$EARep = ObjCreate("EA.Repository")
$F = $EARep.OpenFile("C:\Users\adrian\Documents13 EA Related\WinTestChecks\WinTextCheck.eap")
Local $Models = $EARep.Models ; we only have one model
Local $Model = $EARep.Models.GetAt(0)
Local $Packages = $Model.Packages
$NumPackages = $Packages.Count
; now do the check for package "Package1" with "Class 1"
For $i = 0 to $NumPackages-1
	$P = $Packages($i)
	$N = $P.Name
	if $N = "Package1" Then
 		$Elements = $P.Elements
		 $NumElements = $Elements.Count
		 for $j = 0 to $NumElements -1
			   $Element = $Elements($j)
			   $ElementName = $Element.Name
			  if $ElementName = "Class 1" Then
 				   $Attributes = $Element.Attributes
				    $NumAttributes = $Attributes.Count
				    For $k = 0 to $NumAttributes - 1
					     $Attribute = $Attributes($K)
					     $AttributeName = $Attribute.Name
					     if $AttributeName = "New Attribute"  Then
						       ConsoleWrite("Attribute found" & @CRLF)
						       ConsoleWrite("Closing EA" & @CRLF)
						       $EARep.Close()
						       ConsoleWrite("====== Checks complete ======" & @CRLF)
						       return true
					     endif
			 	   Next
			   EndIf
		  Next
	 EndIf
Next
ConsoleWrite("Closing EA" & @CRLF)
$EARep.Close()
$EARep = 0
$EARep = ""
ConsoleWrite("====== Checks complete ======" & @CRLF)
return False
EndFunc

This did the trick (once again a piece of test code so for the real world would expect to see something a little more refined!)

Final script

Having got the separate parts working I created a script that calls these functions plus including some code to output results to a log file as illustrated below:

#include "AddStuff2EA.au3"
; Script Start - Add your code below here
ConsoleWrite("Starting EA test" & @CRLF)
;---------------
AddStuff2EA(")) ; we add a class to EA
;---------------
ConsoleWrite("Checking EA model" & @CRLF)
 ; log file
	$myfilename = 	"C:\Users\adrian\Documents\testingChecking.txt"
	$FSO = objCreate("Scripting.FileSystemObject")
	If $FSO.FileExists($myFileName) Then
  ; we want to open with append
		  $FileObject= $FSO.OpenTextFile ($myFileName, 8, True)
	Else
  		$FileObject = $FSO.CreateTextFile ($myFileName)
	EndIf
   ;Create new file (or replace an existing file)
   $FileObject.WriteLine("New log entry.. " & _NowTime())
;---------------
$CheckIt = CheckCreated() ; call function to check that the class we added PLUS the work done by the AddIn has been completed successfully
If $CheckIt  Then
	  ConsoleWrite("Item found" & @CRLF)
	  $FileObject.WriteLine("Item found")
Else
	  ConsoleWrite("Item NOT found" & @CRLF)
	  $FileObject.WriteLine("Item NOT found")
EndIf
;---------------
$FileObject.Close()
ConsoleWrite("xxxxx All done xxxxxx" & @CRLF)
Exit

If we now run this script we can see the output in the Console window.

Running test script within script editor

Running test script within script editor

We can see the outcome was successful but not that friendly.

What next?

This code was written as proof of concept to check capability rather than for production use. Furthermore, the scripts would need to be modified to:

  • Create (or copy a clean copy of) the initial EA model to ensure the same base
  • Handle the EA model names – and not rely on setting the model as default
  • Add other tests for the specific target AddIn

One useful feature of AutoIt is its capability to create a GUI interface, and thus for the execution of regression tests we could produce an application that was a little more friendly.

A useful tutorial “learning to script with AutoIt” includes a brief introduction to using the GUI functions.  In addition,and included with the AutoIt download, is a GUI Builder tool (Koda) which can help produce the GUI scripts.

Also AutoIt can be used compile the scripts into an executable, which can eliminate complexity that may be associated with running the tests for those without knowledge of AutoIt.

So to test this, and using Koda to help design to UI, I created a GUI application with my existing test scripts providing most of the functionality.  This simple GUI application is illustrated below.

AutoIt based GUI Tester presents a friendly test tool

AutoIt based GUI Tester presents a friendly test tool

Nothing fancy but straightforward to use.  A much more sophisticated application could be developed if required.  The plus is that the user simply starts a simple windows executable.

Conclusions

In this post I have explored the ability to automatic test EA AddIn.  Using AutoIt scripts to emulate the user input as well as interact with the EA automation API I have demonstrated that this is possible.

I found AutoIt straightforward to use and was impressed by its features.  Also it is a plus that it is freeware.

I hope that this post is of interest to those working with AddIns (as well as other areas) and would welcome your comments or feedback.

Adrian

Advertisements

eaForms – using the eaForms action buttons !!

A couple of weeks ago I outlined my view on the usefulness of lists when editing EA elements. In that post I illustrated the ability to add buttons to an eaForm which could be used to initiate some action such as adding a property.  By providing buttons that initiate action we provide a means to perform the task with minimal context switch and hence minimal effort.

SimpleClassForm illustrating use of buttons

A Simple Class Form illustrating the use of buttons

If we look at this example form we see standard buttons to Save or  Cancel our element editing. We also see buttons which allow us to Add… properties to our element. For example,

Adding an attribute presents simple dialog within context of open element

Adding an attribute presents simple dialog within context of open element

our Add Attribute button will present an attribute form within the context of our already open form – no switching just a single button push.

We have a range of other buttons which cwe can use to add element properties (often they are are specific to the relevant element type) – for example:

  • Add a tagged value
  • For Use Cases we have add Actor, Pre-/Post-condition
  • For Classes we have add Attributes/Operations

We also have a Detail button that can be added to the form and will save updates to the current element then open the element using the standard EA properties editor; a button we feel is useful for the experienced EA user who may at some time wish to switch for an instant.

Create related note or element

In my experience in addition to editing element properties the other may task is to add stuff – notes or new elements related to my current element, so we have added buttons that assist with this task. The example below illustrates a add a linked element button – which when pressed presents the user with a dialogue to enter basic information about the element and its relationship to the current element.

Add linked element to our current element

Add linked element to our current element

Button options include both linking and / or owning the element.

Also, although not a button, within eaForms we offer the related element control which allows the designer to specify a desired connection between the element that the user is editing and another element (optionally with a specific stereotype) and with a specified connector type.

Script buttons

During recent work we have seen the benefit of scripting and have been working with some “experimental” buttons within eaForms that will not only save or potentially save an element but also run a script.  At present we have included 2 script buttons:

1. Save and run script after save – which will save updates to the element and then execute a predefined script, performing some post saving action.  For example, we could add an entry to a file to indicate that an element has been approved or some other project relevant event.

2. Run script and conditionally save the element or remain in edit – this button runs a script and depending on the outcome will either save any element updates or stay in editing mode, without saving any updates, and so presents the user with making any relevant modifications or preventing them saving updates that do not confirm to some project specific standards.  By proving the option to perform pre-saving checks we can help ensure the integrity of the project.

In providing these button options we recognised the potential for issues with scripts.  The first is which script to use and where it is located, so we decided to include the script within the eaForms definition.  So if we look at a very simple form with a script button and its control definition we see the script code can be edited – this script adds an entry to a file

A script button and its definition

A script button and its definition

The second issue is the operation of the script – in our early release we have limited the scripts to VBScript and further do not support INC’s – and hope that is not too restrictive.  We have found that most of what we would see as needed is readily available within the existing windows environment e.g. the use of the File System Objects.  We will review this in due course.

Finally, are there other script buttons that would help. Do we want to include a “Run Script” button that performs a task without doing any pre-defined action?

At present these script buttons are at an early stage and the use cases may be a bit vague but part of the reason for including them was to see if they help the user or other aspects of model quality – which is part of the aim with eaForms

Summary

In this post I have outlined the use of buttons within eaForms –  from the simple add properties to the more powerful buttons with scripts that provide the user with the flexibility to do their own thing.

As always hope this is of interested and welcome your questions and feedback.

Adrian

Scripting follow up on case #6 – prevent execution of script on AddCode – solved??

When researching information for my recent scripting experiments one of the posts I came across highlighted an issues that when adding the script code using the Script Control addcode method the script would execute. The post cited was a simple script:

 sub main
  MsgBox("main executed")
 end sub
 main

I had also observed a few cases myself but it appeared inconsistent.  Now I’ve come back to look at the problem, by just looking at the code with fresh eyes the solution was obvious, wasn’t it?

If I step back to the early 1970’s using basic interpreters, I entered the subroutines/functions then added a line to specify what routine to call. At which point the code would run. So if we look at the code example we see the subroutine plus a line “main” which I can only imagine is saying run “main”.  A quick check with my EA Script Tester AddIn, and yes this appears to solve the issue.  I removed the last line i.e. “main” and the AddCode just adds the code without running, and the run does as we expect.   And to check, putting “main” at the end and the script runs immediately on adding the code as well as when requested to run.  BTW: I’m not sure that is what was referred to as the solution, which referenced needing to strip out the calls; however I felt it was worth checking out so I understand the rules.

Now just as I am cautious and curious I ran a few more tests. I started with a script that included a few subroutines.  Ignoring setting parameters and any required objects the basic code consists of:

 myScriptControl.AddCode(myScriptAsAString)
 result = myScriptControl.Run("NAME OF SUBROUTINE TO RUN")

So if I create a new script, as below, which has several subroutines I performed a few tests.

sub main
 msgbox("Hello")
end sub
sub A
 msgbox("A")
end sub
sub B
 msgbox ("B")
end sub
main
  1. Running the code as illustrated it ran twice.
  2. I then removed the last line (“main”) and it only runs with the .run method is set.   Worth noting that you can specify any subroutine name and it will run accordingly. But don’t forget that if the subroutine has parameters you need to configure and set the parameters object as the second argument of the run call as well as set up any dependent objects.
  3. I added calls to the subroutines between them and yes it does execute on loading .  In testing it looks like all the code is loaded and then executed.
  4. Finally, I added one or more of the subroutine names at the end of the script and as I would expect it would run them in the defined sequence.

In practice, when running EA scripts either in an AddIn or external program, to ensure the code isn’t executed when added we need to strip out any lines containing a reference to a subroutine in our script that are NOT contained within a function or subroutine otherwise they will instruct the script control to execute upon adding code.  This was included in the initial post and although it may be a challenge not sure there is anything else – it’s the way the “interpreter” works and I guess for backwards compatibility will always work.

Of course, we could want this behaviour – in fact I can see a case where some subroutines run to initialise “stuff” before a call is made to a specified subroutine – up to the designer/user.

Having looked at a lot of documents on scripting in windows during my recent experiments I can’t recall any reference to the behaviour outlined above, although there are references to a distinct lack of documentation about the script control in general and perhaps this is the real issue!!

I hope this is useful, and if there are other “script funnies” I’d be interested to here.

Adrian

PS: This was the issue highlight in my test Case #6 if you are interested.

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

 

Scripting follow up on case #5 – running an EA script from a windows application

A short follow up on one of the outstanding items from a previous post on scripting.

Thanks to quadrabyte for his feedback, including his code extract, which forced me to re-read what I had written, and which with hindsight wasn’t very clear; sorry to all those I may have confused.

When I set out the use case (#5) – “WinApp can run script within EA repository” – I was really thinking about a WinApp making a call to EA, through an api which would run a pre-defined script on my behalf.  This followed on from my idea that it could be useful to be able to run a script each time EA was started e.g.

>EA.exe /script="name"

This would allow a range of functions to be performed to help with the administration of my projects.  However, I couldn’t see a way of doing that with in a command line (anybody know?), but was there a way I could emulate the behaviour with a windows application that could start EA and run the script e.g.

myWinApp /script="name"

Well it was looking at the code quadrabyte provided that put me back on the straight and narrow, bringing me back to a world of logic from my roaming into fantasy solutions.  How could I have launch the EA UI and, in parallel, get it to perform functions for me.

However, it is clearly possible to have a windows application running a script – as outlined in previous posts  using its own ScriptControl.   Our windows application could open an EA repository, read and run a user script, provided we ensure that the required objects are initialised prior to running the script.

Although back in my fantasy world wouldn’t it be nice if EA would execute pre-defined scripts on start up, for example start of build scripts, workflow etc.  Perhaps it does and I haven’t found it or perhaps it’s another future feature.

In the meantime., thanks again to quadrabyte.

Adrian

PS: I’ll be following some other short posts on the other issues that were highlighted.

A little update:

Today I was having a quick look at what next and was reviewing some of my experiments with workflow scripts – and reminded myself that when an EA project is opened that EA will call the “Function GetWorkflowTasks” – and so why wouldn’t I use this to run my own scripts on startup?

A few quick tests:

  1. I found some of the capabilities I’ve used previous not working – so need to find out which
  2. Using a message box isn’t a good idea as I found it is hidden and not readily accessible. So probably not a good idea.
  3. I tried using the EA Dialogue and that didn’t work
  4. Accessing files seemed to work file

I would need to do a lot more checking to see what is available – but it does, in part, provide a means to address the task in hand.

More exploring scripts – running EA Scripts from your code

I promised it wouldn’t be too long before I returned to provide the results from my experiments into running EA scripts from an external application (or if you wish from an EA Add-In).  If you missed the previous post on scripting may be worth reading first so that the current post is read in context.

Moving forward within exploring scripting capabilities let me start by providing some background information I discovered that will be relevant to the task in hand with the focus on user scripts.  You will see below I have skipped stuff that I didn’t see necessary for the current experiments.

1. EA stores its scripts in t_script

When we want to run an EA script we need to know where they can be found.  If you look into the EA database, and my suggestion would be to open a local EAP file using Access, you can find a table called t_script as illustrated below.

t_script - contains a range of scripts

t_script – contains a range of scripts including user scripts

If you look through that table you see the following columns which as I interpret have the following meanings:

  • ScriptID – a unique integer
  • ScriptCategory – there are several categories with normal human readable names and some GUID looking numbers (!) which including user scripts(indicated by the category with the value 605A62F7-BCD0-4845-A8D0-7DC45B4D2E3F).  You can also see other scripts such as those used when building code
  • ScriptName – once again either a human string or a GUID looking number
  • ScriptAuthor – another GUID looking number – not sure where this points to yet.  I’ll have to add this in when I find it but didn’t seem important at the time.
  • Notes – specifies the script name, type and language e.g.”<Script Name=”myScript” Type=”Internal” Language=”VBScript”/>”
  • Script – contains the script code in plain text form for user scripts.  When necessary I’ll have to check out the use of this for other types of scripts.

2. Extracting the script

If we want to run any of the user scripts we need to read the t_scripts table and extract the required script.  The method I have used was to:

  • Read the rows from the table, filtering on script category
  • Get the script name
  • For the selected script, extract the text from the Script column

3. Checks before running scripts

INCLUDES – If you look at scripts in EA you will see that some will include other scripts (e.g. INC). If we are running scripts within EA this is not a problem provided they exist within the EA context, however as we will see below, if we are running them elsewhere we need to ensure that we include ALL the relevant code.

RUN TIME OBJECTS – when running scripts within EA use is often made of the following run time objects:

  • Repository – current EA repository
  • Maths – the Cephes maths library
  • Session – which provides functions to interact with the user through the EA UI

If you are executing your scripts in another environment then you will need to supply the relevant references if required; note that the session object is purely for use within the EA script running environment.

Running scripts from the outside

With that bit of background we are now ready to look at running an EA script under the control of our “external” application.  Why is this an issue?  Can we not just ask EA to run the script for us?  Well so far I cannot see a way to interact with the EA script engine via the automation interface.  Hence,  the solution I have used (and I believe used by many others) is to provide my own instance of the script engine, extract the relevant script code and execute.

This isn’t as difficult as it sounds, although my voyage of discovery was interesting – and as seen from various posts on the Sparx Forum and Stack Overflow shared!

Script engine

As far as I can determine EA uses the Microsoft windows scripting engine.  Which I believe is MSScriptControl – which means that VB Script and JScript scripts written for EA can be run by using this control.

To run a script, we can use an instance of this class,  set the relevant attributes and call the run or execute function. So within our application we can:

  • Create an instance of MSScriptControl e.g. myScriptControl
  • Specify the script language e.g. myScriptControl.Language = “VBScript”
  • Set the script code using the relevant text (note: this will need to be complete with any includes expanded. e.g. myScriptControl.AddCode(ScriptCodeText)
  • If required pass objects that will be available to the script using the AddObject method e.g. myScriptControl.AddObject(“myObjectName”, myObjectValue, True) adds a name/value pair and sets this as a global value
  • If required pass parameters to the script.  This is done by setting up an object array containing the relevant arguments e.g. ArgumentObjects.  Remember that the scripting languages are loosely typed so they have no explicit knowledge of typing
  • We can then run the script and for functions capture the result e.g. myResult = myScriptControl.run(“myScriptName”, ArgumentObjects)

Pulling it all together

So with that background I put together a simple program to run a script.  The application provided a means to enter a script and run it.  This allowed me to verify that the mechanism worked fine without any interactions with EA.  However, I need to remember to do things link reset the scriptcontrol if I reuse it (myScriptControl.reset)!

It is also worth noting that I recently saw that there are some observed issues, for example – adding code will automatically execute code – I have only seen this issue when executing the script control within an EA Addin, it seemed to work fine when operating in a standalone program but that doesn’t mean there aren’t instances where this is not the case (the joys of software), hence I need to explore further!

But in the meantime, onto testing with EA – which involves the following steps:

  • Open an EA Repository
  • Get a list of user scripts
  • Select and run a script
  • View the result report

As it turned out I skipped writing a simple windows application as I was confident that would work, so went straight to writing a small Add-In that would perform these tasks. So no need to open the repository just the steps that followed.

My simple Add-In basically lists the available user scripts and provides a means to inspect and run the script.

Script Tester Menu

Script Tester Menu

The form that presents me with a list of scripts (as well as providing a option to refresh the list) is illustrated below.

Script tester list of scripts

Script tester addin displays a list of scripts that can be selected

The user can select a script from the list, and inspect the details:

Script Details

Details of selected script – script can be run by pressing run script button

And if they wish run the script, for example as below.

Results from running the simple script

Results from running the simple script

I am pleased to say that it presented the same result as when running the script natively within EA.

So using this approach there is a a means to run EA scripts under our control, demonstrated with this EA Add-In, and I’m confident it will work from the outside with a windows application, albeit with care regarding the objects that are being expected.

Points to note

Just to recap on the some points for running scripts using the script control.  You need to:

  • Ensure that the code provided is complete
  • Reset the script control before use
  • Pass any required object to the script – note that session is not available and if libraries are required you are responsible for ensuring that they are accessible
  • Provide any required arguments as objects – NOT typed
  • Capture the results

Nearly there or not!

If I refer back to my diagram in the last post (repeated here for reference), that illustrated all the tests I wanted to explore.

Overview of scripting scenarios to explore

Overview of scripting scenarios to explore

We have covered all cases 1 to 6 – albeit that there is ongoing work in checking some potential issues with case 6.

Case 7 – calling our Add-In from a script when working within EA

So what about the EA script control (not our own script control) calling an EA Add In. Just to be clear this is a normal EA script running within EA.  Is there a way?  Well I’m still working on this. I’ve posted and had some interesting observations which I have posted on the Sparx Forum.

This case has presented some interesting windows programming issue which has forced me to spend more time that planned looking into windows processes, but then that is the fun of exploring.

Overall for the most likely use cases all works as one would hope.  It is the edge cases that I thought I needed to explore that have raised some issues and I’ll be looking into:

  • Feedback I’ve received on how to resolve case 5
  • Unwanted code execution issues in case 6
  • Finding the correct reference for case 7

I hope to report back soon with my findings for these cases.

In the meantime, if you have some answers do join the dialogue here or on the forums.  For those happy with other scenarios I hope you have found this useful.

Adrian

 

More exploring scripts – looking at application interoperability

In my last post on scripting  I was looking at using scripts as part of customising EA.    When I finished the last post I was part way through my exploration of “Application Interoperability”. So let me continue, but I must warn you that in this post I won’t be covering too much that is unique to scripting, however it is nevertheless useful stuff that provides some groundwork for subsequent experiments into scripting.

Last time I looked at how a script can gain access to an application such as Excel which can operate as a COM server.  My next set of experiments looked into if and how scripts could interact with code that I’d developed outside of EA, for example, a windows application or from one of my own class libraries (DLL).

To provide a sense of what I was thinking about let me use a diagram to illustrate theses and other options I am exploring.  In the diagram I show EA and its repository together with several other components that may be treated within the current context as being “internal” to EA’s operating environment plus several components that clearly site “outside of the box”.   I have treated everything that EA knows about including any registered AddIns as being inside,  the others, which EA doesn’t know about, are outside.

To guide us through the experiments I have numbered the connections to represent each specific test; remember we are in the world of experimenting, so I am just illustrating capability, whether or not it is of any value is another story.

Overview of scripting scenarios to explore

Overview of scripting scenarios to explore

Overview of interoperability scenarios to explore

1. Calling my Class Library (DLL) from an EA script

This is a relatively straight forward case since it is just another example where we use our script to create an application object,  but in this case the object (DLL class) is one I created.  Of course, there are some rules, for example when we create our class library we must:

  • provide suitably accessible public methods
  • our library needs to be registered as a COM object so that Windows is aware of the class when our script wants to use it.

So here is the code for my class library – very simple.

 Public Class MyEACalledClass
 Public Sub New()
    MsgBox("Hello from my DLL")
 End Sub
 End Class

This class is compiled as a library and then registered for COM interop using regasm, this is the same way that we register an  EA AddIn – see my post One of our Addins is missing for more information.

And then within our EA model we can create a script that calls this class as illustrated in  the screen shot.  When the script runs we get the message box that we have in the class constructor.

Calling my own class library

Calling my own class library

So we have address case 1 – now into case 2.

(2) my DLL can access EA repository

We can extend the functionality of the class library (DLL) as we wish.  For example, we may want our library to perform some functions that involve interacting with the current EA repository.  To do this we simply need to:

  • Write the relevant EA code in our class library (linking with Interop.EA.dll)
  • At run time, we need the script to pass the current EA repository object to our class library

Below is a trivial example.  The setEAModel receives the current EA repository object which is stored locally and then the getEAModelInfo method interacts with EA and returns a value to the calling process, in our case as shown below a simple script.

Private myRepository As EA.Repository = Nothing
Public Sub setEAModel(pRepository As EA.Repository)
  myRepository = pRepository
End Sub
 Public Function getEAModelInfo() As String
 If myRepository IsNot Nothing Then
 Dim myInfoString As String = myRepository.ConnectionString
 Return myInfoString
 End If
 Return "No report"
 End Function

Here is an example script calling our class library and getting information back relating to the current repository.

Calling EA from our DLL

Calling EA from our DLL

Clearly we wouldn’t create a library to perform such a trivial task which can be done locally. However, there may be situations where you want other functions such as interacting with another application / service that cannot be done within a script, but doesn’t warrant the need to create an EA addin, and hence this could be an option.

 (3) / (4) Windows application using our class library and interacting with EA repository

The next 2 examples look at using a windows application as a EA client, firstly just accessing the EA repository and then accessing and running a script.

The first test case (3) of access our own DLL is purely windows and an everyday task for windows application developers, so no more on that.

As with any other windows application you need to ensure that you include the relevant library(s) that supports interoperating with EA ( Interop.EA.dll) which exists in the EA installation directory.  Then to connect to EA:

  1. Create a new EA.Repository object (e.g. myEARepository)
  2. Open your file( or EA server) using myRepository.OpenFile(filenamestring)
  3. Perform required functions as required.

If you are new to working with the EA API I would suggest getting a copy of Scripting Enterprise Architect by Thomas Kilian – it goes through most stuff that you may want to do when working with EA.  And if that isn’t sufficient then check out Sparx Systems Automation Forum

So why would we produce a windows application as an EA client? The simple reason is that you want to provide a client that performs specific functions that do not require the use of the EA UI, for example:

  • to reduce complexity for the user – I have developed clients that make it easy to work with an EA model, as illustrated below a very simple interface with an application that can be provided to new users or more specifically in my case to those resistant to starting to use EA
Example of an EA client

Example of an EA client

  • to provide interaction with a 3rd party application
  • to run automatically to perform some house keeping tasks on the repository – this allows the application to be scheduled with windows Task Scheduler.

BTW: In relation to working with EA I have found it really useful at time to produce a small windows application accessing EA as an external application.  I have developed a small template application which provides the basic infrastructure for interacting with EA, selecting and opening a repository and providing basic element functions that I continually reuse when testing stuff.

(5) Accessing and running EA script’s from the outside

For the final experiment today, I was looking at a means for an external windows application to make a call to EA, and for EA to run a requested script for us. For example, there can be scenarios when you want to run a script on an automatic basis.

As far as I can see EA doesn’t provide a means to open EA from a command line, run a script and close.  The nearest I can see is the Project Shortcut – which nearly fulfils the requirements except that it doesn’t appear to provide a means to run a suitable script of my choice.  Is there a mechanism?

Well in the meantime it looks like I will have to create an application that will open my repository and execute the scripts.  So how can this be done – well as I started I see it’s not a short answer so I’m going to dedicate my next post to that task, as well as look at possible interactions between EA Addins and EA scripts. I’m already writing it so won’t be long.

In the meantime I hope this was of interest.

Any queries about this or other scripting issues let me know as I could add them to my expanding list of scripting experiments.

All the best

Adrian