向Windows服务发送Windows消息

9
有没有工具可以发送(模拟)Windows消息,例如“WM_ENDSESSION”到Windows服务?
或者
如何使用C#向进程发送Windows消息?
(我只知道C#)
编辑:目的:基本上,我必须调试Windows服务以修复仅在系统关闭时出现的错误。

7
我认为它应该发布在stackoverflow.com上。 - Brian R. Bondy
7个回答

12

应该使用ServiceController类来控制服务。

表示一个Windows服务,允许您连接到正在运行或停止的服务、操纵它或获取有关它的信息。

您可以使用这个类来启动、停止和与服务进行通信。


4
通常,服务没有窗口(更不用说消息泵)来接收窗口消息。
如果错误确实只发生在关闭时(而不仅仅是停止服务),那么可能有些东西依赖于即将消失的资源,这些资源没有被优雅地处理(在这种情况下,修复错误的方法可能是正确设置服务依赖关系)。您尝试使用远程调试工具在关闭之前附加到进程了吗?
值得调查一下是否可以在不关闭的情况下出现问题,也许只是通过服务控制管理器停止服务(无需以编程方式执行此操作,因为这是一种调试场景),在这种情况下,您可以在服务中断点OnStop()(我假设使用C#)并观察会发生什么。

我有一种感觉,依赖关系在关闭时没有帮助...但我不确定。 - Rory

3

查看如何在调试时模拟Windows关机的答案。

服务可以订阅名为OnShutdown的“事件”,因此问题可能出现在该代码中。如果代码是.net的,您可以对其进行子类化,以便调用受保护的OnShutdown方法进行调试。但问题也可能是其他人建议的那样,即服务期望可用的资源已被关闭。

此外,如果该服务是在.net 2.0中编写的,请注意在工作站关闭时不会自动调用Stop()命令!这非常令人惊讶,并且已经在.net 3.5中修复,但如果您使用的是.net 2.0,则需要在OnShutdown()内自己调用Stop()。


2
如果您有窗口的hwnd,您可以向其发送消息。唯一的限制是您不能发送包含指针的消息,例如设置窗口文本。
只需使用hwnd的值和要发送的消息调用PostMessage()即可。
要查找hwnd,可以使用Spy++工具。
我不确定如何将所有这些与Windows服务连接起来,因为Windows服务没有窗口。

1
我建议导入并定义以下内容:
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool PostMessage(IntPtr handleWnd, UInt32 Msg, Int32 wParam, Int32 lParam);

const int WM_ENDSESSION = 0x0016,
          WM_TRUE = 0x1,
          WM_FALSE = 0x0;

然后通过0x1或0x0发送wParam消息,分别表示真或假。

因此,在您的代码中,您将使用:

PostMessage(HandleToSendTo, WM_ENDSESSION, WM_TRUE, 0);

HandleToSendTo是您想要发送消息的窗口的窗口句柄。

编辑
如果您不知道窗口句柄,但知道其标题或名称,则可以使用以下方法获取:

[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        public static extern int FindWindowEx(int hwndParent, int hwndEnfant, int lpClasse, string lpTitre);

更多信息可以在这个问题中找到。

或者

我不知道这是否是一个类似的句柄,我怀疑它不是,但如果是的话,有人可以告诉我,但你可以获得一个进程句柄,这意味着你可以使用Process.GetProcessesByName("MyAppName");来获取进程,尽管不要依赖这一点,因为我认为它不会得到你想要的句柄。只是一个建议。


0
我不知道这是否是一个类似的句柄,我怀疑不是,但如果是的话,有人可以告诉我一下。你可以获取进程句柄,这意味着你可以使用Process.GetProcessesByName("MyAppName");获取进程,虽然不要依赖于此,因为我认为它不会得到你想要的句柄。这只是一个建议。
实际上,这种方法是可行的...你只需要访问进程对象的'MainWindowHandle'属性。例如...
Process myProcess;
Int handle;
myProcess = Process.GetProcessesByName("MyAppName");
handle = myProcess.MainWindowHandle;

0

我认为没有工具可以发送任意消息,因为每个消息都可以有任意的LPARAM和WPARAM值。

但是,围绕Windows消息最有用的工具是Spy++。Spy++随Visual Studio一起提供,它可以帮助您查看发送的消息、窗口层次结构等等。

您可以使用SendMessage Win32 API通过C#发送消息。您可以使用类似FindWindowFindWindowEx的Win32 API获取它所需的窗口句柄。

编辑(以匹配问题的编辑):服务会在关闭时自动停止。因此,要修复您的错误,听起来您需要修改服务本身的代码以正确关闭。

编辑2:或者如果您想停止服务,您应该使用Win32 API ControlService,传递SERVICE_CONTROL_STOP0x00000001


但是我需要向作为Windows服务运行的进程发送消息?在这种情况下,FindWindow如何工作? - softwarematter
2
该服务首先必须拥有一个窗口或消息队列,才能够发送消息。而且,根据Vista的设置,您可能无法将消息发送到在不同会话中运行的服务。如果您控制该服务,则更好的选择是使用管道。 - Brian R. Bondy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接