SendMessage、PostMessage、PostThreadMessage

SendMessage()是阻塞型的函数,把消息发送给消息队列后,在消息没有响应完之前这个函数是不会返回的,返回值和消息处理函数是有直接关系的,消息处理函数返回什么,SendMessage就返回什么参数。
而PostMessage()是非阻塞型函数,把消息抛出去(与指定窗口创建的线程相关联的消息队列里)之后就返回,不管消息是否响应,返回的是发送的成功和失败,一般是很少失败,除非窗口不存在。
PostThreadMessage() 没有目标窗口,直接指定目标线程ID来确定目标线程,只能在消息循环中直接根据消息类型做相应的处理。

注意:
跨线程或者跨进程发送消息时推荐使用PostMessage(),因为它不阻塞。
如果是UI线程,则应使用PostMessage;如果是工作线程,则应使用PostThreadMessage,不要为了接收消息而创建窗口。
系统只对系统级的消息(0 ~ WM_USER-1)进行封送处理。发送自定义消息(>= WM_USER)到另一个进程,需要自己对消息进行封送处理。
如果发送一个范围低于WM_USER的消息给异步消息函数(PostMessage、 SendNotifyMessage 或 SendMessageCallback),它的消息参数不能包含指针。否则,操作将失败。函数将在接收线程处理消息之前返回,发送者将在内存被使用之前释放。
请不要使用 PostMessage 函数投递 WM_QUIT 消息;应该使用 PostQuitMessage 函数代替。

hwnd, 32位的窗口句柄(IntPtr)。
wParam,通常是一个与消息相关的数量(比如:WM_USER),也可能是窗口或控件的句柄(IntPtr)。
lParam,通常是一个指向内存中数据的指针。
hwnd、wParam、lParam可以互相转换。

// 发送消息
var handle = Win32ApiHelper.FindWindow(null, "Form2");
var aa = new DemoClassA
{
	BB = 100,
	CC = "10000",
};
var lparam = new Win32ApiHelper.LParamEx()
{
	DataType = aa.GetType().FullName,
	DataValue = aa,
};
Win32ApiHelper.SendMessage(handle, Win32ApiHelper.DEMO_MSG, 0, ref lparam);

// 处理消息
protected override void WndProc(ref Message m)
{
	switch (m.Msg)
	{
		case Win32ApiHelper.DEMO_MSG:
			{
				var lparam = m.LParam.ToInt32();
				var wparam = m.WParam.ToInt32();
				if (wparam == 0)
				{
					var lp = (Win32ApiHelper.LParamEx)m.GetLParam(typeof(Win32ApiHelper.LParamEx));
					var type = Type.GetType(lp.DataType);
					if (type.Name == nameof(DemoClassA))
					{
						var value = (DemoClassA)lp.DataValue;
						Console.WriteLine(string.Format("{0} {1}", value.BB, value.CC));
					}
				}
			}
			break;
		default:
			break;
	}
	base.WndProc(ref m);
}

Win32ApiHelper

using System;
using System.Runtime.InteropServices;
internal static class Win32ApiHelper
{
        #region WindowsMessages WM_
	
        #endregion
	public const int DEMO_MSG = WM_USER + 0x001;

        #region VirtualKeys VK_
        #endregion
        
        #region 窗体参数及标志 HWND_/SWP_/SW_
        #endregion 
      
        #region 自定义消息 WM_USER ~ 0x7FFF
        public const int DEMO_MSG = (int)WindowsMessages.WM_USER + 0x001;
        #endregion 
        
        #region User32.dll
	public struct LParamEx
	{
		/// <summary>
		/// 对象类型说明
		/// </summary>
		public string DataType;
		/// <summary>
		/// 对象值
		/// </summary>
		public dynamic DataValue;
	}
	
	/// <summary>
	/// 发送消息到指定窗口
	/// </summary>
	/// <param name="hWnd">目标窗口的句柄</param>
	/// <param name="msg">发送的消息</param>
	/// <param name="wParam">附加消息</param>
	/// <param name="lParam">附加消息</param>
	/// <returns></returns>
	[DllImport("User32.dll", EntryPoint = "SendMessage")]
	public static extern IntPtr SendMessage(int hWnd, int msg, int wParam, ref LParamEx lParam);
        #endregion
}

Win32ApiHelper的具体扩展,大家可以自行实现。

热键

// 1. 注册
public ctor()
{
    InitializeComponent();
    Win32ApiHelper.RegisterHotKey(Handle, (int)Win32ApiHelper.WindowsMessages.WM_USER + 100, Win32ApiHelper.KeyModifier.Ctrl, Win32ApiHelper.VirtualKeys.VK_C);
}
// 2. 处理
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case (int)Win32ApiHelper.WindowsMessages.WM_HOTKEY:
            switch (m.WParam.ToInt32())
            {
                case (int)Win32ApiHelper.WindowsMessages.WM_USER + 100: // Ctrl + C
                    if (Clipboard.ContainsText())
                    {
                        Win32ApiHelper.SetForegroundWindow(Handle);
                        var data = Clipboard.GetDataObject();
                        if (data.GetDataPresent(typeof(string)))
                        {
                            lblMsg.Text = (string)data.GetData(typeof(string));
                        }
                    }
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
    base.WndProc(ref m);
}
// 3. 取消
private void SerialStringConverter_FormClosed(object sender, FormClosedEventArgs e)
{
    Win32ApiHelper.UnregisterHotKey(Handle, (int)Win32ApiHelper.WindowsMessages.WM_USER + 100);
}

Clipboard

using System.Windows.Forms;

/// <summary>
/// 下一个窗口句柄
/// </summary>
IntPtr _nextClipHwnd;

private void Winform_Load(object sender, EventArgs e)
{
    Clipboard.Clear();
    // 1. 添加,窗体自动响应剪贴板的变化
    _nextClipHwnd = Win32ApiHelper.SetClipboardViewer(this.Handle);
}

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case (int)Win32ApiHelper.WindowsMessages.WM_DRAWCLIPBOARD:
            // 2. 将消息传给下一个窗口
            Win32ApiHelper.SendMessage(_nextClipHwnd, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
            // 3. 处理Clipboard内容
            if (Clipboard.ContainsText())
            {
                Win32ApiHelper.SetForegroundWindow(Handle);
                var data = Clipboard.GetDataObject();
                if (data.GetDataPresent(typeof(string)))
                {
                    lblMsg.Text = (string)data.GetData(typeof(string));
                }
            }
            break;
        default:
            break;
    }
    base.WndProc(ref m);
}


private void SerialStringConverter_FormClosed(object sender, FormClosedEventArgs e)
{
    // 4. 撤销
    Win32ApiHelper.ChangeClipboardChain(Handle, _nextClipHwnd);
    // 5. 将变动消息传递给下一个窗口
    Win32ApiHelper.SendMessage(_nextClipHwnd, (int)Win32ApiHelper.WindowsMessages.WM_CHANGECBCHAIN, Handle.ToInt32(), _nextClipHwnd.ToInt32());
}

posted @ 2020-09-24 18:03  wesson2019  阅读(634)  评论(0编辑  收藏  举报