我如何通过进程ID而不是窗口句柄向特定进程发送消息?

问题描述 投票:3回答:4

为了解决GenerateConsoleCtrlEvent的限制,我必须创建一个中间的“中间人”进程来处理启动某些控制台应用程序的过程。该进程的主要目的是对其自身调用GenerateConsoleCtrlEvent,以使其自身和所有子进程完全关闭,以响应ctrl + break信号(而不是使用Process.Kill)。产生此需求的原因是,除非进程组ID为零,否则GenerateConsoelCtrlEvent基本上不起作用,这意味着它仅对调用进程组本身有效。请参阅:https://stackoverflow.com/a/2431295/88409

所以无论如何...我已经创建了这个中间过程,它启动了一个调用Application.Run的线程,该线程在处理特定用户定义消息的窗体上运行。

我的问题是...如何向此过程发送消息以对其进行控制?

我有Process对象及其进程ID,仅此而已。 Process.MainWindowHandle为零。

因此,我需要一种将消息发送到特定进程或将消息广播到特定进程中的所有窗口的方法。

FindWindow不是选项,因为它试图在任何进程上通过名称和类来标识窗口,这是不可靠的。我想毫无歧义地向特定进程发送消息。

c# .net sendmessage inter-process-communicat
4个回答
2
投票

您既不能发送消息,也不能将消息发布到进程,但是您可以将消息发布到线程。当然,该线程必须已经启动了消息循环才能对其进行处理。


6
投票

在三种情况下,消息可能被[[认为发送或发布到流程中:

    我可以通过将[window]消息发送到该进程中的第一个枚举窗口来将其“发送”或“发布”到特定进程
  1. 我可以通过将[线程]消息发布到特定进程中的第一个枚举线程中,来将其“发布”到特定进程中。
  2. 我可以通过将[线程]消息“发布”到特定进程,将其发布到拥有该进程的第一个枚举窗口的线程中。
  • 方法1可能过于具体,因为它的目标是特定但任意的窗口。方法2可能不够具体,因为第一个枚举线程是任意的,并且可能没有消息循环。方法3是一种混合方法,该方法首先标识一个窗口,然后将线程消息发布到该窗口的线程,因此它的目标不是特定的窗口(即“线程消息”),而是目标是一个线程。因为线程拥有至少一个窗口,所以可能会出现消息循环。

    以下是支持所有三种方法以及方法“发送”和“发布”的实现。方法1包含在下面的方法SendMessage和PostMessage中。下面的方法PostThreadMessage包含方法2和3,具体取决于您是否设置了可选参数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." );


  • 0
    投票
    使用EnumWindows扫描顶级窗口,然后使用GetProcessHandleFromHWnd确定窗口是否在目标进程中。

    然后您可以将消息发送到该hwnd,目标进程应接收到它们(如果目标进程具有消息循环)。


    0
    投票
    我刚刚回答了一个非常类似的问题(带有示例代码)here。快速答案是PostThreadMessage()
    © www.soinside.com 2019 - 2024. All rights reserved.