2012-07-01 4 views
2

Я хотел бы добавить корни в VirtualTreeView http://www.delphi-gems.com/index.php/controls/virtual-treeview с резьбой, как это: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 , Indx's onexecute/onconnect get называют потоком. Поэтому, если одновременно появляются 3+ соединения, приложение вылетает из-за TreeView. То же самое, если клиент отключается, и я хочу удалить узел.

Я использую Delphi7 и Indy9

Любая идея, как исправить это?

EDIT:

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread); 
begin 
VirtualStringTree1.DeleteNode(PVirtualNode(Athread.Data)); // For Disconnection(s) 
end; 

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread); 
begin 
Athread.Data := TObject(VirtualStringTree1.AddChild(NIL)); // For Connection(s); 
end; 

Он отлично работает с ListView (по крайней мере, тем лучше).

EDIT: Вот мой полный код:

Сервер:

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. 

Client (Stresser):

program Project1; 

{$APPTYPE CONSOLE} 

uses 
SysUtils, 
Windows, 
Winsock; 

Const 
// Connection Vars 
Port   = 8080; 
Host   = '127.0.0.1'; 
StressDelay = 1; // Miliseconds! 

var 
WSA   : TWSADATA; 
MainSocket : TSocket; 
Addr   : TSockAddrIn; 

begin 
if WSAStartup($0202, WSA) <> 0 then exit; 
Addr.sin_family  := AF_INET; 
Addr.sin_port  := htons(Port); 
Addr.sin_addr.S_addr := INET_ADDR(Host); 
while true do begin 
    MainSocket   := Socket(AF_INET, SOCK_STREAM, 0); 
    Connect(MainSocket, Addr, SizeOf(Addr)); 
    CloseSocket(MainSocket); // Disconnect! 
    sleep (StressDelay); 
end; 
end. 
+1

Почему бы вам не запустить весь код пользовательского интерфейса в основном потоке? –

+0

Причина Indy вызывает OnConnect в потоке. Не в главной теме. –

+1

И что? Запустите код пользовательского интерфейса в основном потоке. Для достижения этого используйте механизм передачи сообщений. –

ответ

2

Как вы прокомментировали, TIdTCPServer - это многопоточный компонент. Вы должны синхронизироваться с основным потоком, чтобы безопасно получить доступ к пользовательскому интерфейсу из событий TIdTCPServer. Вы можете использовать Инди собственный 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; 
+0

выглядит хорошо, еще не пробовал, потому что я разговариваю по телефону. Синхронизирует; дождитесь завершения процедуры DoSynchronize? Спасибо за ваш ответ. Я попробую, как только я нахожусь на компьютере. –

+1

Да. 'TIdSync.Synchronize()' использует 'TThread.Synchronize()' внутренне. –

+0

Я пробовал ваш код, и он работает. Я также сделал стресс-тест на нем, где клиенты подключаются и отключаются ... Мое приложение вылетает, хотя оно синхронизировано ... Я думал, что будет буфер или что-то еще ?! Есть идеи, как улучшить это? –

2

Поскольку GUI работает на главном потоке вы можете (должны) не напрямую обращаться к нему из своей собственной нити. Вы должны написать свой собственный класс TThread, а затем использовать Sycnhronize для обработки вызовов пользовательского интерфейса.

+0

просто не могу это исправить. не могли бы вы привести пример? –

+1

'TIdTCPServer' уже запускает собственные потоки, поэтому исходный код не работает. Вы можете использовать Indy's 'TIdSync' или' TIdNotify' для синхронизации с основным потоком. –

 Смежные вопросы

  • Нет связанных вопросов^_^