Entwickler-Ecke

Sonstiges (Delphi) - Delphi und COM/ActiveX


Ares - Di 16.12.08 16:32
Titel: Delphi und COM/ActiveX
Hallo!

ich mache gerade meine ersten Schritte mit COM und bin dabei auf ein paar Probleme gestoßen die ich bis jetzt leider nicht löschen konnte.

Worum geht's:
Ich will ein Programm (Server) durch ein anderes (Client) fernsteuern. Sofern der Server nicht bereits läuft soll der Client diesen starten. Vom Server soll immer höchstens eine Instanz aktiv sein.

Was habe ich gemacht:
Für den Server habe ich zunächst ein neues VCL-Form Projekt erstellt und auf das Form habe ich zwei TEdits gesetzt. Dann habe ich über Datei/Neu/Weitere.../ActiveX/ ein Automatisierungsobjekt (TCOMTester) für das Projekt erstellt. Das Interface enthält Getter und Setter für die Edit Texte und eine Methode mit der eine Nachricht angezeigt werden soll:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  ICOMTester = interface(IDispatch)
    ['{70419B55-8B32-434A-8023-BA303A150146}']
    function ShowTestMessage(const Text: WideString): WideString; stdcall;
    function Get_Edit1Text: WideString; safecall;
    procedure Set_Edit1Text(const Value: WideString); safecall;
    function Get_Edit2Text: WideString; safecall;
    procedure Set_Edit2Text(const Value: WideString); safecall;
    property Edit1Text: WideString read Get_Edit1Text write Set_Edit1Text;
    property Edit2Text: WideString read Get_Edit2Text write Set_Edit2Text;
  end;



Delphi-Quelltext
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:
44:
type
  TCOMTester = class(TAutoObject, ICOMTester)
  protected
    function Get_Edit1Text: WideString; safecall;
    function Get_Edit2Text: WideString; safecall;
    function ShowTestMessage(const Text: WideString): WideString; stdcall;
    procedure Set_Edit1Text(const Value: WideString); safecall;
    procedure Set_Edit2Text(const Value: WideString); safecall;
  end;

implementation

uses ComServ, main {Für den Zugriff auf das Form}, Dialogs;

function TCOMTester.Get_Edit1Text: WideString;
begin
  result := Form1.Edit1.Text;
end;

function TCOMTester.Get_Edit2Text: WideString;
begin
  result := Form1.Edit2.Text;
end;

function TCOMTester.ShowTestMessage(const Text: WideString): WideString;
begin
  ShowMessage(Text);
  result := 'Es wurde eine Nachricht mit folgendem Text angezeigt'+#13#13+Text;
end;

procedure TCOMTester.Set_Edit1Text(const Value: WideString);
begin
  Form1.Edit1.Text := Value;
end;

procedure TCOMTester.Set_Edit2Text(const Value: WideString);
begin
  Form1.Edit2.Text := Value;
end;

initialization
  TAutoObjectFactory.Create(ComServer, TCOMTester, Class_COMTester,
    ciSingleInstance, tmApartment);
end.


Für den Client habe ich dann ebenfalls ein neue VCL-Form Anwendung erstellt und über "Komponente/Komponente improtieren..." einen Wrapper für den Server erstellt:


Delphi-Quelltext
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:
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
  TCOMTesterProperties= class;
{$ENDIF}
  TCOMTester = class(TOleServer)
  private
    FIntf: ICOMTester;
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
    FProps: TCOMTesterProperties;
    function GetServerProperties: TCOMTesterProperties;
{$ENDIF}
    function GetDefaultInterface: ICOMTester;
  protected
    procedure InitServerData; override;
    function Get_Edit1Text: WideString;
    procedure Set_Edit1Text(const Value: WideString);
    function Get_Edit2Text: WideString;
    procedure Set_Edit2Text(const Value: WideString);
  public
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;
    procedure Connect; override;
    procedure ConnectTo(svrIntf: ICOMTester);
    procedure Disconnect; override;
    function ShowTestMessage(const Text: WideString): WideString;
    property DefaultInterface: ICOMTester read GetDefaultInterface;
    property Edit1Text: WideString read Get_Edit1Text write Set_Edit1Text;
    property Edit2Text: WideString read Get_Edit2Text write Set_Edit2Text;
  published
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
    property Server: TCOMTesterProperties read GetServerProperties;
{$ENDIF}
  end;


Auf dem Form von dem Client habe ich dann ein Edit, zwei Radio-Buttons und einen Button platziert. Beim Klick auf den Button soll folgendes passieren:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TForm1.Button1Click(Sender: TObject);
var
  tester: TCOMTester;
  str: string;
begin
  tester := TCOMTester.Create(nil);

  // Der Tester soll eine Nachricht ausgeben
  str := tester.ShowTestMessage('Hallo Welt!');  // <-- Fehler!!
  ShowMessage(str);

  // Je nach Radiobutton im Server einen Edittext ändern
  if Edit1RB.Checked then
    tester.Edit1Text := Edit1.Text
  else
    tester.Edit2Text := Edit1.Text;

  tester.Free;
end;



Hierbei kommt es nun zu folgenden Problemen:
1. Wir "tester.ShowTestMessage(...)" kommt es zu einer Zugriffsverletzung in der rpcrt4.dll. Was hat es damit aufsicht?

2. Bei jedem Klick auf den Button öffnet sich ein Fenster des Servers bei dem dann der Edit Text gesetzt wird. Ich möchte aber erreichen, dass der Text immer im gleichen Server geändert wird. Hierfür habe ich ciSingleInstance verwendet aber das scheint ja nicht die Lösung zu sein. Was mache ich also falsch?

Besten Dank für eure Hilfe!
Ares


jaenicke - Mi 17.12.08 02:27

An welcher Adresse ist die Zugriffsverletzung? 0 oder eine andere Adresse?

rpcrt4 würde ich spontan mit Remote Procedure Call RunTime übersetzen, insofern vermute ich einen Fehler bei der Verbindung. Ich bin jetzt zu müde dafür, aber ich schau nachher mal nochmal drüber.


Ares - Mi 17.12.08 13:53

Ganz genau sieht die Meldung so aus:

Quelltext
1:
Zugriffsverletzung bei Adresse 777C2599 in Modul 'RPCRT4.dll'. Lesen von Adresse FFFFFFF8                    


jaenicke - Mi 17.12.08 13:57

Musst du nicht tester.connect vorher aufrufen?

// EDIT:
Obwohl, dann würde sich der Server ja nicht öffnen.


Ares - Mi 17.12.08 14:01

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Musst du nicht tester.connect vorher aufrufen?
// EDIT:
Obwohl, dann würde sich der Server ja nicht öffnen.


Ich bin mir da nicht so sicher, wie gesagt bin ich (noch) kein wirklicher Experte im Thema COM. Aber auch mit tester.Connect kommt es zu der gleichen Fehlermeldung. Auch nur der Aufruf von ShowTestMessage führt zum Fehler. Das Setzen der Edit Texte funktioniert fehlerfrei.

Während du deinen Beitrag geschrieben hast habe ich das Testprojekt mal angehängt. Vielleicht kannst du ja mal einen Blick rein werfen :-)