从 C++ 发送一个结构体使用 WM_COPYDATA 到 WPF
我有一个本机 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果没有有关您的设置的更多信息,我不确定您一定犯了什么错误。我尽可能地复制了代码(在 WPF 应用程序中使用 WndProc,从我自己的 win32 应用程序发送),它对我来说工作得很好。如果您运行 64 位应用程序,肯定会出现一些错误,即 Pack = 1 将导致 COPYDATASTRUCT 未对齐,并且从指针读取可能会以痛苦结束。
仅传递整数就崩溃了?查看传递 LPWSTR 或 wstring 的注释代码将导致严重的问题,尽管在解组发送的数据之前这不会变得明显。
就其价值而言,这是我的代码片段,似乎对我有用,包括获取命令行。
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.
我已经构建了几个应用程序(分别使用 VC++ 和 VC#),解决了问题的“简化”变体(即无法获取该结构),它们似乎工作完美,所以它可能真的是一些东西按照您的设置,如 tyranid 所说。
无论如何,这是代码(只需将其粘贴到新创建的 WIN32 APPLICATION (对于 VC++)和 WINDOWS FORMS APPLICATION 对于 C# 来运行和测试就足够了)
: b>StackProxy.cpp
Form1.cs
希望这有帮助。
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
Form1.cs
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:))