Pergunta

Então, eu tenho uma festa de 3 C ++ base de código nativo Eu estou trabalhando com (lib e os arquivos .hpp) que eu usei para construir um invólucro em C ++ / CLI para eventual uso em C #.

eu correr em um problema particular quando se muda de depuração para o modo Release, em que eu recebo uma violação de acesso de exceção quando um retorno de chamada de código retorna.

O código a partir dos arquivos HPP originais para o formato de função de retorno:

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

Código do ++ Wrapper C / CLI para o formato de função de retorno: (Eu vou explicar porque eu declarei dois em um momento)

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

- Rapidamente, a razão de eu declarou um segundo "UnManagedCallbackFunction" é que eu tentei criar um callback "intermediário" na embalagem, de modo a cadeia mudou de Native C ++> C # para uma versão do Native C ++> C ++ / CLI Wrapper> C # ... a divulgação completa, o problema ainda vive, é apenas sido empurrado para a C ++ / CLI wrapper agora na mesma linha (o retorno).

E, finalmente, o código bater 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 as gravações para o console são feitas e, em seguida, vemos a queda temido no retorno:

Excepção em 0x04d1004c em Helloworld.exe: 0xC0000005: Acesso violação localização leitura 0x04d1004c.

Se eu passo para o depurador a partir daqui, tudo que eu vejo é que a última entrada na pilha de chamadas é:> "04d1004c ()", que é avaliada como um valor decimal: 80805964

O que só é interessante se você olhar para o console que mostra:

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

Agora, eu sei que entre depuração e liberar algumas coisas são bem diferentes no mundo da Microsoft. Estou, naturalmente preocupado com estofo byte e inicialização de variáveis, por isso, se há algo que eu não estou fornecendo aqui, apenas deixe-me saber e eu vou adicionar a (já longa) post. Eu também acho que o código gerenciado não pode estar liberando toda a propriedade e, em seguida, o nativo C coisas ++ (que eu não tenho o código para) pode estar tentando excluir ou matar o objeto pData, falhando assim o aplicativo.

divulgação mais completa, tudo funciona bem (aparentemente) no modo de depuração!

Uma questão zero cabeça real que gostaria de receber qualquer ajuda!

Foi útil?

Solução

Eu acho que a pilha foi esmagado por causa de descasamento entre convenções de chamada: tentar colocar o atributo

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

na declaração callback delegado.

Outras dicas

Este não responder diretamente sua pergunta , mas pode levá-lo na direção certa, na medida do modo de liberação vs. bem modo de depuração não está bem:

Uma vez que o depurador acrescenta um lote de manutenção de registos informações para a pilha, geralmente acolchoar o tamanho e layout do meu programa na memória, I foi “ter sorte” no modo de depuração por rabiscar mais de 912 bytes de memória que weren' t muito importante. Sem o depurador, porém, eu estava rabiscando em cima de coisas bastante importantes, eventualmente, andando fora do meu próprio espaço de memória, causando Interop para a memória de exclusão que não possuía.

O que é a definição de DataLoggerWrap? Um campo de char pode ser demasiado pequena para os dados que você está recebendo.

Eu não sei o que você está tentando alcançar.

Alguns pontos:

1) O coletor de lixo é mais agressivo no modo de versão assim com mau posse o comportamento que você descreve não é incomum.

2) Eu não entende o que o código abaixo está a tentar fazer?

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

Você usa GCHandle.Alloc para bloquear uma instância de DataLoggerWrap na memória, mas então você nunca passá-lo para fora para não gerenciado - então por que você bloqueá-lo? Você também nunca libertá-lo?

A segunda linha, em seguida, pega de volta uma referência - porque o caminho circular? por isso que a referência - você nunca usá-lo

3) Você definir as IntPtrs para null - por quê? -. Isto não terá qualquer efeito fora do escopo da função

4) Você precisa saber o que o contrato de retorno de chamada é. Quem possui pData o retorno de chamada ou a função chamando?

Eu estou com @jdehaan, exceto CallingConvetion.StdCall poderia ser a resposta, especialmente quando a 3ª lib partido está escrito no BC ++, por exemplo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top