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());
}