SzenarioC# Rangierung Bool
Dies sollte eine einfache Aufgabe sein, aber aus irgendeinem Grund kann ich es nicht wie beabsichtigt gehen. Ich muss ein grundlegendes C++ struct
während eines reversed-P/Invoke-Aufrufs (nicht verwalteten Aufruf verwalteten Codes) marshalieren.
Das Problem tritt nur bei der Verwendung von bool
innerhalb der Struktur, so dass ich schneide nur die Seite ++ C auf:
struct Foo {
bool b;
};
Da .NET Marschälle booleans als 4-Byte-Felder standardmäßig Marschall ich die native boolean explizit als 1-Byte-Längenfeld:
public struct Foo {
[MarshalAs(UnmanagedType.I1)] public bool b;
}
wenn ich rufe eine exportierte verwaltet statische Methode mit der folgenden Signatur und Körper:
public static void Bar(Foo foo) {
Console.WriteLine("{0}", foo.b);
}
Ich bekomme die korrekte boolesche Alpha-Darstellung gedruckt. Wenn ich die Struktur mit mehr Feldern erweitern, ist die Ausrichtung korrekt und die Daten sind nach dem Marshalling nicht beschädigt.
Problem
Aus irgendeinem Grund, wenn ich diese struct
als Argument vermarshallten nicht passieren, sondern als Rückgabetyp von Wert:
public static Foo Bar() {
var foo = new Foo { b = true };
return foo;
}
stürzt die Anwendung mit der folgenden Fehlermeldung :
Wenn ich Ändern Sie den verwalteten Struktur eine byte
anstelle eines bool
public struct Foo {
[MarshalAs(UnmanagedType.I1)] public byte b;
}
public static Foo Bar() {
var foo = new Foo { b = 1 };
return foo;
}
der Rückgabewert zu halten, richtig, ohne einen Fehler zu einem nicht verwalteten Bool vermarshallten.
ich nicht unterstand zwei Dinge hier:
- Warum ein Paramter mit
bool
vermarshallten wie oben beschrieben Arbeit, sondern als Rückgabewert einen Fehler geben? - Warum funktioniert ein
byte
marshalled alsUnmanagedType.I1
für Rückgaben, aber einbool
auch mitUnmanagedType.I1
marshalled nicht?
Ich hoffe meine Beschreibung macht Sinn - wenn nicht, lass es mich wissen, damit ich den Wortlaut ändern kann.
EDIT: Meine aktuelle Problemumgehung ist eine verwaltete Struktur wie:
public struct Foo {
private byte b;
public bool B {
get { return b != 0; }
set { b = value ? (byte)1 : (byte)0; }
}
, die ehrlich gesagt, ich ziemlich lächerlich ...
EDIT2: Hier eine fast MCVE ist.Die verwaltete Assembly wurde mit korrekten Symbolexporten neu kompiliert (unter Verwendung der Attribute .export
und .vtentry
im IL-Code), aber sollte keinen Unterschied zu C++/CLI-Aufrufen darstellen. So wird dieser Code nicht funktioniert "wie sie ist", ohne dass die Exporte manuell zu tun:
C++ (native.dll):
#include <Windows.h>
struct Foo {
bool b;
};
typedef void (__stdcall *Pt2PassFoo)(Foo foo);
typedef Foo (__stdcall *Pt2GetFoo)(void);
int main(int argc, char** argv) {
HMODULE mod = LoadLibraryA("managed.dll");
Pt2PassFoo passFoo = (Pt2PassFoo)GetProcAddress(mod, "PassFoo");
Pt2GetFoo getFoo = (Pt2GetFoo)GetProcAddress(mod, "GetFoo");
// Try to pass foo (THIS WORKS)
Foo f1;
f1.b = true;
passFoo(f1);
// Try to get foo (THIS FAILS WITH ERROR ABOVE)
// Note that the managed method is indeed called; the error
// occurs upon return. If 'b' is not a 'bool' but an 'int'
// it also works, so there must be something wrong with it
// being 'bool'.
Foo f2 = getFoo();
return 0;
}
C# (managed.dll):
using System;
using System.Runtime.InteropServices;
public struct Foo {
[MarshalAs(UnmanagedType.I1)] public bool b;
// When changing the above line to this, everything works fine!
// public byte b;
}
/*
.vtfixup [1] int32 fromunmanaged at VT_01
.vtfixup [1] int32 fromunmanaged at VT_02
.data VT_01 = int32(0)
.data VT_02 = int32(0)
*/
public static class ExportedFunctions {
public static void PassFoo(Foo foo) {
/*
.vtentry 1:1
.export [1] as PassFoo
*/
// This prints the correct value, and the
// method returns without error.
Console.WriteLine(foo.b);
}
public static Foo GetFoo() {
/*
.vtentry 2:1
.export [2] as GetFoo
*/
// The application crashes with the shown error
// message upon return.
var foo = new Foo { b = true; }
return foo;
}
}
Können wir einen MCVE haben? So können wir auch die Funktionsaufrufe sehen. –
@DavidHeffernan Ich habe ein MCVE hinzugefügt, so gut ich konnte. Da ich umgekehrte P/Invoke-Mechanismen verwende und den IL-Code direkt manipuliere, läuft er nicht ohne manuelle Änderungen an der Assembly ab. – PuerNoctis
Die IL manipulieren? Hmm. Sicherlich wird das relevant sein !! –