Question

Je travaille sur un client IRC. Je l'ai frappé un accroc majeurs qui, jusqu'à pas que je suis en mesure de travailler autour. Je vais vous montrer le code ci-dessous. Qu'est-ce qui je vais avoir un problème avec crée des fenêtres enfant MDI dans les gestionnaires d'événements de idIRC.

Par exemple, si je veux créer une nouvelle forme de canal (FrmChannel), je peux y arriver facilement en l'appelant est de créer la procédure quand je surprends la commande « / join ».

Cependant, si je veux le faire de la bonne façon, et d'attendre jusqu'à ce que je l'ai fait rejoint le canal, et recevoir une confirmation de ce à partir du serveur (en le manipulant dans le gestionnaire d'événements onjoin) alors mon appel à ma forme procédure de création provoque l'application à accrocher.

La même chose vaut pour les fenêtres d'état. Par exemple, si je mets ma fenêtre d'état d'appel de procédure de création d'un événement onclick TButton, très bien. formulaire enfant créé. Cependant, si je tente la même chose quand je reçois effectivement un message privé, en vérifiant le gestionnaire d'événements ... application se bloque, aucune exception, ni MDI enfant.

Voici le code approprié (pour le bien de résoudre ce que je vais faire face à la fenêtre de requête uniquement).

Tout d'abord, la création effective MDI enfant va comme ceci. J'ai un TComponentList ici pour gérer une liste de cette catégorie de la forme (au cas où vous vous demandez). Il y a quelques autres choses ici qui gardent la trace de la forme aussi, mais les commentant n'empêche pas le coup (je l'ai essayé).

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;

Voici le gestionnaire d'événements pour mon composant 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;

Je l'ai essayé en commentant les différentes parties du code pour essayer de traquer la cause de la pendaison. Le blocage est causé par l'enfant: = TFrmMessage.Create (Application); appeler spécifiquement. Ce qui donne?

J'ai essayé la mise en œuvre des fils pour voir si cela pourrait être un problème. Si c'est ce que vous pensez que le problème est, je vais avoir besoin d'aide avec mon filetage, car apparemment que le code compile, j'appelle encore quelque chose de mal (parce que même ma version filetée se bloque).

Merci d'avance.

Était-ce utile?

La solution

Comme je vous l'ai dit dans alt .comp.lang.borland-delphi plus tôt aujourd'hui , le problème est que Indy exécute ses gestionnaires d'événements dans le même thread qui fait les appels de socket de blocage, ce qui est pas le même fil que votre interface graphique. Toutes les opérations de l'interface graphique doit avoir lieu dans le même fil, mais vous créez une nouvelle fenêtre dans le thread socket.

Pour le résoudre, votre gestionnaire d'événements devrait afficher une notification au thread principal, que le thread principal se chargera de manière asynchrone à chaque fois qu'il se passe au contrôle suivant pour les messages.

Si vous avez une assez récente version Delphi, vous pouvez essayer TThread.Queue méthode , qui fonctionne un peu comme Synchronize , à l'exception le thread appelant ne bloque pas attendre le thread principal pour exécuter la méthode donnée. Ils ont tous deux les mêmes limites en ce qui concerne leurs paramètres de la méthode, bien que; ils acceptent seulement une méthode zéro paramètre. Cela le rend lourd à transférer des informations supplémentaires pour la méthode à utiliser quand il est finalement appelé. Il est particulièrement mauvais pour méthodes puisque toutes les données supplémentaires que vous fournissez pour eux doit rester intacte aussi longtemps que nécessaire pour le thread principal pour l'exécuter; le thread appelant a besoin pour vous assurer qu'il ne remplace pas les données supplémentaires avant que la méthode mis en attente est appelée.

Un meilleur plan est probablement juste après un message à une fenêtre désignée du thread principal. Application.MainForm est une cible tentante, mais les formes Delphi sont susceptibles d'être re -Création sans préavis, quelle que soit poignée de fenêtre vos autres threads utilisent peut-être pas valide au moment où ils tentent de poster un message. Et la lecture de la propriété MainForm.Handle sur la demande n'est pas sûr, non plus, car si la forme n'a pas de poignée à l'époque, il se crée dans le contexte de thread socket, ce qui entraînera toutes sortes de problèmes plus tard. Au lieu de cela, ont le thread principal crée une nouvelle fenêtre dédiée pour recevoir des messages de fil avec AllocateHWnd .

Une fois que vous avez une cible pour les messages d'aller, vous pouvez organiser des discussions et d'afficher les recevoir. Définir une valeur de message et les poster avec PostMessage.

const
  am_NewQuery = wm_App + 1;

PostMessage(TargetHandle, am_NewQuery, ...);

Pour envoyer les données supplémentaires le destinataire devra gérer pleinement l'événement, les messages ont deux paramètres. Si vous avez seulement besoin de deux éléments d'information, vous pouvez transmettre vos données directement dans les paramètres. Si les messages ont besoin de plus d'informations, cependant, vous devrez définir un enregistrement pour le tenir tout. Il pourrait ressembler à ceci:

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

Préparer et poster le message comme ceci:

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;

Notez que l'appelant alloue un nouveau pointeur d'enregistrement, mais il ne libère pas. Il va se libérer par le destinataire.

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;

Je l'ai fait deux autres changements à votre code. La première est que j'ai fait le constructeur de formulaire enfant accepte les deux informations dont il a besoin pour se créer correctement. Si la forme veut sa légende pour être le surnom, puis juste dire le surnom et laisser le formulaire faire ce qu'il doit avec cette information.

Autres conseils

Il a été un moment que je programmé en Delphi et lutté contre des problèmes similaires ...

En Java, les notifications d'information socket arrive sur un très différent fil de celui qui maintient l'interface graphique, et vous êtes pratiquement interdit d'apporter des modifications à l'interface graphique de l'extérieur du fil de l'interface graphique (mais vous avez donné des mécanismes pour demander légalement le fil de GUI pour faire le mod). Dans Delphi, tous les événements proviennent de la même boucle d'événements, mais quand même ... j'obtenir un sentiment nauséeux demandant une mise à jour majeure de l'interface graphique comme une fenêtre ouverte sur la base d'un événement de prise.

Ce que je voudrais essayer de faire est d'obtenir l'événement comm laisser une notification sur une file d'attente ou quelque chose, et d'obtenir le fil GUI pour traiter que dans le gestionnaire d'onIdle ou quelque chose comme ça.

Ceci est un coup de poignard dans le noir, cependant. Prenez ma recommandation avec beaucoup de sel!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top