![]() |
Wie ein array of record von C# dll mit COM an Delphi übergeben?
Hallo,
ich bin am verzeifeln. Ich möchte in Delphi eine Methode von einer C# DLL aufrufen und einen array of record in Delphi bekommen. Das Record könnte so aussehen:
Delphi-Quellcode:
Dazu Ich habe einfach in Visual Studio 2019 eine "Class Library (.NET Framework)" Project erstellt und eine DLL mit einer einzigen Methode erstellt. Diese DLL lade ich einfach in Komponenten importieren->Typenbibliothek rein und erhalte eine TLB Unit in Delphi. Wenn die Funktionsparameter nur aus String oder Integer bestehen funktioniert es auch wunderbar. Doch wie kann ich von C# aus array of struct/record als Parameter übergeben?
RMEDIA = packed record
Name: String; ID: Integer; end; Auf C# Seite habe ich mal folgendes versucht:
Code:
Wenn ich diese erstellte DLL in Delphi über die Komponente installieren->Typenbibliothek importierenreinlade erhalte folgende Methode:
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace DelphiTest { public class CrossDLL { public struct RMEDIA { public String Name; public int ID; } public string HelloWorldArray(out RMEDIA[] structArray, ref int arraySize) { arraySize = 2; // Create an array of MyStruct structArray = new RMEDIA[arraySize]; // Populate the array with data for (int i = 0; i < arraySize; i++) { structArray[i].ID = i + 1; structArray[i].Name = "Hallo"; } return $""; } } }
Delphi-Quellcode:
Wenn ich folgendes versuche:
function HelloWorldArray(out structArray: PSafeArray; var arraySize: Integer): WideString;
Delphi-Quellcode:
bekomme ich gleich folgende eine Fehlermeldung:
procedure TForm1.Button2Click(Sender: TObject);
var CrossDLL: TCrossDLL; Items: PSafeArray; arraySize: Integer; begin CrossDLL :=TCrossDLL.Create(self); CrossDLL.HelloWorldArray(Items, arraySize); end;
Code:
Hat jemand einen Ansatz für mich? :gruebel: :gruebel:
---------------------------
Project1 --------------------------- Altes Format oder ungültige Typbibliothek. (Ausnahme von HRESULT: 0x80028019 (TYPE_E_UNSUPFORMAT)) --------------------------- OK --------------------------- |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Mir geht es im Moment nicht gut, deshalb nur kurz:
Ich verwende das NuGet Paket DllExport und erstelle damit nativ exportierte Funktionen. Die DLL kann man dann in Delphi einfach mit LoadLibrary laden. Ich habe darum herum dann ein Framework gebaut, mit dem ich C#-Interfaces generisch aus Delphi heraus abrufen oder umgekehrt in der C#-DLL Interfaces aus der Delphi-Hostanwendung abrufen kann. Auf diese Weise kann ich auch z.B. direkt generische Listen in beiden Richtungen als Parameter verwenden. Aber für dich reichen ja die nativen Funktionen vermutlich schon. Nächste Woche kann ich dazu bei Bedarf auch mehr schreiben. Den entsprechenden Quelltext kann ich aber leider nicht zeigen. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Was willst du im C# mit diesem Record machen?
Wenn nur lesen, dann kann String als PChar angesehen werden (also char* und vielleicht auch char[] bis 2007 und wchar ab Delphi 2009) Überschreiben einzelner Chars ginge zwar auch, aber nur sicher, wenn vorher ![]() und die länge Ändern bedingt einen Zugriff auf den Delphi-Speichermanager, also grundsätzlich somit erstmal NEIN, für Schreibzugriffe. Warum benutzt du denn keinen WideString? Das ist eine Kapselung des BSTR vom C++, also von ![]() |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
@himitsu: zunächst möchte die Record Arrays in Delphi nur mal einlesen können. Wie schon gesagt funktioniert die Sache mit einfachen Typen wie String und Integer ohne Probleme. Ich weiß halt nur nicht wie ich ein Array of Record von C# an Delphi weitergebe, so dass der Delphi Typenbibliothek Import einwandfrei klappt.
Der Hintergrund ist, dass es vom Hersteller nur eine C# DLL existiert. Und auch die ganze Doku für C# ausgelegt ist. Da ich es aber in Delphi vewenden möchte, habe ich mir gedacht, dass ich dafür eine C# COM Wrapper DLL schreibe, die ich einfach über die Delphi Typenbibliothek importiere. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Ach andersrum :oops:
in Delphi gefüllt -> in C# lesen in C# gefüllt -> in Delphi lesen Jetzt müsste man wissen, wie im C# deren "String" intern aufgebaut ist. Ich würde mal vermuten es ist ein PPChar ... ein PChar-kompatibler Typ, der in einem Objekt verpackt wurde. :freak: Hilft aber nur, wenn du manuell die Konvertierung/Übergabe machst. Vermutete einfachste Lösung: benutze das, was im C# einem BSTR (aka WideString) entspricht. Oder eben einen OleVariant. Man kann sich auf das Minimum konzentrieren und muß auf der anderen Seite etwas Gleichwertiges, oder zumindest Kompatibles finden. PChar ist ein Zeiger auf Chars, die durch #0 terminiert (abgeschlossen) werden. Die "LongStrings" von Delphi (String, AnsiString, UnicocdeString) sind intern kompatibel mit einem PChar. -> die Verwaltungsdaten liegen rückwertig vor dem internen Zeiger (der auf das erste Char zeigt) und hinter dem letzten Char befinden sich implizit zwei #0. Drum lässt sich ein String problemlos in einen PChar casten. Der ShortString (hieß gaaaaanz früher mal "string") ist ein Record, bzw. statisches CharArray, wo der Char[0] das Längenbyte darstellt. (drum fangen im Delphi auch alle Strings auch mit 1 an :wink:) Der "OLE-String" WideString basiert intern auf einem BSTR und benutzt die APIs SysAllocString/SysReAllocString/SysFreeString der oleauto.h bzw. OleAut32.dll. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
@jaenicke: vielen Dank für den Hinweis mit dem DllExport. Wie so oft hast du mir ja schon weitergeholfen! :thumb: :thumb: Ich habe zwar nicht DllExport Package verwendet, sondern das
![]() ![]() ![]() Was dein Angebot bzgl. deinen Ansatz mit DllExport betrifft, so wäre ich sehr interessiert. Natürlich erst, wenn es dir wieder besser geht. Vielleicht könntest du ein kurzes Beispiel für DllExport zeigen. Wünsche gute Besserung!!! lg, jus |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
Zitat:
Darüber kann man dann noch einen generischen Aufsatz schreiben, damit es einfach zu verwenden ist. Diesen Ansatz habe ich nun schon dreimal entworfen und er wurde jedesmal besser. :-D Zuerst habe ich damit privat gespielt, aber das war nur ein Grundgerüst, dann habe ich es zweimal richtig für berufliche Zwecke implementiert, am Ende dann auch mit C#. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
Code:
Es funktioniert bei mir irgendwie nicht. Irgendwas mache ich da falsch. Sobald ich den Befehl [DllExport] kommt da folgende Fehlermeldungen:
using System;
using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace DelphiCrossV2 { public class Test { [DllExport] public static int TestExport(int left, int right) { return left + right; } } }
Code:
error CS0246: The type or namespace name 'DllExport' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DllExport' could not be found (are you missing a using directive or an assembly reference?) |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Liste der Anhänge anzeigen (Anzahl: 1)
- Klassenbibliothek (.NET Framework) erstellen
- In den Projekteigenschaften unter Build --> Allgemein das Plattformziel auf x86 setzen - Bezeichnungen der Klassen / Namespace usw. setzen - DllExport installieren --> Es öffnet sich dieses Fenster: Anhang 56072 Dort das Häkchen bei "Installed" setzen wie im Screenshot zu sehen und Apply drücken! Der Namespace sollte automatisch passen, wenn es ein neues Projekt ist, aber den kann man dort ggf. auch anpassen. Dann auf Aufforderung des Visual Studios die Mappe neu laden (weil die Projektdatei extern geändert wurde). Nun sollte DllExport gefunden werden. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
![]() Viel Material gibt es dazu aber wirklich nicht. Zum Beispiel musste ich auch erst herausfinden, dass man dabei ein paar Einschränkungen hat was Rückgabewerte usw. angeht. Das größte Problem ist aber das Debugging. Visual Studio unterstützt bei solchen DLLs leider keinerlei Debugging, so dass man von Anfang an ein gutes Logging einbauen sollte. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
@jaenicke: Bei Übergabe von array of record/struct zwischen Delphiprogramm und einer Delphi DLL kann man natürlich locker ein array of record im Parameter übergeben. Zwischen Delphiprogramm und C DLL soll ja am besten nur den Zeiger auf den 1.Record vom Array geben und die Arrayanzahl. Aber wie kann man ein array of record/struct mit IInterface zwischen Delphiprogramm und C# übergeben? Wäre es dann eine InterfaceList statt einem Array? :gruebel: |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Du kannst natürlich das Array auch als Parameter einer Funktion des Interfaces übergeben. Ich persönlich nutze allerdings lieber eine Interface-Liste.
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
@jaenicke: ich versuche einbisschen durch deinen Hinweis mit Interfaces zu experimentieren. :-D Ich erstelle ein Interfaceobjekt in Delphi und übergebe es an die C# DLL Methode als Parameter.
Delphi-Quellcode:
type
TMedia = record Name: WideString; ID: Integer; end; TMediaArray = array of TMedia; ITransportListIntf = interface ['{F059A4D4-1E57-4E4C-A300-5139F53E4062}'] procedure Clear; safecall; procedure Add( Name: WideString; ID: Integer ); safecall; procedure getList( var items: TMediaArray ); safecall; end; TTransportList = class(TInterfacedObject, ITransportListIntf) private FData: TMediaArray; public procedure Clear; safecall; procedure Add( Name: WideString; ID: Integer ); safecall; procedure getList( var items: TMediaArray ); safecall; end; implementation uses Dialogs; { TTransportList } procedure TTransportList.Add(Name: WideString; ID: Integer); begin SetLength(FData, Length(FData) + 1); FData[High(FData)].Name := Name; FData[High(FData)].ID := ID; end; procedure TTransportList.Clear; begin SetLength(FData,0); end; procedure TTransportList.getList(var items: TMediaArray); begin items := FData; end; end.
Code:
in Delphi wird es dann wie folgt gestartet:
[Guid("F059A4D4-1E57-4E4C-A300-5139F53E4062"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITransportListIntf { void Clear(); void Add([MarshalAs(UnmanagedType.BStr)] String Name, int ID); void getList(out RMEDIA[] items); } [DllExport] public static int getMediaList([In,Out, MarshalAs(UnmanagedType.Interface)] ITransportListIntf TransportList) { //... TransportList.Add( mediaName, mediaID ); //.... }
Delphi-Quellcode:
Grundsätzlich funktioniert es auch ohne Probleme. Jetzt habe ich aber festgestellt, dass es ein Speicherleck (Fastmm zeigt es beim Beenden an) produziert. Anscheinend wird TransportList Objekt nicht freigeben. Was muss man tun, damit es freigeben wird ? :gruebel: :gruebel:type TgetMediaList = function ( MediaList: ITransportListIntf ): Integer; stdcall; procedure TForm1.Button1Click(Sender: TObject); var TransportList: ITransportListIntf; MediaList: TMediaArray; begin TransportList := TTransportList.Create; getMediaList( TransportList ); // <--- hier wird die C# DLL Methode aufgerufen TransportList.getList( MediaList ); end; |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
ok, bin jetzt einen kleinen Schritt weiter. Meine Vermutung ist, dass die Referenzzählung in Verbindung mit der C# DLL bei mir Probleme macht. Darum wäre meine Quick&Dirty Lösung, dass ich direkt beim Beenden der Methode in den Referenzzähler reinpfusche:
Delphi-Quellcode:
Damit gibt es nun keine Meldung von Fastmm, dass es einen Speicherleck gibt. Meine Frage an die Experten: Ist es total verwerflich, was ich da mache? Und gibt es da einen besseren Weg?
procedure TForm1.Button1Click(Sender: TObject);
var TransportList: ITransportListIntf; MediaList: TMediaArray; i, j: Integer; begin TransportList := TTransportList.Create; try getMediaList( TransportList ); // <--- hier wird die C# DLL Methode aufgerufen TransportList.getList( MediaList ); finally if TransportList <> nil then begin j := TransportList._AddRef; for i := j downto 2 do TransportList._Release; //<----- setze manuell den Referenzzähler end; end; end; |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Hast du denn mal _Addref und _Release selbst implementiert und einen Haltepunkt drauf gesetzt? Dann sollte doch klar werden, wo die zusätzliche Referenz herkommt.
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Ich habe nun die angesprochene Funktionalität in neu geschriebener Form veröffentlicht:
![]() |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
vielen vielen Dank!! :thumb: Das muß ich mir mal in Ruhe anschauen :-D
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
Ich habe vor weit über 10 Jahren aufgegeben, mit jeder neuen Version von msbuild oder .Net den Code anpassen zu müssen, der ildasm/ilasm findet. War mir damals zu heikel es mit Mono.Cecil oder IKVM.Reflection zu machen. |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Vielen Dank für deine Antwort.
Mit neueren Versionen als .NET 4.8 habe ich allerdings auch mit der neueren Version noch ein paar Meinungsverschiedenheiten. Den Quelltext habe ich ![]() |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Ich glaube nicht dass .net core überhaupt die Sachen hat, die sie damals für managed C++ und später C++/CLI eingebaut haben.
und das ist ja was ich da ausgenutzt habe |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Doch, .NET Core wird offiziell unterstützt. Es gibt auch keine Fehler, wenn ich ein solches Projekt kompiliere und die Exporte werden auch gefunden, aber es knallt, wenn ich diese aufrufe. Aber das muss ich mir noch in Ruhe anschauen. Ich verwende Com-Interfaces via Marshalling mit C# Objekten und vielleicht gibt es die Probleme auch nur dabei.
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Ich verwende auch eine selbst erstelle c# DLL in Delphi.
Schon länger, also ohne Nuget Tools/Hilfen. Nur einfache Datentypen. Aber ihr schreibt Debuggen geht nicht. Das Debuggen geht ganz normal in 32 und 64bit Was ich noch nicht probiert habe sind C# DLL‘s die nicht Registriert werden müssen |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
|
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Zitat:
Hatte es damals hauptsächlich gemacht, weil Delphi.net es konnte und Chrome (später Oxygene) konnte es nicht. Und weil ich zu oft auf stackoverflow gelesen hatte dass es unmöglich ist. :-D Das Problem mit netcore ist, dass es AFAIR keine wirklichen DLLs baut, nur Assemblies. Und selbst wenn, dann bin mir nicht sicher dass die runtime automatisch thunks erzeugt, die vom extern call in den managed call übersetzen und sicherstellen dass die shared runtime in den Prozess geladen wird. Das ist alles super kompliziert und MS hat das nur wegen C++/cli gebaut. Weil es dann schon da war, haben sie es bis 4.8 drin gelassen aber nie wieder angefasst. netcore ist von der runtime eher Mono, das hatte das nie in dem Umfang. kann aber auch falsch liegen, müsste dafür erst specs und dotnet runtime code durchwühlen… |
AW: Wie ein array of record von C# dll mit COM an Delphi übergeben?
Vielen Dank für deine Antwort. Ich habe mir das nun noch einmal angeschaut. Es sah schon so aus, als ob das Richtige passiert. Es wird in der Execution Engine GetTargetForVTableEntry und darin CorDllMainWorkerForThunk aufgerufen. Dann geht es in die CLR, aber dort kommt dann eine externe Exception, aber erst, wenn, soweit ich das verstanden habe, schon auf das Objekt zugegriffen werden soll. Daraufhin habe ich ein wenig gesucht.
Lösung: Wenn man DllExport installiert oder die Batchdatei DllExport.bat im Projektverzeichnis manuell aufruft, gibt es unten rechts die Option "Use our IL Assembler. Try to fix 0x13 / 0x11 opcodes.", welche "Rebase System.Object: System.Runtime > mscorlib" freischaltet. Beide müssen gesetzt werden, dann klappt es auch z.B. mit .NET 6.0. Ich werde nun eine entsprechende Dokumentation zu meinem AppCentral-Projekt erstellen, die C#-Seite komplett implementieren, dann sollte das auf einem guten Weg sein. Ich habe übrigens nun auch eine passende Java-Implementierung angefangen, so dass man auch von dort eine solche C#-DLL oder auch eine Delphi-DLL einbinden kann. Das sieht aber auch nicht ganz so einfach aus... aber mal schauen. :-D |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:24 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