Automating Legacy Applications - Part 2

Environment: Visual C++ 6.0, Windows 2000

This article is an extension of my previous article "Automating Legacy Applications."

Introduction

My previous article presented an approach to "automation" of legacy applications with a combination of DLL injection and creation of COM object(s) in the injected DLL. The companion sample illustrated this concept. But I feel that the picture is somewhat incomplete. The sample showed the usage of a "direct" interface, that is, the ability of a client application to activate certain features of a target ("server") application. But such important functionality as asynchronous callbacks notification of the client by the server was not presented in the sample (although being mentioned in the Further Development portion). This article intends to fill this gap.

Technique Description

The presented technique is the evolution of the approach described in my previous article. So, let's assume we are already know how to get a COM object embedded in the target application process and to register this object with a Running Object Table (ROT). The embedded object implements some direct interface, similarly to the previous case. But now it also defines the outgoing interface to notify clients and implements the connection point mechanism. The outgoing interface may be inserted manually into an IDL file and the connection point implementation for this interface is possible to add with the appropriate Visual Studio service (see the Test Sample portion for details). It is preferable to have the outgoing interface being an IDispatch-based (or dual) one to allow a script client to implement it. In the case of the script client, make sure that the Invoke() method of IDispatch is used in your Fire_[SomeEvent]() method of the back notification class.

The client application has to implement the outgoing interface and to marshal ("advise") its pointer to an embedded object. The advising should be made immediately after the client got embedded in the object's proxy.

With these arrangements in place, the embedded object is able to notify client(s) about changes in the target application by firing the client's event.

Test Sample

Here I focus only on the differences and additions with respect to the sample in my previous article.

  • The outgoing source dual interface IHandlerEvents is added to the NotepadHandler project (in file NotepadHandler.idl). The interface contains the method HRESULT SentenceCompleted([in] BSTR bsText).
  • The NotepadHandler project implements the connection point mechanism. Appropriate code may be added by activating of the Implement Connection Point... item in the right-click menu on CHandler in ClassView tab of Visual Studio workspace and the dialog followed.
  • Under my scenario (just for illustration), the server (embedded COM object) senses the end of the sentence inserted to Notepad editor (for example, the ".", "?", and "!" characters) and the Fire_ SentenceCompleted() event, thus providing the client with the sentence completed (content of appropriate buffer). So, CHandler::SetText() method was changed accordingly (in file Handler.cpp).
  • The client application implements the IHandlerEvents interface. It may be done in various ways. I choose to create a special COM component (in-process server), Notification. To construct it, the additional project NotepadHandlerNotification was added to the workspace with the ATL COM AppWizard. Then an ATL object was inserted into the component, using the right-click menu. This object implements additional an interface that I called INotification. This interface is not a compulsory one, but may be useful to provide the component with data from the client application via appropriate methods (I supply to the component handle of the client application's main window that way). The CNotification class was modified to implement the IHandlerEvents interface (by activating the Implement Interaface... right-click menu item followed by the corresponding dialog).
  • An advising mechanism should be added to the client application. In my test sample, it was put in class CAdvise. The method CAutomationClientDlg::OnButtonAutomate() contains the code responsible for advising.
  • Implemented in the Notification component, the SentenceCompleted() method of the IHandlerEvents interface is called by the server. The application main window handle (in this case) is supplied to the Notification component with the SetMainWnd() method of the INotification interface. Having this handle in its possession, the SentenceCompleted() method posts a notification message to the main window of the client application, passing a pointer to a buffer with data received from the server.

Running the Test

An already compiled demo is available for the test sample. Its functionality is extended relatively to the previous version. Now, the automated Notepad sends ("fires") the Sentence Completed event to its clients as soon as any character from the set .?! is inserted. Upon this event, the appropriate buffer content is sent to clients and displayed by them.

To run the sample, first you have to register COM components (NotepadHandler.dll and NotepadHandlerNotification.dll) by activating the Register Components.bat file. Then one or more copies of AutomationClient.exe may be started. By pressing the AUTOMATE NOTEPAD button, clients start and automate (or automate of a running copy) Notepad and "subscribe" to its Sentence Completed event. Now, if the user types some characters followed by a sentence conclusion sign (".", "?", or "!"), the character sequence will be reproduced in an edit box of all clients' applications subscribed to the event. The rest of demo functionality remains the same with respect to the demo in the previous article.

To compile the demo, the program NotepadAuto.dsw should be loaded to VC++ Studio. Please, make sure that files psapi.h, psapi.lib, and psapi.dll are installed on your machine, and set the proper path to them in your test projects. If there are no such files, they may be loaded from the download section of this article. The _Build_All_Projects project has to be built in order to build all modules. Please see the previous article for more details about the demo built.

Conclusions

An approach to automation of legacy application is presented in the two articles. An application is converted to a COM automation server without change and even knowing of its code.

Downloads

Download demo project - 30 Kb
Download source - 47 Kb
Psapi library files - 11 Kb



Comments

  • Distributing the psapi files

    Posted by Bud Fox on 03/25/2012 07:08pm

    Hi Igor, Thanks for your post. By the way, is it legal to distribute the psapi.h, psapi.dll and psapi.lib files or do we need explicit permission from Microsoft Corp? Bud

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds