Autor Beitrag
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 01.07.22 08:36 
Hallo,

ich habe wieder ein Problem mit der Nutzung von Interfaces. In Demo-Projekten klappt alles, aber wenn ich das ganze produktiv nutzen möchte, gibt es folgenden Fehler:
Zitat:
Project Demo.exe raised exception class EOleException with message 'Das COM-Objekt des Typs "System.__ComObject" kann nicht in den Schnittstellentyp "Demo.ITestInterface" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die COM-Komponente für die Schnittstelle mit der IID "{3D6D3BB2-A365-4F8E-A104-4494D6D0BB69}" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte: Schnittstelle nicht unterstützt (Ausnahme von HRESULT: 0x80004002 (E_NOINTERFACE))'.


Das passiert beim Aufruf einer nach außen gelieferten Interface-Methode:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("5D00A751-A837-42DA-8772-710D8810DCB5")]
    [ComVisible(true)]
    public interface IDemoInterface
    {
        int ExecuteRequest(ITestInterface AConfig);
    }

    public class DemoInterface : IDemoInterface
    {
        public int ExecuteRequest(ITestInterface AConfig)
        {
            AConfig.GetCount();
        }
    }


Ich habe QueryInterface in dem übergebenen Objekt überschrieben und sehe so, dass dort mehrere Interface-GUIDs abgefragt werden, aber nicht die von ITestInterface:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("3D6D3BB2-A365-4F8E-A104-4494D6D0BB69")]
    [ComVisible(true)]
    public interface ITestInterface
    {
        int GetCount();
    }


Leider habe ich keine Ahnung wo ich suchen muss. Ich habe doch als Interface-Typ InterfaceIsIUnknown angegeben und das Interface wird direkt mit dem konkreten Typ übergeben. Warum wird da nun überhaupt QueryInterface verwendet und warum nicht mit der GUID des Interfaces, sondern mit solchen von IMarshal... Interfaces usw.?

Vielen Dank und viele Grüße
Sebastian
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 01.07.22 09:33 
Hallo,

kann es sein, daß du auch beim konkreten Typ DemoInterface die Attribute InterfaceType und ComVisible setzen mußt?

Edit: Laut Verfügbarmachen von .NET Core-Komponenten in COM sowie dem Beispielprojekt COMServerDemo mußt du zwar nicht den InterfaceType angeben, dafür aber die gleiche GUID wie beim Interface (du verwendest zwar noch .NET Framework, und nicht .NET Core bzw. .NET 5/6, sollte jedoch so ähnlich sein - einen Versuch ist es auf jeden Fall wert).

2. Edit:
In COM-Beispielklasse (C#-Programmierhandbuch) ist dagegen eine eigene GUID für jeweils Interface und Klasse angegeben.

3. Edit: Für COM-Klassen gibt es wohl stattdessen das Attribut ClassInterface (kann aber sein, daß der Standard AutoDispatch bei dir paßt, meistens habe ich jedoch in den Internet-Beispielen None gefunden).
Als Kurzübersicht habe ich noch System.Runtime.InteropServices gefunden.

Für diesen Beitrag haben gedankt: jaenicke
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 01.07.22 11:31 
Danke!

Um das klarzustellen:
Der Aufruf an ExecuteRequest kommt an. Deshalb glaube ich auch weniger, dass die C# Klasse DemoInterface das Problem ist. Erst der Aufruf von GetCount() schlägt fehl, und der geht ja auf eine Klasse, die von außen kommt. Interessant ist daran, dass wie gesagt das QueryInterface dieses Interfaces auch aufgerufen wird, sprich es wird auch irgendwo als Interface korrekt behandelt.

Ich habe die Vorschläge aber trotzdem mal versucht, aber nichts davon hat eine Änderung gebracht.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 01.07.22 12:18 
Ah, OK!
Und wie sieht die Implementierung von dem konkreten Typ aus, das ITestInterface implementiert?
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 01.07.22 13:06 
Ganz unspektakulär:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
  ITestInterface = interface
  ['{3D6D3BB2-A365-4F8E-A104-4494D6D0BB69}']
    function GetCount: LongInt; safecall;
  end;

  TTest = class(TInterfacedObject, ITestInterface)
  private
    FCount: LongInt;
  public
    constructor Create(const ACount: LongInt);
    function GetCount: LongInt; safecall;
  end;

Wie gesagt, in Demo-Projekten klappt das auch. Ich habe also auf beiden Seiten erst einmal den gleichen Quelltext, nur dass es in dem einen Projekt geht und in dem anderen nicht. Deshalb tue ich mich auch etwas schwer hier eine Demo zu erstellen. Aber ich versuche es noch einmal...
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 01.07.22 14:52 
Tja, dann wird es wohl irgend einen (klitzekleinen) Unterschied zwischen den beiden Projekten geben.

Muß denn von Delphi aus irgendwas getan werden, um die COM-Objekte nach außen hin sichtbar zu machen (also dem Pendant zu ComVisible)?
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 05.07.22 15:21 
Bei den Experimenten habe ich etwas übersehen:
Die Ursache ist, dass ich mal aus einer DLL heraus die C#-DLL nutze (im Produktivsystem) und mal aus der Hostanwendung selbst.

Ich hatte vorher schon festgestellt, dass ich die C#-DLL aus der Hostanwendung heraus laden muss, damit ich überhaupt mit Interfaces aus C# arbeiten kann. Ich verstehe nur nicht, wo der Unterschied liegt. Warum funktioniert die .NET Runtime anders, wenn ich sie aus einer DLL heraus lade?

Die QueryInterface-Aufrufe, die von .NET kommen, unterscheiden sich, je nachdem von wo aus ich die Funktion aufrufe.

Ich kann das auch noch als Demo bauen, falls das hilft, und darin auch die GUIDs der QueryInterface-Aufrufe ausgeben.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 05.07.22 16:10 
Eine Frage noch vorweg: verwendest du überall dieselbe Bittigkeit (also 32 oder 64 Bit)?

Gefunden habe ich gerade noch Using a .NET Assembly via COM in Delphi (aber noch nicht detailiert gelesen).
Hast du die C# DLL denn auch schon mittels "regasm" (auf dem Produktivsystem) registriert?

Edit: Auch beim Registrieren muß man auf die Bittigkeit achten (bzw. am besten für beide registrieren): How to do RegAsm so that it cover 32-bit and 64-bit?
Und auch "/codebase" als zusätzlichen Parameter jeweils angeben (s. Regasm.exe (Assembly Registration-Tool)), wenn die C#-DLL nicht im GAC installiert wurde.
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 05.07.22 17:12 
Das ist alles 32-Bit und registriert ist gar nichts, da ich die DLL nativ anspreche und nicht als COM Komponente.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 05.07.22 21:32 
Einen Versuch wäre es aber wert.

Oder kannst du dein Demo-Projekt so umschreiben, daß es auch eine Delphi-DLL verwendet, welche dann die C#-DLL aufruft?
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 06.07.22 09:06 
Ich verstehe nicht, weshalb mir das nicht vorher in den Sinn gekommen ist. Eigentlich war es mir ja klar, aber ich habe nicht darüber nachgedacht:
Der Aufruf im Hostprogramm passiert im Thread.

Vorher war das ein Aufruf an eine externe Exe, so dass das gar kein Problem war, aber nach der Umstellung auf die DLL-Anbindung ist es natürlich relevant.

Daraufhin habe ich dann CoInitializeEx mit COINIT_MULTITHREADED und COINIT_APARTMENTTHREADED versucht, aber das scheint gar keinen Unterschied zu machen. Hast du vielleicht eine Idee?

Ich brauche den Aufruf nur in einen anonymen Thread zu schieben, dann geht es nicht mehr. Mit einer Delphi-DLL auf der anderen Seite gibt es keine Probleme.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 06.07.22 10:42 
Leider habe ich dazu auch keine Idee.
Ich habe nur in c# - How to make make a .NET COM object apartment-threaded? gelesen, daß .NET Objekte standardmäßig "free-threaded" (also MTA) sind. Was das aber genau bedeutet???

Die C#-DLL wird aber auch in diesem Thread geladen (also der LoadLibrary-Aufruf)?

Außerdem habe ich noch Tips & Tricks (for COM Developers) gefunden, das ich dir nicht vorenthalten wollte, aber dort auch keine konkreten Hinweise bzgl. deines Problems gefunden.
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 06.07.22 10:54 
Vielen Dank, es funktioniert doch. Ich habe nicht gemerkt, dass der Projektteil mit dem CoInitializeEx nicht neu kompiliert wurde.

Heißt:
Mit CoInitializeEx in dem aufrufenden Thread auf Seiten von Delphi klappt es doch.

Vielen Dank für deine Hilfe!
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.07.22 10:57 
Zitat:
Daraufhin habe ich dann CoInitializeEx mit COINIT_MULTITHREADED und COINIT_APARTMENTTHREADED versucht, aber das scheint gar keinen Unterschied zu machen. Hast du vielleicht eine Idee?


Möglicherweise ein Reihenfolge Problem. .Net ruft das schon für sich auf und der erste Aufruf zieht. Je nach Aufbau deines Hosts ist möglicherweise die Reihenfolge von CoInitialize und starten des Frameworks anders.

Hast du mal den Return von CoInitializeEx geprüft?

Edit: Minuten zu spät ;)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 06.07.22 11:29 
Ich habe ja hierfür wenig beitragen können, aber es freut mich, daß es jetzt bei dir läuft - ich weiß, wie nervig es ist, wenn man so ein generelles Problem hat.