utilizar usuario try todas tipos que por para manejar las existen excepciones errores ejemplos definidas como catch capturar c# c++ c++-cli callback access-violation

usuario - todas las excepciones en c#



Violación de acceso Excepción/bloqueo de la devolución de llamada de C++ a la función C# (4)

Así que tengo una base de código C ++ nativa de terceros con la que estoy trabajando (archivos .lib y .hpp) que utilicé para compilar un contenedor en C ++ / CLI para su uso final en C #.

Me he encontrado con un problema en particular al cambiar de Debug a modo Release, en el que aparece una excepción de infracción de acceso cuando se devuelve el código de una devolución de llamada.

El código de los archivos hpp originales para el formato de la función de devolución de llamada:

typedef int (*CallbackFunction) (void *inst, const void *data);

Código de C ++ / CLI Wrapper para el formato de la función de devolución de llamada: (Explicaré por qué he declarado dos en un momento)

public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData); public delegate int UnManagedCallbackFunction (void* inst, const void* data);

- Rápidamente, la razón por la que declaré una segunda "Función de Llamada no gestionada" es que traté de crear una devolución de llamada "intermediaria" en el contenedor, por lo que la cadena cambió de C ++ nativo> C # a una versión de C ++ nativo> C ++ / Contenedor CLI> C # ... Divulgación total, el problema aún vive, simplemente se ha enviado al Contenedor C ++ / CLI ahora en la misma línea (el retorno).

Y finalmente, el código de bloqueo de C #:

public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData) { Console.WriteLine("in hReceiveLogEvent..."); Console.WriteLine("pInstance: {0}", pInstance); Console.WriteLine("pData: {0}", pData); // provide object context for static member function helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target; if (hw == null || pData == null) { Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data/n"); return 0; } // typecast data to DataLogger object ptr IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target; //Do Logging Stuff Console.WriteLine("exiting hReceiveLogEvent..."); Console.WriteLine("pInstance: {0}", pInstance); Console.WriteLine("pData: {0}", pData); Console.WriteLine("Setting pData to zero..."); pData = IntPtr.Zero; pInstance = IntPtr.Zero; Console.WriteLine("pData: {0}", pData); Console.WriteLine("pInstance: {0}", pInstance); return 1; }

Todas las escrituras en la consola están listas y luego vemos el temido bloqueo en la devolución:

Excepción no controlada en 0x04d1004c en helloworld.exe: 0xC0000005: ubicación de lectura de violación de acceso 0x04d1004c.

Si paso al depurador desde aquí, todo lo que veo es que la última entrada en la pila de llamadas es:> "04d1004c ()" que evalúa a un valor decimal de: 80805964

Lo cual solo es interesante si miras la consola que muestra:

entering registerDataLogger pointer to callback handle: 790848 fp for callback: 2631370 pointer to inst: 790844 in hReceiveLogEvent... pInstance: 790844 pData: 80805964 exiting hReceiveLogEvent... pInstance: 790844 pData: 80805964 Setting pData to zero... pData: 0 pInstance: 0

Ahora, sé que entre la depuración y la versión algunas cosas son bastante diferentes en el mundo de Microsoft. Estoy, por supuesto, preocupado por el relleno de bytes y la inicialización de variables, así que si hay algo que no estoy proporcionando aquí, házmelo saber y lo agregaré a la publicación (ya larga). También creo que el código administrado NO puede liberar toda la propiedad y luego las cosas nativas de C ++ (que no tengo el código) pueden estar tratando de eliminar o matar el objeto pData, lo que bloquea la aplicación.

¡Divulgación más completa, todo funciona bien (aparentemente) en modo de depuración!

¡Un verdadero problema de cero que agradecería cualquier ayuda!


Creo que la pila se aplastó debido a las convenciones de llamadas no coincidentes: prueba a poner el atributo

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]

en la declaración del delegado de devolución de llamada.


Esto no responde directamente a su pregunta , pero puede llevarlo en la dirección correcta en cuanto al modo de depuración está correcto, en comparación con el modo de liberación, no está bien:

Como el depurador agrega una gran cantidad de información de registro a la pila, generalmente rellenando el tamaño y el diseño de mi programa en la memoria, estaba "teniendo suerte" en el modo de depuración garabateando más de 912 bytes de memoria que no eran muy importantes . Sin embargo, sin el depurador, estaba garabateando sobre cosas bastante importantes, eventualmente saliendo de mi propio espacio de memoria, haciendo que Interop borrara la memoria que no le pertenecía.

¿Cuál es la definición de DataLoggerWrap? Un campo char puede ser demasiado pequeño para los datos que está recibiendo.


Estoy con @jdehaan, excepto CallingConvetion.StdCall podría ser la respuesta, especialmente cuando la lib de terceros está escrita en BC ++, por ejemplo.


No estoy seguro de lo que estás tratando de lograr.

Algunos puntos:

1) El recolector de basura es más agresivo en el modo de lanzamiento, por lo que con la mala propiedad, el comportamiento que describes no es infrecuente.

2) ¿No entiendo lo que el código de abajo está tratando de hacer?

IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData))); DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

Utiliza GCHandle.Alloc para bloquear una instancia de DataLoggerWrap en la memoria, pero luego nunca la pasa a no administrada, entonces, ¿por qué la bloquea? ¿Nunca lo liberaste?

La segunda línea recupera una referencia: ¿por qué la ruta circular? ¿Por qué la referencia? ¿Nunca la usaste?

3) Establece los IntPtrs en nulo, ¿por qué? - Esto no tendrá ningún efecto fuera del alcance de la función.

4) Necesita saber cuál es el contrato de la devolución de llamada. ¿A quién pertenece pData la devolución de llamada o la función de llamada?