Delphi VirtualTreeView使用线程添加根

Delphi VirtualTreeView使用线程添加根,delphi,delphi-7,indy,virtualtreeview,indy-9,Delphi,Delphi 7,Indy,Virtualtreeview,Indy 9,我想使用如下线程将根添加到VirtualTreeView: function AddRoot ( p : TForm1 ) : Integer; stdcall; begin p.VirtualStringTree1.AddChild(NIL); end; var Dummy : DWORD; i : Integer; begin for i := 0 to 2000 do begin CloseHandle(CreateThread(NIL,0, @ADDROOT

我想使用如下线程将根添加到VirtualTreeView:

function AddRoot ( p : TForm1 ) : Integer; stdcall;
begin
 p.VirtualStringTree1.AddChild(NIL);
end;    

var
 Dummy : DWORD;
 i     : Integer;
begin
 for i := 0 to 2000 do begin
  CloseHandle(CreateThread(NIL,0, @ADDROOT, Self,0, Dummy));
 end;
end;
这样做的原因是我想将所有连接从INDY服务器添加到TreeView。Indy的onexecute/onconnect get被称为线程。因此,如果3+个连接同时出现,应用程序将因TreeView而崩溃。如果客户机断开连接,我想删除节点,情况也是如此

我正在使用Delphi7和Indy9

知道怎么解决吗

编辑:

它可以与ListView配合使用(至少更好)

编辑:这是我的完整代码:

服务器:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, ComCtrls, IDSync, IdBaseComponent, IdComponent, IdTCPServer,
 VirtualTrees;

type
 TForm1 = class(TForm)
 IdTCPServer1: TIdTCPServer;
 VirtualStringTree1: TVirtualStringTree;
 procedure FormShow(Sender: TObject);
 procedure IdTCPServer1Connect(AThread: TIdPeerThread);
 procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
private
 { Private declarations }
public
 { Public declarations }
end;

type
 TAddRemoveNodeSync = class(TIdSync)
protected
 procedure DoSynchronize; override;
public
 Node   : PVirtualNode;
 Adding : Boolean;
end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

procedure TAddRemoveNodeSync.DoSynchronize;
begin
 if Adding then
  Node := Form1.VirtualStringTree1.AddChild(nil)
 else
  Form1.VirtualStringTree1.DeleteNode(Node);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
 IDTCPServer1.DefaultPort := 8080;
 IDTCPServer1.Active      := TRUE;
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
 with TAddRemoveNodeSync.Create do
  try
   Adding := True;
   Synchronize;
   AThread.Data := TObject(Node);
  finally
   Free;
 end;
end;

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
 with TAddRemoveNodeSync.Create do
  try
   Adding := False;
   Node := PVirtualNode(AThread.Data);
   Synchronize;
  finally
   Free;
   AThread.Data := nil;
  end;
end;

end.
客户(压力源):

程序项目1;
{$APPTYPE控制台}
使用
SysUtils,
窗户,
温索克;
常数
//连接变量
端口=8080;
主机='127.0.0.1';
应力延迟=1;//毫秒!
变量
WSA:TWSADATA;
主插座:t插座;
地址:TSockAddrIn;
开始
如果WSAStartup($0202,WSA)0,则退出;
Addr.sin_family:=AF_INET;
地址SINU端口:=htons(端口);
Addr.sin\u Addr.S\u Addr:=INET\u Addr(主机);
真正的开始
MainSocket:=套接字(AF\u INET,SOCK\u STREAM,0);
连接(MainSocket,Addr,SizeOf(Addr));
闭合插座(主插座);//断开
睡眠(应激延迟);
终止
终止

由于GUI在主线程上运行,您可以(不应该)从自己的线程直接访问它。您应该编写自己的TThread类,然后使用Synchronize处理UI调用。

由于GUI在主线程上运行,因此您不能(不应该)直接从自己的线程访问它。您应该编写自己的TThread类,然后使用synchronize处理UI调用。

正如您所评论的,
TIdTCPServer
是一个多线程组件。必须与主线程同步,才能从
TIdTCPServer
事件安全地访问UI。为此,您可以使用Indy自己的
TIdSync
(同步)或
TIdNotify
(异步)类,例如:

type
  TAddRemoveNodeSync = class(TIdSync)
  protected
    procedure DoSynchronize; override;
  public
    Node: PVirtualNode; 
    Adding: Boolean;
  end;

procedure TAddRemoveNodeSync.DoSynchronize;
begin
  if Adding then
    Node := Form1.VirtualStringTree1.AddChild(nil)
  else
    Form1.VirtualStringTree1.DeleteNode(Node);
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 
begin 
  with TAddRemoveNodeSync.Create do
  try
    Adding := True;
    Synchronize;
    AThread.Data := TObject(Node);
  finally
    Free;
  end;
end; 

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread); 
begin 
  with TAddRemoveNodeSync.Create do
  try
    Adding := False;
    Node := PVirtualNode(AThread.Data);
    Synchronize;
  finally
    Free;
    AThread.Data := nil;
  end;
end; 
更新:根据新信息,我会做类似的事情:

type
  TAddRemoveClientNotify = class(TIdNotify)
  protected
    fAdding: Boolean;
    fIP, fPeerIP: string;
    fPort, fPeerPort: Integer;
    ...
  public
    constructor Create(AThread: TIdPeerThread; AAdding: Boolean); reintroduce;
    procedure DoNotify; override;
  end;

constructor TAddRemoveClientNotify.Create(AThread: TIdPeerThread; AAdding: Boolean);
begin
  inherited Create;
  fAdding := AAdding;
  with AThread.Connection.Socket.Binding do
  begin
    Self.fIP := IP;
    Self.fPeerIP := PeerIP;
    Self.fPort := Port;
    Self.fPeerPort := PeerPort;
  end;
end;

procedure TAddRemoveClientNotify.DoNotify;
var
  Node: PVirtualNode;
begin
  if fAdding then
  begin
    Node := Form1.VirtualStringTree1.AddChild(nil);
    // associate fIP, fPeerIP, fPort, fPeerPort with Node as needed...
  end else
  begin
    // find the Node that is associated with fIP, fPeerIP, fPort, fPeerPort as needed...
    Node := ...;
    if Node <> nil then
      Form1.VirtualStringTree1.DeleteNode(Node);
  end;
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 
begin 
  TAddRemoveClientNotify.Create(AThread, True).Notify;
end; 

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread); 
begin 
  TAddRemoveClientNotify.Create(AThread, False).Notify;
end; 
类型
TAddRemoveClientNotify=class(tidtnotify)
受保护的
渐弱:布尔;
fIP,FPERIP:字符串;
fPort,fPeerPort:整数;
...
平民的
构造函数创建(AThread:TIdPeerThread;AAdding:Boolean);重新引入;
程序提示;推翻
终止
构造函数TAddRemoveClientNotify.Create(AThread:TIdPeerThread;AAdding:Boolean);
开始
继承创造;
渐减:=渐减;
使用AThread.Connection.Socket.Binding do
开始
Self.fIP:=IP;
Self.fpeip:=PeerIP;
Self.fPort:=端口;
Self.fPeerPort:=PeerPort;
终止
终止
程序TAddRemoveClientNotify.DoNotify;
变量
节点:PVirtualNode;
开始
如果褪色那么
开始
节点:=Form1.VirtualStringTree1.AddChild(nil);
//根据需要将fIP、FPERIP、fPort、fPeerPort与节点关联。。。
结束其他
开始
//根据需要查找与fIP、FPERIP、fPort、fPeerPort关联的节点。。。
节点:=。。。;
如果节点为nil,则
Form1.VirtualStringTree1.DeleteNode(节点);
终止
终止
程序TForm1.IdTCPServer1Connect(AThread:TIdPeerThread);
开始
创建(AThread,True).Notify;
终止
程序TForm1.idtcpserver1断开连接(AThread:TIdPeerThread);
开始
创建(AThread,False).Notify;
终止

正如您所评论的,
TIdTCPServer
是一个多线程组件。必须与主线程同步,才能从
TIdTCPServer
事件安全地访问UI。为此,您可以使用Indy自己的
TIdSync
(同步)或
TIdNotify
(异步)类,例如:

type
  TAddRemoveNodeSync = class(TIdSync)
  protected
    procedure DoSynchronize; override;
  public
    Node: PVirtualNode; 
    Adding: Boolean;
  end;

procedure TAddRemoveNodeSync.DoSynchronize;
begin
  if Adding then
    Node := Form1.VirtualStringTree1.AddChild(nil)
  else
    Form1.VirtualStringTree1.DeleteNode(Node);
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 
begin 
  with TAddRemoveNodeSync.Create do
  try
    Adding := True;
    Synchronize;
    AThread.Data := TObject(Node);
  finally
    Free;
  end;
end; 

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread); 
begin 
  with TAddRemoveNodeSync.Create do
  try
    Adding := False;
    Node := PVirtualNode(AThread.Data);
    Synchronize;
  finally
    Free;
    AThread.Data := nil;
  end;
end; 
更新:根据新信息,我会做类似的事情:

type
  TAddRemoveClientNotify = class(TIdNotify)
  protected
    fAdding: Boolean;
    fIP, fPeerIP: string;
    fPort, fPeerPort: Integer;
    ...
  public
    constructor Create(AThread: TIdPeerThread; AAdding: Boolean); reintroduce;
    procedure DoNotify; override;
  end;

constructor TAddRemoveClientNotify.Create(AThread: TIdPeerThread; AAdding: Boolean);
begin
  inherited Create;
  fAdding := AAdding;
  with AThread.Connection.Socket.Binding do
  begin
    Self.fIP := IP;
    Self.fPeerIP := PeerIP;
    Self.fPort := Port;
    Self.fPeerPort := PeerPort;
  end;
end;

procedure TAddRemoveClientNotify.DoNotify;
var
  Node: PVirtualNode;
begin
  if fAdding then
  begin
    Node := Form1.VirtualStringTree1.AddChild(nil);
    // associate fIP, fPeerIP, fPort, fPeerPort with Node as needed...
  end else
  begin
    // find the Node that is associated with fIP, fPeerIP, fPort, fPeerPort as needed...
    Node := ...;
    if Node <> nil then
      Form1.VirtualStringTree1.DeleteNode(Node);
  end;
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 
begin 
  TAddRemoveClientNotify.Create(AThread, True).Notify;
end; 

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread); 
begin 
  TAddRemoveClientNotify.Create(AThread, False).Notify;
end; 
类型
TAddRemoveClientNotify=class(tidtnotify)
受保护的
渐弱:布尔;
fIP,FPERIP:字符串;
fPort,fPeerPort:整数;
...
平民的
构造函数创建(AThread:TIdPeerThread;AAdding:Boolean);重新引入;
程序提示;推翻
终止
构造函数TAddRemoveClientNotify.Create(AThread:TIdPeerThread;AAdding:Boolean);
开始
继承创造;
渐减:=渐减;
使用AThread.Connection.Socket.Binding do
开始
Self.fIP:=IP;
Self.fpeip:=PeerIP;
Self.fPort:=端口;
Self.fPeerPort:=PeerPort;
终止
终止
程序TAddRemoveClientNotify.DoNotify;
变量
节点:PVirtualNode;
开始
如果褪色那么
开始
节点:=Form1.VirtualStringTree1.AddChild(nil);
//根据需要将fIP、FPERIP、fPort、fPeerPort与节点关联。。。
结束其他
开始
//根据需要查找与fIP、FPERIP、fPort、fPeerPort关联的节点。。。
节点:=。。。;
如果节点为nil,则
Form1.VirtualStringTree1.DeleteNode(节点);
终止
终止
程序TForm1.IdTCPServer1Connect(AThread:TIdPeerThread);
开始
创建(AThread,True).Notify;
终止
程序TForm1.idtcpserver1断开连接(AThread:TIdPeerThread);
开始
创建(AThread,False).Notify;
终止

为什么不在主线程上运行所有UI代码?那又怎样?在主线程中运行UI代码。使用一些消息传递机制来实现这一点。将连接信息添加到线程安全队列中。从主线程很容易将信息排到虚拟树视图中。不要直接从断开/连接事件调用虚拟字符串树。创建具有必要结构的对象并将其添加到队列中。在主线程的计时器事件中,您可以检查队列是否已填充,并将对象出列。请参阅,以获取此类队列的示例。@LU发布消息比轮询要好得多,轮询通常是一个非常糟糕的想法。为什么不在主线程上运行所有UI代码?那又怎样?在主线程中运行UI代码。使用一些消息传递机制来实现这一点。将连接信息添加到线程安全队列中。从你的主线来看,这是非常有趣的