2011-04-28 16 views
5

Hey! Acabo de empezar a jugar con Pinvoke y he encontrado un problema. Obtengo la AccessViolationException. Antes que nada, ¿hay alguna forma de depurar o rastrear qué campo está causando este error? Lo único que se escribe es el resultado struct.Pinvoke struct que necesita la ayuda necesaria - System.AccessViolationException

El c llamada ++ parece:

MyFunc(int var1, _tuchar *var2, _tuchar *var3, _tuchar *var4, MyStruct *Result, 
     _tuchar *var5, _tuchar *var6); 

La estructura C++:

typedef struct MyStruct 
{ 
    _tuchar *id; 
    _tuchar *ErrorMessages; 
    int int1; 
    _tuchar language[3]; 
    _tuchar *result; 
    int type; 
    int number; 
    int *type2; 
    _tuchar **blocks; 
} 

El C# estructura:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct MyStruct 
{ 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string Id; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)] 
    public char[] ErrorMessages; 

    public int int1; 

    [MarshalAs(UnmanagedType.LPStr)] 
    public string language; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)] 
    public char[] result; 

    public int type; 

    public int number; 

    public int type2; 

    [MarshalAs(UnmanagedType.ByValArray)] 
    public string[] blocks; 

La declaración de C# método:

[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true, 
      CharSet = CharSet.Unicode)] 
internal static extern int MyFunc(int var1, string var2, string var3, 
     string var4, ref MyStruct Result, string var5, string var6); 

El C# Llamada:

var result = new MyStruct(); 
MyFunc(0, "var2", "var3", "var4", ref result, "var5", "var6"); 

espero no haber dejado nada fuera. ¡Gracias por cualquier ayuda!

+0

¿Cómo se ve la declaración 'MyFunc' en C#? – Justin

+0

ah ofc olvidé algo, gracias amigo ^^ – Dashu

+0

¿El tamaño de 'int' en C++ garantiza ser de 32 bits/convertible con System.Int32? System.Int32 tiene una garantía muy precisa, incluso en bit-ness. –

Respuesta

4

Ooooh, hombre! Has escogido un caso bastante complejo para tu primera experiencia de violín. Recomiendo hacer algo más simple primero, y luego pasar a lo real.

Primero, CharSet=CharSet.Ansi parece sospechoso. Todas sus cadenas y caracteres son _tuchar, y creo que el u significa "Unicode", ¿no es así? Si ese es el caso, necesita CharSet=CharSet.Unicode.

En segundo lugar, (y esta es la causa más probable) por qué es el campo ErrorMessages calculan como ByValArray? Sabes que ByVal significa "por valor", ¿no? Como en, no por referencia. Y sabes que ese pequeño asterisco en C++ significa "referencia", ¿verdad? Entonces, ¿por qué su campo de referencia ErrorMessages se clasifica como una matriz de valores por valor? En caso de que no lo sepa, generalmente se dice que una matriz se pasa "por valor" cuando se pasa todo su contenido, en lugar de simplemente pasar una referencia (puntero) a una ubicación de memoria donde se almacena todo ese contenido. En la definición de estructura de C++, especifica _tuchar*, que significa "una referencia (puntero) a una memoria que contiene uno o más de _tuchars", mientras que en C# especifica [MarshalAs(UnmanagedType.ByValArray, SizeConst=500)], lo que significa "500 _tuchars se supone que están aquí, no más y no Menos". Al ver cómo una referencia (puntero) usualmente toma 4 bytes (u 8 bytes en máquinas de 64 bits), y 500 caracteres Unicode toman 1000 bytes, aquí hay una discrepancia obvia.

En tercer lugar y en cuarto lugar , mismo punto va para result y blocks campos.

En quinto lugar, el campo language se invierta exactamente la situación: el código C++ dice "hay 3 _tuchars aquí", mientras que C# código dice "hay una referencia (puntero) a una cadena aquí" (en caso de que don sé, LPStr significa 'Long Puntero a string')

y finalmente, después de haber fijado todos esos problemas, recomiendo a ejecutar su programa e imprimir el resultado de la llamada a Marshal.SizeOf(typeof(MyStruct)). Eso le dará exactamente qué tan grande es su estructura, en opinión de .NET. Vaya al lado de C++ e imprima sizeof(MyStruct). Eso le dará lo que C++ piensa sobre el tamaño.

Si resultan diferentes, vea lo que está mal. Intente eliminar campos uno por uno, hasta que se conviertan en iguales. Esto le dará el campo (s) culpable. Trabaja con ellos

En general, sugiero que necesita una mejor comprensión de cómo funcionan las cosas primero. Este caso es demasiado complejo para un principiante.

¡Buena suerte!

+0

Bueno, señor, usted es el hombre:) Le agradezco mucho su aporte. Veré si puedo resolver este lío. Una cosa que realmente no puedo entender son estas cadenas en las que escribirá C++ ... Pensé que si enviaba una cadena con 500 caracteres, el C++ dll no escribiría fuera de los límites. ¿Existe un límite en cuanto a qué tan grande puede ser el resultado? – Dashu

+0

¿Podría ser que tener campos de cadenas escribibles en una estructura es imposible? ¿O sabe cómo deberían definirse (mensajes de error y resultado)? – Dashu

0

Esto es un poco de un tiro en la oscuridad, pero ¿ha intentado decorar los parámetros de cadena con MarshalAs(UnmanagedType.LPWStr):

[DllImport(MyPath, EntryPoint = "MyEntryPoint", SetLastError = true, 
    CharSet = CharSet.Unicode)] 
internal static extern int MyFunc(
    int var1, 
    [MarshalAs(UnmanagedType.LPWStr)] 
    string var2, 
    [MarshalAs(UnmanagedType.LPWStr)] 
    string var3, 
    [MarshalAs(UnmanagedType.LPWStr)] 
    string var4, 
    ref MyStruct Result, 
    [MarshalAs(UnmanagedType.LPWStr)] 
    string var5, 
    [MarshalAs(UnmanagedType.LPWStr)] 
    string var6); 

creo que el valor por defecto de cálculo de referencias elegido para cadenas es BStr y _tuchar debería ampliar a wchar_t así que supongo que LPWStr es el método correcto de clasificación (puntero a una cadena de caracteres anchos).


Actualización: Varias cosas en MyStruct no se ven del todo bien:

ErrorMessages se marca como ByValArray, por lo que la interoperabilidad .NET es probablemente esperando MyStruct que mirar un poco como esto:

typedef struct MyStruct 
{ 
    _tuchar *id; 
    _tuchar ErrorMessages[500]; 
    // Rest of MyStruct 

Probablemente va a causar problemas, lo mismo para result.

También creo que debería language a utilizar ByValArray con un tamaño de 3.

Finalmente blocks probablemente se debe pasar usando LPArray - ByValArray no me parece bien.

(Todo esto es sobre todo cierto conjeturas - espero que esto es lo que apunta en la dirección correcta, pero no tengo que mucha experiencia con P/Invoke interoperabilidad)


Otra actualización: En MyStruct declara que el juego de caracteres es Ansi, pero en MyFunc es Unicode ... ¿es el dll no administrado compilado con Unicode o Ansi?Si usa Unicode, entonces creo que debe usar LPWStr al ordenar las cadenas, y con Ansi debe ser LPStr.

+0

hmm intentaré decorar las cuerdas, pero estas cuerdas solo se leen, no escritas. creo que el problema está dentro del resultado, he leído que la cadena en la que se escribe debe usar stringbuilder, pero no se puede usar stringbuilder en una estructura sin obtener una excepción de clasificación. No sé sobre uni o ansi, ¿hay alguna forma de resolverlo? – Dashu

+0

decorar cadenas no lo resolvió – Dashu

+0

@Dashu Algunos de los atributos de 'MyStruct' tampoco parecen del todo bien - He actualizado mi pregunta, pero tenga en cuenta que probablemente no sé de lo que estoy hablando ! :-p – Justin

Cuestiones relacionadas