2012-06-21 16 views
5

Moi deklaracje C są następujące:Jak przekazać wskaźnik do wskaźnika tablicy struktur?

int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data); 

typedef struct { 
    byte Rel; 
    __int64 Time; 
    char Validated; 
    unsigned char Data[1]; 
} DATASTRUCT ; 

Moich C deklaracji # przedstawiają się następująco:

[DllImport("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data); 

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public struct DATASTRUCT 
{ 
    public sbyte Rel; 
    public long Time; 
    public byte Validated; 
    public double Data; 
} 

I potem wywołać funkcję zarządzanego w sposób następujący:

string dataToShow = "description"; 
long Time; 
uint maxData; // How many structs will be returned, i.e. how much data is available 
uint myHandle = 1; 

DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // doesn't matter what I specify as the array size? 

myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation); 

Po wykonaniu z powyższym Funkcja powróci z powodzeniem tylko z jedną strukturą, nawet jeśli są 3 do powrotu. Dlaczego tak jest?

dodatkowe informacje; Próbowałem przechodzącą wskaźnik do wskaźnika do tablicy kodowanym następujących sposobów:

- ref DATASTRUCT[] data; // Works but only returns one struct 
- [Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage 

Jak rozumiem może muszę zrobić ręcznej rozrządowych korzystając IntPtr, nie wiem jak zaimplementować to jednak więc każda rada byłaby doceniona.

+0

W jaki sposób funkcja C przypisuje struktury? Czy używa tablicy wejściowej jako bufora, czy generuje nowe struktury z malloc? –

+0

Aha, a także deklaracja C# wygląda źle. Niepodpisany znak nie ma takiego samego rozmiaru jak podwójny (1 do 8 bajtów). –

+0

@JasonLarke, Niestety dokumentacja biblioteki DLL nie określa, czy przydziela pamięć dla struktury czy nie. Zakładam, że tak nie jest. Nie dostarczyłem pełnego opóźnienia dla 'Dane' to w rzeczywistości zmienna char, która może przyjąć maksymalnie 8 bajtów, a więc przyczynę uporządkowania jej jako podwójnej, ta zmienna zdecydowanie zwraca poprawne wyniki za każdym razem Przetestowałem to. – user1470994

Odpowiedz

6

Ok, wydaje się, że biblioteka ojczystym robi alokacji, tak naprawdę wszystko, co trzeba zrobić, to zapewnić wskaźnik za pomocą którego można uzyskać dostęp do przydzielonych danych.

Zmiana definicji API do (uwaga, zmieniłem param MAXDATA do Uint, długo wynosi 64 bitów w .NET i 32 bitów w natywnej.

[DllImportAttribute("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData); 

Off górze mojej głowie Nie mogę . dość pamiętać, jeśli potrzebujesz out kluczowe dla końcowego parametru, ale myślę, że tak

Następnie zadzwonić Mydata:

uint nAllocs = 0, time = 0; 
IntPtr pAllocs = IntPtr.Zero; 
myData(1, "description", out time, out nAllocs, out pAllocs); 

teraz pAllocs powinien wskazywać pamięci niekontrolowana, aby zebrać te int O udało pamięć nie jest zbyt trudne:

[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)] 
public struct DATASTRUCT 
{ 
    public byte Rel; 
    public long Time; 
    public byte Validated; 
    public IntPtr Data; //pointer to unmanaged string. 
} 


int szStruct = Marshal.SizeOf(typeof(DATASTRUCT)); 
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs]; 
for(uint i = 0; i < nallocs; i++) 
    localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT)); 

A teraz trzeba mieć tablicę lokalnych strukturach.

Warto zauważyć może trzeba ustawić swój projekt skompilować jak x86, w celu ujednolicenia rozmiaru IntPtr do 4 bajtów (DWORD) zamiast domyślnie AnyCPU za 8.

+0

Dziękuję Jason, więc wierzę, że gdzieś się dostaniemy. Teraz otrzymuję poprawną ilość zwróconych struktur, a także dane wypełniające struktury. Używając 'public IntPtr Data;' dane zwrócone dla 'danych' wydają się być śmieciami, jeśli zmienię je z powrotem na' publiczne podwójne Dane; 'zwraca dane poprawnie. – user1470994

0

Wskaźnik do wskaźnika może być reprezentowana w swojej deklaracji dllimport jako dane ref IntPtr, więc deklaracja staną:

[DllImportAttribute("myData.dll", EntryPoint = "myData")] 
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data); 

(Tak na marginesie, myślę, że długo w C jest po prostu odpowiednikiem int w C#. długa w C# jest Int64, który będzie długo długo w C)

rozrządowych swoją DATASTRUCT [] do IntPtr można zrobić przy użyciu klasy GCHandle

DATASTRUCT [] dataInformation = new DATASTRUCT[3]; 
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned); 
IntPtr ptr = gch.AddrOfPinnedObject(); 
myData(myHandle, dataToShow, out Time, out maxData, ref ptr); 
//It's absolutely essential you do this next bit so the object can be garbage collected again, 
//but it should only be done once the unmanaged code is definitely done with the reference.  
gch.Free(); 

Używanie klasy Marshal i metody StructureToPtr lub Copy to również opcja, ale aby udowodnić, że koncepcja przynajmniej GCHandle powinna załatwić sprawę, nie jest idealna w przypadku scenariuszy, w których niezarządzany kod od dawna działa, ponieważ przypiął ten obiekt w miejscu i GC nie może go przesunąć, dopóki go nie uwolnisz.

+0

Podjęłam próbę powyższego, zwraca 3 struktury w tablicy dataInformation, jednak wszystkie są puste. Zmieniłem również mój pierwotny wpis, aby odpowiednio odwoływać się do Int64, ponieważ w rzeczywistości jest to __int64. – user1470994

+0

Czy zaktualizowałeś również definicję C# struct? Niepodpisany znak w oryginale nie przełożyłby się na podwójną wartość, jak w przypadku @JasonLarke zaobserwowaną w innych komentarzach. Jeśli otrzymasz rozmiar struktury, to odpowiada on oczekiwanemu rozmiarowi zgodnie z kodem C? – Nanhydrin

+0

Być może warto wypróbować Marshala i po prostu przekazać wskaźnik do wskaźnika do tablicy tablic bajtów zamiast do tablicy DATASTRUCTS. Przynajmniej powinieneś być w stanie zobaczyć, czy w ogóle tam są jakieś bajty. – Nanhydrin