Environment: Visual C++ 6.0, Windows 2000
This article is an extension of my previous article "Automating Legacy Applications."
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.
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.
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.
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.