Domanda

Sto lavorando su un client IRC. Ho colpito un intoppo major, che, fino a quando non sono stato in grado di risolvere. Ti faccio vedere codice di seguito. Cosa c'è Sto avendo un problema con è la creazione di finestre figlio MDI all'interno dei gestori di eventi di idIRC.

Per esempio, se voglio creare una nuova forma di canale (FrmChannel), posso raggiungere questo obiettivo facilmente chiamando è creare procedura quando prendo il '/ unirsi al' comando.

Tuttavia, se voglio farlo nel modo giusto, e attendere che in realtà ho unito il canale, e ricevere la conferma di questo dal server (gestendo nel gestore di eventi ONJOIN) quindi la mia chiamata alla mia forma procedura di creazione causa l'applicazione per bloccarsi.

Lo stesso vale per le finestre di stato. Per esempio, se ho messo la mia finestra di stato procedura di creazione chiamata in evento onclick di un TButton, bene. form figlio creato. Tuttavia, se provo la stessa cosa quando ho effettivamente ricevo un messaggio privato, controllando il gestore di eventi ... applicazione si blocca, non fa eccezione, e nessun figlio MDI.

Ecco il codice corrispondente (per il bene di risolvere questo mi occuperò con la finestra di query solo).

In primo luogo, la creazione effettiva figlio MDI va come questa. Ho un TComponentList in qui per gestire un elenco di questa classe di modulo (nel caso in cui vi state chiedendo). Ci sono alcune altre cose qui dentro che tengono traccia della forma e, anche se commentandoli non impedisce il blocco (ho provato).

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;

Ecco il gestore di eventi per la mia componente 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;

Ho provato a commentare le varie parti del codice per cercare di rintracciare la causa del pensile. Il blocco è causato dal bambino: = TFrmMessage.Create (Application); chiamare in particolare. Ciò che dà?

Ho provato le discussioni attuazione per vedere se questo potrebbe essere un problema. Se questo è quello che stai pensando che il problema è, avrò bisogno di aiuto con il mio filettatura perché a quanto pare se il codice è la compilazione, sto ancora chiamando qualcosa di sbagliato (perché anche la mia versione filettata si blocca).

Grazie in anticipo.

È stato utile?

Soluzione

Come ti ho detto in alt .comp.lang.borland-Delphi prima di oggi , il problema è che Indy gestisce i suoi gestori di eventi nello stesso filo che fa le chiamate socket di blocco, che non è lo stesso filo come interfaccia grafica. Tutte le operazioni di GUI devono avvenire nello stesso thread, ma si sta creando una nuova finestra nel thread presa.

Per risolverlo, il gestore di eventi deve inviare una notifica al thread principale, che il filo conduttore in grado di gestire in modo asincrono ogni volta che succede al controllo successivo per i messaggi.

Se si dispone di un recente sufficientemente versione di Delphi, si potrebbe provare la TThread.Queue metodo , che funziona un po 'come Synchronize , ad eccezione il thread chiamante non blocca in attesa del filo principale per eseguire il metodo indicato. Entrambi hanno la stessa limitazione per quanto riguarda i loro parametri di metodo, però; si accettano solo un metodo zero parametro. Che lo rende ingombrante per trasferire le informazioni extra per il metodo da utilizzare quando è finalmente chiamato. E 'particolarmente grave per in coda metodi poiché qualsiasi dato aggiuntivo da Lei forniti per loro deve rimanere intatto per tutto il tempo necessario per il thread principale per eseguirlo; il thread chiamante ha bisogno per assicurarsi che non sovrascrive i dati aggiuntivi prima che il metodo viene chiamato in coda.

Un piano migliore è probabilmente quello di semplicemente inviare un messaggio a qualche finestra designata del thread principale. Application.MainForm è un bersaglio allettante, ma forme di Delphi sono suscettibili di essere ri -Creato senza preavviso, in modo da qualunque finestra di gestire i tuoi altri thread utilizzano potrebbe non essere valida nel momento in cui cercano di lasciare un messaggio. E leggendo la proprietà MainForm.Handle su richiesta non è sicuro, sia, dal momento che se la forma non ha maniglia, al momento, otterrà creato nel contesto del thread socket, che farà sì che tutti i tipi di problemi in seguito. Invece, hanno il thread principale creare una nuova finestra dedicata per la ricezione di messaggi di filo con AllocateHWnd .

Una volta che hai un obiettivo per i messaggi di andare a, si può organizzare per le discussioni di inviare e li ricevono. Definire un valore di messaggio e di inviare con PostMessage.

const
  am_NewQuery = wm_App + 1;

PostMessage(TargetHandle, am_NewQuery, ...);

Per inviare i dati aggiuntivi il destinatario avrà bisogno di gestire completamente l'evento, i messaggi hanno due parametri. Se è necessario solo due pezzi di informazioni, allora si può passare i dati direttamente in quei parametri. Se i messaggi hanno bisogno di più informazioni, però, allora avrete bisogno di definire un record per contenere tutto. Potrebbe essere simile a questa:

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

Preparare e inviare il messaggio come questo:

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;

Si noti che il chiamante assegna un nuovo puntatore del record, ma non libera esso. Si otterrà liberato dal destinatario.

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;

Ho fatto un paio di altre modifiche al codice. Uno è che ho fatto il costruttore della form figlio accettare i due pezzi di informazioni di cui ha bisogno per creare in sé in modo corretto. Se la forma vuole la sua voce di essere il soprannome, poi basta dire che il soprannome e lasciare che il modulo di fare tutto ciò che ha bisogno di tali informazioni.

Altri suggerimenti

E 'stato un po' da quando ho programmato in Delphi e battagliato problemi simili ...

In Java, informazioni presa notifiche accada su un filo molto diversa da quella che mantiene l'interfaccia grafica, e il gioco è praticamente proibito di apportare modifiche alla GUI da fuori discussione GUI (ma si è dato meccanismi per chiedere legalmente il filo GUI per rendere il mod). In Delphi, tutti gli eventi sono provenienti dallo stesso ciclo di eventi, ma ancora ... mi piacerebbe avere una sensazione di nausea per chiedere un importante aggiornamento GUI come una finestra aperta sulla base di un evento di presa.

Quello che vorrei provare a fare è ottenere l'evento comm di lasciare una notifica su una coda o qualcosa del genere, e ottenere il thread GUI per elaborare che nel gestore onIdle o qualcosa del genere.

Questo è un salto nel buio, però. Prendete il mio consiglio con un sacco di sale!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top