(德尔福2009)idIRC,MDI和问题悬((Delphi 2009) idIRC, MDI, a

2019-10-18 07:47发布

我工作的IRC客户端。 我已经打了一个专业钉子其中,直到不,我已经能够解决。 我将在下面显示的代码。 什么是我在与被idIRC的事件处理程序中创建MDI子窗口的问题。

例如,如果我想创建一个新的渠道形式(FrmChannel),我可以很容易地通过调用做到这一点它的创建过程时,我赶上“/加入”命令。

但是,如果我想这样做的正确的方式,等到我实际上已经加入了通道,并从服务器接收此确认(由onjoin事件处理程序处理它),然后我呼吁我的表单创建程序的原因应用程序挂起。

这同样适用于状态窗口。 例如,如果我把我的状态窗口创建过程调用上一个TButton的onclick事件,罚款。 子窗体创建。 不过,如果我尝试同样的事情时,我居然收到私信,通过检查事件处理程序...应用程序挂起,也不例外,并没有MDI子。

下面是相关的代码(解决这个我会只处理查询窗口的缘故)。

首先,实际的MDI子创作是这样的。 我有一个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;

我试着注释掉的代码的各个部分,试图追查挂的原因。 挂起是由儿童引起的:= TFrmMessage.Create(应用程序); 特别呼吁。 是什么赋予了?

我试图执行的线程来看看是否有可能是一个问题。 如果这是你在想什么问题,我需要帮助,我的线程,因为虽然表面上的代码编译,我还在打电话了问题(因为即使我的线程版本挂起)。

提前致谢。

Answer 1:

正如我在alt.comp.lang.borland德尔福今天早些时候告诉你 ,问题是,印中运行,做阻塞套接字调用,这是不是同一个线程的GUI相同的线程的事件处理程序。 所有GUI操作必须发生在同一个线程,但你正在创建的套接字线程的新窗口。

为了解决这个问题,你的事件处理程序应该张贴到主线程,每当它发生的消息下一次检查主线程将异步处理的通知。

如果您有最新的,足够的Delphi版本,你可以试试TThread.Queue方法,它的工作原理很像Synchronize ,除了调用线程不会阻塞等待主线程来运行给定的方法。 他们都有关于他们的方法的参数相同的限制,虽然; 他们只接受一个零参数的方法。 这使得它繁琐的传输额外信息,使用时,它最终调用的方法。 这对排队的方法,因为无论你为他们提供额外的数据必须保持完好,只要它需要主线程来运行它特别坏的; 调用线程需要确保它不会覆盖额外的数据排队方法被调用之前。

一个更好的计划可能是刚刚发布一条消息给主线程的一些指定窗口。 Application.MainForm是一个诱人的目标,但德尔福形式容易,恕不另行通知重新建立,所以无论窗口处理您的其他线程使用在他们尝试发布信息的时间可能是无效的。 和读取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;

我做了几个其他更改您的密码。 其中之一是,我所做的子窗体的构造函数接受两个信息,它需要正确地创建自己。 如果表单希望其标题为昵称,然后就告诉它的昵称,让形式做任何事情需要处理这些信息。



Answer 2:

它已经有一段时间,因为我在Delphi编程和作战类似的问题...

在Java中,插座信息通知发生在从维护GUI中的一个非常不同的线程,你几乎从GUI线程之外在更改GUI(禁止的,但你给机构依法向GUI线程使MOD)。 在Delphi中,所有事件都来自同一事件循环来了,但仍...我会得到一个想吐的感觉,要求像一个窗口的主要GUI更新打开基于套接字事件。

我会尝试做的是获得通讯事件留在队列或某事的通知,并获得了GUI线程来处理,在onIdle处理程序或类似的东西。

这是一个在黑暗中刺伤,虽然。 就拿我有很多盐的推荐!



文章来源: (Delphi 2009) idIRC, MDI, and problems with hanging