Laden...

Ist ein eigener return-Type bei COM-Interop möglich?

Erstellt von bredator vor 9 Jahren Letzter Beitrag vor 9 Jahren 1.742 Views
B
bredator Themenstarter:in
357 Beiträge seit 2010
vor 9 Jahren
Ist ein eigener return-Type bei COM-Interop möglich?

Hallo zusammen,

mal wieder das leidige Thema COM-Interop/Schwarze Magie. Ganz grob zu meinem System: Wir haben eine große Warenwirtschaft, die in C++ mit der MFC geschrieben wurde. Aufgrund der teils nervtötenden Umwege, die man für viele Dinge gehen muss (die man aus .NET gewohnt ist), haben wir uns entschieden, diverse Sachen in .NET-Dlls auszulagern, also eigentlich in COM-Komponenten. Das funktioniert bei zwei Komponenten bisher ganz gut, da diese nichts weiter tun, als Daten aufzubereiten und darzustellen. So weit so gut.

Bei der jetzt geplanten Komponente sollen allerdings die Daten nicht direkt in der Komponente als Endstation abgelegt werden, sondern in der Warenwirtschaft weiter verwendet werden. Dazu sollen Strukturen hin und her geschubst werden (also C++-WaWi <-> .Net-Dll). Wenn ich jetzt auf der .NET Seite meine Klassen soweit fertig geschrieben habe, diese mit


[StructLayout(LayoutKind.Sequential)]
[ComVisible(true)]

versehen habe und ansonsten auch alles, wie bei den anderen Komponenten gemacht habe, dann wird mir ja von VS die Type Library ausgegeben und die Dll registriert. Einen Pointer bekomme ich auch wunderbar zurück und auch simple Anfragen und Rückgaben funktionieren. Also bool, integer und auch string-Arrays (BSTR-Arrays) sind problemlos möglich.

Das Problem kommt auf, wenn ich eine eigene Klasse als Rückgabetyp verwenden möchte. Das sieht dann so aus:


public KeyListe HoleKeyListe()
{		
	KeyListe keys = MethodeUmKeyListeZuErstellen();
	return keys;		
}


[StructLayout(LayoutKind.Sequential)]
[ComVisible(true)]
public class KeyListe
{
	[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
	public string[] key_paulist;
	[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
	public string[] key_veblist;
	
	public KeyListe()
	{
		
	}
}

In der .tlh, die mir von der nativen Anwendung aus der TypeLibrary erstellt wird, sehe ich auch die ganzen Schnittstellen beschrieben.

Darin sieht die Klasse KeyListe dann so aus:


struct __declspec(uuid("f8e88956-1b18-3ccf-b290-b4620faf59dd"))
com_KeyListe
{
    SAFEARRAY * key_paulist;
    SAFEARRAY * key_veblist;
};

Methods:
struct KeyListe * HoleKeyListe ();

Mein Gedanke war jetzt, dass es dann doch gehen sollte, diesen com-Typ, der erstellt wurde, in C++ zu nutzen und darin das Ergebnis von HoleKeyListe abzulegen. Das wird mir auch alles brav kompiliert:


KeyListe * keys = s_ptr->HoleKeyListe();

Beim return selbst bekomme ich aber einen TypeMismatch-Fehler. Ich gehe daher einfach mal davon aus, dass es halt doch nicht ganz so einfach zu lösen ist. Jetzt stehe ich allerdings etwas auf dem Schlauch, wie ich jetzt weitermachen soll. Ich habe auch schon ausprobiert, eine Struktur in C++ zu bauen, die derjenigen in C# entspricht und dann einen Pointer auf ein Objekt der Struktur an eine etwas modifizierte C#-Methode weiterzureichen und den Inhalt dann nach einem MarshalPtrToStructure zu bearbeiten. Allerdings mit demselben Ergebnis.

Wie immer will ich nicht unbedingt fertige Lösungen, dazu fuchst mich das Problem zu sehr, als dass ich es nicht verstehen möchte. Ich wäre allerdings für Ansätze dankbar, wie ich z.B. anschauen kann, WAS von der Methode zurückkommt, noch bevor es versucht wird, meinem Pointer auf der C++ Seite zuzuweisen (was ja offenbar erst an dieser Stelle den Type-Mismatch verursacht), so dass ich mir das genauer anschauen kann. Vielleicht finde ich so ja eine Lösung, wie ich das Ergebnis umwandeln muss, um es auf der C++-Seite weiterzuverwenden. Oder ob das überhaupt so möglich ist und ob ich nicht doch ganz was anderes machen muss (was natürlich schon etwas ärgerlich wäre, vor allem, weil es eben so aussieht, als würde nur noch ein winzigkleines Stückchen fehlen für das richtige Ergebnis).

Danke schonmal fürs Lesen und evtl. weitere Zeit, die ihr für mich erübrigt.

Gruß

C
1.214 Beiträge seit 2006
vor 9 Jahren

Ich weiß nicht so genau, was .NET hier macht...

In C++ würde ich das COM Objekt irgendwie so aufbauen:

HRESULT GetKeyList(IKeyList ** list);

Normalerweise wird erwartet, dass Methoden von COM Objekten HRESULT zurückgeben. Steht auch in den Design Rules. Ist in .NET jetzt aber wiederum nicht üblich.
IKeyList wär dann wieder ein Interface und es würde eine Klasse geben, die das implementiert. Das wurde aber anscheinend auch in der Form generiert, tlh Dateien sind mir nicht geläufig.

Was heißt überhaupt type mismatch in C++? Das kann doch eigentlich nur ein HRESULT return code sein, E_TYPEMISMATCH oder so. Das würde aber kommen, wenn man die Methode über IDispatch aufrufen würde. Das seh ich bei dir aber nicht, ich seh einen direkten Aufruf.

P
157 Beiträge seit 2014
vor 9 Jahren

Schau mal hier, vielleicht hilft dir das:
Native C++ and C# interop

Da ist unten die Guid am .net Typ über ein Attribut deklariert. Normalerweise bekommt den Typemissmatch, wenn ein typ nicht gefunden wird.

Wenn's zum weinen nicht reicht, lach drüber!

B
bredator Themenstarter:in
357 Beiträge seit 2010
vor 9 Jahren

Bisher kein Weiterkommen über diesen Weg. Ich werde mir mal die Lösung anschauen, über eine Zwischen-DLL in C++/CLI zu gehen. Trotzdem danke schonmal.

H
3 Beiträge seit 2014
vor 9 Jahren

Dazu sollen Strukturen hin und her geschubst werden

unabhängig welche Sprache, eigene "Typen" kann COM bzw. der Midl nicht.
Was geht sind:
Alle OLE Typen, inklusive VARIANT als Union, und SAFEARRAY als Mengentyp.

  • Interface zeiger auf irgendwelche Instanzen.

Interface Pointer sind wiederum nicht einfach zu haendeln und gibt ne Menge zu beachten.

  • normales Interface oder Dispatch Interface
  • thread (Apartment) model

Bei Nicht Dispatch interfaces ist wiederum die frage, wer Instanziert die klasse, und wo bekommst zur compilezeit die Info (Typelib) her.
Da gibts auch wieder 2 varianten (in das COM Binary reinkompiliert, und oder in der Regestry verlinkt).

Ne andere Frage ist, warum COM ?
Com bringt paar vorteile, die sollt man dann auch nutzen ^^ (Referenzzählung, einfaches marschaling ... )

Generell wenn ich "nur" Interoperabilität zwischen 2 Sprachen brauch, geh ich über C-Schnittstellen ...

C++ kann C-Schnittstellen bedienen, und C# sollte sie auch benutzen können.
Klassen nach C zu wrappen sollte eigentlich jeder fortgeschrittene Programmierer beherrschen. Ist auch gar nicht schwer, sondern meist nur ... langweilig ^^
Gibt auch Tools (SWIG) die sowas automatisch wrappen. Wenn man den erzeugten C-Code keiner Formatierungs und Schönheitskontrolle unterziehen will, funktionieren die auch ganz gut.

Einarbeitungszeit hasst soweiso immer irgendwo. und COM hat ehrlich schon ne recht steile Lernkurve ... also wenn man es low level programmieren will/soll.

Ciao ...