通过跟踪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

 鼠标双击:

 鼠标滚轮滚动

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐