![]() |
COM Events und Threads - Problem
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:
An der Stelle
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; "(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 ! |
Re: COM Events und Threads - Problem
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. |
Re: COM Events und Threads - Problem
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 ? |
Re: COM Events und Threads - Problem
Zitat:
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. ![]() |
Re: COM Events und Threads - Problem
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; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:19 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz