Pregunta

Estoy tratando de obtener una aplicación C # WPF para comunicarse con otra aplicación escrita en C usando WM_COPYDATA. La aplicación C está intentando enviar una estructura de la siguiente manera:

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

En mi aplicación C #, he definido una estructura de la siguiente manera:

[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;
};

Y el código para recibir el mensaje WM_COPYDATA es el siguiente:

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;
}

Estoy recibiendo mensajes de la aplicación C, pero todos los datos en la estructura son galimatías. Antes de esto, pude extraer manualmente una matriz de bytes del puntero lParam y luego usar System.BitConverter y System.Text.Encoding.ACII para interpretar la matriz de bytes, y eso funcionó bastante bien. Pero ahora estoy tratando de hacerlo de una manera más limpia y simplemente no funciona.

¿Fue útil?

Solución

Después de mucho tiempo buscando la respuesta a esta pregunta, me di cuenta de que me faltaba un paso MUY importante. Me siento como un idiota, pero aquí está la respuesta a mi propia pregunta.

La estructura C # debería verse así:

[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;
};

Y se debe definir otra estructura para recibir la información WM_COPYDATA. Se ve así:

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

Y el método WndProc debería cambiarse para que se vea así:

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;
}

Estaba usando CopyDataStruct en mi solución de trabajo anterior, y simplemente olvidé usarlo en la versión más reciente.

Otros consejos

Parte del problema es que el miembro str debe ser ByValTStr, no LPSTR, ya que es una matriz de cadenas en línea. Pruebe la siguiente definición

[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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top