2011-02-04 9 views
5

Budujemy aplikację na C#, .net 4.0, na Win7 x64, targetowanie x32.Wywołanie, wskaźniki i kopia tablicy

Używamy biblioteki innej firmy w naszej aplikacji. Rozumiemy, że ta biblioteka jest napisana przy użyciu C++. Jednakże, aby umożliwić programistom C# wykorzystanie tej biblioteki, owinęli ją za pomocą P/Invoke, dlatego w ten sposób nazywamy funkcje API.

Jednym z API jest następujący:

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data); 

Funkcja ta odczytuje numBytes danych z urządzenia zewnętrznego i umieszcza go w danych []. Jak widać, spodziewa się, że tablica C# Byte będzie trzecim argumentem. Teraz naszym problemem jest to, że chcielibyśmy odczytać dane do dowolnej lokalizacji w predefiniowanej tablicy. Na przykład:

Byte[] myData = new Byte[1024*1024*16]; 
ReadFromDevice(0x100, 20000, &myData[350]) // Obviously not possible in C# 

Gdybyśmy używali C/C++, byłoby to banalnie proste. Biorąc pod uwagę, że bazowe API jest napisane w C++, uważam, że powinniśmy móc to zrobić również w języku C#, jednak nie mogę wymyślić, jak to zrobić w języku C#. Może w jakiś sposób możemy wywołać bibliotekę bazową nie za pośrednictwem dostarczonego interfejsu P/Invoke i napisać niestandardowy interfejs?

Wszelkie pomysły będą mile widziane.

Pozdrawiam,

Odpowiedz

3

Podczas gdy inne odpowiedzi tutaj są bliskie, żadna nie jest kompletna.

Najpierw wystarczy zadeklarować własną deklarację p/invoke. To jest słodkie o p/invoke; Nigdy nie ma na to jednego sposobu.

[DllImport("whatever.dll")] 
unsafe extern static void ReadFromDevice(int deviceAddress, int numBytes, byte* data); 

Teraz można nazwać z

unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes) 
{ 
    fixed (byte* p = data) 
    { 
     ReadFromDevice(deviceAddress, numBytes, p + offset); 
    } 
} 
2

Jesteś jeszcze w stanie wykonać wskaźnik arytmetycznych w C#.

Jeśli ponownie zadeklarować Ci dll importu (wobec biblioteki C++), aby użyć IntPtr przekazać tablicę bajtów, to może być zwiększana o 350 przy użyciu metody Add (To faktycznie zwraca nowy IntPtr)

Istnieje przykład czegoś podobnego tutaj: http://msdn.microsoft.com/en-us/library/system.intptr.add.aspx#Y811

3

Można używać wskaźników, ale wymaga to, aby w trybie niebezpiecznym sprawdzać.

Byte[] myData = new Byte[1024*1024*16]; 
fixed(Byte * pB = &myData[350]) 
{ 
    ReadFromDevice(0x100, 20000,pB ) 
} 

Najpierw jednak należy zmienić podpis wywożonej metody na.

ReadFromDevice(int deviceAddress, int numBytes, Byte * data); 
+0

Nie ma konwersja z '*' bajt do bajta '[]', ten kod nie będzie nawet skompilować. Gdyby miał kontrolę nad źródłem C#, to byłaby jedna rzecz, ale nie brzmi tak, jak z oryginalnego pytania. –

+0

To powiedziawszy, po ponownym przeczytaniu pytania * tak * brzmi jak OP ma również dostęp do biblioteki C++, więc powinieneś po prostu zasugerować dodanie własnego DllImport i zmienić podpis, aby zaakceptował 'bajt *' zamiast 'byte []', to by działało. Usuwanie downvote. –

+0

"" Budujemy aplikację na C#, .net 4.0, na Win7 x64, targetowanie x32. Używamy biblioteki innej firmy w naszej aplikacji. Rozumiemy, że ta biblioteka jest napisana przy użyciu C++. programiści używają tej biblioteki, owinęli ją za pomocą P/Invoke, więc w ten sposób nazywamy funkcje API. " Więc mają kontrolę nad C# i piszą wrapper dla biblioteki C++! – N4rk0

0

Tak łatwe, jak to:

ReadFromDevice(int deviceAddress, int numBytes, ref byte data);

...


Byte[] myData = new Byte[1024*1024*16]; 
ReadFromDevice(0x100, 20000, ref myData[350]) // Obviously possible in C# 
+0

Ale zmieniłeś deklarację ReadFromDevice(). Nie mam tego kodu; pochodzi z biblioteki innej firmy. – SomethingBetter

+0

jeśli jest .net, zawsze masz kod xD. To wygląda jak dllimport po prostu włącz reflektor i skopiuj 2 linie do twojej aplikacji. – Zotta

+0

Zewnętrzna biblioteka dostarczyła zarówno plik kompilujący C++ (oczywiście nie można go zmienić), jak i klasę opakowania C#. Proszę zobaczyć moją odpowiedź na oczywiste rozwiązanie. –

0

Rozwiązaniem tego problemu wydaje się jakby oczywiste.

Prawdą jest, że można po prostu stworzyć własną klasę opakowania i DllImport i napisać własną klasę opakowania, dzięki czemu rozwiązanie jest łatwiejsze.

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data); 

W kontekście danych pocztowych jest parametrem wyjściowym. Rozwiązaniem byłoby wysłanie pustej tablicy Byte o odpowiednim rozmiarze, a następnie umieszczenie wypełnionej tablicy w istniejącej macierzy.

Wszystko, co musisz zrobić, to określić rozmiar zwróconej tablicy, którą znałeś wcześniej, a następnie wypełnić i wymienić.

Po prostu wyprostuję się i powiem, że pomysł wykorzystania istniejącej macierzy prawdopodobnie zadziała, jeśli wysłany zostanie adres konkretnego elementu w tablicy (co jest możliwe, ale jest ograniczony do biblioteki C++ faktycznie oczekuje, który prawdopodobnie jest tylko adresem samej tablicy Bajtów.

+0

Chodziło o uniknięcie tych operacji kopiowania, ponieważ są one kosztowne (pod względem czasu.Ta aplikacja przetwarza ponad 30 MB/s danych). Jeśli zrobimy to w sposób opisany przez ciebie, dla każdego danych, które odczytamy z zewnętrznego urządzenia, zewnętrzny API skopiuje dane do małej tablicy (trzeci argument w wywołaniu ReadFromDevice), a wtedy będziemy musieli zrobić kolejna operacja kopiowania, aby uzyskać dane w małej tablicy zapisanej w naszej dużej tablicy. Tak więc na każde 30 MB danych odczytanych z urządzenia zewnętrznego będzie 60 MB przesyłu danych. Chcemy raz zapisać dane, ze względu na wydajność. – SomethingBetter