C#实现不同窗口间的消息通信
有时候我们需要在一个程序中向另外一个窗口传递数据,以实现不同窗口间的数据通信,下面介绍两种通信方式,一种是使用窗体的消息处理机制,另一种是使用委托实现消息通信。
1 重写窗体消息发送机制来发送自定义消息
首先创建一个窗口程序A,添加数据发送以及打开窗口B的按钮,并重写消息发送函数,如图:
窗口程序A代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormTest
{
public struct CustomMsg
{
public string str;
}
public partial class Form1 : Form
{
//自定义消息
public const int USER = 0x800;
public const int MYMESSAGE = USER + 1;
/// <summary>
/// 重写消息发送函数
/// </summary>
/// <param name="hWnd">信息发往的窗口的句柄</param>
/// <param name="Msg">消息ID</param>
/// <param name="wParam">参数1</param>
/// <param name="lParam">自定义结构</param>
/// <returns></returns>
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref CustomMsg lParam);
/// <summary>
/// 获取窗口句柄
/// </summary>
/// <param name="lpClassName"></param>
/// <param name="lpWindowName">窗口名称</param>
/// <returns></returns>
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
private void UnitySendMessage(CustomMsg msg)
{
IntPtr windows = FindWindow(null, "FormB");
if (windows != IntPtr.Zero)
{
SendMessage(windows, MYMESSAGE, 0, ref msg);
}
}
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
FormB formB = new FormB();
formB.Show();
}
private void button1_Click(object sender, EventArgs e)
{
CustomMsg customMsg = new CustomMsg();
customMsg.str = textBox2.Text;
UnitySendMessage(customMsg);
}
}
}
|
窗口程序B接收代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormTest
{
public partial class FormB : Form
{
//自定义消息
public const int USER = 0x800;
public const int MYMESSAGE = USER + 1;
public FormB()
{
InitializeComponent();
}
//重写窗体的消息处理函数DefWndProc,从中加入自定义消息 MYMESSAGE 的检测处理入口
protected override void DefWndProc(ref Message m)
{
switch (m.Msg)
{
//接收自定义消息MYMESSAGE,并显示其参数
case MYMESSAGE:
CustomMsg msg = new CustomMsg();
Type t = msg.GetType();
msg = (CustomMsg)m.GetLParam(t);
ShowMessage(msg);
break;
default:
//调用基类函数,以便系统处理其他消息
base.DefWndProc(ref m);
break;
}
}
public delegate void ShowMessageCallback(CustomMsg msg);
public void ShowMessage(CustomMsg msg)
{
if (this.InvokeRequired)
{
ShowMessageCallback d = new ShowMessageCallback(ShowMessage);
this.Invoke(d, new object[] { msg });
}
else
{
textBox1.Text = msg.str;
}
}
}
}
|
最后运行程序进行测试,测试结果如图:
2 使用委托进行进程间或窗体间通信
首先在类A或窗体A中创建委托事件并且发布事件,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#region 调试
public delegate void DebugHandler(SerialMsg msg);
/// <summary>
/// 调试使用
/// </summary>
public event DebugHandler DebugEvent;
/// <summary>
/// 直接输出接收到的数据
/// </summary>
/// <param name="msg"></param>
public void SerialToolsDebug(SerialMsg msg)
{
DebugEvent?.Invoke(msg);
}
#endregion
|
A发布了事件DebugEvent,在B中进行事件订阅,如:
1
2
3
4
5
6
7
8
9
|
Class_A.DebugEvent += GetDataFromDebugEvent;
private void GetDataFromDebugEvent(SerialMsg msg)
{
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, (Action)(() =>
{
//处理msg消息
}));
}
|
实际使用时,当A中调用SerialToolsDebug(msg)发布消息后,B中就可以接收到A发布的消息,使用函数GetDataFromDebugEvent来处理,B中处理消息时使用匿名的委托函数来进行后台更新数据,可以避免频繁刷新UI界面时造成卡顿的情况。