简介
在使用中发现原文章中WendMessageToTargetWindow函数(如图)
调用这个函数传的int类型和string 类型数据,int可以接收但是string接收到的是一个句柄值,尝试对句柄取值得到的是一个空,想到作者上面有一个函数是传string字符串,这里我就改成了传两个int类型数据类似坐标。
修改后的代码
首先是MsgHandler类(在发送端NetFramework4._0Demo项目中创建)
using System;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace NetFramework4._0Demo
{
/// <summary>
/// 发送消息处理程序类
/// 调用该方法“SendMessageToTargetWindow”发送
/// </summary>
public class MsgHandler
{
/// <summary>
/// 系统定义的消息
/// </summary>
private const int WM_COPYDATA = 0x004A;
/// <summary>
/// 用户己定义消息
/// </summary>
private const int WM_DATA_TRANSFER = 0x0437;
// 使用Windows API的FindWindow方法
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
//使用Windows API的IsWindow方法
[DllImport("User32.dll", EntryPoint = "IsWindow")]
private static extern bool IsWindow(int hWnd);
// 使用Windows API的SendMessage方法
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
// 使用Windows API的SendMessage方法
[DllImport("User32.dll", EntryPoint = "SendMessageA")]
private static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);
/// <summary>
/// 要发信息数据结构
/// </summary>
private struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;//信息的长度
public IntPtr lpData;
}
/// <summary>
/// 向目标窗口发送消息
/// </summary>
/// <param name="wndName">查找目标窗体名称</param>
/// <param name="msg">要发送的消息,字符串</param>
/// <returns>返回的数据是否成功或则失败</returns>
public static bool SendMessageToTargetWindow(string wndName, string msg)
{
int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
#if DEBUG
string strError = string.Format("SendMessageToTargetWindow: 没有发现{0}窗体!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
#endif
return false;
}
else
{
byte[] bytData = null;
bytData = Encoding.Default.GetBytes(msg);
COPYDATASTRUCT cdsBuffer;
cdsBuffer.dwData = (IntPtr)100;
cdsBuffer.cbData = bytData.Length;
cdsBuffer.lpData = Marshal.AllocHGlobal(bytData.Length);
Marshal.Copy(bytData, 0, cdsBuffer.lpData, bytData.Length);
// 使用系统定义的消息wmcopydata来发送消息。
int iReturn = SendMessage(iHWnd, WM_COPYDATA, 0, ref cdsBuffer);
if (iReturn < 0)
{
#if DEBUG
string strError = string.Format("SendMessageToTargetWindow: 没有发现{0}窗体!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
#endif
return false;
}
return true;
}
}
/// <summary>
/// 向目标窗口发送消息
/// </summary>
/// <param name="wndName">查找目标窗体名称</param>
/// <param name="wParam">整型数据</param>
/// <param name="lParam">整型数据</param>
/// <returns>数据发送成或者失败</returns>
public static bool SendMessageToTargetWindow(string wndName, int wParam, int lParam)
{
int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
#if DEBUG
string strError = string.Format("SendMessageToTargetWindow: The target window [{0}] was not found!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
#endif
return false;
}
else
{
//使用定义的消息wmdatatransfer来发送消息。(测试传int数据正常接收,传string数据接收有问题)
int iReturn = SendMessage(iHWnd, WM_DATA_TRANSFER, wParam, lParam);
if (iReturn < 0)
{
#if DEBUG
string strError = string.Format("SendMessageToTargetWindow: Send message to the target window [{0}] failed!", wndName);
MessageBox.Show(strError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Debug.WriteLine(strError);
#endif
return false;
}
return true;
}
}
}
}
然后创建一个通用重写部分功能的窗体,放一些窗体通用操作(在接收端NetFramework2._0Demo项目创建)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace NetFramework2._0Demo
{
public partial class OverrideForm : Form
{
public OverrideForm()
{
InitializeComponent();
}
public OverrideForm(IContainer container)
{
container.Add(this);
InitializeComponent();
}
#region 解决窗体加载时控件闪烁
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
#endregion
#region 程序与程序之间相互通讯(接收端)
/// <summary>
///系统定义的消息
/// </summary>
private const int WM_COPYDATA = 0x004A;
/// <summary>
/// 自定义的消息
/// </summary>
private const int WM_DATA_TRANSFER = 0x0437;
/// <summary>
/// 传输的COPYDATASTRUCT结构体
/// </summary>
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
/// <summary>
///覆盖DefWndProc函数,以便通过它接收消息。
/// </summary>
/// <param name="m">message</param>
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
switch (m.Msg)
{
// 这里,我们使用wmcopydata消息来接收COPYDATASTRUCT
case WM_COPYDATA:
COPYDATASTRUCT cds = new COPYDATASTRUCT();
cds = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
byte[] bytData = new byte[cds.cbData];
Marshal.Copy(cds.lpData, bytData, 0, bytData.Length);
this.ProcessIncomingData(bytData);
break;
// 在这里,我们使用我们定义的消息wmdatatransfer来接收
// 普通数据,例如整数。
case WM_DATA_TRANSFER:
int iWParam = (int)m.WParam;
//string sLParam = m.LParam.ToString();
int sLParam = ((int)m.LParam);
ProcessIncomingData(iWParam, sLParam);
break;
default:
base.DefWndProc(ref m);
break;
}
}
/// <summary>
/// 处理传入的数据
/// </summary>
/// <param name="data">输入数据</param>
private void ProcessIncomingData(byte[] bytesData)
{
try
{
string str = Encoding.Default.GetString(bytesData);//接受的值
Console.WriteLine(str);
#region 根据接受值做操作
#endregion
}
catch (Exception)
{
}
//Console.WriteLine(str);
//lstReceivedMsg.Items.Add(strRevMsg);
}
/// <summary>
/// 处理传入的数据
/// </summary>
/// <param name="iWParam">一个整数</param>
/// <param name="sLParam">一个整数</param>
private void ProcessIncomingData(int iWParam, int sLParam)
{
Console.WriteLine(iWParam + "," + sLParam);
#region 根据接受值做操作
#endregion
}
#endregion
}
}
最后在NetFramework2._0Demo项目主窗体继承通用窗体
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
namespace NetFramework2._0Demo
{
public partial class NetFramework : OverrideForm
{
public NetFramework()
{
InitializeComponent();
}
}
}
通讯效果:
NetFramework4._0Demo程序发送消息
NetFramework2._0Demo程序接收消息
看到原文章有人留言能不能互相通讯,文章中已经实现由A程序发送到B程序接收,我们按照步骤实现B程序发送到A程序接收,互相通讯就实现了(注意窗体名别搞混了)
使用中单独传字符串无法满足需求,所以弄了个List对象传输,顺便完善这篇文章。
原理:把List转成byte[]再调用SendMessageToTargetWindow进行发送。
/// <summary>
/// 向目标窗口发送消息
/// </summary>
/// <param name="wndName">查找目标窗体名称</param>
/// <param name="msg">要发送的对象字节流</param>
/// <returns>返回的数据是否成功或则失败</returns>
public static bool SendMessageToTargetWindow(string wndName, byte[] bytData)
{
int iHWnd = FindWindow(null, wndName);
if (iHWnd == 0)
{
return false;
}
else
{
COPYDATASTRUCT cdsBuffer;
cdsBuffer.dwData = (IntPtr)100;
cdsBuffer.cbData = bytData.Length;
cdsBuffer.lpData = Marshal.AllocHGlobal(bytData.Length);
Marshal.Copy(bytData, 0, cdsBuffer.lpData, bytData.Length);
//使用系统定义的消息wmcopydata来发送消息。 (使用WM_DATA_TRANSFER发送,接受时会报错)
int iReturn = SendMessage(iHWnd, WM_COPYDATA, 0, ref cdsBuffer);
if (iReturn < 0)
{
return false;
}
return true;
}
}
//对象序列化
public static byte[] ObjectToBytes(object obj)
{
using (MemoryStream ms = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, obj); return ms.GetBuffer();
}
}
//反序列化对象
public static object BytesToObject(byte[] Bytes)
{
using (MemoryStream ms = new MemoryStream(Bytes))
{
IFormatter formatter = new BinaryFormatter(); return formatter.Deserialize(ms);
}
}
接收程序只要把获取到的byte[]流反序列化成指定对象即可。
按这个思路我试着传了一个自定义的对象Test,在接收反序列化时报了一个未找到Test项目集的错误,盲猜是如果两个程序共用的对象应该可以传输,上面我用的int[]就验证了这点。
待序列化的自定义类前加[Serializable]不加序列化会报错,大家可以试试。