gpt4 book ai didi

multithreading - (Delphi 2009) idIRC、MDI 和挂起问题

转载 作者:行者123 更新时间:2023-12-03 19:23:16 27 4
gpt4 key购买 nike

我正在开发一个 IRC 客户端。我遇到了一个专业的障碍,直到没有我能够解决。我将在下面显示代码。我遇到的问题是在 idIRC 的事件处理程序中创建 MDI 子窗口。

例如,如果我想创建一个新的 channel 表单(FrmChannel),我可以通过在捕获“/join”命令时调用它的创建过程来轻松完成此操作。

但是,如果我想以正确的方式进行操作,并等到我真正加入 channel ,并从服务器接收确认(通过在 onjoin 事件处理程序中处理它),那么我对表单创建过程的调用会导致要挂起的应用程序。

状态窗口也是如此。例如,如果我将状态窗口创建过程调用放在 TButton 的 onclick 事件上,那很好。子窗体已创建。但是,如果我在实际收到私有(private)消息时尝试同样的事情,通过检查事件处理程序......应用程序挂起,没有异常,也没有 MDI 子级。

这是相关代码(为了解决这个问题,我将只处理查询窗口)。

首先,实际的 MDI Child 创建过程是这样的。我在这里有一个 TComponentList 来管理此类表单的列表(以防您想知道)。这里还有一些其他的东西也可以跟踪表单,尽管将它们注释掉并不能阻止挂起(我已经尝试过)。

procedure TFrmMain.NewQuery(const Server, MsgFrom: String);
var
Child: TFrmMessage;
TN: TTreeNode;
begin

///
/// Create form, set some data so we can reference it later.
///
///

Child := TFrmMessage.Create(Application);
// QueryManager.Add(Child); //TComponent List -- Used to find the Form Later On

with Child do
begin
MyServer := Server; {What server this PM window is on}
QueryWith := MsgFrom; {nickaname of the other person}
Caption := MsgFrom; {Asthetic}
end;

Child.Echo('*** Conversation with ' + MsgFrom); //Herro World

///
/// The following code is working.
/// I'm pretty sure it's not causing the hangs.
///

TN := GetNodeByText(ChanServTree, Server, True); {Find our parent node}

with ChanServTree.Items.AddChild(TN, MsgFrom) do
begin
Selected := True;
Tag := 2; {TYPE OF QUERY}
Data := Pointer(Integer(Child)); //Pointer to Form we created
end;

end;

这是我的 IRC 组件的事件处理程序:
procedure TFrmMain.IRCPrivateMessage(ASender: TIdContext; const ANicknameFrom,
AHost, ANicknameTo, AMessage: string);
var
CheckVr: String;
aThread: TNQThread;
begin
//DEBUG:
(StatusManager[0] as TFrmStatus).Echo('From: ' + ANickNameFrom + 'AMESSAGE: ' + '''' +AMessage + '''');

///
/// Handle Drone Version Requests!
/// This is REQUIRED on servers like irc.blessed.net - or they won't let you join
/// channels! - It's part of the Registration proccess
///

{The Drones on some server's don't follow specifications, so we need to search
hard for their presence}

CheckVr := AMessage;

StringReplace(CheckVr,' ','',[rfReplaceAll, rfIgnoreCase]);
StringReplace(CheckVr,#1,'',[rfReplaceAll, rfIgnoreCase]);
(StatusManager[0] as TFrmStatus).Echo('Message was: ' + '''' + CheckVr + '''');

if Trim(CheckVr) = 'VERSION' then
begin
IRC.CTCPReply(ANickNameFrom,'VERSION','mIRC v6.01 Khaled Mardam-Bey');
(StatusManager[0] as TFrmStatus).Echo('*** Sent Version Reply to ' + ANickNameFrom);

exit; {Because if we don't, this could mess things up}
end;

///
/// The Following code sends the PM to the appropriate window.
/// If that window does not exist, we will create one first.
///


if Pos('#',Amessage) = 1 then
begin
//Handled Elsewhere
end else {is PM}
begin

if FindQueryFrm(ANickNameTo,IRC.Host) = nil then
begin

NewQuery(IRC.Host, ANickNameFrom);
exit;
end;

end;

// FindChannelFrm(ANickNameTo,IRC.Host).ChannelMessage(ANicknameFrom, AMessage);

end;

我已经尝试注释掉代码的各个部分以试图找出挂起的原因。挂起是由 Child := TFrmMessage.Create(Application); 引起的专门打电话。是什么赋予了?

我已经尝试实现线程,看看这是否可能是一个问题。如果这就是您认为的问题所在,我将需要线程方面的帮助,因为显然尽管代码正在编译,但我仍然调用错误(因为即使我的线程版本挂起)。

提前致谢。

最佳答案

As I told you in alt.comp.lang.borland-delphi earlier today ,问题是 Indy 在执行阻塞套接字调用的同一线程中运行其事件处理程序,这与您的 GUI 不同。所有 GUI 操作都必须在同一个线程中进行,但是您正在套接字线程中创建一个新窗口。

为了解决这个问题,您的事件处理程序应该向主线程发布一个通知,主线程将在下一次检查消息时异步处理该通知。

如果你有足够新的 Delphi 版本,你可以试试 TThread.Queue 方法,很像 Synchronize ,除了调用线程不会阻塞等待主线程运行给定的方法。但是,它们在方法参数方面都有相同的限制;他们只接受零参数方法。这使得传输额外信息以供最终调用时使用的方法变得很麻烦。这对于排队的方法尤其不利,因为您为它们提供的任何额外数据都必须在主线程运行它所需的时间内保持不变;调用线程需要确保在调用排队的方法之前它不会覆盖额外的数据。

一个更好的计划可能是只将消息发布到主线程的某个指定窗口。 Application.MainForm 是一个诱人的目标,但 Delphi 表单可能会在没有通知的情况下重新创建,因此您的其他线程使用的任何窗口句柄在他们尝试发布消息时可能无效。并阅读 MainForm.Handle按需属性也不安全,因为如果表单当时没有句柄,它将在套接字线程的上下文中创建,这将导致以后出现各种问题。相反,让主线程创建一个新的专用窗口,用于接收带有 AllocateHWnd 的线程消息。 .

一旦您有了消息要发送的目标,您就可以安排线程来发布和接收它们。定义消息值并使用 PostMessage 发布它们.

const
am_NewQuery = wm_App + 1;

PostMessage(TargetHandle, am_NewQuery, ...);

要发送接收者完全处理事件所需的额外数据,消息有两个参数。如果您只需要两条信息,那么您可以直接在这些参数中传递您的数据。但是,如果消息需要更多信息,那么您需要定义一个记录来保存所有信息。它可能看起来像这样:
type
PNewQuery = ^TNewQuery;
TNewQuery = record
Host: string;
FromNickname: string;
end;

准备并发布这样的消息:
procedure NewQuery(const Server, MsgFrom: string);
var
Data: PNewQuery;
begin
New(Data);
Data.Host := Server;
Data.FromNickname := MsgFrom;
PostMessage(TargetHandle, am_NewQuery, 0, LParam(Data));
end;

请注意,调用者分配了一个新的记录指针,但并没有释放它。它将被收件人释放。
class procedure TSomeObject.HandleThreadMessage(var Message: TMessage);
var
NewQueryData: PNewQuery;
begin
case Message.Msg of
am_NewQuery: begin
NewQueryData := PNewQuery(Message.LParam);
try
Child := TFrmMessage.Create(NewQueryData.Host, NewQueryData.FromNickname);
TN := GetNodeByText(ChanServTree, NewQueryData.Host, True); // Find parent node
with ChanServTree.Items.AddChild(TN, NewQueryData.FromNickname) do begin
Selected := True;
Tag := 2; // TYPE OF QUERY
Data := Child; // reference to form we created
end;
finally
Dispose(NewQueryData);
end;
end;
else
Message.Result := DefWindowProc(TargetHandle, Message.Msg, Message.WParam, Message.LParam);
end;
end;

我对您的代码进行了其他一些更改。一是我让子表单的构造函数接受了正确创建自身所需的两条信息。如果表单希望它的标题是昵称,那么只需告诉它昵称并让表单对这些信息做任何它需要的事情。

关于multithreading - (Delphi 2009) idIRC、MDI 和挂起问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1734757/

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