1

在我的 Delphi 项目中,我派生了一个线程类 TMyThread,并按照论坛的建议使用 AllocateHWnd 创建窗口句柄。在 TMyThread 对象中,我调用 SendMessage 将消息发送到窗口句柄。

当发送的消息量较小时,应用程序运行良好。但是,当消息量很大时,应用程序会死锁并失去响应。我认为可能是 LogWndProc 中的消息队列已满,只有处理消息的代码,但没有从队列中删除消息的代码,这可能导致所有已处理的消息仍然存在于队列中并且队列已满. 那是对的吗?

代码附在下面:

var
hLogWnd: HWND = 0;

procedure TForm1.FormCreate(Sender: TObject);
begin
hLogWnd := AllocateHWnd(LogWndProc);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
if hLogWnd <> 0 then
DeallocateHWnd(hLogWnd);
end;

procedure TForm1.LogWndProc(var Message: TMessage);
var
S: PString;
begin
if Message.Msg = WM_UPDATEDATA then
begin
S := PString(msg.LParam);
try
List1.Items.Add(S^);
finally
Dispose(S);
end;
end else
Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam,
Message.LParam);
end;

procedure TMyThread.SendLog(I: Integer);
var
Log: PString;
begin
New(Log);
Log^ := 'Log: current stag is ' + IntToStr(I);
SendMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(Log));
Dispose(Log);
end;
4

1 回答 1

6

您正在处理分配的字符串两次。充其量,您将在SendMessage()退出后在您的工作线程中获得一个异常,如果您没有捕获该异常,则终止您的线程。更糟糕的是,您可能不会遇到异常,但您会浪费内存,使您的应用程序处于不良状态,因此可能会发生各种随机事件。您只需处理分配的字符串一次。

您不负责从队列中删除已发送的消息,因为SendMessage()没有将消息放入队列。但是,它确实需要接收线程为新消息抽出队列,即使队列中没有新消息,以便发送跨越线程边界的已发送消息,就像您的消息一样。如果SendMessage()是阻塞,那么您的主线程没有在您未显示的代码中正确地抽取队列,例如,如果您有其他代码阻止了主消息循环运行。

至于您确实显示的代码,我建议进行以下更改:

procedure TForm1.LogWndProc(var Message: TMessage);
begin
  if Message.Msg = WM_UPDATEDATA then
    List1.Items.Add(PString(Message.LParam)^)
  else
    Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam, Message.LParam);
end;

procedure TMyThread.SendLog(I: Integer);
var
  Log: String;
begin
  Log := 'Log: current stag is ' + IntToStr(I);
  SendMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(@Log));
end;

如果使用 ,则不需要动态分配字符串SendMessage(),因为它会阻塞调用线程,直到消息被处理,从而确保字符串保持有效。如果您使用的是PostMessage(),那么您将需要动态分配(并修复您对 的错误使用Dispose()):

procedure TForm1.LogWndProc(var Message: TMessage);
var
  S: PString;
begin
  if Message.Msg = WM_UPDATEDATA then
  begin
    S := PString(msg.LParam);
    try
      List1.Items.Add(S^);
    finally
      Dispose(S);
    end;
  end else
    Message.Result := DefWindowProc(hLogWnd, Message.Msg, Message.WParam, Message.LParam);
end;

procedure TMyThread.SendLog(I: Integer);
var
  Log: PString;
begin
  New(Log);
  Log^ := 'Log: current stag is ' + IntToStr(I);
  if not PostMessage(hLogWnd, WM_UPDATEDATA, 0, LPARAM(Log)) then
    Dispose(Log);
end;
于 2013-10-19T20:22:40.147 回答