Вопрос

Я пытаюсь заставить приложение C # WPF взаимодействовать с другим приложением, написанным на C, используя WM_COPYDATA. Приложение C пытается отправить структуру следующим образом:

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

В моем приложении на C # я определил структуру следующим образом:

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

И код для получения сообщения WM_COPYDATA выглядит следующим образом:

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

Я получаю сообщения из приложения C, но все данные в структуре - бред. До этого я был в состоянии вручную извлечь массив байтов из указателя lParam, а затем использовать System.BitConverter и System.Text.Encoding.ACII для интерпретации байтового массива, и это работало довольно хорошо. Но сейчас я пытаюсь сделать это более чистым способом, и он просто не работает.

Это было полезно?

Решение

После долгого поиска ответа на этот вопрос я понял, что пропускаю ОЧЕНЬ важный шаг. Я чувствую себя идиотом, но вот ответ на мой собственный вопрос.

Структура C # должна выглядеть следующим образом:

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

И еще одна структура должна быть определена для получения информации WM_COPYDATA. Это выглядит так:

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

И метод WndProc должен быть изменен так, чтобы он выглядел следующим образом:

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

Я использовал CopyDataStruct в моем предыдущем рабочем решении, и я просто забыл использовать его в более новой версии.

Другие советы

Частично проблема заключается в том, что член str должен быть ByValTStr, а не LPSTR, поскольку это строковый массив. Попробуйте следующее определение

[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;
};
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top