Runtime Thread Access Violation Errors - multithreading

My idea is to download all files from a folder and subfolders in a stringlist.
Next I use the SHGetFileInfo function to retrieve the names and type the date and links from the files to load into my Access database.
My application works fine, but when I use a large folder that contains hundreds of files, it blocks what I need to use the thread.
When I use the thread and my table is empty, it displays error messages, but the second time when my table contains records it shows no problem.
Procedure of searche
procedure FileSearche(const PathName: string; var lstFiles: TStringList);
const
FileMask = '*.*';
var
Rec: TSearchRec;
Path: string;
begin
Path := IncludeTrailingBackslash(PathName);
if FindFirst(Path + FileMask, faAnyFile - faDirectory, Rec) = 0 then
try
repeat
lstFiles.Add(Path + Rec.Name);
until FindNext(Rec) <> 0;
finally
FindClose(Rec);
end;
if FindFirst(Path + '*.*', faDirectory, Rec) = 0 then
try
repeat
if ((Rec.Attr and faDirectory) <> 0) and (Rec.Name <> '.') and
(Rec.Name <> '..') then
FileSearche(Path + Rec.Name, lstFiles);
until FindNext(Rec) <> 0;
finally
FindClose(Rec);
end;
end;
Procedure of thread
//--------------------------------------------------------------
{ debloc }
procedure debloc.execute;
var
icn: HICON;
SHFileInfo: TSHFileInfo;
SearchRecord: TSearchRec;
Size, I: Integer;
lstFiles: TStringList;
State: SHELLSTATE;
lien, path: string;
isEmpty : boolean;
begin
// to request windows to display the extension of all files
SHGetSetSettings(State, SSF_SHOWEXTENSIONS, false);
State.Data := State.Data or SSF_SHOWEXTENSIONS;
SHGetSetSettings(State, SSF_SHOWEXTENSIONS, True);
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, nil, nil);
// for select folder
if SelectDirectory('Choisi un dossier ', ' ', path) then
Lien := IncludeTrailingPathDelimiter(path) else exit;
isEmpty := IsDirectoryEmpty(path) ;
// To verify that the folder is not empty
if isEmpty = false then
Begin
if MessageDlg('Remarque Le dossier :'+#13+path +#13+'est vide il n y pas des fichiers à importer', mtInformation,
[mbOK], 0, mbOK) = mrOk then
exit;
End;
// To verify that the folder is not folder systeme
if
(Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_WINDOWS)))
or (Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_SYSTEM)))
or (Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_PROGRAM_FILES)))
or (Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_PROGRAM_FILESX86)))
or (Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_MYPICTURES)))
or (Lien = IncludeTrailingPathDelimiter(SpecialFolder(CSIDL_PROGRAM_FILES_COMMONX86)))
or (Lien = 'C:\')
then
begin
// ShowMessage(Lien+#13+'Erro, Les dossiers système sont ignoré pour votre sécurité');
if MessageDlg(Lien+#13+'Attention, Pour des raison de sécurité les dossiers système sont ignoré ', mtWarning,
[mbYes], 0, mbYes) = mrYes then
exit;
end
else
begin
//To list the files in the StringList
begin
lstFiles := TStringList.Create;
FileSearche(lien, lstFiles);
end;
if lstFiles.Count > 0 then
for I := 0 to lstFiles.Count - 1 do
begin
//To get the name, type, date, links of all files
SHGetFileInfo(PChar(lstFiles[I]), 0, SHFileInfo, SizeOf(TSHFileInfo),
SHGFI_TYPENAME or SHGFI_DISPLAYNAME or SHGFI_SYSICONINDEX or
SHGFI_ICON);
FindFirst(lstFiles[I], 0, SearchRecord);
Size := SearchRecord.Size;
//To fill the Field of the table
Form1.FDTable1.Edit;
Form1.FDTable1.Insert;
Form1.FDTable1.FieldByName('nom_file').ASSTRING := (SHFileInfo.szDisplayName);
Form1.FDTable1.FieldByName('type_file').ASSTRING := (SHFileInfo.szTypeName);
Form1.FDTable1.FieldByName('size_file').ASSTRING := (GetFileSizeAsString(Size));
Form1.FDTable1.FieldByName('date_time_file').ASSTRING :=
(DateTimeToStr(FileDateToDateTime(SearchRecord.Time)));
Form1.FDTable1.FieldByName('lien_file').ASSTRING :=
(ExtractFilePath(lstFiles[I]));
Form1.ProgressBar1.Max := Form1.FDTable1.RecordCount;
Form1.ProgressBar1.Position := Form1.FDTable1.RecordCount;
end ;
Form1.FDTable1.Post;
Form1.FDTable1.First;
Form1.StatusBar1.Panels[0].Text := 'Nombre d"enregistrements: ' +
IntToStr(Form1.FDTable1.RecordCount);
// to request windows to hide the extension of all files
SHGetSetSettings(State, SSF_SHOWEXTENSIONS, false);
State.Data := State.Data and ($FFFFFFFF xor SSF_SHOWEXTENSIONS);
SHGetSetSettings(State, SSF_SHOWEXTENSIONS, True);
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, nil, nil);
// procedure to rearrange the automatic columns
SetGridColumnWidths(Form1.dbgrid1);
Application.ProcessMessages;
end;
end;
to execute thread
procedure TForm1.Button1Click(Sender: TObject);
BEGIN
with debloc.Create do
FreeOnTerminate:=true;
END;
When I use the thread and the table is empty, it displays the error message
violation d'accès à l'adresse 00732BB1
But the second time, when my table is saved, it is not a problem.
Note: Despite this code that annoys me, the application works as even
another thing I do not know how to stop the thread when the folder is very big. I close the application for the stop.

I solved the problem by replacing the dbgrid component with the listview component
procedure debloc.transfertdata;
var
Myitem : TListItem;
MyColumn : TListColumn;
begin
ListView1.Items.Clear;
ListView1.Columns.Clear;
MyColumn:= ListView1.Columns.Add;
MyColumn.Caption:= 'Nom' ;
MyColumn.Width := -1;
MyColumn:= ListView1.Columns.Add;
MyColumn.Caption:= 'Type' ;
MyColumn.Width := -1;
MyColumn:= ListView1.Columns.Add;
MyColumn.Caption:= 'Taille' ;
MyColumn.Width := -1;
MyColumn:= ListView1.Columns.Add;
MyColumn.Caption:= 'Date de modification' ;
MyColumn.Width := -1;
MyColumn:= ListView1.Columns.Add;
MyColumn.Caption:= 'Lien' ;
MyColumn.Width := -1;
FDTable1.First;
while not FDTable1.Eof do
begin
ListView1.Items.BeginUpdate;
Myitem := ListView1.items.Add;
Myitem.Caption:= FDTable1.FieldByName('nom_file').ASSTRING;
Myitem.SubItems.Add(FDTable1.FieldByName('type_file').ASSTRING) ;
Myitem.SubItems.Add(FDTable1.FieldByName('size_file').ASSTRING) ;
Myitem.SubItems.Add(FDTable1.FieldByName('date_time_file').ASSTRING) ;
Myitem.SubItems.Add(FDTable1.FieldByName('lien_file').ASSTRING) ;
FDTable1.Next;
ListView1.Items.EndUpdate;
end;
end;
and in the thread I added
Synchronize(transfertdata);

Related

Download and process files with IOmniPipeline

My goal is a VCL app where I need to concurrently download some files (URLs stored in TDataSet), then each of downloaded file must be processed (one-by-one). During app working GUI must not be hanged, user user should be able to cancel (interrupt) process in any stage.
I totally rewrite my first example, now there is no third-party classes (Omni Thread Library 3.07.6 and VCL only). Of course, it's still demo and some checks was removed from code, but however, this sample still not short unfortunately.
Downloading part based on this answer (thanks!).
So, when any file downloading I need to show progress to this files in GUI. Downloader class generate "event" OnProgressChange (because in real app I'll use TALWinInetHTTPClient class instance from Alcinoe library and it has real event OnProgressChange). I think it's enough just write progress value in DB, then DBGrid show progress values correctly.
Of course I saw this answers (and some other related with OTL):
How to Stop all Pipeline tasks correctly
How to use Pipeline pattern in Delphi (it's similar to my tasks, but differences in details).
Also I saw OTL docs and examples but I can't find some real example for doing similar task.
I created some classes to solve this task, and it's works, but have some critical troubles:
At first stage downloading not starts separately (not parallels, but one-by-one).
Cancellation not works properly.
Below some code to illustrate my problem. It's contain two units, one is main form (GUI, preparing data, interaction with user), second is Pipeline wrapper and downloader.
DFM for main form is:
object fmMain: TfmMain
Left = 628
Top = 172
Caption = 'WorkSpace preparer'
ClientHeight = 262
ClientWidth = 700
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Segoe UI'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 17
object DBGridApps: TDBGrid
AlignWithMargins = True
Left = 3
Top = 3
Width = 694
Height = 207
Align = alClient
DataSource = dsApps
ReadOnly = True
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -13
TitleFont.Name = 'Segoe UI'
TitleFont.Style = []
end
object Panel1: TPanel
AlignWithMargins = True
Left = 3
Top = 216
Width = 694
Height = 43
Align = alBottom
TabOrder = 1
object bbExit: TBitBtn
AlignWithMargins = True
Left = 549
Top = 4
Width = 141
Height = 35
Align = alRight
Caption = 'Exit'
TabOrder = 0
OnClick = bbExitClick
end
object bbCancel: TBitBtn
AlignWithMargins = True
Left = 151
Top = 4
Width = 141
Height = 35
Align = alLeft
Caption = 'Cancel'
TabOrder = 1
OnClick = bbCancelClick
ExplicitTop = 0
end
object bbStart: TBitBtn
AlignWithMargins = True
Left = 4
Top = 4
Width = 141
Height = 35
Align = alLeft
Caption = 'Start'
TabOrder = 2
OnClick = bbStartClick
end
end
object dsApps: TDataSource
DataSet = cdsApps
Left = 32
Top = 88
end
object cdsApps: TClientDataSet
Aggregates = <>
Params = <>
Left = 16
Top = 72
end
end
Main form code:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, DBClient, Grids, DBGrids, StdCtrls, Buttons, ExtCtrls,
PipelineHolder;
type
TfmMain = class(TForm)
DBGridApps: TDBGrid;
dsApps: TDataSource;
Panel1: TPanel;
bbExit: TBitBtn;
bbCancel: TBitBtn;
bbStart: TBitBtn;
cdsApps: TClientDataSet;
procedure bbExitClick(Sender: TObject);
procedure bbCancelClick(Sender: TObject);
procedure bbStartClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
PH : TPipelineHolder;
procedure SwitchControlState;
public
{ Public declarations }
end;
var
fmMain: TfmMain;
implementation
{$R *.dfm}
procedure TfmMain.bbExitClick(Sender: TObject);
begin
Close;
end;
procedure TfmMain.bbCancelClick(Sender: TObject);
begin
if Assigned(PH) then
begin
SwitchControlState;
PH.Stop;
end;
end;
procedure TfmMain.bbStartClick(Sender: TObject);
begin
if not Assigned(PH) then
PH := TPipelineHolder.Create;
SwitchControlState;
PH.Make(cdsApps);
end;
procedure TfmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := MessageBox(0, 'Exit now?', 'Exit', MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON2 + MB_TOPMOST) = IDYES;
if CanClose then bbCancel.Click;
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
//Prepare dataset
cdsApps.Close;
With cdsApps do
begin
FieldDefs.Add('progress', ftFloat);
FieldDefs.Add('status', ftString, 30);
FieldDefs.Add('id', ftString, 30);
FieldDefs.Add('uid', ftString, 30);
FieldDefs.Add('title', ftString, 30);
FieldDefs.Add('url', ftString, 255);
FieldDefs.Add('silent_parameters', ftString, 255);
FieldDefs.Add('target_file', ftString, 255);
CreateDataSet;
LogChanges := False;
Open;
// Below you can change URL as you wish.
// For example I'll use VirtualBox distrib from this page: https://www.virtualbox.org/wiki/Downloads
// To correct progress values web-server must response with correct content-lenght values and must
// support HEAD command.
// Record 1
Append;
Fields[0].AsFloat := 0.0;
Fields[1].AsString := 'Ready';
Fields[2].AsString := '5be2e746ce46a1000cdc8b90';
Fields[3].AsString := 'SomeApp1';
Fields[4].AsString := 'VirtualBox 6.0.10';
Fields[5].AsString := 'https://download.virtualbox.org/virtualbox/6.0.10/VirtualBox-6.0.10-132072-Win.exe';
Fields[6].AsString := '/S';
Fields[7].AsString := '001_installer.exe';
Post;
// Record 2
Append;
Fields[0].AsFloat := 0.0;
Fields[1].AsString := 'Ready';
Fields[2].AsString := '5be31c63ce46a1000b268bb2';
Fields[3].AsString := 'SomeApp2';
Fields[4].AsString := 'VirtualBox 6.0.10';
Fields[5].AsString := 'https://download.virtualbox.org/virtualbox/6.0.10/VirtualBox-6.0.10-132072-Win.exe';
Fields[6].AsString := '';
Fields[7].AsString := '002_installer.exe';
Post;
// Record 3
Append;
Fields[0].AsFloat := 0.0;
Fields[1].AsString := 'Ready';
Fields[2].AsString := '5be3428ace46a1000b268bc0';
Fields[3].AsString := 'SomeApp3';
Fields[4].AsString := 'VirtualBox 6.0.10';
Fields[5].AsString := 'https://download.virtualbox.org/virtualbox/6.0.10/VirtualBox-6.0.10-132072-Win.exe';
Fields[6].AsString := '/VERY_SILENT';
Fields[7].AsString := '003_installer.exe';
Post;
// Record 4
Append;
Fields[0].AsFloat := 0.0;
Fields[1].AsString := 'Ready';
Fields[2].AsString := '5be3428ace46a1000b268bc1';
Fields[3].AsString := 'SomeApp4';
Fields[4].AsString := 'VirtualBox 6.0.10';
Fields[5].AsString := 'https://download.virtualbox.org/virtualbox/6.0.10/VirtualBox-6.0.10-132072-Win.exe';
Fields[6].AsString := '';
Fields[7].AsString := '004_installer.exe';
Post;
// Record 5 - it's not exe, just simple testing file, in this demo at
// Install method with this file will set status to error.
Append;
Fields[0].AsFloat := 0.0;
Fields[1].AsString := 'Ready';
Fields[2].AsString := '5be512bb4a9bbb000b6de944';
Fields[3].AsString := 'SomeFile';
Fields[4].AsString := 'Demo File (not executable)';
Fields[5].AsString := 'https://speed.hetzner.de/100MB.bin';
Fields[6].AsString := '';
Fields[7].AsString := '005_sample_100MB.bin';
Post;
First;
end;
end;
procedure TfmMain.SwitchControlState;
begin
bbStart.Enabled := not bbStart.Enabled;
end;
end.
Second unit for pipeline working implementation:
unit PipelineHolder;
interface
uses
Windows, SysUtils, Classes, OtlCommon, OtlCollections, OtlParallel, Forms,
DB, Generics.Defaults, StrUtils, Generics.Collections, Messages, OtlComm,
OtlTask, OtlTaskControl, ShellAPI, Dialogs, OtlSync, Math, WinInet;
// Messages
const
WM_PROGRESSCHANGED = WM_APP + 105;
// Process states
type
TAppState = (asReady = 0, asCancelled = 1, asError = 2, asDownloading = 3, asDownloaded = 4, asInstalling = 5, asCompleted = 6);
TAppStateNames = array[asReady..asCompleted] of string;
const
AppState: TAppStateNames = ('Ready', 'Canceled', 'Error', 'Downloading', 'Downloaded', 'Installing', 'Installed');
type
// Data structs for progress message
PProgressInfo = ^TProgressInfo;
TProgressInfo = record
Read : Int64;
Total : Int64;
ID : string;
URL : string;
end;
//Structure for record info
TRecordInfo = record
Filename: string;
URL: string;
ID: string;
Cmd : string;
end;
// Class for downloading
TDBAppItem = class
private
FHandle : HWND;
FDS : TDataSet;
FFilename: string;
FURL: string;
FId: string;
FCmd : string;
FFileSize : Int64;
FDownloaded : Int64;
function GetWinInetError(ErrorCode: Cardinal): string;
procedure ParseURL(const lpszUrl: string; var Host, Resource: string);
function GetRemoteFileSize(const Url : string): Integer;
function DownloadFile(const url: string; const TargetFileName: string): boolean;
procedure InternalDownloadProgress(Sender: TObject; Read: Integer; Total: Integer);
public
constructor Create(const OwnerHandle: HWND; var DS: TDataSet; const URL, ID: string; const Cmd: string; const TargetFilename: string);
destructor Destroy; override;
function Download : Boolean; overload;
end;
// Main class, pipeline holder
TPipelineHolder = class
private
FDS : TDataSet;
FHandle : HWND;
FPipeline : IOmniPipeline;
FInProcess: Boolean;
procedure Retrieve(const input: TOmniValue; var output: TOmniValue);
procedure RetrieveAll(const input, output: IOmniBlockingCollection);
procedure Install(const input, output: IOmniBlockingCollection);
procedure JobDone;
procedure WndProc(var Message: TMessage);
procedure WMProgressChanged(var msg: TMessage); message WM_PROGRESSCHANGED;
public
constructor Create;
destructor Destroy; override;
procedure Make(SourceDS : TDataSet);
function Stop: Boolean;
property InProcess: Boolean read FInProcess write FInProcess;
end;
implementation
{ Tools }
function RunAsAdmin(const Handle: HWnd; const Filename, Params: string): Boolean;
var
sei: TShellExecuteInfo;
begin
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.Wnd := Handle;
sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PChar(Filename);
sei.lpParameters := PChar(Params);
sei.nShow := SW_SHOWNORMAL;
Result := ShellExecuteEx(#sei);
end;
{TPipelineHolder}
constructor TPipelineHolder.Create;
begin
inherited Create;
FHandle := AllocateHWnd(WndProc);
FInProcess := False;
end;
destructor TPipelineHolder.Destroy;
begin
if FInProcess then
if Assigned(FPipeline) then
begin
FPipeline.Cancel;
FPipeline := nil;
FInProcess := False;
end;
if FHandle <> 0 then DeallocateHWnd(FHandle);
inherited;
end;
procedure TPipelineHolder.Install(const input, output: IOmniBlockingCollection);
var
app : TOmniValue;
appFile : string;
appParams: string;
ID : string;
State : string;
AppInfo : TRecordInfo;
begin
// In real app here is downloaded file must be started as separate process and
// we must wait when it will be completed.
for app in input do
begin
if not app.IsEmpty then
begin
AppInfo := app.ToRecord<TRecordInfo>;
appFile := AppInfo.Filename;
appParams := AppInfo.Cmd;
ID := AppInfo.ID;
if (appFile <> EmptyStr) and (FileExists(appFile)) then
begin
// Change file state
FDS.DisableControls;
try
if FDS.Locate('id', ID, [loCaseInsensitive]) then
begin
FDS.Edit;
FDS.FieldByName('Status').AsString := AppState[asInstalling];
FDS.Post;
end;
finally
FDS.EnableControls;
end;
// Try to execute intsaller
if RunAsAdmin(Application.Handle, appFile, appParams) then
begin
State := AppState[asCompleted]
end
else
begin
State := AppState[asError];
end;
// Change state again
FDS.DisableControls;
try
if FDS.Locate('id', ID, [loCaseInsensitive]) then
begin
FDS.Edit;
FDS.FieldByName('Status').AsString := State;
FDS.Post;
end;
finally
FDS.EnableControls;
end;
end;
end;
end;
end;
procedure TPipelineHolder.Retrieve(const input: TOmniValue; var output: TOmniValue);
var
App: TDBAppItem;
Info : TRecordInfo;
begin
// Checking cancellation flag
if not FInProcess then Exit;
// Preparing
Info := input.ToRecord<TRecordInfo>;
App := TDBAppItem.Create(FHandle, FDS, Info.URL, Info.ID, Info.Cmd, Info.Filename);
// Downloading
try
if App.Download then
output := TOmniValue.FromRecord<TRecordInfo>(Info)
else
output.Clear;
finally
FreeAndNil(App);
end;
end;
procedure TPipelineHolder.RetrieveAll(const input,
output: IOmniBlockingCollection);
var
App: TDBAppItem;
Info : TRecordInfo;
value : TOmniValue;
begin
// Preparing
for value in input do
begin
if not FInProcess then Exit;
Info := value.ToRecord<TRecordInfo>;
App := TDBAppItem.Create(FHandle, FDS, Info.URL, Info.ID, Info.Cmd, Info.Filename);
// Downloading
try
if App.Download then
output.Add(TOmniValue.FromRecord<TRecordInfo>(Info));
finally
FreeAndNil(App);
end;
end;
end;
function TPipelineHolder.Stop: Boolean;
begin
if FInProcess then
begin
if Assigned(FPipeline) then
begin
FPipeline.Cancel;
FPipeline := nil;
FInProcess := False;
end;
end;
Result := not FInProcess;
end;
procedure TPipelineHolder.WMProgressChanged(var msg: TMessage);
var
MsgRec : TProgressInfo;
Percent, Current : Double;
Read, Total : Int64;
ID : string;
begin
MsgRec := PProgressInfo(Msg.LParam )^;
Read := MsgRec.Read;
Total := MsgRec.Total;
Percent := 100 * Read / Total;
ID := MsgRec.ID;
// Write data to db
if FDS.Locate('id', ID, [loCaseInsensitive]) then
begin
FDS.DisableControls;
try
Current := FDS.FieldByName('Progress').AsFloat;
if Current <> Trunc(Percent) then
begin
FDS.Edit;
FDS.FieldByName('Progress').AsFloat := Round(Percent);
if Percent >= 99 then
begin
FDS.FieldByName('Status').AsString := AppState[asDownloaded];
end;
FDS.Post;
end;
finally
FDS.EnableControls;
end;
end;
end;
procedure TPipelineHolder.WndProc(var Message: TMessage);
begin
Dispatch(Message);
inherited;
end;
procedure TPipelineHolder.JobDone;
begin
FPipeline := nil;
FInProcess := False;
end;
procedure TPipelineHolder.Make(SourceDS: TDataSet);
var
BM : TBookmark;
RecInfo : TRecordInfo;
begin
if SourceDS = nil then Exit;
if not SourceDS.Active then Exit;
if SourceDS.IsEmpty then Exit;
FDS := SourceDS;
FInProcess := True;
// Here at first stage calling Retrive or RetrieveAll gives same effect, no
// matter what we uses value or queue.
FPipeline := Parallel.Pipeline
.Stage(RetrieveAll, //Retrieve
Parallel.TaskConfig.OnMessage(Self)).NumTasks(Environment.Process.Affinity.Count * 2)
.Stage(Install)
.OnStop(JobDone)
.Run;
// Get URLs to be downloaded
BM := FDS.GetBookmark;
FDS.DisableControls;
try
FDS.First;
while not FDS.Eof do
begin
// Get data from database
RecInfo.URL := Trim(FDS.FieldByName('url').AsString);
RecInfo.Id := Trim(FDS.FieldByName('id').AsString);
RecInfo.Cmd := Trim(FDS.FieldByName('silent_parameters').AsString);
RecInfo.Filename := ExtractFilePath(ParamStr(0)) + 'Downloads\' + Trim(FDS.FieldByName('target_file').AsString);
if RecInfo.URL = EmptyStr then
begin
// Skips empty URLs
FDS.Next;
Continue;
end;
FDS.Edit;
FDS.FieldByName('Status').AsString := AppState[asDownloading];
FDS.Post;
FPipeline.Input.Add(TOmniValue.FromRecord<TRecordInfo>(RecInfo));
FDS.Next;
end;
finally
if FDS.BookmarkValid(BM) then SourceDS.GotoBookmark(BM);
FDS.FreeBookmark(BM);
FDS.EnableControls;
end;
FPipeline.Input.CompleteAdding;
// Wait for pipeline to complete - I'm not use it to avoid GUI freezing
// FPipeline.WaitFor(INFINITE);
end;
constructor TDBAppItem.Create(const OwnerHandle: HWND; var DS: TDataSet; const URL, ID, Cmd, TargetFilename: string);
begin
inherited Create;
FDS := DS;
FURL := URL;
FId := ID;
FCmd := Cmd;
FFilename := TargetFilename;
FHandle := OwnerHandle;
FFileSize := -1;
FDownloaded := 0;
end;
destructor TDBAppItem.Destroy;
begin
FDS := nil;
inherited;
end;
function TDBAppItem.Download: Boolean;
var
path : string;
begin
path := ExtractFilePath(FFilename);
if not DirectoryExists(path) then
if not ForceDirectories(path) then
raise Exception.Create('Cannot create directory: "'+path+'".');
if FileExists(FFilename) then
try
if not DeleteFile(FFilename) then
raise Exception.Create('Cannot delete file: "'+FFilename+'".');
except on E: Exception do
raise Exception.Create('Cannot delete file: "'+FFilename+'".'+sLineBreak + E.Message);
end;
Result := DownloadFile(FURL, FFilename);
if Result then Result := FileExists(FFilename);
end;
function TDBAppItem.DownloadFile(const url, TargetFileName: string): boolean;
var
hInet: HINTERNET;
hFile: HINTERNET;
localFile: file;
buffer: array[1..65535] of Byte;
bytesRead: DWORD;
b: boolean;
begin
b := False;
if FFileSize < 0 then FFileSize := GetRemoteFileSize(url);
FDownloaded := 0;
hInet := WinInet.InternetOpen('MyFileAgent', INTERNET_OPEN_TYPE_DIRECT, nil, nil, 0);
if Assigned(hInet) then
begin
hFile := InternetOpenURL(hInet, PChar(url), nil, 0, INTERNET_FLAG_PRAGMA_NOCACHE, 0);
if Assigned(hFile) then
begin
AssignFile(localFile, TargetFileName);
Rewrite(localFile, 1);
bytesRead := 0;
repeat
InternetReadFile(hFile, #buffer, SizeOf(buffer), bytesRead);
BlockWrite(localFile, buffer, bytesRead);
Inc(FDownloaded, bytesRead);
//In real app this event occurs in TALWinHttpClient from Alcinoe library.
InternalDownloadProgress(Self, FDownloaded, FFileSize);
until bytesRead = 0;
CloseFile(localFile);
InternetCloseHandle(hFile);
end;
InternetCloseHandle(hInet);
b := true;
end;
DownloadFile := b;
FFileSize := -1;
FDownloaded := 0;
end;
function TDBAppItem.GetRemoteFileSize(const Url: string): Integer;
const
sUserAgent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36';
var
hInet : HINTERNET;
hConnect : HINTERNET;
hRequest : HINTERNET;
lpdwBufferLength: DWORD;
lpdwReserved : DWORD;
ServerName, Resource: string;
// Prot, Host, User, Pass, Path, Extra: string;
ErrorCode : Cardinal;
begin
Result := -1;
ParseURL(Url, ServerName, Resource);
hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if hInet=nil then
begin
ErrorCode:=GetLastError;
raise Exception.Create(Format('InternetOpen Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
end;
try
hConnect := InternetConnect(hInet, PChar(ServerName), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
if hConnect=nil then
begin
ErrorCode:=GetLastError;
raise Exception.Create(Format('InternetConnect Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
end;
try
hRequest := HttpOpenRequest(hConnect, PChar('HEAD'), PChar(Resource), nil, nil, nil, 0, 0);
if hRequest<>nil then
begin
try
lpdwBufferLength := SizeOf(Result);
lpdwReserved := 0;
if not HttpSendRequest(hRequest, nil, 0, nil, 0) then
begin
ErrorCode := GetLastError;
raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
end;
if not HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER, #Result, lpdwBufferLength, lpdwReserved) then
begin
Result := 0;
ErrorCode := GetLastError;
raise Exception.Create(Format('HttpQueryInfo Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
end;
finally
InternetCloseHandle(hRequest);
end;
end
else
begin
ErrorCode:=GetLastError;
raise Exception.Create(Format('HttpOpenRequest Error %d Description %s',[ErrorCode,GetWinInetError(ErrorCode)]));
end;
finally
InternetCloseHandle(hConnect);
end;
finally
InternetCloseHandle(hInet);
end;
end;
function TDBAppItem.GetWinInetError(ErrorCode: Cardinal): string;
const
winetdll = 'wininet.dll';
var
Len: Integer;
Buffer: PChar;
begin
Len := FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM or
FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_IGNORE_INSERTS or FORMAT_MESSAGE_ARGUMENT_ARRAY,
Pointer(GetModuleHandle(winetdll)), ErrorCode, 0, #Buffer, SizeOf(Buffer), nil);
try
while (Len > 0) and (CharInSet(Buffer[Len - 1], [#0..#32, '.'])) do Dec(Len);
SetString(Result, Buffer, Len);
finally
LocalFree(HLOCAL(Buffer));
end;
end;
procedure TDBAppItem.ParseURL(const lpszUrl: string; var Host,
Resource: string);
var
lpszScheme : array[0..INTERNET_MAX_SCHEME_LENGTH - 1] of Char;
lpszHostName : array[0..INTERNET_MAX_HOST_NAME_LENGTH - 1] of Char;
lpszUserName : array[0..INTERNET_MAX_USER_NAME_LENGTH - 1] of Char;
lpszPassword : array[0..INTERNET_MAX_PASSWORD_LENGTH - 1] of Char;
lpszUrlPath : array[0..INTERNET_MAX_PATH_LENGTH - 1] of Char;
lpszExtraInfo : array[0..1024 - 1] of Char;
lpUrlComponents : TURLComponents;
begin
ZeroMemory(#lpszScheme, SizeOf(lpszScheme));
ZeroMemory(#lpszHostName, SizeOf(lpszHostName));
ZeroMemory(#lpszUserName, SizeOf(lpszUserName));
ZeroMemory(#lpszPassword, SizeOf(lpszPassword));
ZeroMemory(#lpszUrlPath, SizeOf(lpszUrlPath));
ZeroMemory(#lpszExtraInfo, SizeOf(lpszExtraInfo));
ZeroMemory(#lpUrlComponents, SizeOf(TURLComponents));
lpUrlComponents.dwStructSize := SizeOf(TURLComponents);
lpUrlComponents.lpszScheme := lpszScheme;
lpUrlComponents.dwSchemeLength := SizeOf(lpszScheme);
lpUrlComponents.lpszHostName := lpszHostName;
lpUrlComponents.dwHostNameLength := SizeOf(lpszHostName);
lpUrlComponents.lpszUserName := lpszUserName;
lpUrlComponents.dwUserNameLength := SizeOf(lpszUserName);
lpUrlComponents.lpszPassword := lpszPassword;
lpUrlComponents.dwPasswordLength := SizeOf(lpszPassword);
lpUrlComponents.lpszUrlPath := lpszUrlPath;
lpUrlComponents.dwUrlPathLength := SizeOf(lpszUrlPath);
lpUrlComponents.lpszExtraInfo := lpszExtraInfo;
lpUrlComponents.dwExtraInfoLength := SizeOf(lpszExtraInfo);
InternetCrackUrl(PChar(lpszUrl), Length(lpszUrl), ICU_DECODE or ICU_ESCAPE, lpUrlComponents);
Host := lpszHostName;
Resource := lpszUrlPath;
end;
procedure TDBAppItem.InternalDownloadProgress(Sender: TObject; Read,
Total: Integer);
var
MsgRec : PProgressInfo;
begin
// Create progress changed message
New(MsgRec);
MsgRec^.ID := Fid;
MsgRec^.Read := Read;
MsgRec^.Total := Total;
MsgRec^.URL := FURL;
SendMessage(FHandle, WM_PROGRESSCHANGED, 0, LongInt(MsgRec));
end;
end.
My basic idea is creating pipeline with 2 stages:
Retrieve: downloading all files at the same time (threads count is constrained by NumTasks from OTL).
Install: As soon as any file be downloaded, it must be processed by this stage. Action in this stage must be one by one, i.e. only one action in same time (in real app I won't start many installers together).
I try to understand how OTL works here, but I have not many experience with this library yet.
So, dear community, how I must rewrite my code to:
Have parallel downloadings at Stage 1 (now it works one-by-one).
Have possibility to correctly stop Pipeline with GUI (now I call FPipeline.Cancel by TButton pressing and it cannot stop tasks immediately).
Sources also placed here.
Thanks in advance. I'll be glad meet any advice here.
1) Download works in parallel just fine - as far as OTL is concerned. On my machine the test app starts three parallel downloads each time I press F9. The other two downloads get stuck in the
hFile := InternetOpenURL(hInet, PChar(url), nil, 0, INTERNET_FLAG_PRAGMA_NOCACHE, 0);
call. IOW, all five downloader threads enter InternetOpenURL, but only three exit immediately and start downloading. I have no idea why (and it is related to WinINET, not OTL).
2) Cancellation doesn't work because noone tells the DownloadFile method to stop. IOmniPipeline.Cancel just calls CompleteAdding on each pipeline and tells each stage to stop processing input. It cannot stop the code which is already working on an input element (i.e. your DownloadFile method). You must do that yourself.
One way to do that is to create a global Cancel flag and change DownloadFile so that it checks whether this flag is set in the following loop:
repeat
InternetReadFile(hFile, #buffer, SizeOf(buffer), bytesRead);
BlockWrite(localFile, buffer, bytesRead);
Inc(FDownloaded, bytesRead);
//In real app this event occurs in TALWinHttpClient from Alcinoe library.
InternalDownloadProgress(Self, FDownloaded, FFileSize);
if FCancelled then break; // <-----------
until bytesRead = 0;
You could also change InternalDownloadProgress and add a var cancelled: boolean parameter which could be set in the event handler when the pipeline needs to shut down.

Inno Setup Finding folder and using multiple choice of directories

I have a big problem to solve which is if you have a directory ..\App which has two folders but you don't know the folders names:
C:\Program Files (x86)\App\EFRTJKD
C:\Program Files (x86)\App\UDSIDJF
How can Inno script help identify the EFRTJKD and UDSIDJF and show them as choices in the installation page? Instead of the Browse directory option?
The two folders both have a file named Program.exe and Version.txt. The Version.txt contains a description of the folder. I want to display the description in the folder selection.
Thank you very much. I really appreciate your help.
Use FindFirst/FindNext to find your folders.
And then you can put them for example to TNewCheckListBox. Hide the DirEdit. And update its hidden contents based on what user selected in the TNewCheckListBox
[Code]
var
DirCheckListBox: TNewCheckListBox;
Dirs: TStringList;
procedure DirCheckListBoxClick(Sender: TObject);
begin
{ When user changes selection, update the path in hidden edit box }
WizardForm.DirEdit.Text := Dirs[DirCheckListBox.ItemIndex];
end;
procedure InitializeWizard();
var
FindRec: TFindRec;
RootPath: string;
Path: string;
Name: AnsiString;
begin
DirCheckListBox := TNewCheckListBox.Create(WizardForm);
DirCheckListBox.Parent := WizardForm.DirEdit.Parent;
DirCheckListBox.Top := WizardForm.SelectDirBrowseLabel.Top;
DirCheckListBox.Left := WizardForm.DirEdit.Left;
DirCheckListBox.Width := WizardForm.DirEdit.Width;
DirCheckListBox.Height :=
WizardForm.DiskSpaceLabel.Top - DirCheckListBox.Top - ScaleY(8);
DirCheckListBox.Color := WizardForm.TasksList.Color;
DirCheckListBox.WantTabs := WizardForm.TasksList.WantTabs;
DirCheckListBox.MinItemHeight := WizardForm.TasksList.MinItemHeight;
DirCheckListBox.ParentColor := WizardForm.TasksList.ParentColor;
DirCheckListBox.BorderStyle := WizardForm.TasksList.BorderStyle;
WizardForm.DirEdit.Visible := False;
WizardForm.DirBrowseButton.Visible := False;
WizardForm.SelectDirBrowseLabel.Visible := False;
RootPath := ExpandConstant('{pf}\App');
Dirs := TStringList.Create;
if FindFirst(RootPath + '\*', FindRec) then
begin
repeat
if ((FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY) <> 0) and
(FindRec.Name <> '.') and
(FindRec.Name <> '..') then
begin
Path := RootPath + '\' + FindRec.Name;
{ LoadStringFromFile can handle only ascii/ansi files, no Unicode }
if LoadStringFromFile(Path + '\' + 'version.txt', Name) then
begin
Dirs.Add(Path);
DirCheckListBox.AddRadioButton(Name, '', 0, False, True, nil);
{ If already installed, check the path that was selected previously, }
{ otherwise check the first one }
if (DirCheckListBox.Items.Count = 1) or
(CompareText(WizardForm.DirEdit.Text, Path) = 0) then
begin
DirCheckListBox.ItemIndex := DirCheckListBox.Items.Count - 1;
DirCheckListBox.Checked[DirCheckListBox.ItemIndex] := True;
end;
end;
end;
until not FindNext(FindRec);
end;
if DirCheckListBox.Items.Count = 0 then
begin
RaiseException('No folder found.');
end;
DirCheckListBox.OnClickCheck := #DirCheckListBoxClick;
DirCheckListBoxClick(nil);
end;

Inno setup Query in Skip Pages functionality

I am creating a set up which involves in conditionally skipping pages based on a value I get in the 3rd page. I create all the possible pages in the InitializeWizard() and while debugging I can see all the pages created. When I try to skip pages in the ShouldSkipPage, I am unable to skip as I expected.
At which scenario a page becomes invalid or is there any way to check through log or something about the status of the page.
P.S - The page is very much valid and available and I am only not able to skip it.
The code snippet is given below...
[Code]
var
PageSQL2008SetupFile2: Integer;
PageSQL2008R2SetupFile2: Integer;
PageSQL2012SetupFile2: Integer;
PageSQL2014SetupFile2: Integer;
procedure InitializeWizard();
begin
MyModeTypical := false;
PageProductName := CreateInputQueryPage(wpSelectComponents,
'Settings',
'Product Name',
'Please specify the product name, then click Next.');
PageProductName.Add('Product Name:', False);
PageProductName.Values[0] := ProductName;
//SQL Server selection page
PageSQLServerSelection := CreateInputOptionPage(PageProductName.ID,
'SQL Server Selection', 'Please select the version of SQL Server you want to install.',
'',
True, False);
PageSQLServerSelection.Add('SQL 2008');
PageSQLServerSelection.Add('SQL 2008R2');
PageSQLServerSelection.Add('SQL 2012');
PageSQLServerSelection.Add('SQL 2014');
PageSQLServerSelection.Values[0] := False;
PageSQLServerSelection.Values[1] := False;
PageSQLServerSelection.Values[2] := False;
PageSQLServerSelection.Values[3] := False;
// Creating Setup pages for the 4 servers
PageSQL2008SetupFile2 := MSSQL2008SETUPDIR_CreatePage(PageSQLServerSelection.ID);
PageSQL2008R2SetupFile2 := MSSQL2008R2SETUPDIR_CreatePage(PageSQL2008SetupFile2);
PageSQL2012SetupFile2 := MSSQL2012SETUPDIR_CreatePage(PageSQL2008R2SetupFile2);
PageSQL2014SetupFile2 := MSSQL2014SETUPDIR_CreatePage(PageSQL2012SetupFile2);
// some more logic...
end;
// Function for creating the Setup pages for 4 SQL Servers (Same logic for all 4 servers)
function MSSQL2008SETUPDIR_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
BMPFile: String;
begin
SelectedSQLServerVersion := GetSQLServerVersion('');
Page := CreateCustomPage(
PreviousPageId,
ExpandConstant('Database Settings'),
ExpandConstant('Special note for the Microsoft SQL Server 2008 Setup')
);
BMPFile:= ExpandConstant('{tmp}\caution.bmp');
if not FileExists(BMPFile) then ExtractTemporaryFile(ExtractFileName(BMPFile));
{ BitmapImage1 }
BitmapImage1 := TBitmapImage.Create(Page);
with BitmapImage1 do
begin
Bitmap.LoadFromFile(BMPFile);
Parent := Page.Surface;
Left := ScaleX(8);
Top := ScaleY(8);
Width := ScaleX(97);
Height := ScaleY(209);
end;
{ NewStaticText1 }
NewStaticText1 := TNewStaticText.Create(Page);
with NewStaticText1 do
begin
Parent := Page.Surface;
Caption := 'To install Microsoft SQL Server 2008 you have to insert the Microsoft SQL Server 2008 Setup CD, when XXX setup requests it. The installation path of your Microsoft SQL Server 2008 setup and the additional Service Packs can be defined on the next pages.';
Left := ScaleX(112);
Top := ScaleY(8);
Width := ScaleX(292);
Height := ScaleY(77);
AutoSize := False;
TabOrder := 0;
WordWrap := True;
end;
{ NewStaticText2 }
NewStaticText2 := TNewStaticText.Create(Page);
with NewStaticText2 do
begin
Parent := Page.Surface;
Caption :=
'CAUTION: When autorun is activated, dont click in the autorun menu of Microsoft SQL Server 2008.' + #13 +
'Otherwise you must reboot your machine and restart the setup !';
Left := ScaleX(112);
Top := ScaleY(88);
Width := ScaleX(293);
Height := ScaleY(62);
AutoSize := False;
Font.Color := -16777208;
Font.Height := ScaleY(-11);
Font.Name := 'Tahoma';
Font.Style := [fsBold];
ParentFont := False;
TabOrder := 1;
WordWrap := True;
end;
with Page do
begin
//OnActivate := #MSSQLSETUPDIR_Activate;
//OnShouldSkipPage := #MSSQLSETUPDIR_ShouldSkipPage;
//OnBackButtonClick := #MSSQLSETUPDIR_BackButtonClick;
//OnNextButtonClick := #MSSQLSETUPDIR_NextButtonClick;
//OnCancelButtonClick := #MSSQLSETUPDIR_CancelButtonClick;
end;
Result := Page.ID;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
var
begin
if PageID = wpSelectComponents then begin
Result := MyModeTypical;
end else if PageID = PageProductName.ID then begin
Result := MyModeTypical;
end;
Log('Server is...' + SelectedSQLServerVersion);
if SelectedSQLServerVersion <> '' then begin
// Database Settings Warning
if (SQLServer2008Flag = True) and (IsComponentSelected('DBS\SERVER')) then begin
if PageID = PageSQL2008SetupFile2 then begin
Result := false;
end else if PageID = PageSQL2008R2SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2012SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2014SetupFile2 then begin
Result := true;
end;
end else if (SQLServer2008R2Flag = True) and (IsComponentSelected('DBS\SERVER')) then begin
if PageID = PageSQL2008R2SetupFile2 then begin
Result := false;
end else if PageID = PageSQL2012SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2014SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2008SetupFile2 then begin
Result := true;
end;
end else if (SQLServer2012Flag = True) and (IsComponentSelected('DBS\SERVER')) then begin
if PageID = PageSQL2012SetupFile2 then begin
Result := false;
// This PageSQL2014SetupFile2 is not skipped.....
end else if PageID = PageSQL2014SetupFile2 then begin
Result := true;
// This PageSQL2008SetupFile2 and PageSQL2008R2SetupFile2 are skipped properly.....
end else if PageID = PageSQL2008SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2008R2SetupFile2 then begin
Result := true;
end;
end else if (SQLServer2014Flag = True) and (IsComponentSelected('DBS\SERVER')) then begin
if PageID = PageSQL2014SetupFile2 then begin
Result := false;
end else if PageID = PageSQL2008SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2008R2SetupFile2 then begin
Result := true;
end else if PageID = PageSQL2012SetupFile2 then begin
Result := true;
end;
end;
// some more logic
end;
DeeJay

Read strings from .exe files(Like Strings.exe) in Delphi

I want to write a program for read/extract all of the valid strings in a .exe files (For example: "This program must be run under Win" or "MZ"), Exactly like Strings.exe of sysinternals.
Actually i want to scan a .exe file and if that contain special string value such as "ekrn.exe" or "Filrefox.exe" then detect that file as a suspicious file (Killing ekrn.exe or inject malcode to firefox.exe).
I wrote the following code in Delphi :
const
TargetName = 'E:\AntiDebugg.exe';
var
hFile: THandle;
tmp: AnsiString;
dwFileSize, lChar, lSearch: Integer;
dwNumRead: Cardinal;
dwBuffer: array of AnsiChar;
begin
mmo1.Clear;
hFile := CreateFileA(TargetName, GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
dwFileSize := GetFileSize(hFile, nil);
SetLength(dwBuffer, dwFileSize);
lChar := 0;
lSearch := 0;
while lChar <= dwFileSize do
begin
ReadFile(hFile, dwBuffer[lChar], SizeOf(dwBuffer), dwNumRead, nil);
while dwBuffer[lChar] <> '' do
begin
tmp := tmp + dwBuffer[lChar];
Inc(lChar, 1);
end;
lSearch := 0;
Inc(lChar, 1);
end;
mmo1.Text := (tmp);
CloseHandle(hFile);
The result of running my code is (A small piece):
MZPےے¸#؛´ ح!¸Lح!گگThis program must be run under Win32
$7PEL
%0فQà´أ\
¤"0Bگب.textd­ .itext| .data`#.bssطN.idata\
#.didataب#.tls.rdata#.reloc¤"#.rsrc###Boolean#alseTrueSystem4#AnsiCharP# Charےh#Integerے€#Byteک#Wordے°#Pointerؤ#Cardinalےےےà# NativeIntےےےü#
NativeUIntے#ShortStringے,# PAnsiChar0#D#stringT#TClassŒ#h#HRESULTے€#TGUID
But this isn't my desired result and my desired result is :
MZP
This program must be run under Win32
.text
`.itext
`.data
.bss
.idata
.didata
.tls
.rdata
#.reloc
B.rsrc
Boolean
False
True
System
AnsiChar
Char
Integer
Byte
Word
Pointer
Cardinal
NativeInt
NativeUInt
ShortString
PAnsiChar0
string
TClass
HRESULT
TGUID
In this example the AntiDebugg.exe compiled by Delphi .
The result of Strings.exe for strings of "AntiDebugg.exe"
Any idea ?
What should i to do ?
Try something like this:
const
TargetName = 'E:\AntiDebugg.exe';
MinStringLength = 2;
var
hFile: THandle;
hMapping: THandle;
pView: Pointer;
dwFileSize: DWORD;
pCurrent, pEOF, pStart: PAnsiChar;
iLen: Integer;
begin
mmo1.Clear;
hFile := CreateFile(TargetName, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
dwFileSize := GetFileSize(hFile, nil);
if dwFileSize = $FFFFFFFF then RaiseLastOSError;
hMapping := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, dwFileSize, nil);
if hMapping = 0 then RaiseLastOSError;
try
pView := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, dwFileSize);
if pView = nil then RaiseLastOSError;
try
pCurrent := PAnsiChar(pView);
pEOF := pCurrent + dwFileSize;
pStart := nil;
while pCurrent < pEOF do
begin
if pCurrent^ in [#9, #10, #13, #32..#128] then
begin
if pStart = nil then
pStart := pCurrent;
end
else if pStart <> nil then
begin
iLen := Integer(pCurrent - pStart);
if iLen >= MinStringLength then
begin
SetString(tmp, pStart, iLen);
mmo1.Lines.Add(tmp);
end;
pStart := nil;
end;
Inc(pCurrent);
end;
finally
UnmapViewOfFile(pView);
end;
finally
CloseHandle(hMapping);
end;
finally
CloseHandle(hFile);
end;
end;
AsciiDump coded by {steve10120#ic0de.org}
function FileToPtr(szFilePath: string; var pFile: Pointer;
var dwFileSize: DWORD): Boolean;
var
hFile: DWORD;
dwRead: DWORD;
begin
Result := FALSE;
hFile := CreateFile(PChar(szFilePath), GENERIC_READ, 0, nil,
OPEN_EXISTING, 0, 0);
if (hFile <> INVALID_HANDLE_VALUE) then
begin
dwFileSize := GetFileSize(hFile, nil);
if (dwFileSize > 0) then
begin
pFile := VirtualAlloc(nil, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (Assigned(pFile)) then
begin
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
ReadFile(hFile, pFile^, dwFileSize, dwRead, nil);
if (dwRead = dwFileSize) then
Result := TRUE;
end;
end;
CloseHandle(hFile);
end;
end;
function FindASCIIStringsA(szFilePath: string; dwMinLength: DWORD;
szDumpPath: string): Boolean;
var
pFile: Pointer;
dwFileSize: DWORD;
i: DWORD;
szDump: string;
dwLength: DWORD;
hFile: TextFile;
begin
Result := FALSE;
if (FileToPtr(szFilePath, pFile, dwFileSize)) then
begin
dwLength := 0;
AssignFile(hFile, szDumpPath);
// yeah I don't like it but its easiest for writing lines..
Rewrite(hFile);
for i := 0 to (dwFileSize - 1) do
begin
if (PByte(DWORD(pFile) + i)^ in [$20 .. $7E]) then
begin
szDump := szDump + Char(PByte(DWORD(pFile) + i)^);
// WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
Inc(dwLength);
end
else
begin
if (dwLength >= dwMinLength) then
WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
dwLength := 0;
szDump := '';
end;
end;
if (FileSize(hFile) > 0) then
Result := TRUE;
CloseFile(hFile);
VirtualFree(pFile, 0, MEM_RELEASE);
end;
end;
function FindASCIIStrings(szFilePath:string; dwMinLength:DWORD; szDumpPath:string):Boolean;
var
pFile: Pointer;
dwFileSize: DWORD;
IDH: PImageDosHeader;
INH: PImageNtHeaders;
i: DWORD;
szDump: string;
dwLength: DWORD;
hFile: TextFile;
begin
Result := FALSE;
if (FileToPtr(szFilePath, pFile, dwFileSize)) then
begin
IDH := pFile;
if (IDH^.e_magic = IMAGE_DOS_SIGNATURE) then
begin
INH := Pointer(DWORD(pFile) + IDH^._lfanew);
if (INH^.Signature = IMAGE_NT_SIGNATURE) then
begin
dwLength := 0;
AssignFile(hFile, szDumpPath); // yeah I don't like it but its easiest for writing lines..
Rewrite(hFile);
for i := INH^.OptionalHeader.SizeOfHeaders to (dwFileSize - 1) do
begin
if (PByte(DWORD(pFile) + i)^ in [$20..$7E]) then
begin
szDump := szDump + Char(PByte(DWORD(pFile) + i)^);
Inc(dwLength);
end
else
begin
if (dwLength >= dwMinLength) then
WriteLn(hFile, '0x' + IntToHex(i - dwLength, 8) + ': ' + szDump);
dwLength := 0;
szDump := '';
end;
end;
if (FileSize(hFile) > 0) then
Result := TRUE;
CloseFile(hFile);
end;
end;
VirtualFree(pFile, 0, MEM_RELEASE);
end;
end;
procedure TForm2.btn1Click(Sender: TObject);
begin
FindASCIIStrings('e:\AntiDebugg.exe', 2,
IncludeTrailingPathDelimiter(ExtractFilePath(param str(0))) +
ExtractFileName(paramstr(1)) + '.dmp')
end;

Delphi XE4 - get your local IP in statusbar

Seen most of the examples posted here but none seem to work on XE4. I am trying to display my IP in the status bar.
AdvOfficeStatusBar1.Panels[0].Text := GetIP;
Functions I have seen here mostly fail on xe4 as they were written in older delphi versions.I would like to get just the local IP,not the one behind the router. Well, for the sake of learning, it would be usefull to know that one too...:)
I tried this :
function getIP: string;
type
TaPInAddr = array [0..10] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
phe: PHostEnt;
pptr: PaPInAddr;
Buffer: array [0..63] of char;
i: Integer;
GInitData: TWSADATA;
begin
WSAStartup($101, GInitData);
Result := '';
GetHostName(Buffer, SizeOf(Buffer));
phe :=GetHostByName(buffer);
if phe = nil then Exit;
pptr := PaPInAddr(Phe^.h_addr_list);
i := 0;
while pptr^[i] <> nil do
begin
result:=StrPas(inet_ntoa(pptr^[i]^));
Inc(i);
end;
WSACleanup;
end;
but it would not work .... many others too ... Seems i am missing something,but what?
Tried this too :
function getIPs: Tstrings;
type
TaPInAddr = array[0..10] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
phe: PHostEnt;
pptr: PaPInAddr;
Buffer: array[0..63] of Char;
I: Integer;
GInitData: TWSAData;
begin
WSAStartup($101, GInitData);
Result := TstringList.Create;
Result.Clear;
GetHostName(Buffer, SizeOf(Buffer));
phe := GetHostByName(buffer);
if phe = nil then Exit;
pPtr := PaPInAddr(phe^.h_addr_list);
I := 0;
while pPtr^[I] <> nil do
begin
Result.Add(inet_ntoa(pptr^[I]^));
Inc(I);
end;
WSACleanup;
end;
wont work stops on : (GetHostName(Buffer, SizeOf(Buffer));
This however WORKS ! :
Function GetIPAddress: String;
type pu_long = ^u_long;
var varTWSAData : TWSAData;
varPHostEnt : PHostEnt;
varTInAddr : TInAddr;
namebuf : Array[0..255] of ansichar;
begin
try
try
If WSAStartup($101,varTWSAData) <> 0 Then
Result := ''
Else Begin
gethostname(namebuf,sizeof(namebuf));
varPHostEnt := gethostbyname(namebuf);
varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
Result := inet_ntoa(varTInAddr);
End;
except
Result := '';
end;
finally
WSACleanup;
end;
end;
Function GetIPAddress: String;
type pu_long = ^u_long;
var varTWSAData : TWSAData;
varPHostEnt : PHostEnt;
varTInAddr : TInAddr;
namebuf : Array[0..255] of ansichar;
begin
try
try
If WSAStartup($101,varTWSAData) <> 0 Then
Result := ''
Else Begin
gethostname(namebuf,sizeof(namebuf));
varPHostEnt := gethostbyname(namebuf);
varTInAddr.S_addr := u_long(pu_long(varPHostEnt^.h_addr_list^)^);
Result := inet_ntoa(varTInAddr);
End;
except
Result := '';
end;
finally
WSACleanup;
end;
end;

Resources