Entwickler-Ecke

Windows API - Daten aus unmanagged code in managed code übernehmen


ASMFreak - Mo 30.01.12 14:00
Titel: Daten aus unmanagged code in managed code übernehmen
Hallo, liebe Community,

manchmal wünsche ich mir die guten, alten Pointer zurück!

Ich habe ein Problem, mit dem ich noch keine Erfahjrung habe, da ich bislang entweder im unmanaged code herumgewerkelt habe, ODER im managed. Jetzt habe ich aber ein Problem, das beides betrifft.

Ich benötige Informationen aus unmanaged code in Form einer Liste von GUIDs. Um die zu ermitteln, gibt es eine Schnittstellenfunktion, die wie folgt deklariert ist:

C#-Quelltext
1:
2:
3:
4:
HRESULT GetIds(
  [out]      GUID **ppId,
  [inout]  UINT *pCount
);

Die versuche ich, mit Delphi anzusprechen. Daher habe ich einen Delegaten deklariert nach

Delphi-Quelltext
1:
funktion GetIDs(out pKD: KommtGleich; out Count: Integer): HResult                    

Das Natürliche wäre nun, eine Variable

Delphi-Quelltext
1:
2:
3:
4:
type
  TGUIDArray = Array of TGUID;
var
  GUIDArray: TGUIDArray

zu deklarieren und der Funktion zu übergeben. Tut man das, erhält man die Exception "Es wurde ein SafeArray vom Rang 14852 an eine Methode übergeben, die ein Array vom Rang 1 erwartet hat." OK! Das ist dar Problem der Datenübertragung zwischen unmanaged und managed code, wie ich vermute. Also: marshallen. Aber: wie? Meine diversen Versuche haben nichts gebracht.

Also zweiter Versuch:

Delphi-Quelltext
1:
function GetIDs(out pKD: IntPtr; out Count: Integer): HResult;                    

Und der Vesuch, analog zur damaligen Zuweisung eines PGUIDArray an eine Variable das Problem zu lösen. Aber auch hier führten meine Versuche des Marshallens nicht zum Erfolg. Entweder, der zurückgegebene Pointer erzeugte eine Exception nach dem Motto: "Es wurde versucht, in geschütztem Code zu lesen oder zu schreiben" oder so ähnlich, oder der Versuch, dass array auszulesen, wurde mit der Meldung bestraft, dass der Index den erlaubten Bereich überschreitet - auch beim Index 0 und obwohl mir Count sehr glaubhaft mitteilt, dass es jede Menge Einträge in der Liste gibt.

Kann mir jemand helfen?

Gruß,
ASMFreak


mandras - Mo 30.01.12 14:52

auch wenn meine c-zeiten lange zurückliegen, wie wäre es mit:



Delphi-Quelltext
1:
2:
3:
4:
type
  TGUIDArray = Array [0..10000of TGUID;

funktion GetIDs(var pKD: TGUIDArray; var Count: Integer): HResult


der Unterschied array of und array [0 .. 10000] of:
bei ersterem "bastelt" der Compiler noch etliches herum um mit diesem dynamischen Array arbeiten zu können damit auch Funktionen wie setlength etc. funktionieren.

Durch die var-Definition der Variablen übergibt der Compiler beim Funktionsaufruf nur die Adresse der Variablen an die aufgerufene Funktion, was diese damit macht und evtl. dort Werte änert ist ihm dann egal.

Die Grenze 10000 habe ich willkürlich gewählt.


ASMFreak - Mo 30.01.12 15:09

Habe ich vergessen zu sagen: Natürlich habe ich es auch damit versucht: Mit dem gleichen Ergebnis. Es schient kein Problem des "Herumbastelns" beim dynamischen Array zu sein, sondern ein Problem, an strukturierte Daten aus einem unmanaged code-Bereich zu kommen.


mandras - Mo 30.01.12 15:19

Hast Du die Funktion als stdcall definiert? liegt sie evtl. sogar in einer DLL?


ASMFreak - Mo 30.01.12 15:58

Nein, sie ist Teil einer Schnittstellendefinition. Wenn ich da mit "stdcall" ran gehe, meckert der Compiler, dass das bei externen Deklarationen nicht erlaubt sei!

Das Ganze sieht wie folgt aus (ich kopiere jetzt nicht die ganze Unit, daher nur das Prinzip):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
unit Test;

interface

type
  [ComImport,
  GuidAttribute ...
  ISchnittstelle = interface
    :
    :
    [PreserveSig]
    function GetIDs(out ppkFID: TGUIDArray; var Count: Integer): HResult;
    :
    :
  end;


später wird dann eine Klasse definiert, in der dann steht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
type
  EineKlasse = class(TObject)
    :
  public
    :
    procedure GetTheIDs(Ifc: ISchnittstelle);
    :
  end;


Und im Implementationsteil folgt dann


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure EineKlasse.GetTheIDs(Ifc: ISchnittstelle);
  var
    IDs: TGUIDArray;
    Count: Integer;
begin
  OleCheck(Ifc.GetIDs(IDs, Count));
end;


Das funktioniert mit allen anderen Methoden der Schnittstelle hervorragend, nur diese zickt rum. Zugegeben: Alle anderen Methdoen haben auch "einfache" Daten wie Strings oder Uints. Diese hier hat strukturierte Daten (Array). Und, wie gesagt, es ist egal, ob TGUIDArray = Array of TGUID oder TGUIDList = Array[0..irgendwas] of TGUID.


mandras - Mo 30.01.12 17:04

Da muß ich leider passen, evtl. würde das Debug/CPU-Fenster weiterhelfen?


ASMFreak - Mo 30.01.12 17:11

Nicht wirklch! Danke trotzdem!