![]() |
Stringübergabe in eine Delphi Win32-DLL
Hallo,
ich veruche eine Funktion aus einer Delphi Win32 DLL unter C# aufzurufen. Ich muss hier zwei strings übergeben und scheitere daran kläglich. In meiner DLL kommt immer nur Schrott an. Hier in der Forensuche finden sich zwar einige Beträge die Stringübergaben behandeln, aber die Tipps bringen mich nicht nach vorn. Meine C# Implemetierung:
Code:
Unter Delphi habe ich die Funktion wie folgt aufgebaut
[return: MarshalAs(UnmanagedType.I4)]
[DllImport("wsa.dll", CharSet = CharSet.Auto)] static extern Int32 ConvertArchiveToXml( [MarshalAs(UnmanagedType.BStr)] string archivePath, [MarshalAs(UnmanagedType.BStr)] string xmlPath); // Für das Marshalling habe ich auch schon ".LPStr" und ".LPWStr" ausbrobiert. // Leider mit gleichem Ergebnis
Delphi-Quellcode:
Was bitte mache ich denn falsch?
library wsa;
... function ConvertArchiveToXml(achivePath: PAnsiChar; xmlPath: PAnsiChar): integer; // In der Deklaration habe ich bereits PChar und widestring ausprobiert. var amountOfDataLines : integer; lastErrorCode : integer; begin // Testausgaben in eine lokale Protokolldatei um die Übergaben in die DLL zu prüfen SetLogItem('Archiv: ' + archivePath); SetLogItem('XML: ' + xmlPath); try result := ExtractArchiveContentToTable(archivePath); // (Die Funktion liefert einen INT) except result := -1; end; end; ... exports ConvertArchiveToXml, initDLL, freeDLL; ... :?: |
AW: Stringübergabe in eine Delphi Win32-DLL
Wo ist der Einsprungspunkt?
Zumindest kann ich den in deinem Code nicht entdecken. Beispiel aus meiner DLL! C# Seite
Code:
Delphi Seite
//BASSVIS_GetVersion
public static string BASSVIS_GetVersion() { IntPtr ptr = BASSVIS_GetVersionPtr(); if (ptr != IntPtr.Zero) { return Marshal.PtrToStringAnsi(ptr); } return null; } [DllImport("bass_vis.dll", EntryPoint = "BASSVIS_GetVersion", CharSet = CharSet.Auto)] private static extern IntPtr BASSVIS_GetVersionPtr();
Delphi-Quellcode:
function BASSVIS_GetVersion: PAnsiChar; stdcall;
const cverfor = '%d.%d.%d.%d'; var ver : TBASSVIS_VERSION; str : string; begin ver := GetVersionNumber; str := format(cverfor,[ver.Major,ver.Minor,ver.Special,ver.Build]); result := PAnsiChar(Ansistring(str)); end; gruss |
AW: Stringübergabe in eine Delphi Win32-DLL
Das Problem ist einfach das fehlende stdcall, das in dem Beispiel von EWeiss drin ist. Denn ohne Angabe der Aufrufkonvention wird bei .NET stdcall benutzt.
@EWeiss: Einen PAnsiChar zurückzugeben ist mutig... Wenn der Speicher bei der Verwendung in der aufrufenden Prozedur zufällig schon überschrieben ist, knallt es... |
AW: Stringübergabe in eine Delphi Win32-DLL
Bei D2006 sollte man auch kein CharSet.Auto verwenden. Hier wäre CharSet.Ansi angebracht.
Besser wäre gleiche PWideChar zu verwenden und alles gleich Unicode-Sicher zu realisieren. oder willst du unbedingt in 2012 noch Win9x unterstützen? |
AW: Stringübergabe in eine Delphi Win32-DLL
Zitat:
Und geknallt hat es schon gar nicht ;) Ist doch die frage wie meine Classe in der die Funktion implementiert ist aufgebaut ist. Um so eine Aussage treffen zu können muss man den gesamten Code des Wrappers gesehen habe. Aber der ist closed Source.
Code:
[SuppressUnmanagedCodeSecurity]
public sealed class BassVis { Zitat:
Zitat:
gruss |
AW: Stringübergabe in eine Delphi Win32-DLL
Zitat:
Insofern ist der Rest des Codes ziemlich egal. Es funktioniert nur deshalb, weil an der Stelle im Speicher nichts geschrieben wird bis der String bei dir umkopiert ist. (Zum Beispiel durch Zuweisung an einen String.) Sich darauf zu verlassen ist aber nicht unbedingt sinnvoll. // EDIT: Um hier keine unbewiesenen Behauptungen zu posten... kleines Beispiel:
Delphi-Quellcode:
Du wirst sehen, dass (zumindest unter XE) zweimal 10xb ausgegeben wird. Einfach weil die selbe Speicheradresse wiederverwendet wird bevor der erste String ausgegeben wird... Und das ist ja auch vollkommen korrekt, da der Speicherbereich schon wieder freigegeben ist. Dass darauf noch ein Pointer existiert, darf nicht passieren. Deshalb ist der Code so schlicht fehlerhaft.
function Test(const Value: string): PChar;
begin Result := PChar(DupeString(Value, 10)); end; procedure TForm240.FormCreate(Sender: TObject); var TestResult1, TestResult2: PChar; begin TestResult1 := Test('a'); TestResult2 := Test('b'); ShowMessage(TestResult1); ShowMessage(TestResult2); end; |
AW: Stringübergabe in eine Delphi Win32-DLL
Mag ja alles sein.. ;)
Aber ich bin ja auch kein Studierter .. solange es funktioniert und keine Fehlermeldungen seitens der Anwender kommen lasse ich es erstmal so wie es ist. Letztendlich wollte ich dem Thread ersteller helfen. Ob er davon etwas annimmt kann mir am ende egal sein. :) gruss |
AW: Stringübergabe in eine Delphi Win32-DLL
Zitat:
Ich meinte das auch eher für die Zukunft. Bei anderem Code funktioniert es vielleicht mal nicht und du suchst dir nen Wolf nach dem Fehler. :wink: Zitat:
|
AW: Stringübergabe in eine Delphi Win32-DLL
Zitat:
Möchte ja nicht Undankbar erscheinen. gruss |
AW: Stringübergabe in eine Delphi Win32-DLL
Zitat:
Denn FastMM behält sich Speicher noch eine Weile, um ihn schneller wiederverwenden zu können und nicht jedesmal langwierig bei Windows beantragen zu müssen. DupeString liefert eine Stringvariable zurück, wofür Delphi eine temporäre Variable anlegt, welche bei Funktionsende wieder freigegeben wird.
Delphi-Quellcode:
Und es ist Zufall, wenn hier wieder die selbe PChar-Adresse zurückgegeben wird.
function Test(const Value: string): PChar;
var Temp: string; begin Temp := DupeString(Value, 10); Result := PChar(Temp); end; // und im FunktionsENDe steht quasi ein Temp := ''; Nämlich nur dann, wenn FastMM zufällig den selben Speicherbereich für den Result-String von DupeString wiederverwendet. (was bei einer so einfachen Funktion und bei SingleThreadding zufällig etwas öfters passieren kann, da die Berechnung/Ermittling des nächst freien Speicherblocks ja gleich abläuft) Tipp: Versuch es mal mit einem größeren String, wie z.B.
Delphi-Quellcode:
, denn da ist es wahrscheinlich, daß FastMM den "großen" Speicher sofort wieder an Windows zurückgibt.
DupeString(Value, 1000000000);
Sowas wäre eher möglich, aber das ist natürlich nicht für Multithread, also nicht für mehrere gleichzeitige Aufrufe dieser Funktion, da es nur einen StringSpeicher gibt.
Delphi-Quellcode:
Einfacher ist ShareMem und direkte Stringübergabe, oder die Verwendung von WideString.
var
Test_Result: string; // den String auch noch nach dem Funktionsende erhalten function Test(const Value: string): PChar; begin Test_Result := DupeString(Value, 10); Result := PChar(Test_Result); end; procedure TForm240.FormCreate(Sender: TObject); var TestResult1, TestResult2: string; // Rckgabewerte sofort in einen String umwandeln, also kopieren begin TestResult1 := Test('a'); TestResult2 := Test('b'); ShowMessage(TestResult1); ShowMessage(TestResult2); end; WideString ist eine Kaspelung von String-APIs der OleAut32.dll ( ![]() ![]() ![]() Schlecht ist es auch, wenn keine generischen Typen verwendet werden. PChar und String können sich je nach Compiler (z.B. vor und nach ab Delphi 2009) verändern und schon verändert sich auch die Schnittstelle (PAnsiChar <> PWideChar | AnsiString <> UnicodeString), aber Schnittstellen müssen statisch/unveränderlich sein. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:34 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