Auf meine alten Tage befasse ich mich doch tatsächlich auch noch mit solchen Dingen
wie Microsoft .NET. Wenn man mir das während meines Studiums und meiner hyperaktiven
Atari-Zeit prophezeit hätte...
Nun denn. Kommt mir neulich so ein häßlicher kleiner Käfer entgegen. Und das kam so:
Wenn man Werte vom Typ
bool
aus "managed code" in "unmanaged code" per Marshaling
überträgt, geht das in so richtig großen Stil schief.
Der "managed code" (in C++) stellt einen einfachen Aufruf zur Verfügung mit einem
Rückgabewert vom Typ
bool
. Vereinfachter Beispielcode:
public __gc __interface IBool {
bool Foo(void);
};
public __gc class Booltest : public IBool
{
public:
Booltest() {}
bool Foo(void) { return false; }
};
Diesen Code übersetzt man in ein Assembly, und daraus produziert man mittels
regasm
eine Typenbibliothek (tlb-Datei). Gleichzeitig registriert
regasm
das Assembly; und hinterher
werfen wir das Assembly in den Schlund des GAC. Auszug aus der erzeugten
Typenbibliothek:
interface IBool : IDispatch {
[id(0x60020000)]
HRESULT Foo([out, retval] unsigned char* pRetVal);
};
Man beachte, daß der Typ des Rückgabewerts vom
bool
nach
unsigned char
abgeändert wurde. Soweit keine Überraschung, denn
Nathan
erwähnt in seiner COM-Interop-Bibel, daß der Marshaling-Typ für Werte vom Typ
bool
eben
UnmanagedType::U1
ist - also
unsigned char
.
So richtig übel wird es aber, wenn ich nun
Foo()
aus einem COM-Client zu rufen versuche.
Der COM-Client erzeugt sich einen "smart pointer" vom Typ
IBoolPtr
und ruft dann
Foo()
:
bool ret = pBool->Foo();
Nach diesem Aufruf ist allerdings der Stack beschädigt. Läft man im Einzelschritt
durch den Code, merkt man, daß der COM-Client denkt, der Rückgabewert sei ein
Byte gross; daher legt er auch nur ein Byte auf dem Stack für die Ergebnisvariable
ret
an. Der Marshaling-Code allerdings schreibt munter
vier Bytes!
Das gleiche passiert, wenn man im "managed code" das Attribut
[MarshalAs(UnmanagedType::U1)]
ausdrücklich anwendet. Microsoft beschreibt
im
Knowledge-Base-Artikel 823071
einen möglicherweise verwandten Fehler beim Marshaling von
bool-Werten,
allerdings hilft der vorgeschlagene Hotfix in meinem Fall nicht. Ändere
ich den Marshaling-Typ auf
U2
, wird's noch lustiger: Dann überschreibt
der Marshaler zwar keinen Speicher mehr, räumt dafür aber den Stackpointer
nach dem Aufruf nicht mehr auf!
Hat jemand Ideen?
(Siehe auch
http://groups.google.de/groups?hl=de&lr=&threadm=10gd95ifu6uoe9a%40corp.supernews.com.)
PS: Einige Zeit später hat uns Microsoft bestätigt, daß das in der Tat
ein Fehler in .NET 1.1 war. .NET 2.0 macht's nun richtig.