C#利用Win32的SendMessage实现子线程更新UI(方式3)
通过跟踪winform的Invoke方法,发现Control控件也是调用Win32的API实现子线程更新UI,只不过是多加了线程安全控制同时也发现,有个死循环调用的虚方法,类似于Unity3D的Update方法没错,上面的方法就是UI控件处理Window消息并实时更新UI的方法,这样我们就可以监听类似于鼠标双击和鼠标滚轮滚动的消息了,然后可以做一些额外的操作UI布局如下:新增类Win32API(网
通过跟踪winform的Invoke方法,发现Control控件也是调用Win32的API实现子线程更新UI,只不过是多加了线程安全控制
同时也发现,有个死循环调用的虚方法,类似于Unity3D的Update方法
没错,上面的方法就是UI控件处理Window消息并实时更新UI的方法,这样我们就可以监听类似于鼠标双击和鼠标滚轮滚动的消息了,然后可以做一些额外的操作
UI布局如下:
新增类Win32API(网上抄的),并编辑如下:
public class Win32API
{
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
/// <summary>
/// 自定义的结构
/// </summary>
public struct My_lParam
{
public int i;
public string s;
}
/// <summary>
/// 使用COPYDATASTRUCT来传递字符串
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
//消息发送API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
int lParam //参数2
);
//消息发送API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
ref My_lParam lParam //参数2
);
//消息发送API
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
ref COPYDATASTRUCT lParam //参数2
);
//消息发送API
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
int lParam // 参数2
);
//消息发送API
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
ref My_lParam lParam //参数2
);
//异步消息发送API
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(
IntPtr hWnd, // 信息发往的窗口的句柄
int Msg, // 消息ID
int wParam, // 参数1
ref COPYDATASTRUCT lParam // 参数2
);
}
本例子中,用到了两个,分别是
1 获取Form(或Control)控件的句柄
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
第一个是Form(或Control)控件对应的类名,第二个是Form(或Control)控件对应的Text的属性值,如下图1,其实用一个就能找到对应的句柄了,我用第一个参数发现没用,然后我只设置第二个参数发现是成功了,设置如下图2
图1
图2
第二个是子线程往UI线程发送消息 Win32API.SendMessage
Form1代码如下:
public partial class Form1 : Form { public Form1() { InitializeComponent(); Console.WriteLine("UI初始化线程Id:" + Thread.CurrentThread.ManagedThreadId); } private void button1_Click(object sender, EventArgs e) { Task.Run(() => { Console.WriteLine("Task子线程Id:" + Thread.CurrentThread.ManagedThreadId); string strTest = "哈哈111"; Win32API.COPYDATASTRUCT cds; cds.dwData = (IntPtr)100; cds.lpData = strTest; byte[] sarr = System.Text.Encoding.UTF8.GetBytes(strTest); int len = sarr.Length; cds.cbData = len + 1; //在子线程发送消息 Win32API.SendMessage(Win32API.FindWindow(null, "Form1"), 0x63, 5, ref cds);//传递整型参数和不定长的字符串成功' }); } protected override void WndProc(ref Message m) { if (m.Msg == 0x63) //自定义的消息 { Win32API.COPYDATASTRUCT mystr = new Win32API.COPYDATASTRUCT(); Type mytype = mystr.GetType(); mystr = (Win32API.COPYDATASTRUCT)m.GetLParam(mytype); string str2 = mystr.lpData; //获取到消息 this.textBox1.Text = str2; //更新到TextBox上 } else if (m.Msg == 515) { MessageBox.Show("鼠标双击"); } else if (m.Msg == 522) { MessageBox.Show("鼠标滚动"); } base.WndProc(ref m); } } |
代码解释:
1 重写WndProc方法(该方法会循环调用),只要是继承自Winform的Control控件就能重写该方法,Winform窗体也不例外,用于接收子线程发来的消息,并显示到UI控件上
2 在按钮点击事件private void button1_Click(object sender, EventArgs e)中启用的子线程中调用Win32API.SendMessage往From1中发送文本消息(当然如果是对象,可以考虑序列化为文本)
程序运行结果如下图:
点击button1
鼠标双击:
鼠标滚轮滚动
更多推荐
所有评论(0)