服务应用程序中的PostMessage [英] PostMessage in service applications

查看:134
本文介绍了服务应用程序中的PostMessage的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

There is a problem I am unable to solve. I created two service applications in Delphi and tried to post messages within them. Of course, there are no windows in such applications and PostMessage needs a window handle parameter to send a message.

因此,我使用AllocateHWnd(MyMethod:TWndMethod)函数创建了一个窗口句柄,并将要在以下情况下调用的过程作为 MyMethod参数传递收到消息。如果它是窗口应用程序,则使用AllocateHWnd方法返回的句柄调用PostMessage()肯定会发送一条消息,然后该消息将被 MyMethod过程接收。

Therefore, I created a window handle using the AllocateHWnd(MyMethod: TWndMethod) function and passed, as the 'MyMethod' parameter, a procedure I want to be called when a message is received. If it was a windowed application, PostMessage() called using the handle returned by the AllocateHWnd method would certainly send a message that would then be received by the 'MyMethod' procedure.

但是,在我的服务应用程序中情况却有所不同。我不明白为什么,但是在其中一个中,以这种方式发布消息可以正常工作,而在第二个中,则不能这样做(根本不接收消息)。仅当服务停止时,我才注意到 MyMethod收到了两条消息:WM_DESTROY和WM_NCDESTROY。此过程从未接收过我使用PostMessage发送的消息。另一方面,第一个服务始终会收到我发送的所有邮件。

The situation, however, is different in my service applications. I do not understand why, but in one of them posting messages this way works fine, whereas in the second one it does not (the messages are not received at all). Only when the service is being stopped do I notice that two messages are received by 'MyMethod': WM_DESTROY and WM_NCDESTROY. The messages I send using PostMessage are never received by this procedure. On the other hand, the first service always receives all messages I send.

能否请您给我一个提示,以帮助我找到第二个服务未接收到的原因我的留言?我不知道它们可以以什么方式不同。我检查了服务的设置,它们似乎是相同的。为什么然后其中一个可以正常工作而第二个却不能正常工作(就发送消息而言)?

Could you please give me a clue that would help me find the reason of the second service not receiving my messages? I do not know in what way they can differ. I checked the settings of the services and they seem to be identical. Why then one of them works fine and the second one does not (as far as sending messages is concerned)?

感谢您的任何建议。
Mariusz。

Thanks for any advice. Mariusz.

推荐答案

如果没有更多信息,将很难帮助您调试它,尤其是为什么它可以在一个调试器中运行服务,但没有其他。但是:

Without more information it will be difficult to help you debug this, especially why it works in one service but not in the other. However:

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

Instead of trying to fix the problem in your code you might want to remove the windows altogether, and use PostThreadMessage() instead of PostMessage(). For the posting of messages to work correctly you need a message loop, but not necessarily receiving windows.

编辑:我正在尝试回复一口气获得所有答案。

I'm trying to reply to all your answers in one go.

首先-如果您想让生活变得轻松,则应该真正查看 OmniThreadLibrary 通过 gabr 。我不知道它是否可以在Windows服务应用程序中运行,甚至不知道是否已经尝试过。您可以在论坛中提问。但是,它具有许多强大的功能,值得研究,即使只是为了学习效果。

First - if you want to make your life easy you should really check out OmniThreadLibrary by gabr. I don't know whether it does work in a Windows service application, I don't even know whether that has been tried yet. You could ask in the forum. It has however a lot of great features and is worth looking into, if only for the learning effect.

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

But of course you can also program this for yourself, and you will have to for Delphi versions prior to Delphi 2007. I will simply add some snippets from our internal library, which has evolved over the years and works in several dozen programs. I don't claim it to be bug-free though. You can compare it with your code, and if anything sticks out, feel free to ask and I'll try to clarify.

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

This is the simplified Execute() method of the worker thread base class:

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;

重要的是不要让异常得到处理,因此在所有内容周围都有一个顶级异常处理程序。对异常的处理取决于您的选择,并且取决于应用程序,但是必须捕获所有异常,否则应用程序将被终止。在服务中,您唯一的选择可能是记录它们。

It is important to not let exceptions get unhandled, so there is a top-level exception handler around everything. What you do with the exception is your choice and depends on the application, but all exceptions have to be caught, otherwise the application will get terminated. In a service your only option is probably to log them.

有一种特殊的方法可以启动线程关闭,因为当线程位于内部时,需要将其唤醒 GetMessage()

There is a special method to initiate thread shutdown, because the thread needs to be woken up when it is inside of 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接口时。这就是为什么使用以下代码代替所有简单的 WaitFor()来释放所有正在运行的线程的原因:

Posting WM_QUIT will cause the message loop to exit. There is however the problem that code in descendant classes could rely on Windows messages being properly handled during shutdown of the thread, especially when COM interfaces are used. That's why instead of a simple WaitFor() the following code is used to free all running threads:

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()方法中,因为所有线程都需要在子代控制器类的析构函数开始释放线程可能使用的任何对象之前将其释放。

This is in the overridden BeforeDestruction() method because all threads need to be freed before the destructor of the descendant controller class begins to free any objects the threads might use.

这篇关于服务应用程序中的PostMessage的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆