Delphi VirtualTreeView使用线程添加根
我想使用如下线程将根添加到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
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代码。使用一些消息传递机制来实现这一点。将连接信息添加到线程安全队列中。从你的主线来看,这是非常有趣的