AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

COM Events und Threads - Problem

Ein Thema von bierwart · begonnen am 22. Mär 2008 · letzter Beitrag vom 24. Mär 2008
Antwort Antwort
bierwart

Registriert seit: 15. Okt 2006
Ort: Geldern
3 Beiträge
 
Delphi 7 Professional
 
#1

COM Events und Threads - Problem

  Alt 22. Mär 2008, 10:20
Hallo,

ich habe folgende Applikation erstellt:

COM Server als Exe mit der Unterstützung von Events. (Automatisierungsobjekt)
Der Server soll als TCP/IP Server fungieren. Daten werden empfangen und sollen
anschliessend über das SinkObject an einen Client (Hier: Navision) übergeben werden.
Dort werden die Daten verarbeitet und ein Ergebnis zurückgeliefert.

Der Server ist als "Singleton-Server" ausgeführt.
Auf dem Serverformular wurde ein Button erstellt, der zu Testzwecken das Event "zündet".
Dies funktioniert einwandfrei. Die Daten werden übergeben und es wird auch gewartet, bis die
Funktion im Client abgearbeitet wurde und ein Rückgabewert existiert.


Problem:
Beim Aufruf über die "Execute" Funktion der TCPServer Komponente (Indy 9) läuft alles
vermeintlich korrekt, jedoch wird das Event im Client nicht gezündet.
Nachdem ich nun probiert habe expliziert über alle Clients zu iterieren, bekomme
ich zumindest die Fehlermeldung: "EInftCastError - Schnittstelle wird nicht unterstützt".

Meine Vermutung ist, dass das Problem der eigene Thread ist, indem die Execute Methode der Indy Komponente
ausgeführt wird.
(Habe leider auch in der Bibel "COM/DCOM/COM+" hier nichts passendes gefunden).
Wie kann ich eine threadübergreifende korrekte Verwednung der Sink-Objekte erreichen ?

Delphi-Quellcode:
procedure TOLEInterface.Ev_NewXMLData_All(Sender : TObject);
var
  aEnum : IEnumConnections;
  aCData : TConnectData;
  aSink : IUnknown;
  iFetch : Cardinal;
  OLEInVariant : OLEVariant;
  OLEOutVariant : OLEVariant;
  XMLOut : IXMLDocument;
  XMLIn : IXMLDocument;
  XMLOutStream : TMemoryStream;

begin
  aEnum := GetConnectionEnumerator;
  If aEnum <> Nil Then begin
    XMLOutStream := TMemoryStream.Create;
    Form1.GetXMLOut(XMLOut);
    XMLOut.SaveToStream(XMLOutStream,ofNone);
    XMLOutStream.Position := 0;
    OLEOutVariant := StreamToOleVariant(XMLOutStream, XMLOutStream.Size);

    while aEnum.Next(1,aCData,@iFetch) = S_OK do begin
      aSink := aCData.pUnk;
      If Assigned(aSink) Then
        (aSink as IOLEInterfaceEvents).NewXMLData(OLEOutVariant,OLEInVariant);
    end;
  end;
end;
An der Stelle
"(aSink as IOLEInterfaceEvents).NewXMLData(OLEOutVariant,OLEI nVariant);"
kommt es zu besagter Fehlermeldung.

Wie bereits erwähnt, beim Aufruf über einen Formularbutton funktioniert
die ganze Sache. Bin hier ziemlich ratlos !
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 16. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

Re: COM Events und Threads - Problem

  Alt 22. Mär 2008, 16:35
Mit COM-Events ist das so eine Sache.
Die Events können von einer early-Binding Schnittstelle (hier: IOLEInterfaceEvents) vom Client
empfangen werden oder von einer IDispatch-Schnittstelle.
Wenn der Client ein Script ist, kann er nur Event über die Dispatch-Schnittstelle empfangen.
Nur wenn ein Compiler beim Client am Werk ist, dann gibt es eine CoClass die dein Interface IOLEInterfaceEvents unterstützt.

Wenn du also über QueryInterface() oder Supports() feststellst, dass IOLEInterfaceEvents nicht unterstützt wird, dann muss man das Event über die IDispatch-Schnittstelle schicken.
  Mit Zitat antworten Zitat
bierwart

Registriert seit: 15. Okt 2006
Ort: Geldern
3 Beiträge
 
Delphi 7 Professional
 
#3

Re: COM Events und Threads - Problem

  Alt 23. Mär 2008, 20:05
Hallo,

vielen Dank für die fixe Antwort.

Allerdings wir nur eine Art von Clients verwendet (auch nur eine Instanz in Phase 1).
Das Iterieren über alle Senken war hier nur ein Lösungsversuch meinerseits, um das
beschriebene Problem zu umgehen.

Über das Formular des Automation Servers kann ich ja auch das Event zünden. Hier läuft alles
wie gewünscht. Wird die gleiche Funkion jedoch innerhalb des TCP Server Threads verwendet,
statt aus dem Formular, erscheint die besagte Fehlermeldung.

Kann es sein, dass innerhalb eines neuen Threads (statt aus dem Primär-Thread des Formulars)
der Zeiger auf das Sink-Objekt nicht korrekt gesetzt wird ?
Muss ich hier was umsetzen ?
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 16. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#4

Re: COM Events und Threads - Problem

  Alt 24. Mär 2008, 04:15
Zitat von bierwart:
Kann es sein, dass innerhalb eines neuen Threads (statt aus dem Primär-Thread des Formulars)
der Zeiger auf das Sink-Objekt nicht korrekt gesetzt wird ?
Muss ich hier was umsetzen ?
Wenn du einen Interfacezeiger vom Hauptthread einem anderen Thread übergibst, musst du ihn "marshallen".
Ausserdem muss jeder Thread am Anfang das COM-System mit CoInitialize oder CoInitializeEx initialisieren (CoUninitialize am Ende nicht vergessen).
Der Interfacezeiger wird über eine API-Funktion (CoMarshalInterThreadInterfaceInStream )in einen IStream verpackt und innerhalb deines Threads wieder entpackt.
http://www.brainssoft.de/dcom2.htm
  Mit Zitat antworten Zitat
bierwart

Registriert seit: 15. Okt 2006
Ort: Geldern
3 Beiträge
 
Delphi 7 Professional
 
#5

Re: COM Events und Threads - Problem

  Alt 24. Mär 2008, 15:12
O.K.,
habe ich verstanden, jedoch

a) Wie bekomme ich die Instanz des Interface Zeigers im Primär Thread ?
b) Wie jedoch bekomme ich dann den Schritt vom "korrektem Interfacezeiger" zum Zünden des Events in diesem
Kontext hin ? Zur Zeit wird das Event durch Verknüpfung mit einem Formular Event gezündet.

Habe mal probiert meine "Button-Methode" entsprechend umzuschreiben um mein Problem aufzuzeigen
(obwohl diese ja nur im Primär Thread läuft )

Delphi-Quellcode:
procedure TForm1.btnNavtestClick(Sender: TObject);
var XMLDoc : IXMLDocument;
    XMLDoc_RESP : IXMLDocument;
    SizeOfIncoimingStream : Integer;
    sFileName : string;
    sFilePath : string;
    sFilePathName : string;
begin

  RequestType := 'FLATFILE';
  sWorkingDirectory := Ini.GetWorkingPath;
  sFileCreationDate := FormatDateTime('ddmmyyyy',date)+FormatDateTime('hhnnss',now)+'_';
  XMLDoc := CreateXMLDoc;

  sFilePathName := Ini.GetIniFilePathName;
  sFileName := ExtractFilename(sFilePathName);
  CopyFileTo(sFilePathName,(sWorkingDirectory+sFileCreationDate+sFileName));

  Ini.GetXMLFile((sFileCreationDate+sFileName),XMLDoc,RequestType);
  XMLDoc.Save(sWorkingDirectory+sFileCreationDate+sFilename +'.XML');
  XMLDoc.load(sWorkingDirectory+sFileCreationDate+sFilename +'.XML');

  // Aktualisierung der Übergabe XML mit Pfadangabe
  XMLManagement.AddWorkingDirToXML(XMLDoc,sWorkingDirectory);

  NAV_XMLOut := XMLDoc;


  // Verarbeitung in Navision ******************************
  //SendDataToNavision(XMLDoc,XMLDoc_RESP);

  // <-- Aufruf muss im Primär Thread erfolgen, z.B.Thread.create, Wie bekomme ich die Instanz von FOLEInterface ?
  OLECheck(CoMarshalInterThreadInterfaceinStream(IOLEInterface,FOLEInterface,ISTREAM(FStream)));

  // <-- Aufruf im Sekundär Thread, Zeiger anschliessend in FOLEInterface
  OLECheck(CoInitialize(nil));
  OLECheck(CoGetInterfaceandReleaseStream(ISTREAM(FStream),IOLEInterface,FOLEInterface));

  // --> Hier Zünden des Events Im Kontext von FOLEInterface nötig ???
  

  CoUnInitialize;
  // **************************************************

end;

Zur Zeit erfolgt der Aufruf des Events über ein Event des Formulars. Was ich ja benötigen würde
wäre wohl die "AutoFactory.EventIID" ??

Delphi-Quellcode:
procedure TOLEInterface.Initialize;
begin
  inherited Initialize;
  FConnectionPoints := TConnectionPoints.Create(Self);
  if AutoFactory.EventTypeInfo <> nil then
    FConnectionPoints.CreateConnectionPoint(
      AutoFactory.EventIID, ckMulti, EventConnect);
  Form1.NewDataAvailable := self.Ev_NewXMLData_All; //Event verknüpfen
end;

procedure TOLEInterface.Ev_NewXMLData_All(Sender : TObject);
var
  aEnum : IEnumConnections;
  aCData : TConnectData;
  aSink : IUnknown;
  iFetch : Cardinal;
  OLEInVariant : OLEVariant;
  OLEOutVariant : OLEVariant;
  XMLOut : IXMLDocument;
  XMLIn : IXMLDocument;
  XMLOutStream : TMemoryStream;

begin
  aEnum := GetConnectionEnumerator(self);
  If aEnum <> Nil Then begin
    XMLOutStream := TMemoryStream.Create;
    Form1.GetXMLOut(XMLOut);
    XMLOut.SaveToStream(XMLOutStream,ofNone);
    XMLOutStream.Position := 0;
    OLEOutVariant := StreamToOleVariant(XMLOutStream, XMLOutStream.Size);

    while aEnum.Next(1,aCData,@iFetch) = S_OK do begin
      aSink := aCData.pUnk;
      If Assigned(aSink) Then
        //(aSink as IOLEInterfaceEvents).NewXMLData(OLEOutVariant,OLEInVariant);
        (aSink as IOLEInterfaceEvents).NewXMLData(OLEOutVariant);
    end;
  end;
end;

function TOLEInterface.GetConnectionEnumerator(FOLEInterface : IOLEInterface) : IEnumConnections;
var
  aCPC : IConnectionPointContainer;
  aCP : IConnectionPoint;
begin
  Result := nil;
  OLECheck(FOLEInterface.QueryInterface(ICOnnectionPointContainer,aCPC));
  OLECheck(aCPC.FindConnectionPoint(AutoFactory.EventIID,aCP)); //<----- !!
  aCP.EnumConnections(Result);
end;
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:53 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz