gpt4 book ai didi

delphi - 服务应用程序中的 PostMessage

转载 作者:行者123 更新时间:2023-12-03 19:01:47 25 4
gpt4 key购买 nike

有一个问题我无法解决。我在 Delphi 中创建了两个服务应用程序并尝试在其中发布消息。当然,此类应用程序中没有窗口,PostMessage 需要一个窗口句柄参数来发送消息。

因此,我使用 AllocateHWnd(MyMethod: TWndMethod) 函数创建了一个窗口句柄,并作为“MyMethod”参数传递了一个我希望在收到消息时调用的过程。如果它是一个窗口应用程序,使用 AllocateHWnd 方法返回的句柄调用 PostMessage() 肯定会发送一条消息,然后由“MyMethod”过程接收。

但是,在我的服务应用程序中,情况有所不同。我不明白为什么,但是其中一个以这种方式发布消息可以正常工作,而在第二个中则不行(根本没有收到消息)。只有在服务停止时,我才注意到“MyMethod”收到两条消息:WM_DESTROY 和 WM_NCDESTROY。此过程永远不会收到我使用 PostMessage 发送的消息。另一方面,第一个服务总是接收我发送的所有消息。

您能否给我一个线索,帮助我找到第二个服务没有收到我的消息的原因?我不知道他们有什么不同。我检查了服务的设置,它们似乎是相同的。为什么其中一个工作正常而第二个不能(就发送消息而言)?

感谢您的任何建议。
马吕斯。

最佳答案

如果没有更多信息,将很难帮助您进行调试,尤其是为什么它在一个服务中工作而在另一个服务中不起作用。然而:

与其尝试修复代码中的问题,不如完全删除窗口,并使用 PostThreadMessage()而不是 PostMessage()。为了使消息的发布正常工作,您需要一个消息循环,但不一定需要接收窗口。

编辑:我试图一口气回复你所有的答案。

首先 - 如果你想让你的生活更轻松,你真的应该看看 OmniThreadLibrarygabr .我不知道它是否在 Windows 服务应用程序中工作,我什至不知道是否已经尝试过。你可以在论坛里问。然而,它有很多很棒的功能,值得研究,如果只是为了学习效果。

当然,您也可以自己编写程序,而且您必须使用 Delphi 2007 之前的 Delphi 版本。我将简单地从我们的内部库中添加一些片段,这些库已经发展了多年,并在几十个程序中工作。我不声称它是没有错误的。您可以将其与您的代码进行比较,如果有任何问题,请随时询问,我会尽力澄清。

这是工作线程基类的简化 Execute() 方法:

procedure TCustomTestThread.Execute;
var
Msg: TMsg;
begin
try
while not Terminated do begin
if (integer(GetMessage(Msg, HWND(0), 0, 0)) = -1) or Terminated then
break;
TranslateMessage(Msg);
DispatchMessage(Msg);

if Msg.Message = WM_USER then begin
// handle differently according to wParam and lParam
// ...
end;
end;
except
on E: Exception do begin
...
end;
end;
end;

重要的是不要让异常未经处理,因此所有事物都有一个顶级异常处理程序。你对异常做什么是你的选择,取决于应用程序,但所有异常都必须被捕获,否则应用程序将被终止。在服务中,您唯一的选择可能是记录它们。

有一个特殊的方法来启动线程关闭,因为线程需要在 GetMessage() 内部时被唤醒:
procedure TCustomTestThread.Shutdown;
begin
Terminate;
Cancel; // internal method dealing with worker objects used in thread
DoSendMessage(WM_QUIT);
end;

procedure TCustomTestThread.DoSendMessage(AMessage: Cardinal;
AWParam: integer = 0; ALParam: integer = 0);
begin
PostThreadMessage(ThreadID, AMessage, AWParam, ALParam);
end;

发布 WM_QUIT 将导致消息循环退出。然而,后代类中的代码可能依赖于在线程关闭期间正确处理 Windows 消息的问题,尤其是在使用 COM 接口(interface)时。这就是为什么使用以下代码而不是简单的 WaitFor() 来释放所有正在运行的线程的原因:
procedure TCustomTestController.BeforeDestruction;
var
i: integer;
ThreadHandle: THandle;
WaitRes: LongWord;
Msg: TMsg;
begin
inherited;
for i := Low(fPositionThreads) to High(fPositionThreads) do begin
if fPositionThreads[i] <> nil then try
ThreadHandle := fPositionThreads[i].Handle;
fPositionThreads[i].Shutdown;
while TRUE do begin
WaitRes := MsgWaitForMultipleObjects(1, ThreadHandle, FALSE, 30000,
QS_POSTMESSAGE or QS_SENDMESSAGE);
if WaitRes = WAIT_OBJECT_0 then begin
FreeAndNil(fPositionThreads[i]);
break;
end;
if WaitRes = WAIT_TIMEOUT then
break;

while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
except
on E: Exception do
// ...
end;
fPositionThreads[i] := nil;
end;
end;

这是在重写的 BeforeDestruction() 方法中,因为在后代 Controller 类的析构函数开始释放线程可能使用的任何对象之前,需要释放所有线程。

关于delphi - 服务应用程序中的 PostMessage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/582115/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com