2009-04-30 15 views
4

Ich arbeite an einem PInvoke-Wrapper für eine Bibliothek, die Unicode-Zeichenfolgen nicht unterstützt, aber Multi-Byte-ANSI-Zeichenfolgen unterstützt. Bei der Untersuchung von FxCop-Berichten über die Bibliothek habe ich festgestellt, dass das verwendete String-Marshalling einige interessante Nebenwirkungen hatte. Die PInvoke-Methode verwendet die "bestmögliche" Zuordnung, um eine ANSI-Zeichenfolge mit einem Byte zu erstellen. Zur Veranschaulichung ist das, was ein Verfahren aussieht:Wie kann ich einen Multi-Byte-ANSI-String initialisieren?

[DllImport("thedll.dll", CharSet=CharSet.Ansi)] 
public static extern int CreateNewResource(string resourceName); 

Das Ergebnis des Aufrufs diese Funktion mit einer Zeichenfolge, die Nicht-ASCII-Zeichen enthält, ist, dass Windows einen „close“ Charakter findet, in der Regel sieht aus wie es endet "???" sein. Wenn wir vorgeben, dass "a" ein Nicht-ASCII-Zeichen ist, würde die Übergabe von "cat" als Parameter eine Ressource mit dem Namen "c? T" erzeugen.

Wenn ich die Richtlinien in der FxCop Regel folgen, habe ich am Ende mit etwas wie folgt auf:

[DllImport("thedll.dll", CharSet=CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] 
public static extern int CreateNewResource([MarshalAs(UnmanagedType.LPStr)] string resourceName); 

Dieses eine Änderung im Verhalten führt; Wenn ein Charakter nicht zugeordnet werden kann, wird eine Ausnahme ausgelöst. Dies betrifft mich, weil dies eine bahnbrechende Änderung ist, daher möchte ich versuchen, die Strings als Multi-Byte-ANSI zu verwalten, aber ich sehe keinen Weg, dies zu tun. UnmanagedType.LPStr wird als ANSI-Zeichenfolge mit einem Byte angegeben, LPTStr will be Unicode or ANSI depending on the system, and LPWStr is not what the library expects.

How would I tell PInvoke to marshal the string as a multibyte string? I see there's a WideCharToMultiByte() API-Funktion, kann ich die Signatur ändern, um ein IntPtr zu einer Zeichenfolge zu erwarten, die ich in nicht verwaltetem Speicher erstelle? Es sieht so aus, als hätte es immer noch viele der Probleme, die die aktuelle Implementierung hat (es muss immer noch Zeichen löschen oder ersetzen), also bin ich mir nicht sicher, ob dies eine Verbesserung ist. Gibt es eine andere Marshalling-Methode, die ich vermisse?

Antwort

6

ANSI ist Multibyte, und ANSI-Strings werden entsprechend der aktuell im System aktivierten Codepage codiert. WideCharToMultiByte funktioniert genauso wie P/Invoke.

Vielleicht, was Sie wollen, ist die Konvertierung in UTF-8. Obwohl WideCharToMultiByte dies unterstützt, glaube ich nicht, dass P/Invoke dies tut, da es nicht möglich ist, UTF-8 als systemweite ANSI-Codepage zu übernehmen. An diesem Punkt würden Sie stattdessen die Zeichenfolge als IntPtr übergeben, obwohl Sie die verwaltete Encoding-Klasse anstelle von WideCharToMultiByte auch für die Konvertierung verwenden können.

+0

Ich sehe, Sie haben Recht; Ich habe mit Zeichen außerhalb meiner aktuellen Codepage getestet und konnte mir keine Multibyte-Zeichen vorstellen, die tatsächlich auf meiner Codepage funktionieren würden. Ich fummle herum und versuche eine Code-Seite/Zeichenkombination zu finden, die ich der Funktion übergeben kann, um etwas Selbstvertrauen zu gewinnen, aber ich denke, du hast Recht. – OwenP

+0

Ich habe herausgefunden, wie ich es testen kann: Ich habe ein Bild für japanisch lokalisierte XP verwendet und einige Ressourcen mit Namen eingerichtet, die aus vielen japanischen Zeichen bestehen. Dies funktionierte hervorragend auf der japanischen Maschine, scheiterte aber kläglich an der englischen Maschine. Ich würde * Dinge * gerne so tun, als ob ich Unicode benutzen würde, aber von deiner Erklärung und meinen Experimenten sehe ich, dass das unmöglich ist, und ich bin schon so nah dran wie ich werde. Ich muss nur darauf warten, dass die Betreuer der Bibliothek die Unicode-Unterstützung implementieren. – OwenP

1

Hier ist der beste Weg, den ich gefunden habe, um dies zu erreichen. Anstatt als String zu marshalieren, marshale als Byte []. Legen Sie die Verantwortung für den Aufrufer der pinvoke-Funktions-API fest, um sie auf die am besten geeignete Weise in ein Byte-Array zu konvertieren. Höchstwahrscheinlich mit einer der Text.Encoding-Klassen.

0

Wenn Sie WideCharToMultiByte manuell aufrufen müssen, würde ich p/invoke loswerden und manuell mit WideCharToMultiByte in einer C++/CLI-Wrapper-Funktion marshalieren. Managed C++ ist in diesen Interop-Szenarien viel besser als C#.

Wenn dies jedoch der einzige p/Aufruf ist, den Sie haben, ist es wahrscheinlich nicht wert.

+0

Warum eine andere Sprache vorschlagen, wenn Lösungen bei C# bleiben? – NineBerry

Verwandte Themen