Júlio
asked on
Delphi: Listview and Thread
I want to populate a listview with thread.
What is wrong with my thread?
Thread Unit (69~?):
Form1 I call it:
Ty!
What is wrong with my thread?
Thread Unit (69~?):
unit unit_download;
interface
uses
System.Classes, idHttp,Dialogs, StdCtrls, SysUtils, Windows, ComCtrls, MSHTML, Variants, ActiveX;
type
TDownloadList = class(TThread)
private
CHttp : TidHttp;
CStatus : TStatusBar;
CListView : TListView;
procedure Baixar;
protected
procedure Execute; override;
public
constructor Criar(http : tidhttp; statusbar : tStatusBar; listview: Tlistview);
end;
implementation
uses
unit_principal;
{ TDownloadList }
procedure TDownloadList.Baixar;
Var
Pagina : IHTMLDocument2;
itens : IHTMLElement;
Valores: OleVariant;
List : TListItem;
Buffer : String;
I, X : Integer;
Caption, a, b, c : String;
begin
CoInitialize(nil);
CListview.Clear;
CStatus.Panels[0].Text := 'Updating...';
CHttp.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
Try
Buffer := Chttp.Get(Link_download);
except
On E : Exception do
Begin
ShowMessage('Error: ' + e.message);
CStatus.Panels[0].Text := 'Erro:' + e.Message;
Exit;
End;
End;
CStatus.Panels[0].Text := 'Finish.';
Pagina := coHTMLDocument.Create as IHTMLDocument2;
Valores := VarArrayCreate([0,0], VarVariant);
Valores[0] := Buffer;
Pagina.write(PSafeArray(TVarData(Valores).VArray));
Pagina.close;
X := Pagina.all.length;
CStatus.Panels[0].Text := 'Creating list...';
CListView.Items.BeginUpdate;
Try
for I := 0 to X -1 do
Begin
itens := Pagina.all.item(I, varEmpty) as IHTMLElement;
Try
caption := '';
a := '';
b := '';
c := '';
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
b := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF', VarEmpty)) > 0) Then
c := Itens.innerText;
List := CListView.Items.Add;
List.Caption := Caption;
List.SubItems.Add(a);
List.SubItems.Add(b);
List.SubItems.Add(c);
Except
on E: Exception do
Begin
ShowMessage('Error: ' + e.message);
End;
End;
End;
except
On E : Exception do
Begin
ShowMessage('Error: ' + e.message);
Exit;
End;
End;
CListView.Items.EndUpdate;
CStatus.Panels[0].Text := 'List OK. ' + IntToStr(CListView.Items.Count);
CoUninitialize;
end;
constructor TDownloadList.Criar(http: tidhttp; statusbar : tstatusbar; listview : TListView);
begin
inherited Create(False);
FreeOnTerminate := True;
Chttp := http;
Cstatus := statusbar;
CListview := Listview;
end;
procedure TDownloadList.Execute;
begin
If not (Terminated) Then
begin
baixar;
end;
end;
end.
Form1 I call it:
begin
if IsOnline then
Begin
Download := TDownloadList.Criar(idhttp1, statusbar1, Listview1);
End
Else
StatusBar1.Panels[0].Text := 'Connect first..';
end;
Ty!
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Access violation:
unit unit_download;
interface
uses
System.Classes, idHttp,Dialogs, StdCtrls, SysUtils, Windows, ComCtrls, MSHTML, Variants, ActiveX;
type
TDownloadList = class(TThread)
private
CHttp : TidHttp;
CStatus : TStatusBar;
CListView : TListView;
Pagina : IHTMLDocument2;
itens : IHTMLElement;
Valores : OleVariant;
X : Integer;
procedure Baixar;
procedure FillList;
protected
procedure Execute; override;
public
constructor Criar(http : tidhttp; statusbar : tStatusBar; listview: Tlistview);
end;
implementation
uses
unit_principal;
{ TDownloadList }
procedure TDownloadList.Baixar;
Var
Buffer : String;
begin
CoInitialize(nil);
CStatus.Panels[0].Text := 'Updating...';
CHttp.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
Try
Buffer := Chttp.Get(Link_download);
except
On E : Exception do
Begin
ShowMessage('Error: ' + e.message);
CStatus.Panels[0].Text := 'Erro:' + e.Message;
Exit;
End;
End;
CStatus.Panels[0].Text := 'Finish.';
Pagina := coHTMLDocument.Create as IHTMLDocument2;
Valores := VarArrayCreate([0,0], VarVariant);
Valores[0] := Buffer;
Pagina.write(PSafeArray(TVarData(Valores).VArray));
Pagina.close;
X := Pagina.all.length;
CStatus.Panels[0].Text := 'Creating list...';
Synchronize(FillList);
CoUninitialize;
end;
constructor TDownloadList.Criar(http: tidhttp; statusbar : tstatusbar; listview : TListView);
begin
inherited Create(False);
FreeOnTerminate := True;
Chttp := http;
Cstatus := statusbar;
CListview := Listview;
end;
procedure TDownloadList.Execute;
begin
If not (Terminated) Then
begin
baixar;
end;
end;
procedure TDownloadList.FillList;
Var
List : TListItem;
I : Integer;
Caption, a, b, c, d, e : String;
begin
CListview.Clear;
CListView.Items.BeginUpdate;
Try
for I := 0 to X -1 do
Begin
itens := Pagina.all.item(I, varEmpty) as IHTMLElement;
Try
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
b := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
c := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
d := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF', VarEmpty)) > 0) Then
e := Itens.innerText;
List := CListView.Items.Add;
List.Caption := Caption;
List.SubItems.Add(a);
List.SubItems.Add(b);
List.SubItems.Add(c);
List.SubItems.Add(d);
List.SubItems.Add(e);
Except
on E: Exception do
Begin
ShowMessage('Error: ' + e.message);
End;
End;
CStatus.Panels[0].Text := 'List OK. ' + IntToStr(CListView.Items.Count);
End;
Except
on E: Exception do
Begin
ShowMessage('Error: ' + e.Message);
End;
End;
end;
end.
ASKER
Marco, with you unit i got Access Violation too.
Details?
ASKER
"Access violation at address 52D279F4 in module 'mshtml.dll'. Read of address 00000180"
Fantastic! Debugging threads is a nightmare :)
Please try to comment out the synchronize call so we can understand if the problem is in the first half of code (Baixar) or in the second one (FillList) I think it is in the second one but the access violation in mshtml seems indicate there is something related to the getting value from html page, uh?
Please try to comment out the synchronize call so we can understand if the problem is in the first half of code (Baixar) or in the second one (FillList) I think it is in the second one but the access violation in mshtml seems indicate there is something related to the getting value from html page, uh?
ASKER
yes the problem is with the "FillList". But i think the variable "buffer" is empty because i try a "showmessage(buffer);" and i got a access violation too.
Okay, I tried to fix your last snippet. Keep in mind you can't use ShowMessage() from within a thread, nor you can update directly any element of the main window: you always must use Sinchronize for both status.panel and list view:
unit unit_download;
interface
uses
System.Classes, idHttp,Dialogs, StdCtrls, SysUtils, Windows, ComCtrls, MSHTML, Variants, ActiveX;
type
TDownloadList = class(TThread)
private
CHttp : TidHttp;
CStatus : TStatusBar;
CListView : TListView;
Pagina : IHTMLDocument2;
itens : IHTMLElement;
Valores : OleVariant;
X : Integer;
Msg : string;
Caption, a, b, c, d, e : String;
procedure Baixar;
procedure FillList;
procedure UpdateStatus;
protected
procedure Execute; override;
public
constructor Criar(http : tidhttp; statusbar : tStatusBar; listview: Tlistview);
end;
implementation
uses
unit_principal;
{ TDownloadList }
procedure TDownloadList.Baixar;
Var
Buffer : String;
I : Integer;
begin
CoInitialize(nil);
Msg := 'Updating...';
Synchronize(UpdateStatus);
CHttp.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
Try
Buffer := Chttp.Get(Link_download);
except
On E : Exception do
Begin
ShowMessage('Error: ' + e.message);
CStatus.Panels[0].Text := 'Erro:' + e.Message;
Exit;
End;
End;
Msg := 'Finish.';
Synchronize(UpdateStatus);
Pagina := coHTMLDocument.Create as IHTMLDocument2;
Valores := VarArrayCreate([0,0], VarVariant);
Valores[0] := Buffer;
Pagina.write(PSafeArray(TVarData(Valores).VArray));
Pagina.close;
X := Pagina.all.length;
Msg := 'Creating list...';
Synchronize(UpdateStatus);
CListview.Clear;
CListView.Items.BeginUpdate;
Try
for I := 0 to X -1 do
Begin
itens := Pagina.all.item(I, varEmpty) as IHTMLElement;
Try
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
b := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
c := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
d := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF', VarEmpty)) > 0) Then
e := Itens.innerText;
Synchronize(FillList);
except
on E: Exception do
begin
Msg := 'Error: ' + e.message;
Synchronize(UpdateStatus);
end;
End;
end;
except
on E: Exception do
begin
Msg := 'Error: ' + e.message;
Synchronize(UpdateStatus);
end;
End;
CoUninitialize;
end;
constructor TDownloadList.Criar(http: tidhttp; statusbar : tstatusbar; listview : TListView);
begin
inherited Create(False);
FreeOnTerminate := True;
Chttp := http;
Cstatus := statusbar;
CListview := Listview;
end;
procedure TDownloadList.Execute;
begin
If not (Terminated) Then
begin
baixar;
end;
end;
procedure TDownloadList.FillList;
Var
List : TListItem;
begin
List := CListView.Items.Add;
List.Caption := Caption;
List.SubItems.Add(a);
List.SubItems.Add(b);
List.SubItems.Add(c);
List.SubItems.Add(d);
List.SubItems.Add(e);
Msg := 'List OK. ' + IntToStr(CListView.Items.Count);
Synchronize(UpdateStatus);
end;
procedure TDownloadList.UpdateStatus;
begin
CStatus.Panels[0].Text := Msg;
end;
end.
ASKER
Link_Download is a global variable and i fill it in form1's event "onCreate".
ASKER
Your new code do not add any item to ListView, but statusbar is working.
ASKER
Form1 unit:
unit unit_principal;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.Menus, Vcl.ExtCtrls,
Vcl.AppEvnts, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdHTTP, commctrl, Vcl.StdCtrls, wininet, IdCookieManager;
type
TForm1 = class(TForm)
MainMenu1: TMainMenu;
Lists1: TMenuItem;
BF4Last501: TMenuItem;
PBDownload1: TMenuItem;
Downloadlast1: TMenuItem;
N1: TMenuItem;
VisitSite1: TMenuItem;
ListView1: TListView;
StatusBar1: TStatusBar;
About1: TMenuItem;
Update1: TMenuItem;
ools1: TMenuItem;
PlayerAlert1: TMenuItem;
Configuration1: TMenuItem;
TrayIcon1: TTrayIcon;
ApplicationEvents1: TApplicationEvents;
PopupMenu1: TPopupMenu;
Open1: TMenuItem;
UpdateCurrentList1: TMenuItem;
N2: TMenuItem;
Close1: TMenuItem;
IdHTTP1: TIdHTTP;
ProgressBar1: TProgressBar;
procedure ApplicationEvents1Minimize(Sender: TObject);
procedure Open1Click(Sender: TObject);
procedure Close1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure BF4Last501Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdHTTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
procedure TrayIcon1Click(Sender: TObject);
procedure IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
AWorkCountMax: Int64);
procedure IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
private
{ Private declarations }
public
{ Public declarations }
end;
type
THackControl = class(TControl);
var
Form1 : TForm1;
Link_Download : string;
Path_lista : string;
implementation
uses
unit_download;
Var
Download : TDownloadlist;
function IsOnLine: boolean;
var
dwConnectionTypes : DWORD;
begin
dwConnectionTypes :=
INTERNET_CONNECTION_MODEM +
INTERNET_CONNECTION_LAN +
INTERNET_CONNECTION_PROXY;
result := InternetGetConnectedState(@dwConnectionTypes,0);
end;
{$R *.dfm}
procedure TForm1.ApplicationEvents1Minimize(Sender: TObject);
begin
Form1.Hide;
end;
procedure TForm1.BF4Last501Click(Sender: TObject);
begin
if IsOnline then
Begin
Download := TDownloadList.Criar(idhttp1, statusbar1, Listview1);
End
Else
StatusBar1.Panels[0].Text := 'Connect first..';
end;
procedure TForm1.Close1Click(Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
PanelRect: TRect;
begin
Link_Download := 'http://www.pbbans.com/mbi-view-latest50-unofficial-bf4-lfb44.html';
Path_lista := ExtractFilePath(Application.ExeName) + 'list.html';
THackControl(ProgressBar1).SetParent(StatusBar1);
SendMessage(StatusBar1.Handle, SB_GETRECT, 1, Integer(@PanelRect));
with PanelRect do
ProgressBar1.SetBounds(Left, Top, Right - Left, Bottom - Top);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Form1.Width := 651;
Form1.Height := 358;
end;
procedure TForm1.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
begin
progressbar1.Position := AworkCount;
Statusbar1.Repaint;
Progressbar1.Repaint;
end;
procedure TForm1.IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
AWorkCountMax: Int64);
begin
Progressbar1.Position := 0;
progressbar1.Max := aworkcountmax;
end;
procedure TForm1.IdHTTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
TrayIcon1.ShowBalloonHint;
end;
procedure TForm1.Open1Click(Sender: TObject);
begin
Form1.Show;
end;
procedure TForm1.TrayIcon1Click(Sender: TObject);
begin
Form1.Show;
Form1.BringToFront;
end;
end.
Are you totally sure that items to add to the list are correctly filled. I mean:
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
b := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
c := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
d := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF', VarEmpty)) > 0) Then
e := Itens.innerText;
if theese conditions are false all items Caption, a,b ,c and so on will be blank...
You could try to add an else in order to have value not empty:
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText
else
Caption := 'Caption not found';
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText
else
a := 'a not found';
and so on.
Can you give it a try?
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF',
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
b := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
c := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
d := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF',
e := Itens.innerText;
if theese conditions are false all items Caption, a,b ,c and so on will be blank...
You could try to add an else in order to have value not empty:
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF',
Caption := itens.innerText
else
Caption := 'Caption not found';
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText
else
a := 'a not found';
and so on.
Can you give it a try?
ASKER
I fix it:
But look the result:
procedure TDownloadList.FillList;
Var
List : TListItem;
begin
CListView.Items.BeginUpdate;
List := CListView.Items.Add;
List.Caption := Caption;
List.SubItems.Add(a);
List.SubItems.Add(b);
List.SubItems.Add(c);
List.SubItems.Add(d);
List.SubItems.Add(e);
Msg := 'List OK. ' + IntToStr(CListView.Items.Count);
Synchronize(UpdateStatus);
CListView.Items.EndUpdate;
end;
But look the result:
ASKER
I want to load this table: http://www.pbbans.com/mbi-view-latest50-unofficial-bf4-lfb44.html
So the 'a' value is wrong? And is it the only wrong value?
Try to modify this:
moving Synchronize(FillList); after the first try..except block
for I := 0 to X -1 do
Begin
itens := Pagina.all.item(I, varEmpty) as IHTMLElement;
Try
if (itens.tagName = 'A') and (Pos('mbi-viewban-', Itens.getAttribute('HREF', VarEmpty)) > 0) and (Itens.title = '') then
Caption := itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') and (itens.title = 'Even Balance Violation') Then
a := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
b := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-red') Then
c := Itens.innerText;
if (itens.tagName = 'SPAN') and (itens.className = 'text-green') then
d := itens.innerText;
if (itens.tagName = 'A') and (Pos('http://www.pbbans.com/msi-server-', Itens.getAttribute('HREF', VarEmpty)) > 0) Then
e := Itens.innerText;
except
on E: Exception do
begin
Msg := 'Error: ' + e.message;
Synchronize(UpdateStatus);
end;
End;
Synchronize(FillList);
end;
moving Synchronize(FillList); after the first try..except block
I suppose that didn't solve the problem, uh?
ASKER
Apologies, I will now attempt, I had to give an output. Women ..
ASKER
Just Caption, A, B and E is correct.
ASKER
But the table have 50 rows, with this code, add 1789 rows. Crazy!
Hi. Women, women... we can't live with nor without them :)
Now the problem seems not to be the code to update the ListView but the code wich processes the data retrivied rom the external site...
Later I'll try to test it, rebuilding a form like yours.
Cheers
Now the problem seems not to be the code to update the ListView but the code wich processes the data retrivied rom the external site...
Later I'll try to test it, rebuilding a form like yours.
Cheers
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Please, can you attach an archive with unit_principal pas and dfm? I'm trying to test but I get an 'Argument out of range' error and I think there is something I'm missing with the TListView setup...
just something i see which potentially causes problems with form reuse
don't use Form1 for the instance name, use self instead ... or leave it out
with self:
procedure TForm1.TrayIcon1Click(Sender: TObject);
begin
Form1.Show;
Form1.BringToFront;
end;
don't use Form1 for the instance name, use self instead ... or leave it out
procedure TForm1.TrayIcon1Click(Sender: TObject);
begin
Show;
BringToFront;
end;
with self:
procedure TForm1.TrayIcon1Click(Sender: TObject);
begin
Self.Show;
Self.BringToFront;
end;
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Ty all!
You are the best!
You are the best!
ASKER
I learned a lot from past information! Thank you very much!
Open in new window