mikrocontroller.net

Forum: PC-Programmierung C#/DLLImport: String in C-struct / C#-struct


Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

vielleicht kann mir jemand bei einem kleinen Problem weiterhelfen:

Ich möchte in C# eine native DLL verwenden. Hierfür schreibe ich mir 
auch gerade einen entsprechenden Wrapper. In der DLL gibt es ein paar 
Methoden, welche ein struct erwarten.

Ein struct in C/C++ ist ja eigentlich nur die Zusammenfassung mehrerer 
Variablen unterschiedlichen Typs in einer strukturierten Form - struct 
eben :)

In C# sieht das ja ganz anders aus, ein struct kann Methoden, etc. 
haben.

Um ein C-kompatibles struct in C# zu definieren, bei dem die enthaltenen 
Variablen auch der gleichen Reihenfolge wie bei der Definition 
entsprechen, definiert man das struct folgendermaßen:
[StructLayout(LayoutKind.Sequential)]
struct bla {
  int a;
  byte b;
  long c;
}
Somit werden die Variablen auch in dieser Reihenfolge im Speicher 
angelegt.
Soweit so gut...

In dem von der DLL erwarteten struct befinden sich auch Strings. Im 
normalen C ist ein String eine Folge einzelner Bytes, deren Werte 
ASCII/ANSI-Buchstaben entsprechen und null-terminiert sind.
In C# handelt es sich bei einem String um Unicode-Werte, d.h. jedes 
Zeichen ist 2-Byte groß.
Die Entsprechung eines C-Strings in C# ist eigentlich ein Byte-Array 
(wie es ja implizit in C der Fall ist).

Logischerweise möchte ich im Wrapper der entsprechenden Methode 
natürlich C#-Strings übergeben. Wie löse ich das denn nun?

1. Ich definiere das C-struct und parallel dazu das C#-Äquivalent. Die 
Wrapper-Methode erhält das C#-struct als Parameter, erzeugt ein C-struct 
und wandelt die C#-Strings in Byte-Arrays, die restlichen struct-Member 
werden kopiert.
2. Ich verwende Pointer in einem unsafe-Kontext
3. Ich mache es per Marshalling (habe aber bis dato keine Erfahrung 
damit)

Lösung 1) ist etwas aufwendig, weil das struct dummerweise an die 100 
Member hat.
Lösung 2) sagt mir nicht zu, da ich auf unsafe-Blöcke verzichten möchte 
wenn es geht
Lösung 3) ist Neuland :( Prinzipiell nichts gegen Neuland, wäre 
zumindest momentan aber schön, wenn's auch anders geht :)
Lösung 4-n) Kenne ich noch nicht, bin für Vorschläge offen :)

Ralf

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.ANSI)]
struct TestStruct {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 123)]
    public string TestString;
}

sowas in der Art ;-)

http://msdn.microsoft.com/en-us/library/system.run...;

http://msdn.microsoft.com/library/s9ts558h(VS.80).aspx

Wenn auf Geschwindigkeit ankommt, wird auch sowas interessant
http://www.codeproject.com/KB/cs/ReadingStructuresEmit.aspx

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Arc Net,

danke für die Links, das sieht gar nicht mal schlecht aus :)

Bin jetzt aber noch nicht ganz sicher, ob ich es richtig verstanden 
habe. Ich würde denke ich eine Lösung basierend auf deinem Link 
bevorzugen.

Dazu hätte ich ein paar Fragen:
- dasin deinem Beispiel-struct verwendete 
[StructLayout(LayoutKind.Sequential)] habe ich bereits verwendet. Das 
zusätzliche (CharSet = CharSet.ANSI) sorgt dafür, dass nicht UNICODE 
(2-Byte) verwendet wird, sondern eine 8-Bit Zeichendarstellung?

- Wenn ich das:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 123)]
public string TestString;
durch
[MarshalAs(UnmanagedType.LPStr)] public String TestString;
ersetze, müsste ich doch "näher" an einem C-String sein, oder? 
Null-terminiert und ohne feste Längenangabe. (Die Sache mit der 
Längenangabe muss ich noch prüfen, es gibt vorgeschriebene Maximallängen 
der Strings für die DLL, vielleicht ist deine vorgeschlagene Variante 
dann doch vorzuziehen)

- Zum Verständnis: Wenn ich das struct mit dem o.g. Marshalling 
verwende, dann brauche ich es ja nur einmal anlegen und nicht eine C- 
und eine C#-Variante, korrekt?

- Funktioniert das dann in beide Richtungen? Die DLL erwartet einerseits 
einen Pointer auf ein solches struct, wenn gelesen wird (habe ich 
mittels "ref" implementiert) und will andererseits mit einem solchen 
struct gefüttert werden.

Ralf

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:
> Hallo Arc Net,
>
> danke für die Links, das sieht gar nicht mal schlecht aus :)
>
> Bin jetzt aber noch nicht ganz sicher, ob ich es richtig verstanden
> habe. Ich würde denke ich eine Lösung basierend auf deinem Link
> bevorzugen.
>
> Dazu hätte ich ein paar Fragen:
> - dasin deinem Beispiel-struct verwendete
> [StructLayout(LayoutKind.Sequential)] habe ich bereits verwendet. Das
> zusätzliche (CharSet = CharSet.ANSI) sorgt dafür, dass nicht UNICODE
> (2-Byte) verwendet wird, sondern eine 8-Bit Zeichendarstellung?

Richtig

> - Wenn ich das:
>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 123)]
> public string TestString;
> 
> durch
>
[MarshalAs(UnmanagedType.LPStr)] public String TestString;
> ersetze, müsste ich doch "näher" an einem C-String sein, oder?

Jein, beides "sind" C-Strings, LPStr ist das was dransteht, ein Zeiger 
auf einen C-String (char *), ByValTStr wäre in C char TestString[123];

> Null-terminiert und ohne feste Längenangabe. (Die Sache mit der
> Längenangabe muss ich noch prüfen, es gibt vorgeschriebene Maximallängen
> der Strings für die DLL, vielleicht ist deine vorgeschlagene Variante
> dann doch vorzuziehen)
>
> - Zum Verständnis: Wenn ich das struct mit dem o.g. Marshalling
> verwende, dann brauche ich es ja nur einmal anlegen und nicht eine C-
> und eine C#-Variante, korrekt?

>> Ich möchte in C# eine native DLL verwenden
?

>
> - Funktioniert das dann in beide Richtungen? Die DLL erwartet einerseits
> einen Pointer auf ein solches struct, wenn gelesen wird (habe ich
> mittels "ref" implementiert) und will andererseits mit einem solchen
> struct gefüttert werden.

Sollte...

> Ralf

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ArcNet,

>> ...sondern eine 8-Bit Zeichendarstellung?
> Richtig
Okay, dann bleibt schon mal die Größe identisch :)

> Jein, beides "sind" C-Strings, LPStr ist das was dransteht, ein Zeiger
> auf einen C-String (char *), ByValTStr wäre in C char TestString[123];
Ah, gut. Dann muss ich schauen, wie im Falle von LPStr die Syntax für 
die Übergabe lauten muss, da Pointer ja nur im Unsafe-Kontext erlaubt 
sind.
Müsste aber mit dem ref-Schlüsselwort gedeckelt sein, denke ich.

>>> - Zum Verständnis: Wenn ich das struct mit dem o.g. Marshalling
>>> verwende, dann brauche ich es ja nur einmal anlegen und nicht eine C-
>>> und eine C#-Variante, korrekt?
>> Ich möchte in C# eine native DLL verwenden
>?
Das Fragezeichen bezog sich auf "nur einmal anlegen"?

Damit meinte ich, dass die Strings in C ja byte-Arrays sind, während es 
in C# ja den Datentyp string gibt.
Damit ich in C# auch "direkt" einen String ohne Typkonvertierung 
verwenden kann, brauch ich dann zwei Structs, jeweils nach C/C# Syntax.
Wenn aber durch Marshalling in C# ein C-kompatibles Struct erzeugt wird, 
welches mit C#-Strings gefüttert werden kann, so brauche ich nur eine 
Struct-Definition.
So war das gemeint.

Ralf

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.