2013-04-07 18 views
5

Próbuję utworzyć Win32 DLL naraża niektórych funkcji, które są nazywane w języku C# następującoPodania bajt array między C++ i C# ByRef podnosi AccessViolationException

__declspec(dllexport) int GetData(unsigned char* *data, int* size) 
{ 
    try 
    { 
     int tlen = 3; 
     unsigned char* tchr = new unsigned char[5]; 
     tchr[0] = 'a'; 
     tchr[1] = 'b'; 
     tchr[2] = 'c'; 

     *size = tlen; 
     *data = tchr; 

     return 1; 
    } 
    catch (char *p) 
    { 
     return 0; 
    } 
} 

A na C# strony

[DllImport("MyDll.dll")] 
static extern int GetData(ref byte[] data, ref int size); 

static void Main() 
{ 
    try 
    { 
     int hr = 0; 
     byte[] gData = null; 
     int gSize = 0; 
     hr = GetData(ref gData, ref gSize); 
     Console.WriteLine(gSize); 
     for (int i = 0; i < gSize; i++) 
      Console.WriteLine((char)gData[i]); 
    } 
    catch (Exception p) 
    { 
     Console.WriteLine(p.ToString()); 
    } 
} 

Kiedy Uruchamiam kod C#, AccessViolationException dzieje się na funkcji GetData, która jest znakiem wyjątku w kodzie C++ jednak, po C++ fragment kodu działa poprawnie bez żadnego błędu.

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    unsigned char* data = NULL; 
    int size = NULL; 
    GetData(&data, &size); 
    printf("%d", size); 
    for (int i = 0; i < size; i++) 
     printf("%c,", data[i]); 
    return 0; 
} 

Jeśli porównać C# main funkcji i C++ _tmain, są one niemal analogiczny więc gdzie mogę popełnić błąd?

+0

Mam nadzieję, że to ci pomoże. http://stackoverflow.com/questions/8199874/c-sharp-and-voidpointpointers –

Odpowiedz

9

Zwracasz tablicę przydzieloną przez wywołanie do C++ new i mając nadzieję, że Marshaler zmieni ją na C# byte []. Tak się nie stanie.

Będziesz musiał podać wskaźnik według referencji, a następnie przygotować go ręcznie. Twój p/wywołać powinna wyglądać następująco:

[DllImport("MyDll.dll")] 
static extern int GetData(out IntPtr data, out int size); 

Gdy dane funkcja zwraca będzie wskazywać na tablicy można odczytać zawartości przy użyciu klasy Marshal. Chyba miałbyś copy it to a new byte array.

var arr = new byte[size]; 
Marshal.Copy(data, arr, 0, size); 

Niektóre inne punkty:

  1. Konwencje telefoniczne nie pasują. Natywną stroną jest cdecl, a zarządzanym stdcall.
  2. Musisz wyeksportować dealokatora, aby usunąć pamięć zwróconą przez natywną funkcję. Rozważ ponowne zaprojektowanie, w którym dzwoniący przydziela bufor.
+0

Dzięki, zaoszczędziłeś mi dużo – anonim

+0

... lub przydzieliłeś z CoTaskMemAlloc/g_malloc w NAtive i używasz Marshal.FreeCoTaskMem na zwróconym IntPtr w zarządzanym –