Sonntag, 7. September 2008

entwickler.com Magazine Konferenzen Akademie Entwickler-Forum Jobbörse Bücher
Software & Support Verlag

Andere Fachanwendung fernsteuern

Frage: Ich muss mein Programm so erweitern, dass es mit einer anderen Anwendung (an der ebenfalls Änderungen vorgenommen werden können) zusammenarbeiten kann, um dort bestimmte Programmfunktionen auszulösen. Dabei werden verschiedene Parameter übergeben, wobei das Ganze auch umgekehrt funktionieren muss. Wie ist sowas am einfachsten möglich?

Antwort: Wenn es nur darum geht, programmgesteuert Einträge in die Controls der Benutzeroberfläche der anderen Anwendung einzufügen, wäre auch der Win32-Weg über Botschaften verfügbar, um diese Einträge einzufügen oder auszulesen. Stabiler und leistungsfähiger ist es jedoch, beide Anwendungen um ein Automation-Objekt (COM-Objekt) zu erweitern, da dann beide Anwendung direkt die Interface-Methoden der jeweils anderen Anwendung aufrufen können. Da auch Events unterstützt werden, können diese über die von Delphi generierten VCL-Wrapperkomponenten wie gewohnt im Objektinspektor konfiguriert werden. Das folgende Beispiel demonstriert dies, in Delphi werden dazu die folgenden Schritt abgearbeitet:


1. OSAutoMaster.dpr: neue Anwendung (kein COM)
2. OSAutoClient.dpr: neue Anwendung
- neues Automationsobjekt OSAutoCltObj mit Ereignissen
- Anbindung OnInfo + OnExit an Client-Formular
3. VCL-Wrapperkomponente für OSAutoCltObj erzeugen
4. OSAutoMaster - TOSAutoCltObj einbinden, über Objektinspektor Ereignisbehandlungsmethoden für OnInfo/OnExit anlegen

A) Anwendung Master (OSAutoMaster.dpr)

Damit sowohl die Master- als auch Client-Anwendung unabhängig voneinander gestartet und geschlossen werden können, ohne dass die Windows-Sicherheitsabfrage über das aktive COM-Objekt erscheint, nutzt der Master das OnExit-Ereignis aus, um die ConnectionPoint-Verbindung immer dann zu trennen, wenn der Client (COM-Server) geschlossen werden soll:

{ ****************************************************************
  Autor            :  Andreas Kosch
  Compiler         :  Delphi 6.0 Enterprise
  Betriebssystem   :  Windows 2000 SP2
  Erstellt am      :  31.01.2002
  Beschreibung     :  Zwei Anwendungen über Automation verbinden
 **************************************************************** }
unit OSAutoMasterFrm;

interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, OleServer, OSAutoClient_TLB;
type TFormMaster = class(TForm) Label1: TLabel; StaticText1: TStaticText; ButtonClient: TButton; MemoLog: TMemo; StatusBar1: TStatusBar; OSAutoCltObj1: TOSAutoCltObj; procedure OSAutoCltObj1Info(Sender: TObject; vInfo: OleVariant); procedure OSAutoCltObj1Exit(Sender: TObject); procedure ButtonClientClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end;
var FormMaster: TFormMaster;
implementation
{ *.dfm}
// // Schritt 1: ConnectionPoint-Verbindung aufbauen //
procedure TFormMaster.ButtonClientClick(Sender: TObject); var aCltObj : IOSAutoCltObj; swSrvMsg: WideString; begin MemoLog.Lines.Add('CoOSClientObj.Create...'); aCltObj := CoOSAutoCltObj.Create; aCltObj.DoWork('Test', swSrvMsg); MemoLog.Lines.Add(' -> DoWork: ' + swSrvMsg); OSAutoCltObj1.Connect; MemoLog.Lines.Add(' -> OSAutoCltObj1.Connect'); aCltObj := nil; MemoLog.Lines.Add('IOSAutoCltObj freigegeben.'); end;
// // Schritt 2: Event vom Client auswerten //
procedure TFormMaster.OSAutoCltObj1Info(Sender: TObject; vInfo: OleVariant); begin MemoLog.Lines.Add(' -> OSAutoCltObj-Event OnInfo eingetroffen'); StaticText1.Caption := vInfo; end;
// // Schritt 3: ConnectionPoint-Verbindung trennen //
procedure TFormMaster.OSAutoCltObj1Exit(Sender: TObject); begin MemoLog.Lines.Add(' -> OSAutoCltObj-Event OnExit eingetroffen'); OSAutoCltObj1.Disconnect; MemoLog.Lines.Add('OSAutoCltObj.Disconnect'); end;
end.
B) Client (OSAutoClient.dpr)
Besonderheit: Direkter Zugriff auf die ConnectionPoint-Verbindung aus der Benutzeroberfläche heraus (Formular). 
Dazu wird im Public-Abschnitt der Klasse ein eigenes Objekfeld für das Speichern des Interface-Zeigers vorgesehen:
unit OSAutoClientFrm;

{ SYMBOL_PLATFORM OFF}
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, OSAutoClient_TLB; // von Hand hinzugefügt
type TFormClient = class(TForm) Label1: TLabel; EditData: TEdit; ButtonSetData: TButton; StatusBarClient: TStatusBar; procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure ButtonSetDataClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } FEventsBuffer: IOSAutoCltObjEvents; end;
var FormClient: TFormClient;
implementation
uses ComServ, OSAutoClient_Impl; // von Hand hinzugefügt
{ *.dfm}
procedure TFormClient.FormCreate(Sender: TObject); begin if (Comserver.StartMode = smAutomation) then StatusBarClient.SimpleText := 'Automation'; end;
procedure TFormClient.FormClose(Sender: TObject; var Action: TCloseAction); begin // Schritt 1: Master benachrichtigen -> Disconnect if FEventsBuffer <> nil then FEventsBuffer.OnExit; // Interfacezeiger freigeben FEventsBuffer := nil; end;
procedure TFormClient.ButtonSetDataClick(Sender: TObject); begin if FEventsBuffer <> nil then FEventsBuffer.OnInfo(EditData.Text); end;
end.
C) Automation-Objekt (COM-Objekt)

Das COM-Objekt stellt den Zeiger für das Events-Interface direkt über das Objektfeld des Formulars zur Verfügung,
damit die Benutzeroberfläche von OSAutoClient.dpr direkt das Event zur Benachrichtigung des Masters auslösen kann:
{ ****************************************************************
  Autor            :  Andreas Kosch
  Compiler         :  Delphi 6.0 Enterprise
  Betriebssystem   :  Windows 2000 SP2
  Erstellt am      :  31.10.2002
  Beschreibung     :  Client für OSAutoMaster.exe liefert
                      Daten über Events OnInfo/OnExit beim
                      Master ab.
 **************************************************************** }

unit OSAutoClient_Impl;
{ SYMBOL_PLATFORM OFF}
interface
uses ComObj, ActiveX, AxCtrls, Classes, OSAutoClient_TLB, StdVcl;
type TOSAutoCltObj = class(TAutoObject, IConnectionPointContainer, IOSAutoCltObj) private { Private-Deklarationen } FConnectionPoints: TConnectionPoints; FConnectionPoint: TConnectionPoint;
// auskommentiert, siehe EventSinkChanged //FEvents: IOSAutoCltObjEvents;
public procedure Initialize; override; protected { Protected-Deklarationen } property ConnectionPoints: TConnectionPoints read FConnectionPoints implements IConnectionPointContainer; procedure EventSinkChanged(const EventSink: IUnknown); override; function Get_Version: Integer; safecall; procedure DoWork(sInfo: OleVariant; out sSrvMsg: WideString); safecall; end;
implementation
uses ComServ, OSAutoClientFrm; // von Hand hinzugefügt
procedure TOSAutoCltObj.EventSinkChanged(const EventSink: IUnknown); begin
// von Hand geändert: privates Objektfeld wird nicht verwendet //FEvents := EventSink as IOSAutoCltObjEvents; // statt dessen Public-Objektfeld des Formulars
FormClient.FEventsBuffer := EventSink as IOSAutoCltObjEvents; end;
procedure TOSAutoCltObj.Initialize; begin inherited Initialize; FConnectionPoints := TConnectionPoints.Create(Self); if AutoFactory.EventTypeInfo <> nil then FConnectionPoint := FConnectionPoints.CreateConnectionPoint( AutoFactory.EventIID, ckSingle, EventConnect) else FConnectionPoint := nil; end;
function TOSAutoCltObj.Get_Version: Integer; begin Result := 1; // von Hand hinzugefügt end;
procedure TOSAutoCltObj.DoWork(sInfo: OleVariant; out sSrvMsg: WideString); begin sSrvMsg := sInfo + ' (ok)'; // von Hand hinzugefügt end;
initialization TAutoObjectFactory.Create(ComServer, TOSAutoCltObj, Class_OSAutoCltObj, ciMultiInstance, tmApartment); end.
Sie folgende Abbildung verdeutlicht das Prinzip:






Software & Support Verlag GmbH