|
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:
|