Jan Reich

Jan Reich

I'm a master's student in applied computer science with emphasis on automotive systems. Apart from university, I'm highly involved in the development of Enterprise Architect extensions (Integrated Safety Engineering add-in suite) at Fraunhofer Institute for Experimental Software Engineering (IESE) in Kaiserslautern, Germany.

My special interests are model-based development, safety and reliability of embedded systems as well as architectural software design in general.

Introduction

 

I've been an EA add-in developer folk since version 9 now and every time Sparx announces to release a new version of EA, I look at it with a smile in one eye and a tear in the other. New features on the one hand allow us developers to deliver add-ins with greater user experience, but on the other hand, it also forces us to keep on track with changes in the API and adapting our yet working solutions to them.

 

However, the EA release notes coming with every new build give only a subset of API changes for developers among mostly EA user-related stuff which is perfectly fine, but not very effective for developers.

 

In this article, I'm going to show you a small tool I developed for exactly these purposes:

 

  1. Highlight for each existing object model class and each broadcast event the changes from a selected source version to a selected target version
  2. Make the result of the comparison persistent so that it can be possibly copied around or saved in version control
  3. If possible, show only the actual changed classes/events to have a condensed view on only those

 

How does the tool work?

 

The approach, I used, before I had the idea to develop the tool was more or less like you see it in the picture below:

 

  1. Fire up to instances of the browser having the old online help version of each class on the left and the new online help version on the right
  2. Scroll through both side by side and try to not overlook but identify the changes
  3. Do this for every class/event that exists in the API

 

 

Being a computer scientist, it was a natural thing to think about how this process can be speeded up or even automated!

 

So I searched the net for a tool being able to perform a diff of web pages, because that is obviously not an easy task on HTML source code level.

I found a web page that is exactly doing the change detection/diff for two provided URLs. The advantage is I have only one page where the changes are highlighted plus I don't have to read the rest.

  

The result looks like this:

 

 

 

Making use of the web page still forced me to perform the diff for each and every class or event separately, i.e. copy URLs of the EA online help pages in the change detection web site and examine the results.

 

In the end, I automated this task as well by writing a .NET tool that maintains a manually collected list of online help pages and sends a diff request to the change detection website. The response is saved on the local machine class by class in its own html file. Around all these html files I built a simple website that allows easy navigation to the diff of each class.

 

How does the result look like?

 

As can be seen in the screen below, on the left frame of the index page, we see a navigation bar to the classes that have changed. Clicking on any of those loads the respective diff in the main frame on the right. 

 

 

Unfortunately, the change detection website detects ALL changes in the whole HTML file. This means that it cannot be avoided to have false positives, when Sparx changes page layouts or the table headers/footers systematically. As can be seen in the screenshot, the diff between version 12.1 and 13 seems to work quite well :-)

 

Where can I get the tool and how can I use it?

 

The tool comes with a minimalistic UI, were source and target version can be selected and after having selected a folder where the results should be saved the process starts to run automatically.

 

 

The tool executable is attached to this article and needs an installed .NET framework 4 on the machine. By the time of this writing, EA versions 9.3, 10, 11, 12.0, 12.1 and 13 are supported. An advice of mine is to diff version by version to avoid having too many false positives because of layout changes.

Note that the tool contacts the change detection website and therefore requests an outgoing connection to the internet. 

 

Conclusion

I plan to update the tool every time a new EA version will be shipped so that the attachment will be up-to-date. In case you have comments or find things that don't work as expected, feel free to write a comment here or contact me through mail (This email address is being protected from spambots. You need JavaScript enabled to view it.). 

Introduction

 

Add-Ins are a very powerful means of extending Enterprise Architect's built-in functionality. However, in some cases you have a hard time navigating down to deeply nested items of your add-in menu in order to trigger their associated functions.

Wouldn't it come in handy to have the possibility of defining an arbitrary number of keyboard shortcuts that invoke custom functionalities of your add-in? Of course, it would and this tutorial presents how this can be achieved. 

Firstly, I'm going to explain which technical challenges existed and how they were tackled in order to get the solution running properly. This might be especially useful for add-in developers that want to grasp the concepts and toy with the notion of extending the feature with some cool stuff. In case you just want to get a hands-on guide on how to setup custom hotkeys for your add-in, feel free to skip this section. Finally, you'll find the used C# source files attached to this tutorial. 

 

Technical Challenges

 

Class Diagram

 

Registering hotkeys for global usage

 

Before the add-in is able to react to pressed keyboard combinations, we first have to register them somewhere so that the operating system, which in fact processes incoming keyboard actions, knows who should be notified in case something is pressed on the keyboard. For this purpose, Windows provides a native interface located in user32.dll. For the task of (un-)registering hotkeys, two methods are of interest to us:

 You can see that in each case a pointer to a specific window handle is needed for (un-)registration, i.e. global hotkeys can only be registered to existing windows. Note that it doesn't play a role if that window is currently active or not. Since the registration's scope is globally, there must only be one window associated with a specific hotkey at a time implying that registrations can fail if that hotkey combination is already taken by another tool. Keep that in mind in case the functionality is not invoked properly. 

In a nutshell, we need a window that has the following properties:

  1. It should be a descendant of System.Windows.Forms.IWin32Window to serve for hotkey registration
  2. It needs to be extendable in a way that we can process incoming window processing events

 

But back to Enterprise Architect scope:

The first idea coming to my mind was getting the window handle of the Enterprise Architect application itself. Recently, Geert Bellekens shared his way of achieving this with the community (Link to forum topic). Unfortunately, it cannot be used for registering hotkeys due to point 2 of the preceding list, because at least I didn't find any possibility to override the window processing event handler for the Enterprise Architect main window class.

Thus, the approach taken here is to create a custom System.Windows.Forms.Form which has all the needed properties by default. Due to its purpose of hotkey processing, it is made invisible to the user. The class is called InvisibleHotkeyForm in the above diagram. It has proven useful to create and show the form in the EA_Connect() event handler of the add-in's main class.

In order to finally register hotkeys, a list of hotkey definitions needs to passed to the InvisibleHotkeyForm constructor. Each hotkey definition is an instance of the Hotkey class, consisting of three properties:

  • Key is the character being pressed within the combination, e.g. A in Ctrl+Shift+A. It is represented by the enumeration System.Windows.Forms.Keys
  • Modifiers are the modifiers within the combination, e.g. Ctrl+Shift in Ctrl+Shift+A. It is represented by a disjunction of bitvalues of the enumeration Modifiers, that contains a bit pattern for each modifier
  • Handler is a C# delegate that holds a function pointer to the user-defined function which should be invoked when pressing the hotkey

 

After instantiating and showing the InvisibleHotkeyForm, the existing classes take care of the rest of the work.

 

Getting notified of pressed key combinations and handling them

 

Having created an instance of InvisibleHotkeyForm, the Windows API has registered it to receive window processing events. The class System.Windows.Forms.NativeWindow, which is a base class of System.Windows.Forms.Form, declares the method void WndProc(ref Message m), which is invoked by the operating system, when a window processing event has occurred. The overriden implementation of it  in InvisibleHotkeyForm is: 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
protected override void WndProc(ref Message m)
{
    if (m.Msg == Hotkey.WM_HOTKEY_MSG_ID)
    {
        foreach (Hotkey key in _hotkeys)
        {
            if (key.IsPressedKeyCombination(m.LParam))
            {
                key.Handler();
                break;
            }
        }
    }
    base.WndProc(ref m);
}

 

Since there exist more window processing event types besides hotkey events, we have to check for that first. After that we ask each hotkey in the list, if it's associated with the pressed hotkey combination, which is encoded in the LParam property of the window processing event message. If there is a match with one of our defined hotkeys, we invoke its delegate function that contains the custom behavior. Note that currently, the delegate type neither has parameters nor a return type, since it should only be an entry point for triggering further add-in behavior. However, this could be customized with minor efforts.

 

Dealing with multiple running instances of the Enterprise Architect application

 

Because of the fact that we deal with global hotkeys that are restricted to be registered to only one window, the whole approach fails as soon as we open up two instances of the Enterprise Architect application. The reason is that each instance is running in its own process and therefore the add-in is loaded once for each instance, which means we have also two instances of InvisibleHotkeyForm that try to register the same set of hotkeys. This of course succeeds only with the first Enterprise Architect application instance.  

In order to tackle this problem, I assumed that the user always works in only one Enterprise Architect instance, which implies that its main window is active only for that time. In this manner, each add-in instance has to be aware, if its Enterprise Architect instance's main window is currently active or not.

Upon this information the add-in can decide, if:

  • it has to register its hotkeys as soon as its Enterprise Architect window gets focused by the user
  • it has to unregister its hotkeys as soon as its Enterprise Architect window looses focus

Thus, the hotkeys are only registered for the focused Enterprise Architect instance and the registration in the operating system never fails. 

Technically, this is achieved by first capturing the process id of the Enterprise Architect instance during startup. Next, a System.ComponentModel.BackgroundWorker is used for periodically polling the process id of the process whose window handle is currently focused by the user. Finally, we have to compare these process ids and determine, whether the Enterprise Architect instance got focus or lost focus in order to register or unregister the hotkeys.

The implementation of the DoWork event handler of the background worker performing this task is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void worker_DoWork(object sender, DoWorkEventArgs e)
{
    int activeId = ActiveProcess.GetActiveProcess().Id;
    if (_lastActiveProcessId == activeId)
        return;
    if (_thisProcessId == activeId) //This EA instance got focus
    {
        BeginInvoke((MethodInvoker)RegisterHotKeys);
    }
    else if (_thisProcessId != activeId) //This EA instance lost focus
    {
        BeginInvoke((MethodInvoker)UnregisterHotKeys);
    }
    _lastActiveProcessId = activeId;
}

 

In the function implementation's first line, the process id of the process with active window is retrieved by invoking ActiveProcess.GetActiveProcess(). This function again makes use of two native methods of the user32.dll, namely

 

How can I use this feature in my add-in?

 

First, you have to copy the folder src/GlobalHotkeys of the attached archive to your project and possibly adjust the namespace names of its classes according to your project structure.

An example for the integration of the hotkey feature into your add-in is given below. For modularity reasons, I created a static class that encapsulates all hotkey definitions together with their handler functions. Note that during the definition of the modifiers for the hotkey you have to use the bitwise OR operator and not the logical OR operator to combine different modifiers. In addition, it's theoretically also possible to define single characters as a hotkey, i.e. no modifiers are needed (Modifiers.NoMod) then to activate it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using EA;
using GlobalHotkeys;
 
namespace Addin
{
    class AddinMainClass
    {
        public String EA_Connect(IDualRepository repository)
        {
            HotkeyHandlers.SetupGlobalHotkeys();
            return "";
        }
    }
 
    internal static class HotkeyHandlers
    {
        public static void SetupGlobalHotkeys()
        {
            List<Hotkey> hotkeys = new List<Hotkey>
            {
                new Hotkey(Keys.D1, Modifiers.Ctrl | Modifiers.Shift, HandleHotKey1), //Keys.D1 is the digit 1 on the keyboard
                new Hotkey(Keys.D2, Modifiers.Ctrl | Modifiers.Win | Modifiers.Alt, HandleHotKey2),
                new Hotkey(Keys.A, Modifiers.NoMod, HandleHotKey2)
            };
 
            Form hotkeyForm = new InvisibleHotKeyForm(hotkeys);
            hotkeyForm.Show();
        }
 
        private static void HandleHotKey1()
        {
            //Perform some action
        }
 
        private static void HandleHotKey2()
        {
            //Perform some other action
        }
    }
}

 

 Conclusion

 

This tutorial provided the knowledge and practical instructions to enrich Enterprise Architect add-ins with the possibility to define and react to custom hotkeys. In addition to that, it should bring the fact home to add-in developers that searching for solutions beyond the .NET layer (=native Windows interfaces) highly extends the space of possibilities. Finally, I want to encourage the readers of this tutorial to give me feedback in any shape to improve its quality.

 

Source Files

 

Link: https://dl.dropboxusercontent.com/u/15260967/GlobalHotkeysEA.zip