Laden...

[gelöst] Methodensignatur für importierte Methode aus C++

Erstellt von JunkyXL vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.671 Views
JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren
[gelöst] Methodensignatur für importierte Methode aus C++

Hallo zusammen,

ich habe eine Funktion in C++ mit folgender Signatur:

LPCWSTR GetString(wchar_t* str1);

wie müsste das in .NET korrekt eingebunden werden?

Mit

[DllImport("LicenseKeyGenerator.dll", CharSet=CharSet.Auto)]
static extern string GetString(string s1);

funktioniert es schonmal nicht. Ich erhalte beim Zurückgeben des Wertes eine AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Die Parameterübergabe funktioniert ohne Probleme und der String wird weiterverarbeitet. Also lediglich beim Zurückliefern des Wertes von C++ auf .NET gibt es das Problem.

Wäre für jede Hilfe dankbar!

3.170 Beiträge seit 2006
vor 14 Jahren

Hallo,

versuch es mal so:

[DllImport("LicenseKeyGenerator.dll", CharSet=CharSet.Auto)]
static extern IntPtr GetString(string s1);

Die MSDN-Doku sagt:

When an unmanaged routine returns a pointer to a string, it should be cast as an IntPtr. The string pointed to by the IntPtr can then be converted to a managed string using the Marshal class's PtrToStringUni method. The following routine calls the GetCommandLine Windows API, which returns a pointer to the command line as its return value.
(Quelle:
>
)

also danach:

IntPtr ptr = GetString(s1);
    string result = Marshal.PtrToStringUni(ptr);

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

1.361 Beiträge seit 2007
vor 14 Jahren

Hi JunkyXL,

also es scheint ja doch alles soweit zu stimmen (und auch standardmäßig mit Unicode).

Was wohl das eigentlichte Problem ist:
string::c_str() gibt dir einen Pointer auf ein (von string) internes wchar_t-Array zurück.

Allerdings ist deine string-Variable nur lokal erzeugt. Wenn du also den Scope (die Funktion) verlässt, wird der string wieder zerstört und damit auch das interne Array freigegeben.

Wenn du nun trotzdem einen Pointer auf dieses Array an C# zurückgibst, und der Marshaller versucht einen String an dieser Position herauszulesen, dann liegt da aber nix. Ja es kann halt sogar zu einer AccessViolation kommen.

Du müsstest der Methode einen passenden Puffer von außen übergeben. Beispielsweise einen zusätzlichen Out Parameter. Hierfür eigenet sich der StringBuilder, dem du eine feste Bufferlänge vorgeben kann.

Jedenfalls brauchst du irgendeinen Speicher für den Rückgabestring, der nicht nur innerhalb der C/C++-Methode existiert !

beste Grüße
zommi

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Ich bin gerade dabei eine MFC Anwendung zu erstellen. Wenn du mir das ersparen willst, wäre es nett, wenn du mir bei dem StringBuilder helfen könntest. Habe die Variante nämlich schonmal probiert, dass ich einen StringBuilder als ref reingegeben habe und die entsprechende char* Variable in C++ gefüllt habe. -> StringBuilder war danach leer.

Hallo MarsStein,

das mit IntPtr als Rückgabewert habe ich auch schon probiert und anschließend per Marshal.PtrToX() den string Wert geholt. Mehr als Hyroglyphen kamen da leider nicht raus.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi,

den StringBuilder ohne "ref"-Schlüsselwort übergeben.
Das ist auch einer der wenigen (wenn nicht sogar der einzige) Typ, der standardmäßig als Parameter mit "[In,Out]" gemarshallt wird und nicht nur "[In]".

Ein Beispiel ist auch bei Standardmäßiges Marshalling für Zeichenfolgen: Zeichenfolgenpuffer mit fester Länge zu finden.

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Habe das jetzt so geändert. StringBuilder bleibt nach Aufruf leer...

Header:

#include "stdafx.h"

extern "C"
{
    void __declspec(dllexport) GetString(char* str, LPTSTR key);
}

CPP:

void GetString(char* str, LPTSTR key)
{
    key = GetString(str);
}

.NET:

StringBuilder sb = new StringBuilder(256);
GetString("test", sb);

[DllImport("TestDLL.dll")]
static extern void GetString(string str, StringBuilder key);

Gebe ich den StringBuilder als out Parameter rein, ist er anschließend null, ansonsten instanziert ohne Inhalt.
Was nun?

1.361 Beiträge seit 2007
vor 14 Jahren

Hi Junky,

Der Fehler liegt bei:


void GetString(char* str, LPTSTR key)
{
    key = GetString(str);
}

"key" ist doch ein Pointer, also in C#-Sprech sowas wie eine Referenz.
wenn du key intern einfach umbiegst auf einen neuen Zeigern, merkt das von außen doch keiner !

Also musst du nicht key umbiegen, sondern alles was du berechnest, da reinschreiben.
Also beispielsweise:


void GetString(char* str, LPTSTR key)
{
    strcpy(key, GetString(str));
}

Wobei ich mir nun die Frage stelle: Wenn GetString(str) schon einen Zeiger auf einen char*-Block zurückgibt, wo wird der freigegeben?

beste Grüße
zommi

3.971 Beiträge seit 2006
vor 14 Jahren

uint __declspec(dllexport) GetString(char* str, LPTSTR key, uint keylength);


StringBuilder sb = new StringBuilder(256);
int len = GetString("test", sb, sb.Length);

[DllImport("TestDLL.dll")]
static extern int GetString(string str, [In,Out] StringBuilder key, int keymaxlength);

GetString gibt die Anzahl der geschriebene Zeichen im Argument Key zurück. Das brauchst du, damit du in C# weißt, wieviele Zeichen du aus dem StringBuilder wieder rausholen musst.

Das keymaxlength ist für dein GetString wichtig, damit es nicht über das Ende von key hinausschreibt und so falsche Adressbereiche oder sogar Prozesse in mitleidenschaft gezogen werden.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

JunkyXL Themenstarter:in
1.665 Beiträge seit 2006
vor 14 Jahren

Vielen Dank zommi!
strcpy hat hier geholfen.

Hallo kleines_eichhoernchen,

[In, Out] scheint nichts zu bewirken, zumindest macht es keinen Unterschied.

K
133 Beiträge seit 2009
vor 14 Jahren

GetString gibt die Anzahl der geschriebene Zeichen im Argument Key zurück. Das brauchst du, damit du in C# weißt, wieviele Zeichen du aus dem StringBuilder wieder rausholen musst.

? Da das ganze ja ein String ist kann man auch einfach den Speicher bis zum null terminator lesen. Wüsste nicht warum man die Anzahl der Zeichen braucht?!

3.971 Beiträge seit 2006
vor 14 Jahren

Hallo kainPlan,
stimmt, hast recht. Hatte ich ganz vergessen.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...