Frage

Ich arbeite an einem IRC-Client. Ich habe einen Majors snag getroffen, die bis vor nicht ich habe in der Lage zu arbeiten, um. Ich zeige Code unten. Was ist ich mit ist die Schaffung untergeordnete MDI-Fenster innerhalb der Event-Handler von idIRC ein Problem.

Zum Beispiel, wenn ich eine neue Kanalform (FrmChannel) erstellt werden soll, kann ich dies erreichen einfach durch den Aufruf ist es Prozedur erstellen, wenn ich den Fang ‚/ join‘ Befehls.

Allerdings, wenn ich will es den richtigen Weg zu tun, und warten, bis ich tatsächlich den Kanal angeschlossen habe, und erhalte Bestätigung dieses von dem Server (indem sie sie in dem OnJoin Event-Handler-Handling), dann meinem Aufruf an meine Form Erzeugungs-Prozedur bewirkt, dass die Anwendung hängen.

Das gleiche gilt für Statusfenster. Zum Beispiel, wenn ich mein Statusfenster Erstellung Prozeduraufruf auf einem Onclick-Ereignisse des TButton setzen, in Ordnung. Child-Formular erstellt. Allerdings, wenn ich versuche, die gleiche Sache, wenn ich tatsächlich eine private Nachricht erhalten, indem Sie die Event-Handler ... Anwendung hängt, keine Ausnahme, und kein MDI Kind.

Hier ist der relevante Code (aus Gründen diese zu lösen ich mit dem Abfragefenster nur beschäftigen werden).

Als erstes geht die eigentliche MDI Child Schöpfung wie diese. Ich habe eine TComponentList hier eine Liste dieser Klasse von Form zu verwalten (falls Sie sich fragen). Es gibt einige andere Dinge in hier, dass verfolgen die Form als auch, sie jedoch Auskommen nicht daran hindert, den Dreh raus (ich habe versucht).

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;

Hier sind die Event-Handler für meine IRC-Komponente:

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;

Ich habe versucht, verschiedene Teile des Codes zu kommentieren, um zu versuchen, die Ursache des Behangs aufzuspüren. Der Hang wird durch das Kind verursacht: = TFrmMessage.Create (Anwendung); rufen speziell. Was soll das?

Ich habe versucht, Fäden Umsetzung zu sehen, ob das ein Problem sein könnte. Wenn das ist, was denken Sie ist das Problem, ich werde Hilfe mit meinem Einfädeln benötigen, weil anscheinend obwohl der Code kompiliert wird, bin ich immer noch etwas nicht in Ordnung rufen (weil auch meine Gewindeausführung hängt).

Vielen Dank im Voraus.

War es hilfreich?

Lösung

Wie ich sagte Ihnen in alt .comp.lang.borland-delphi heute früher , das Problem ist, dass Indy seine Event-Handler im selben Thread ausgeführt wird, die die blockierenden Socket-Aufrufe der Fall ist, die nicht die gleichen Thread wie Ihr GUI ist. Alle GUI-Operationen in der gleichen Faden müssen, aber Sie werden ein neues Fenster in die Buchse Gewinde zu schaffen.

Um es zu lösen, der Ereignishandler eine Benachrichtigung an den Haupt-Thread posten sollte, die der Haupt-Thread asynchron wird handhaben, wenn es für Nachrichten des nächsten Prüfung geschieht.

Wenn Sie eine aktuelle-genug Delphi-Version haben, könnten Sie versuchen, die TThread.Queue Methode, die viel arbeitet wie Synchronize , außer der aufrufende Thread nicht blockiert für den Haupt-Thread wartet auf die gegebene Methode auszuführen. Sie haben beide die gleiche Einschränkung hinsichtlich ihrer Verfahrensparameter, obwohl; sie nur eine Null-Parameter-Methode akzeptieren. Das macht es umständlich für das Verfahren zur Verwendung zusätzliche Informationen zu übertragen, wenn es schließlich genannt. Es ist besonders schlecht für Queued Methoden, da alles, was zusätzliche Daten, die Sie für sie sorgen müssen intakt bleiben, solange es für den Hauptthread dauert es zu laufen; die aufrufende Thread Bedürfnisse sicherstellen, dass es nicht die zusätzlichen Daten nicht überschrieben werden, bevor die Warteschlangen Methode aufgerufen wird.

Ein besserer Plan ist wahrscheinlich nur eine Nachricht zu einem gewissen bezeichneten Fenstern des Haupt-Thread zu posten. Application.MainForm ist verlockend Ziel, aber Delphi Formen haften Re sein -created ohne vorherige Ankündigung, so was Fenstergriff Ihre andere Threads nutzen, möglicherweise nicht zu dem Zeitpunkt gültig, versuchen sie, eine Nachricht zu schreiben. Und auf Nachfrage die MainForm.Handle Eigentum Lesen ist nicht sicher, entweder, da, wenn die Form keinen Griff zum Zeitpunkt hat, wird es in den Kontext des Socket-Thread erstellt bekommen, die später alle möglichen Probleme verursachen wird. Stattdessen haben den Haupt-Thread mit einem neuen eigenen Fenster zur Aufnahme Thread Nachrichten erstellen AllocateHWnd .

Sobald Sie ein Ziel für die Nachrichten zu gehen, können Sie arrangieren für Fäden, sie zu schreiben und zu empfangen. Definieren Sie einen Nachrichtenwert und stellen Sie sie mit PostMessage.

const
  am_NewQuery = wm_App + 1;

PostMessage(TargetHandle, am_NewQuery, ...);

Um die zusätzlichen Daten der Empfänger vollständig müssen zu senden, um das Ereignis verarbeiten, Nachrichten haben zwei Parameter. Wenn Sie nur zwei Informationen benötigen, dann können Sie Ihre Daten direkt in diesen Parameter übergeben. Wenn die Nachrichten mehr Informationen benötigen, aber dann müssen Sie einen Datensatz definieren, um sie alle zu halten. Es könnte wie folgt aussehen:

type
  PNewQuery = ^TNewQuery;
  TNewQuery = record
    Host: string;
    FromNickname: string;
  end;

Erstellen und veröffentlichen die Nachricht wie folgt aus:

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;

Beachten Sie, dass der Anrufer allokiert einen neuen Datensatzzeiger, aber es es nicht befreien. Es wird vom Empfänger befreit erhalten.

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;

Ich habe ein paar andere Änderungen an Ihrem Code. Einer ist, dass es das Kind Form Konstruktor akzeptieren gemacht, um die beiden Informationen muss es selbst richtig erstellen. Wenn die Form seine Beschriftung will den Spitznamen sein, dann ist es nur die Spitznamen sagen und die Form tun lassen, was es mit dieser Information muss.

Andere Tipps

Es ist ein gewesen Weile her, seit ich in Delphi programmiert und kämpfte ähnliche Probleme ...

In Java Socket-Info Benachrichtigungen passieren auf einem ganz anderen Thread aus dem derjenige, der die GUI unterhält, und Sie sind praktisch von Änderungen an der GUI von außerhalb des GUI-Thread verboten (aber Sie sind Mechanismen zur legal gegeben fragen der GUI-Thread die mod) zu machen. In Delphi, wird alle Ereignisse aus der gleichen Ereignisschleife kommen, aber immer noch ... würde ich ein mulmiges Gefühl für ein großes GUI Update gefragt werden, wie ein Fenster basierend auf einem Socket Ereignisse öffnen.

Was würde ich versuchen zu tun, um das comm Ereignis bekomme eine Benachrichtigung auf einer Warteschlange oder etwas zu verlassen, und bekam den GUI-Thread zu verarbeiten, die in dem onIdle Handler oder so ähnlich.

Dies ist ein Stich in der Dunkelheit, though. Nehmen Sie meine Empfehlung mit vielen Salz!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top