Pregunta

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

Me he encontrado con un problema particular al cambiar del modo de depuración al de liberación, ya que obtengo una excepción de infracción de acceso cuando vuelve el código de 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 he declarado una segunda función "No administrada de devolución de llamada" es que intenté crear un "intermediario" devolución de llamada en el contenedor, por lo que la cadena cambió de Native C ++ > C # a una versión de Native C ++ > C ++ / CLI Wrapper > C # ... Revelación completa, el problema persiste, 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 hechas y luego vemos el temido bloqueo en el regreso:

  

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

Si entro en el depurador desde aquí, todo lo que veo es que la última entrada en la pila de llamadas es: > " 04d1004c () " que se evalúa con 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 depurar y liberar algunas cosas son bastante diferentes en el mundo de Microsoft. Por supuesto, estoy preocupado por el relleno de bytes y la inicialización de variables, por lo que si hay algo que no estoy proporcionando aquí, solo avíseme 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 ++ (para las que no tengo el código) pueden estar tratando de eliminar o eliminar el objeto pData, lo que bloquea la aplicación.

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

¡Un problema real de rascarse la cabeza que agradecería cualquier ayuda!

¿Fue útil?

Solución

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

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

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

Otros consejos

Esto no responde directamente a su pregunta , pero puede guiarlo en la dirección correcta en cuanto al modo de depuración correcto versus el modo de liberación no correcto:

  

Dado que el depurador agrega una gran cantidad de información de mantenimiento de registros a la pila, generalmente rellenando el tamaño y el diseño de mi programa en la memoria, tuve "suerte" en el modo de depuración al garabatear más de 912 bytes de memoria que no eran " Muy importante. Sin embargo, sin el depurador, estaba garabateando sobre cosas bastante importantes, eventualmente caminando fuera de mi propio espacio de memoria, haciendo que Interop borre la memoria que no era suya.

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

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 liberación, por lo que con una mala propiedad, el comportamiento que describe no es infrecuente.

2) ¿No entiendo lo que intenta hacer el siguiente código?

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

Usas GCHandle.Alloc para bloquear una instancia de DataLoggerWrap en la memoria, pero nunca la pasas a no administrada, entonces ¿por qué la bloqueas? ¿Nunca lo liberas?

La segunda línea luego recupera una referencia: ¿por qué la ruta circular? ¿Por qué la referencia, nunca la usas?

3) Establece 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?

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top