Pergunta

Eu estou tentando obter um aplicativo C # WPF para comunicar com outro aplicativo escrito em C usando WM_COPYDATA. O aplicativo C está tentando enviar um struct da seguinte forma:

typedef struct
{
    int x;
    int y;
    char str[40];
    double d;
    char c;
} DATASTRUCT;

Na minha C # app que eu tenha definido um struct da seguinte forma:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DATASTRUCT
{
    public int x;
    public int y;
    [MarshalAs(UnmanagedType.LPStr, SizeConst=40)]
    public string s;
    public double d;
    public char c;
};

E o código para receber a mensagem WM_COPYDATA é a seguinte:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
    hwndSource.AddHook(new HwndSourceHook(WndProc));
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == 0x4A)
    {
        DATASTRUCT data = (DATASTRUCT)Marshal.PtrToStructure(lParam, typeof(DATASTRUCT));
        this.updateText(data);
        handled = true;
    }

    return (IntPtr)0;
}

Eu estou recebendo mensagens da aplicação C, mas todos os dados no struct é conversa fiada. Anterior a esta I foi capaz de extrair manualmente uma matriz de bytes a partir do ponteiro lParam e, em seguida, usar System.BitConverter e System.Text.Encoding.ACII para interpretar a matriz de bytes, e que funcionou muito bem. Mas agora eu estou tentando fazer isso de uma maneira mais limpa e ele simplesmente não está funcionando.

Foi útil?

Solução

Depois de muito tempo procurando a resposta a esta pergunta, eu percebi que eu estava faltando um passo muito importante. Eu me sinto como um idiota, mas aqui está a resposta à minha pergunta.

A C # struct deve ficar assim:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public unsafe struct DataStruct
{
    public int x;
    public int y;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
    public string s;
    public double d;
    public char c;
};

E outra struct deve ser definido para receber a informação WM_COPYDATA. Parece que este:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public unsafe struct CopyDataStruct
{
    public IntPtr dwData;
    public int cbData;
    public IntPtr lpData;
}

E o método WndProc deve ser alterado para olhar como esta:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == 0x4A)
    {
        CopyDataStruct cps = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
        DataStruct data = (DataStruct)Marshal.PtrToStructure(cps.lpData, typeof(DataStruct));
        updateText(data);
        handled = true;
    }

    return (IntPtr)0;
}

Eu estava usando o COPYDATASTRUCT na minha solução de trabalho anterior, e eu só esqueceu de usá-lo na versão mais recente.

Outras dicas

Parte do problema é que o membro str precisa ser um ByValTStr não um LPSTR já que é uma matriz de cadeia inline. Tente o seguinte definição

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DATASTRUCT
{
    public int x;
    public int y;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
    public string s;
    public double d;
    public char c;
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top