Laden...

Marshalling im Struct?

Erstellt von Te-Ha vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.794 Views
T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren
Marshalling im Struct?

Hallo,

ich habe folgendes Problem:

Ich möchte in C# einen Messverstärker ansteuern, dessen Code nur in C++ vorliegt. Über einen DLLImport habe ich auch schon die meisten Funktionen und Structs importieren können und das funktioniert auch alles gut.

Ein Import funktioniert allerdings nicht:


[DllImport("gUSBamp.DLL")]
private static extern bool GT_SetDAC(IntPtr hDevice, DAC AnalogOut);

"DAC" ist hierbei ein Struct, der im Original so aussieht:


typedef struct DAC
{
	BYTE WaveShape;
	WORD Amplitude;
	WORD Frequency;
	WORD Offset;
}

Mein Struct, den ich mir für den Import definiert habe, ist folgender:


public struct DAC
{
    public int Amplitude, Frequency, Offset;
    public byte WaveShape;
}

Während der Laufzeit bekomme ich nun die Fehlermeldung:
"Ein Aufruf an die PInvoke-Funktion "BCITestApplication!BCITestApplication.Amplifier::GT_SetDAC" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein."

Ich vermute, dass dieser Fehler daher rührt, dass die beiden Structs nicht exakt gleich aussehen.

Wie kann ich dieses Problem lösen? Kann ich das über Marshalling lösen? Und wenn ja, wie?

Danke für die Antworten!

Viele Grüße
Thorsten

N
750 Beiträge seit 2004
vor 17 Jahren

hallo Te-Ha,

Ich vermute, dass dieser Fehler daher rührt, dass die beiden Structs nicht exakt gleich aussehen.

warum sehen die denn unterschiedlich aus ? hat das irgendwelche gründe??
wenn nein, dann mach sie doch "gleich" und guck ob der fehler immer noch auftritt.

nils

?( wer suchet, der findet auch! :]

B
1.529 Beiträge seit 2006
vor 17 Jahren

Ich vermute eher, dass die Funktion den Struct nicht wirklich auf den Stapel packt (unüblich), sondern nur die Referenz. Also musst du ihn auch per Referenz übergeben.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct DAC
{
    byte WaveShape;
    UInt16 Amplitude;
    UInt16 Frequency;
    UInt16 Offset;
}

[DllImport("gUSBamp.DLL")]
private static extern bool GT_SetDAC(IntPtr hDevice, ref DAC AnalogOut);

Falls es so auch nicht geht, kannst du es ja doch wieder ohne ref probieren.

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Original von nils
hallo Te-Ha,

Ich vermute, dass dieser Fehler daher rührt, dass die beiden Structs nicht exakt gleich aussehen.
warum sehen die denn unterschiedlich aus ? hat das irgendwelche gründe??
wenn nein, dann mach sie doch "gleich" und guck ob der fehler immer noch auftritt.

Das würde ich ja gerne, aber der "Unterschied" ist im Variablentyp "word" (vermute ich). Ich habe das im übrigen auch schon mit "Int16" versucht, aber es hat nicht funktioniert.

Kann natürlich sein, dass ich da gänzlich falsch liege, aber das wäre für mich die Erklärung. Und daher suche ich nach einer Möglichkeit, "int" oder auch "Int16" in den Typ "word" zu casten.

Oder befinde ich mich auf dem Holzweg?

N
750 Beiträge seit 2004
vor 17 Jahren

aber der "Unterschied" ist im Variablentyp "word" (vermute ich).

für mich ist der Unterschied eher die Reihenfolge deiner Elemente.

//edit: Borg hat schon eine (mögliche) Lösung gepostet.

nils

?( wer suchet, der findet auch! :]

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Hi,

also das mit dem "ref" hat zu genau demselben Fehler geführt.

Der Fehler konnte durch ein Ersetzen von "int" nach "UInt16" (sowas simples ist ja fast schon peinlich!) behoben werden. Die Reihenfolge im Struct hatte dabei allerdings keine Auswirkung.

Interessanterweise hatte die Reihenfolge im Struct jedoch eine Auswirkung auf den Rückgabewert der Funktion. So lieferte sie bei der einen Reihenfolge ein "false" und bei der anderen ein "true" zurück.

Das finde ich wiederum lustig. Da muss ich mal weitersehen, woran das denn jetzt liegen könnte.

Danke aber für die schnelle Hilfe!

N
750 Beiträge seit 2004
vor 17 Jahren

Interessanterweise hatte die Reihenfolge im Struct jedoch eine Auswirkung auf den Rückgabewert der Funktion. So lieferte sie bei der einen Reihenfolge ein "false" und bei der anderen ein "true" zurück.

und bei welcher reihenfolge gibt die funktion das für dich "richtige" ergebniss zurück?
bei eingehaltener oder bei anderer Reihenfolge?

nils

?( wer suchet, der findet auch! :]

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Original von nils
und bei welcher reihenfolge gibt die funktion das für dich "richtige" ergebniss zurück?
bei eingehaltener oder bei anderer Reihenfolge?

Bei eingehaltener Reihenfolge wird das "richtige" Ergebnis zurückgeliefert. Ich hätte nicht gedacht, dass die Reihenfolge in einem Struct so extrem wichtig ist... Man lernt halt immer dazu.

B
1.529 Beiträge seit 2006
vor 17 Jahren

Ist doch logisch. Die Funktion erwartet immer die angegebene Reihenfolge. Änderst du diese, liest die Funktion sinnlose Daten. So könnte beispielsweise eine Frequenz von 60kHz verlangt werden. Kein Wunder, dass das false zurückkommt.

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Stimmt. Damit wird ja die Reihenfolge im Speicher angegeben.

Oh man... Man kann auch mit einem Brett vorm Kopf herumlaufen. Ich brauche das Wochenende!

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Das bringt mich doch gleich zu der Frage, wie das mit Arrays im Struct aussieht?

Beispiel:

Struct in der Unmanaged DLL:


typedef struct SCALE
{
	float factor[16];
	float offset[16];
} *PSCALE;

Struct bei mir:


public struct SCALE
{
    public SCALE(int Dummy)
    {
        factor = new float[16];
        offset = new float[16];
    }
    public float[] factor;
    public float[] offset;
}

Ist in diesem Fall die Speicherbelegung in beiden Fällen gleich?

Mein nächstes Problem ist nämlich:

Die Methode in der Unmanaged DLL benötigt als Übergabeparameter einen Pointer von SCALE:


GT_Calibrate(HANDLE hDevice,PSCALE Scaling);

Das löse ich über einen Referenzparameter:


[DllImport("gUSBamp.DLL")]
public static extern bool GT_Calibrate(IntPtr hDevice, ref SCALE Scaling);

Das hat mit einem Struct, das keine Arrays beinhaltet auch wunderbar funktioniert.

In diesem hier beschriebenen Fall bekomme ich aber die Fehlermeldung, dass in den geschützten Speicher geschrieben wird, was natürlich zu einem Abbruch führt.

Kann das daran liegen, dass ausserhalb der Array-Grenzen geschrieben werden soll, was ich natürlich nicht möchte?
Oder wird vielleicht durch meinen definierten Konstruktor ein falscher Startpunkt zum Schreiben ausgewählt?

M
1.439 Beiträge seit 2005
vor 17 Jahren

Probier mal


[StructLayout(LayoutKind.Sequential)]
public struct SCALE {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
    public float[] factor;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
    public float[] offset;
}

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

Danke marsgk!

Dein Tipp war mir wirklich sehr hilfreich. Dadurch wurde der Fehler eliminiert. Super!

Was ist der Grund, warum meine Variante nicht funktioniert hat?

M
1.439 Beiträge seit 2005
vor 17 Jahren

Weil bei deiner Variante factor und offset nur Zeiger auf ein Array sind. Bei meiner hingegen wird der Speicherplatz für das Array direkt im strukt reserviert.

T
Te-Ha Themenstarter:in
8 Beiträge seit 2007
vor 17 Jahren

OK, Danke!