从 C++ 发送一个结构体使用 WM_COPYDATA 到 WPF

发布于 2024-08-14 05:35:42 字数 2403 浏览 12 评论 0原文

我有一个本机 C++ 应用程序,目前只需将其命令行字符串和当前鼠标光标坐标发送到 WPF 应用程序。消息发送和接收都很好,但我无法将 C# 中的 IntPtr 实例转换为结构体。

当我尝试这样做时,应用程序要么毫无例外地崩溃,要么跳过转换它的代码行并收到循环中的下一条消息。这可能意味着发生了本机异常,但我不知道为什么。

这是 C++ 程序。目前我忽略命令行字符串并使用假光标坐标只是为了确保一切正常。

#include "stdafx.h"
#include "StackProxy.h"
#include "string"

typedef std::basic_string<WCHAR, std::char_traits<WCHAR>> wstring;

struct StackRecord
{
    //wchar_t CommandLine[128];
    //LPTSTR CommandLine;
    //wstring CommandLine;
    __int32 CursorX;
    __int32 CursorY;
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    COPYDATASTRUCT data;
    ZeroMemory(&data, sizeof(COPYDATASTRUCT));

    StackRecord* record = new StackRecord();

    wstring cmdLine(lpCmdLine);
    //record.CommandLine = cmdLine;
    record->CursorX = 5;
    record->CursorY = 16;
    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
    {
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
    }
    return 0;
}

这是 WPF 应用程序接收消息的部分。如果整个事情不只是崩溃,则跳过 IF 语句中的第二行。

    public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == Interop.WM_COPYDATA)
        {
            var data = (Interop.CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(Interop.CopyDataStruct));
            var record = (Interop.StackRecord)Marshal.PtrToStructure(data.lpData, typeof(Interop.StackRecord));
            MessageBox.Show(String.Format("X: {0}, Y: {1}", record.CursorX, record.CursorY));
        }
        return IntPtr.Zero;
    }

以下是结构体的 C# 定义。我无休止地摆弄编组属性,但一无所获。

internal static class Interop
{
    public static readonly int WM_COPYDATA = 0x4A;

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

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    public struct StackRecord
    {
        //[MarshalAs(UnmanagedType.ByValTStr)]
        //public String CommandLine;
        public Int32 CursorX;
        public Int32 CursorY;
    }
}

有什么想法吗?

I have a native C++ application that, for the time being simply needs to send its command line string and current mouse cursor coordinates to a WPF application. The message is sent and received just fine, but I cannot convert the IntPtr instance in C# to a struct.

When I try to do so, the application will either crash without exception or the line of code that converts it is skipped and the next message in the loop is received. This probably means there's a native exception occurring, but I don't know why.

Here's the C++ program. For the time being I'm ignoring the command line string and using fake cursor coordinates just to make sure things work.

#include "stdafx.h"
#include "StackProxy.h"
#include "string"

typedef std::basic_string<WCHAR, std::char_traits<WCHAR>> wstring;

struct StackRecord
{
    //wchar_t CommandLine[128];
    //LPTSTR CommandLine;
    //wstring CommandLine;
    __int32 CursorX;
    __int32 CursorY;
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    COPYDATASTRUCT data;
    ZeroMemory(&data, sizeof(COPYDATASTRUCT));

    StackRecord* record = new StackRecord();

    wstring cmdLine(lpCmdLine);
    //record.CommandLine = cmdLine;
    record->CursorX = 5;
    record->CursorY = 16;
    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
    {
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
    }
    return 0;
}

And here is the part of the WPF application that receives the message. The second line inside the IF statement is skipped over, if the whole thing doesn't just crash.

    public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == Interop.WM_COPYDATA)
        {
            var data = (Interop.CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(Interop.CopyDataStruct));
            var record = (Interop.StackRecord)Marshal.PtrToStructure(data.lpData, typeof(Interop.StackRecord));
            MessageBox.Show(String.Format("X: {0}, Y: {1}", record.CursorX, record.CursorY));
        }
        return IntPtr.Zero;
    }

And here are the C# definitions for the structs. I have toyed endlessly with marshalling attributes and gotten nowhere.

internal static class Interop
{
    public static readonly int WM_COPYDATA = 0x4A;

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

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    public struct StackRecord
    {
        //[MarshalAs(UnmanagedType.ByValTStr)]
        //public String CommandLine;
        public Int32 CursorX;
        public Int32 CursorY;
    }
}

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

静水深流 2024-08-21 05:35:42

如果没有有关您的设置的更多信息,我不确定您一定犯了什么错误。我尽可能地复制了代码(在 WPF 应用程序中使用 WndProc,从我自己的 win32 应用程序发送),它对我来说工作得很好。如果您运行 64 位应用程序,肯定会出现一些错误,即 Pack = 1 将导致 COPYDATASTRUCT 未对齐,并且从指针读取可能会以痛苦结束。

仅传递整数就崩溃了?查看传递 LPWSTR 或 wstring 的注释代码将导致严重的问题,尽管在解组发送的数据之前这不会变得明显。

就其价值而言,这是我的代码片段,似乎对我有用,包括获取命令行。

/* C++ code */
struct StackRecord
{
    wchar_t cmdline[128];
    int CursorX;
    int CursorY;
};

void SendCopyData(HWND hFind)
{
    COPYDATASTRUCT cp;
    StackRecord record;

    record.CursorX = 1;
    record.CursorY = -1;

    _tcscpy(record.cmdline, L"Hello World!");
    cp.cbData = sizeof(record);
    cp.lpData = &record;
    cp.dwData = 12;
    SendMessage(hFind, WM_COPYDATA, NULL, (LPARAM)&cp);
}

/* C# code */
public static readonly int WM_COPYDATA = 0x4A;

[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
    public IntPtr dwData;
    public int cbData;
    public IntPtr lpData;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StackRecord
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
    public string CommandLine;
    public Int32 CursorX;
    public Int32 CursorY;
}

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_COPYDATA)
    {
        StackRecord record = new StackRecord();
        try
        {
            CopyDataStruct cp = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
            if (cp.cbData == Marshal.SizeOf(record))
            {
                record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
            }
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.ToString());
        }
        handled = true;
    }
    else
    {
        handled = false;
    }
    return IntPtr.Zero;
}

I am not sure what you are getting wrong necessarily without more info about your setup. I replicated the code as best I could (using WndProc in a WPF app, sending from my own win32 app) and it works fine for me. There are a few errors which will definetly crop up if you are running 64 bit applications, namely the Pack = 1 will cause the COPYDATASTRUCT to become misaligned and reading from the pointer is likely to end in pain.

It is crashing passing just the ints? Looking at your commented code passing a LPWSTR or wstring is going to cause serious issues, although that shouldn't become apparent until you unmarshal the sent data.

For what it is worth, this is snippets of my code which seem to work for me including getting the command line across.

/* C++ code */
struct StackRecord
{
    wchar_t cmdline[128];
    int CursorX;
    int CursorY;
};

void SendCopyData(HWND hFind)
{
    COPYDATASTRUCT cp;
    StackRecord record;

    record.CursorX = 1;
    record.CursorY = -1;

    _tcscpy(record.cmdline, L"Hello World!");
    cp.cbData = sizeof(record);
    cp.lpData = &record;
    cp.dwData = 12;
    SendMessage(hFind, WM_COPYDATA, NULL, (LPARAM)&cp);
}

/* C# code */
public static readonly int WM_COPYDATA = 0x4A;

[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
    public IntPtr dwData;
    public int cbData;
    public IntPtr lpData;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StackRecord
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
    public string CommandLine;
    public Int32 CursorX;
    public Int32 CursorY;
}

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_COPYDATA)
    {
        StackRecord record = new StackRecord();
        try
        {
            CopyDataStruct cp = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
            if (cp.cbData == Marshal.SizeOf(record))
            {
                record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
            }
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.ToString());
        }
        handled = true;
    }
    else
    {
        handled = false;
    }
    return IntPtr.Zero;
}
三生殊途 2024-08-21 05:35:42

我已经构建了几个应用程序(分别使用 VC++ 和 VC#),解决了问题的“简化”变体(即无法获取该结构),它们似乎工作完美,所以它可能真的是一些东西按照您的设置,如 tyranid 所说。

无论如何,这是代码(只需将其粘贴到新创建的 WIN32 APPLICATION (对于 VC++)和 WINDOWS FORMS APPLICATION 对于 C# 来运行和测试就足够了)

: b>StackProxy.cpp

#include "stdafx.h"
#include "StackProxy.h"
#include <string>


struct StackRecord {
    __int32 CursorX;
    __int32 CursorY;
};


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    StackRecord record;

    record.CursorX = 5;
    record.CursorY = 16;

    COPYDATASTRUCT data;

    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = &record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);

    return 0;
}

Form1.cs

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public struct COPYDATASTRUCT
        {
            public System.Int32 dwData;
            public System.Int32 cbData;
            public System.IntPtr lpData;
        }

        int WM_COPYDATA = 0x4A;

        [StructLayout(LayoutKind.Sequential)]
        public struct StackRecord
        {
            public Int32 CursorX;
            public Int32 CursorY;
        }

        public Form1()
        {
            InitializeComponent();
            Text = "Window1";
        }

        protected override void WndProc(ref Message msg)
        {
            if (msg.Msg == WM_COPYDATA) {
                COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
                StackRecord record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
                MessageBox.Show(String.Format("X: {0}, Y: {1}, Data: {2}", record.CursorX, record.CursorY, cp.dwData));
            } 
            base.WndProc(ref msg);
        }
    }
}

希望这有帮助。

PS 我对 C# 和(尤其是)互操作了解不多(主要对 C++ 编程感兴趣),但[几个小时前]看到没有人回答,只是认为尝试这个问题将是一个很好的挑战。更不用说赏金了:)

*嗯,我迟到了:))

I've build a couple of applications (with VC++ and VC#, respectively), addressing the "boiled-down" variant of the problem (i.e. inability to get that struct), they seem to work flawelessly, so it may really be something with your setup, as tyranid says.

Anyway, here's the code (it must be enough to just paste it into newly created WIN32 APPLICATION (for VC++) and WINDOWS FORMS APPLICATION for C# to run and test):

StackProxy.cpp

#include "stdafx.h"
#include "StackProxy.h"
#include <string>


struct StackRecord {
    __int32 CursorX;
    __int32 CursorY;
};


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    StackRecord record;

    record.CursorX = 5;
    record.CursorY = 16;

    COPYDATASTRUCT data;

    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = &record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);

    return 0;
}

Form1.cs

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public struct COPYDATASTRUCT
        {
            public System.Int32 dwData;
            public System.Int32 cbData;
            public System.IntPtr lpData;
        }

        int WM_COPYDATA = 0x4A;

        [StructLayout(LayoutKind.Sequential)]
        public struct StackRecord
        {
            public Int32 CursorX;
            public Int32 CursorY;
        }

        public Form1()
        {
            InitializeComponent();
            Text = "Window1";
        }

        protected override void WndProc(ref Message msg)
        {
            if (msg.Msg == WM_COPYDATA) {
                COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
                StackRecord record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
                MessageBox.Show(String.Format("X: {0}, Y: {1}, Data: {2}", record.CursorX, record.CursorY, cp.dwData));
            } 
            base.WndProc(ref msg);
        }
    }
}

Hope this helps.

P.S. I've not got much knowledge of C# and (especially) interop (having interest primarily in C++ programming), but seeing no one answer [a few hours ago] just thought it would be a nice challenge to try this problem. Not to mention the bounty :)

*amn it, i'm late:))

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文