How to make “serialize” and “unserialize” an array in delphi? - arrays

I need to do “serialize” and “unserialize” on delphi, like on php, so that the resulting array is then stored in the site database. Those. I need to get something like this kind.
a:10:{i:0;a:3:{i:0;s:4:"Obj1";i:1;i:383;i:2;i:339;}i:1;a:3:{i:0;s:4:"Obj2";i:1;i:339;i:2;i:18;}i:2;a:3:{i:0;s:4:"Obj3";i:1;i:386;i:2;i:98;}i:3;a:3:{i:0;s:4:"Obj4";i:1;i:428;i:2;i:286;}i:4;a:3:{i:0;s:4:"Obj5";i:1;i:54;i:2;i:47;}i:5;a:3:{i:0;s:4:"Obj6";i:1;i:328;i:2;i:27;}i:6;a:3:{i:0;s:4:"Obj7";i:1;i:332;i:2;i:371;}i:7;a:3:{i:0;s:4:"Obj8";i:1;i:42;i:2;i:187;}i:8;a:3:{i:0;s:4:"Obj9";i:1;i:314;i:2;i:68;}i:9;a:3:{i:0;s:5:"Obj10";i:1;i:124;i:2;i:120;}}
Here is my code adding an object in delphi:
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Contnrs, StdCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TRect = class
private
p:TPoint;
width, height: Integer;
color : TColor;
str:String;
public
constructor Create(APt:TPoint;w1, h1: Integer; _Color:TColor;str1:String);
Procedure Draw(Cs:TCanvas;x:Integer;y:Integer);
end;
TRectList=class(TObjectList)
private
function GetItems(index:integer):TRect;
procedure SetItems(index:integer; Value:TRect);
public
property Items[index:integer]: TRect read GetItems write SetItems; default;
end;
var
Form1: TForm1;
ImPic : TBitmap;
RectList: TRectList;
rect : TRect;
start : TPoint;
ObjId:Integer;
implementation
{$R *.dfm}
constructor TRect.Create(APt:TPoint; w1, h1: Integer; _Color: TColor;str1:String);
begin
p:=APt;
width := w1;
height := h1;
color := _Color;
str:=str1;
end;
procedure TRect.Draw(Cs:TCanvas; x, y: Integer);
begin
Cs.Brush.Color:=color;
Cs.Rectangle(p.X,p.Y,p.X+width,p.Y+height);
end;
procedure TForm1.Button1Click(Sender: TObject);
var i,x,y:Integer;
begin
x := Random(500);
y := Random(400);
ObjId := ObjId+1;
start := Point(x, y);
rect := TRect.Create(start,20,20,clBlue,'Obj'+IntToStr(ObjId));
RectList.Add(rect);
Memo1.Lines.Clear;
Image1.Canvas.Brush.Color:=clWhite;
Image1.Canvas.FillRect(Image1.ClientRect);
Memo1.Lines.Add('[');
for i := 0 to RectList.Count-1 do
begin
RectList[i].Draw(Image1.Canvas,RectList[i].p.X,RectList[i].p.Y);
Memo1.Lines.Add('["'+RectList[i].str+'",'+IntToStr(RectList[i].p.X)+','+IntToStr(RectList[i].p.Y)+'],');
end;
Memo1.Lines.Add(']');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
RectList := TRectList.Create(true);
randomize();
with Image1.Canvas do
begin
brush.Color:=clSilver;
Rectangle(0,0,width,height);
end;
end;
function TRectList.GetItems(index: integer):TRect;
begin
Result := inherited Items[index] as TRect;
end;
procedure TRectList.SetItems(index: integer; Value: TRect);
begin
inherited Items[index] := Value;
end;
end.
An array is generated in “memo”. Example in the picture:
img
Then you can add this array to the php file, apply serialize, and you get the array that is shown at the top of the post. Php code:
$array = [
["Obj1",383,339],
["Obj2",339,18],
["Obj3",386,98],
["Obj4",428,286],
["Obj5",54,47],
["Obj6",328,27],
["Obj7",332,371],
["Obj8",42,187],
["Obj9",314,68],
["Obj10",124,120],
]
;
$string = serialize( $array );
echo $string."<br>";
$array = unserialize($string);
echo "<pre>";
var_dump($array);
echo "</pre>";
?>
How to do the same thing on delphi “serialize” and “unserialize”, for subsequent storage in the mysql database on the site?

Related

Delphi: TDBGrid not update

I had a form which has a DataSource,AdoQuery,AdoConnection,DBgrid plus couple edit and a memo.
User enter his username,street address, etc.. and hit 'save' button. On that time the application is write the details in a comma separated txt file, which is connected to an Access linked table. When user hit 'save' button it write into the memo instantly but not live update the dbgrid database, only when reopen the app.
I search a lot but everybody has different suggestions: do a dbgrid refresh, adorequery, post, append, showmodal, open and close the database etc.
My question is why the dbgrid liveupdate doesn't work?
Sourcecode is the following:
unit test;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, StrUtils, Grids, Buttons, pngimage, ExtCtrls,
ComCtrls, DBGrids, DB, DBTables, ColorGrd, DirOutln, ADODB,
FMTBcd, SqlExpr, DBCtrls, DBClient, jpeg;
type
TForm1 = class(TForm)
Memo1: TMemo;
exit: TButton;
resetbtn: TButton;
Label3: TLabel;
GroupBox1: TGroupBox;
Label7: TLabel;
Label8: TLabel;
GroupBox2: TGroupBox;
Label1: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Generate: TButton;
GroupBox3: TGroupBox;
Label5: TLabel;
Label6: TLabel;
CheckBox1: TCheckBox;
Image1: TImage;
Image2: TImage;
Button6: TButton;
DateTimePicker1: TDateTimePicker;
GroupBox4: TGroupBox;
Label10: TLabel;
Label9: TLabel;
dellastentry: TButton;
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
Label13: TLabel;
Label14: TLabel;
Label16: TLabel;
Label17: TLabel;
Label18: TLabel;
Label2: TLabel;
Label4: TLabel;
Label11: TLabel;
Label15: TLabel;
ADOQuery1Username: TWideStringField;
ADOQuery1RequestedNumber: TWideStringField;
ADOQuery1AllocatedNumber: TWideStringField;
ADOQuery1DateofRequest: TWideStringField;
procedure exitClick(Sender: TObject);
procedure resetbtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure dellastentryClick(Sender: TObject);
procedure GenerateClick(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function GetCurrentUserName: string;
const
cnMaxUserNameLen = 50;
var
sUserName: string;
dwUserNameLen: DWORD;
begin
dwUserNameLen := cnMaxUserNameLen - 1;
SetLength(sUserName, cnMaxUserNameLen);
GetUserName(PChar(sUserName), dwUserNameLen);
SetLength(sUserName, dwUserNameLen);
Result := sUserName;
end;
procedure TForm1.exitClick(Sender: TObject);
begin
Memo1.Lines.SaveToFile('C:\Numbergen\NumberDB.txt');
form1.Close;
end;
procedure TForm1.resetbtnClick(Sender: TObject);
begin
edit1.Clear;
edit2.Clear;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.LoadFromFile('C:\Numbergen\NumberDB.txt');
Memo1.WordWrap := true;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
form1.ShowModal;
form1.Free;
end;
procedure TForm1.FormShow(Sender: TObject);
var lStrings: TStringList;
begin
DataSource1.DataSet.Append;
Label6.Caption :=GetCurrentUserName;
lStrings := TStringList.Create;
LStrings.Delimiter := ',';
lStrings.DelimitedText := Memo1.Lines[Memo1.Lines.Count-1];
Label8.Caption:= lStrings.Strings[0];
Label13.Caption:= lStrings.Strings[1];
Label14.Caption:= lStrings.Strings[2];
Label15.Caption:= lStrings.Strings[3];
end;
procedure TForm1.dellastentryClick(Sender: TObject);
begin
Memo1.Lines.Delete(Memo1.Lines.Count-1);
end;
procedure TForm1.Button6Click(Sender: TObject);
var val2, sum: Integer;
begin
val2 := StrToInt(Edit3.Text);
sum := val2;
Edit3.Text := (IntToStr(sum+1));
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if checkbox1.Checked
then
Edit1.Text := Label6.Caption
else Edit1.Text :='';
end;
procedure TForm1.GenerateClick(Sender: TObject);
var val1, val2, sum: Integer;
begin
val1 := StrToInt(Edit2.Text);
val2 := StrToInt(Edit3.Text);
sum := val1 + val2;
Edit3.Text := IntToStr(sum);
Memo1.Lines.SaveToFile('C:\Numbergen\NumberDB.txt');
if edit1.Text =('')
then MessageDlg('Invalid/Blank Username! Please enter one!',mtError, mbOKCancel, 0)
else
memo1.Lines.Add(edit1.Text+',' +edit2.Text+','+IntToStr(sum-val1)+'-'+edit3.text+ ','+formatdatetime('yyyy/mm/dd', datetimepicker1.date) );
end;
end.
Thank you
It's not the responsibility of a TDBGrid to save changes to data, you need to do that yourself. In other words, the reason your data isn't getting saved is that you aren't actually calling the "save" method of AdoQuery1. This method is actually called Post, so somewhere in your code, e.g. the non-existent "save" button's OnClick handler you should be doing
if AdoQuery1.State in [dsEdit, dsInsert] then
AdoQuery1.Post;
Btw, the reason your data gets saved at all with your current code is that certain dataset operations (not DBGrid ones) cause any changes to the dataset to be Posted. These include causing the dataset's logical cursor to scroll, e.g. by clicking in a different row in the DBGrid or closing the dataset. But it is extremely bad practice to rely on this - your form should always provide the user with an explicit way to save and cancel changes to a dataset.
Also btw, never write code like you have in your FormKeyDown - it is an accident waiting to happen.
As you can see here:
procedure TForm1.FormShow(Sender: TObject);
var lStrings: TStringList;
begin
DataSource1.DataSet.Append;
Label6.Caption :=GetCurrentUserName;
The DataSet is in dsInsert here , so you have to save changes.
There is two ways to do that:
Check the DataSet state as MartynA answer
Add a line DataSource.DataSet.Post; in your code.
btw, I don't see in your code that you save the data in the database.

Cannot split word into individual letters in a memo

unit frmDisplaySentence_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
type
TfrmDispOneChar = class(TForm)
edtCode: TEdit;
btnDisplay: TButton;
lblMsg: TLabel;
memOutput: TMemo;
procedure btnDisplayClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmDispOneChar: TfrmDispOneChar;
implementation
{$R *.dfm}
procedure TfrmDispOneChar.btnDisplayClick(Sender: TObject);
var
K, iLength : integer;
cOne : char;
sCode : string;
begin
sCode := edtCode.Text;
iLength := Length(sCode);
for K := 1 to iLength do
cOne := sCode[K];
memOutput.Lines.Add(cOne);
end;
This is my code to split a word into it's individual letters in an memo, each in separate lines.
My code only returns the last letter of the word. I feel this is very close but I do not know how to proceed further. Any help would be appreciated.
Delphi is not like ie Python where whitespace is significant and defines block. In Delphi you have to use begin and end to mark the block, in this case your for loop:
procedure TfrmDispOneChar.btnDisplayClick(Sender: TObject);
var
K, iLength : integer;
cOne : char;
sCode : string;
begin
sCode := edtCode.Text;
iLength := Length(sCode);
for K := 1 to iLength do begin
cOne := sCode[K];
memOutput.Lines.Add(cOne);
end;
end;

open multiple files via shell context menu as params

I want to select more than one text file in the windows explorer and open the files via context menu in my app. For one file I found the solution but for more files there some ideas but no (working) solutions.
Anyone here that has the answer?
Here is an example that i've just searched and collected from internet.
Aim: Select multiple folders in Windows Explorer and get list of these folders' names via a shell context menu item "SelectedFolders", or using SendTo menu or drag-and-drop folders from shell onto the application form.
Please put a listbox named lstSelectedFolders and a speed button named sbClearList.
The main form name is frmSelectedFolders.
Here we go.
/////////////////////////////////////////////////////////////
program selectedfolders;
uses
Windows, Messages, SysUtils, Forms,
uSelectedFolders in 'uSelectedFolders.pas' {frmSelectedFolders};
{$R *.res}
var
receiver: THandle;
i, result: integer;
s: string;
dataToSend: TCopyDataStruct;
Mutex : THandle;
begin
Mutex := CreateMutex(nil, True, 'SelectedFolders');
if (Mutex <> 0) and (GetLastError = 0) then
begin
Application.Initialize;
Application.Title := 'Selected Folders';
Application.CreateForm(TfrmSelectedFolders, frmSelectedFolders);
Application.Run;
if Mutex <> 0 then CloseHandle(Mutex);
end
else
begin
receiver := FindWindow(PChar('TfrmSelectedFolders'), PChar('Selected Folders'));
if receiver <> 0 then
begin
for i:=1 to ParamCount do
begin
s := trim(ParamStr(i));
if s <> '' then
begin
dataToSend.dwData := 0;
dataToSend.cbData := 1 + Length(s);
dataToSend.lpData := PChar(s);
result := SendMessage(receiver, WM_COPYDATA, Integer(Application.Handle), Integer(#dataToSend));
//sleep(100);
//if result > 0 then
// ShowMessage(Format('Sender side: Receiver has %d items in list!', [result]));
end;
end; // for i
end;
end;
end.
/////////////////////////////////////////////////////////////
unit uSelectedFolders;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ShellAPI, ActiveX, ComObj, ShlObj, Registry, Buttons;
type
TfrmSelectedFolders = class(TForm)
lstSelectedFolders: TListBox;
sbClearList: TSpeedButton;
procedure FormCreate(Sender: TObject);
procedure sbClearListClick(Sender: TObject);
private { Private declarations }
public { Public declarations }
procedure WMDROPFILES(var Message: TWMDROPFILES); message WM_DROPFILES;
procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
function GetTarget(const LinkFileName: string): string;
end;
var
frmSelectedFolders: TfrmSelectedFolders;
implementation
{$R *.dfm}
procedure RegisterContextMenuForFolders();
const
Key = 'Directory\shell\SelectedFolders\command\';
begin
with TRegistry.Create do
try
// for all users, class registration for directories
RootKey := HKEY_CLASSES_ROOT;
if OpenKey(Key, true) then
WriteString('', '"' + Application.ExeName + '" "%l"');
finally
Free;
end;
end;
procedure TfrmSelectedFolders.WMDROPFILES(var Message: TWMDROPFILES);
var
N, i: integer;
buffer: array[0..255] of Char;
s: string;
begin
try
N := DragQueryFile(Message.Drop, $FFFFFFFF, nil, 0);
for i:=1 to N do
begin
DragQueryFile(Message.Drop, i-1, #buffer, SizeOf(buffer));
s := buffer;
if UpperCase(ExtractFileExt(s)) = '.LNK' then
s := GetTarget(s);
if lstSelectedFolders.Items.IndexOf(s) < 0 then
lstSelectedFolders.Items.Add(s);
end;
finally
DragFinish(Message.Drop);
end;
end;
function TfrmSelectedFolders.GetTarget(const LinkFileName: string): string;
var
//Link : String;
psl : IShellLink;
ppf : IPersistFile;
WidePath : Array[0..260] of WideChar;
Info : Array[0..MAX_PATH] of Char;
wfs : TWin32FindData;
begin
if UpperCase(ExtractFileExt(LinkFileName)) <> '.LNK' then
begin
Result := 'NOT a shortuct by extension!';
Exit;
end;
CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, psl);
if psl.QueryInterface(IPersistFile, ppf) = 0 Then
Begin
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, PChar(LinkFileName), -1, #WidePath, MAX_PATH);
ppf.Load(WidePath, STGM_READ);
psl.GetPath(#info, MAX_PATH, wfs, SLGP_UNCPRIORITY);
Result := info;
end
else
Result := '';
end;
procedure TfrmSelectedFolders.WMCopyData(var Msg: TWMCopyData);
var
s: string;
begin
s := trim(PChar(Msg.copyDataStruct.lpData));
if s = '' then
begin
msg.Result := -1;
exit;
end;
if UpperCase(ExtractFileExt(s)) = '.LNK' then
s := GetTarget(s);
if lstSelectedFolders.Items.IndexOf(s) < 0 then
lstSelectedFolders.Items.Add(s);
msg.Result := lstSelectedFolders.Items.Count;
end;
procedure TfrmSelectedFolders.FormCreate(Sender: TObject);
var
i: integer;
s: string;
begin
RegisterContextMenuForFolders();
DragAcceptFiles(Handle, TRUE);
lstSelectedFolders.Clear;
s := ExtractFileDir(Application.ExeName);
lstSelectedFolders.Items.Add(s);
for i:=1 to ParamCount do
begin
s := trim(ParamStr(i));
if UpperCase(ExtractFileExt(s)) = '.LNK' then
s := GetTarget(s);
if lstSelectedFolders.Items.IndexOf(s) < 0 then
lstSelectedFolders.Items.Add(s);
end;
end;
procedure TfrmSelectedFolders.sbClearListClick(Sender: TObject);
begin
lstSelectedFolders.Clear;
end;
end.

Delphi XE4 E2010 Incompatible types: 'Cardinal' and 'Pointer'

Hello i was trying to compile the firedac dll development sample under delphi xe4 and it came up with the following error
[dcc32 Error] Unit1.pas(61): E2010 Incompatible types: 'Cardinal' and 'Pointer'
I have marked where the error is in the code.
Unit 1 is the executable.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uADStanIntf, uADStanOption, uADStanDef, uADPhysIntf,
uADDatSManager, uADStanParam, uADDAptIntf, StdCtrls, Grids, DBGrids,
DB, uADPhysManager, uADPhysMSAcc, uADGUIxFormsWait, uADCompGUIx, uADCompDataSet,
uADCompClient, uADStanError, uADGUIxIntf, uADStanPool, uADStanAsync,
uADDAptManager, uADPhysODBCBase;
type
TShowDataProc = procedure (ACliHandle: LongWord); stdcall;
TShutdownProc = procedure; stdcall;
TForm1 = class(TForm)
ADConnection1: TADConnection;
ADQuery1: TADQuery;
ADGUIxWaitCursor1: TADGUIxWaitCursor;
ADPhysMSAccessDriverLink1: TADPhysMSAccessDriverLink;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
FhDll: THandle;
FpShowData: TShowDataProc;
FpShutdown: TShutdownProc;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
uADStanUtil;
procedure TForm1.Button1Click(Sender: TObject);
begin
FhDll := LoadLibrary(PChar('Project2.dll'));
if FhDll = 0 then
raise Exception.Create(ADLastSystemErrorMsg);
#FpShowData := GetProcAddress(FhDll, PChar('ShowData'));
if not Assigned(FpShowData) then
raise Exception.Create(ADLastSystemErrorMsg);
#FpShutdown := GetProcAddress(FhDll, PChar('Shutdown'));
if not Assigned(FpShutdown) then
raise Exception.Create(ADLastSystemErrorMsg);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
FpShowData(ADConnection1.CliHandle); << Error is here
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
FpShutdown();
FreeLibrary(FhDll);
FhDll := 0;
#FpShowData := nil;
#FpShutdown := nil;
end;
end.
Unit2 which is the dll
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uADStanIntf, uADStanOption, uADStanDef, uADPhysIntf,
uADDatSManager, uADStanParam, uADDAptIntf, Grids, DBGrids, DB,
uADPhysManager, uADPhysMSAcc, uADGUIxFormsWait, uADCompGUIx, uADCompDataSet,
uADCompClient, uADStanError, uADGUIxIntf, uADStanPool, uADStanAsync,
uADDAptManager, uADPhysODBCBase;
type
TForm2 = class(TForm)
ADConnection1: TADConnection;
ADQuery1: TADQuery;
ADGUIxWaitCursor1: TADGUIxWaitCursor;
ADPhysMSAccessDriverLink1: TADPhysMSAccessDriverLink;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
public
class procedure ShowData(ACliHandle: LongWord);
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TForm2 }
class procedure TForm2.ShowData(ACliHandle: LongWord);
var
oForm: TForm2;
begin
oForm := TForm2.Create(Application);
oForm.ADConnection1.SharedCliHandle := ACliHandle; <<<<<<<<<Error Here
oForm.ADConnection1.Connected := True;
oForm.ADQuery1.Active := True;
oForm.Show;
end;
end.
http://docs.embarcadero.com/products/rad_studio/firedac/frames.html?frmname=topic&frmfile=uADCompClient_TADCustomConnection_SharedCliHandle.html
As you can see SharedCliHandle is pointer, so probably example is old, you need to change LongWord to pointer. Why it was LongWord earlier and pointer now we can only guess, my guess i shared as comment.

detecting windows shell changes

I have very limited knowledge of using C Builder, could you give me an example or point me to tutorial showing how to use FindNextChangeNotification in Delphi or ,if it is possible, how to use the C component in delphi?
One option is use the TJvChangeNotify component, The JVCL support Delphi and C++ Builder.
Another option is use the SHChangeNotifyRegister function.
see this link Monitoring System Shell Changes using Delphi
Bye.
The ReadDirectoryChanges seems to be the function that I am looking for. Here is my attempt using mghie's code Why does ReadDirectoryChangesW omit events?
My objective here is to monitor the directory/path or file you will see in Unit1. I just want a simple showmessage dialogue to popup whenever a change at the location is detected. I can't find where I am supposed to pass my notification procedure or function. Unit2 just holds the unchanged code from mghie. When I compile this project and make a simple change in the directory nothing happens. I am using the ReadDirectoryChanges correctly?
here is Unit1:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
fthread:TWatcherthread;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
//start directory or file watch here
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fThread := TWatcherThread.Create('C:\Users\abe\Desktop\statcious\mitsu\Demo\abc.txt');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if fThread <> nil then begin
TWatcherThread(fThread).Shutdown;
fThread.Free;
end;
end;
end.
Here is Unit2:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TWatcherThread = class(TThread)
private
fChangeHandle: THandle;
fDirHandle: THandle;
fShutdownHandle: THandle;
protected
procedure Execute; override;
public
constructor Create(ADirectoryToWatch: string);
destructor Destroy; override;
procedure Shutdown;
end;
type
TForm2 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
constructor TWatcherThread.Create(ADirectoryToWatch: string);
const
FILE_LIST_DIRECTORY = 1;
begin
inherited Create(TRUE);
fChangeHandle := CreateEvent(nil, FALSE, FALSE, nil);
fDirHandle := CreateFile(PChar(ADirectoryToWatch),
FILE_LIST_DIRECTORY or GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
fShutdownHandle := CreateEvent(nil, FALSE, FALSE, nil);
Resume;
end;
destructor TWatcherThread.Destroy;
begin
if fDirHandle <> INVALID_HANDLE_VALUE then
CloseHandle(fDirHandle);
if fChangeHandle <> 0 then
CloseHandle(fChangeHandle);
if fShutdownHandle <> 0 then
CloseHandle(fShutdownHandle);
inherited Destroy;
end;
procedure TWatcherThread.Execute;
type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: WideChar;
end;
const
BufferLength = 65536;
var
Filter, BytesRead: DWORD;
InfoPointer: PFileNotifyInformation;
Offset, NextOffset: DWORD;
Buffer: array[0..BufferLength - 1] of byte;
Overlap: TOverlapped;
Events: array[0..1] of THandle;
WaitResult: DWORD;
FileName, s: string;
begin
if fDirHandle <> INVALID_HANDLE_VALUE then begin
Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME
or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE;
FillChar(Overlap, SizeOf(TOverlapped), 0);
Overlap.hEvent := fChangeHandle;
Events[0] := fChangeHandle;
Events[1] := fShutdownHandle;
while not Terminated do begin
if ReadDirectoryChangesW (fDirHandle, #Buffer[0], BufferLength, TRUE,
Filter, #BytesRead, #Overlap, nil)
then begin
WaitResult := WaitForMultipleObjects(2, #Events[0], FALSE, INFINITE);
if WaitResult = WAIT_OBJECT_0 then begin
InfoPointer := #Buffer[0];
Offset := 0;
repeat
NextOffset := InfoPointer.NextEntryOffset;
FileName := WideCharLenToString(#InfoPointer.FileName,
InfoPointer.FileNameLength);
SetLength(FileName, StrLen(PChar(FileName)));
s := Format('[%d] Action: %.8xh, File: "%s"',
[Offset, InfoPointer.Action, FileName]);
OutputDebugString(PChar(s));
PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset);
Offset := Offset + NextOffset;
until NextOffset = 0;
end;
end;
end;
end;
end;
procedure TWatcherThread.Shutdown;
begin
Terminate;
if fShutdownHandle <> 0 then
SetEvent(fShutdownHandle);
end;
end.

Resources