作为 Alejandro 所写内容的旁注(我认为这是正确的),您可以稍微简化代码,删除数据的副本。您可以直接“固定”您的byte[]
. 记住“取消固定”它很重要(因此try
/finally
块)
您的代码中还有另一个潜在问题(我仅在第二遍代码中看到的问题):必须终止 C 字符串\0
(因此"Foo"
必须终止"Foo\0"
)。您Encoding.ASCII
不保证\0
终止。经典的做法是使byte[]
“稍微大一点”。我已经完成了必要的更改。
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData; // Any value the sender chooses. Perhaps its main window handle?
public int cbData; // The count of bytes in the message.
public IntPtr lpData; // The address of the message.
}
[StructLayout(LayoutKind.Sequential)]
public struct ExternalGetPositionType
{
public double X;
public double Y;
public double Z;
public double W;
}
const int WM_COPYDATA = 0x004A;
const int EXTERNAL_CD_COMMAND_RUN_ASYNC = 0x8001;
const int EXTERNAL_CD_GET_POSITION_PCS = 0x8011;
const int EXTERNAL_CD_GET_POSITION_MCS = 0x8012;
static void Main(string[] args)
{
Console.WriteLine("{0} bit process.", (IntPtr.Size == 4) ? "32" : "64");
Console.Write("Press ENTER to run test.");
Console.ReadLine();
IntPtr hwnd = FindWindow(null, "Form1");
Console.WriteLine("hwnd = {0:X}", hwnd.ToInt64());
if (hwnd == IntPtr.Zero)
{
throw new Exception("hwnd not found");
}
IntPtr ret = RunAsync(hwnd, @"C:\Users\Desktop\COPYDATATEST.iwp");
Console.WriteLine($"Return value for EXTERNAL_CD_COMMAND_RUN_ASYNC is {ret}");
ret = GetPosition(hwnd, true, new ExternalGetPositionType { X = 1, Y = 2, Z = 3, W = 4 });
Console.WriteLine($"Return value for EXTERNAL_CD_GET_POSITION_PCS is {ret}");
ret = GetPosition(hwnd, false, new ExternalGetPositionType { X = 10, Y = 20, Z = 30, W = 40 });
Console.WriteLine($"Return value for EXTERNAL_CD_GET_POSITION_MCS is {ret}");
Console.ReadLine();
}
public static IntPtr RunAsync(IntPtr hwnd, string str)
{
// We have to add a \0 terminator, so len + 1 / len + 2 for Unicode
int len = Encoding.Default.GetByteCount(str);
var buff = new byte[len + 1]; // len + 2 for Unicode
Encoding.Default.GetBytes(str, 0, str.Length, buff, 0);
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(buff, GCHandleType.Pinned);
var cds = new COPYDATASTRUCT();
cds.dwData = (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC;
cds.lpData = h.AddrOfPinnedObject();
cds.cbData = buff.Length;
ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
public static IntPtr GetPosition(IntPtr hwnd, bool pcs, ExternalGetPositionType position)
{
// We cheat here... It is much easier to pin an array than to copy around a struct
var positions = new[]
{
position
};
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(positions, GCHandleType.Pinned);
var cds = new COPYDATASTRUCT();
cds.dwData = pcs ? (IntPtr)EXTERNAL_CD_GET_POSITION_PCS : (IntPtr)EXTERNAL_CD_GET_POSITION_MCS;
cds.lpData = h.AddrOfPinnedObject();
cds.cbData = Marshal.SizeOf<ExternalGetPositionType>();
ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
请注意,即使ASCII
您可以使用Default
编码,也可以更好一些。
如果您想接收消息,请在您的 Winforms 中执行以下操作:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
COPYDATASTRUCT cds = Marshal.PtrToStructure<COPYDATASTRUCT>(m.LParam);
if (cds.dwData == (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC)
{
string str = Marshal.PtrToStringAnsi(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_COMMAND_RUN_ASYNC: {str}");
m.Result = (IntPtr)100; // If you want to return a value
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_PCS)
{
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_PCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
m.Result = (IntPtr)200;
}
else
{
m.Result = (IntPtr)0;
}
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_MCS)
{
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_MCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
m.Result = (IntPtr)300;
}
else
{
m.Result = (IntPtr)0;
}
}
return;
}
base.WndProc(ref m);
}
请注意,如果您同时控制发送方和接收方,最好使用 Unicode 作为字符串参数。您必须同时修改发送方和接收方:Encoding.Unicode.GetByteCount
/ Encoding.Unicode.GetBytes
, +2 而不是 +1 和Marshal.PtrToStringUni
.