在 3 种情况下,可以将消息视为已发送或发布到进程:
- 我可以将 [window] 消息“发送”或“发布”到特定进程,方法是将其发送到该进程中的第一个枚举窗口
- 我可以通过将 [thread] 消息发布到进程中的第一个枚举线程来将其“发布”到特定进程。
- 我可以通过将 [thread] 消息“发布”到特定进程,方法是将其发布到拥有该进程的第一个枚举窗口的线程。
方法 1 可能过于具体,因为它针对的是一个具体但任意的窗口。方法 2 可能不够具体,因为第一个枚举线程是任意的并且可能没有消息循环。方法 3 是一种混合方法,它首先标识一个窗口,然后将线程消息发布到该窗口的线程,因此它不是针对特定窗口(即它是“线程消息”),而是针对一个线程可能有一个消息循环,因为线程至少拥有一个窗口。
以下是支持所有三种方法以及“send”和“post”两种方法的实现。方法 1 包含在下面的方法 SendMessage 和 PostMessage 中。方法 2 和 3 包含在下面的 PostThreadMessage 方法中,具体取决于您是否设置了可选参数ensureTargetThreadHasWindow
(true = 方法 3,false = 方法 2)。
public static class ProcessExtensions
{
private static class Win32
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
public delegate bool EnumThreadDelegate (IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll", SetLastError=true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}
//Sends a message to the first enumerated window in the first enumerated thread with at least one window, and returns the handle of that window through the hwnd output parameter if such a window was enumerated. If a window was enumerated, the return value is the return value of the SendMessage call, otherwise the return value is zero.
public static IntPtr SendMessage( this Process p, out IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam )
{
hwnd = p.WindowHandles().FirstOrDefault();
if (hwnd != IntPtr.Zero)
return Win32.SendMessage( hwnd, msg, wParam, lParam );
else
return IntPtr.Zero;
}
//Posts a message to the first enumerated window in the first enumerated thread with at least one window, and returns the handle of that window through the hwnd output parameter if such a window was enumerated. If a window was enumerated, the return value is the return value of the PostMessage call, otherwise the return value is false.
public static bool PostMessage( this Process p, out IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam )
{
hwnd = p.WindowHandles().FirstOrDefault();
if (hwnd != IntPtr.Zero)
return Win32.PostMessage( hwnd, msg, wParam, lParam );
else
return false;
}
//Posts a thread message to the first enumerated thread (when ensureTargetThreadHasWindow is false), or posts a thread message to the first enumerated thread with a window, unless no windows are found in which case the call fails. If an appropriate thread was found, the return value is the return value of PostThreadMessage call, otherwise the return value is false.
public static bool PostThreadMessage( this Process p, UInt32 msg, IntPtr wParam, IntPtr lParam, bool ensureTargetThreadHasWindow = true )
{
uint targetThreadId = 0;
if (ensureTargetThreadHasWindow)
{
IntPtr hwnd = p.WindowHandles().FirstOrDefault();
uint processId = 0;
if (hwnd != IntPtr.Zero)
targetThreadId = Win32.GetWindowThreadProcessId( hwnd, out processId );
}
else
{
targetThreadId = (uint)p.Threads[0].Id;
}
if (targetThreadId != 0)
return Win32.PostThreadMessage( targetThreadId, msg, wParam, lParam );
else
return false;
}
public static IEnumerable<IntPtr> WindowHandles( this Process process )
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in process.Threads)
Win32.EnumThreadWindows( (uint)thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero );
return handles;
}
}
要在 Process 对象上使用此扩展方法:
Process process = Process.Start( exePath, args );
IntPtr hwndMessageWasSentTo = IntPtr.Zero; //this will receive a non-zero value if SendMessage was called successfully
uint msg = 0xC000; //The message you want to send
IntPtr wParam = IntPtr.Zero; //The wParam value to pass to SendMessage
IntPtr lParam = IntPtr.Zero; //The lParam value to pass to SendMessage
IntPtr returnValue = process.SendMessage( out hwndMessageWasSentTo, msg, wParam, lParam );
if (hwndMessageWasSentTo != IntPtr.Zero)
Console.WriteLine( "Message successfully sent to hwnd: " + hwndMessageWasSentTo.ToString() + " and return value was: " + returnValue.ToString() );
else
Console.WriteLine( "No windows found in process. SendMessage was not called." );