Cannot split word into individual letters in a memo - loops

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;

Related

Why is my procedure not changing the array

I have a procedure to shift the position of the array values
The string at position 1 is supposed to take the value of the string at position 2, and the string at position 2 is supposed to take the value of the string at position 1(and so forth)
eg.
The array at the start:
Pos Value
1 A
2 B
3 C
The array at the end (what it's supposed to become):
Pos Value
1 B
2 C
3 A
This is the code:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
type
arrRotor = array [1 .. 10] of string;
TForm2 = class(TForm)
Button1: TButton;
RichEdit1: TRichEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure ArrayShiftRowUp(arrArrayShift: arrRotor );
end;
var
Form2: TForm2;
j, i : integer;
temps:string;
arrTest: arrRotor;
implementation
{$R *.dfm}
procedure TForm2.ArrayShiftRowUp(arrArrayShift: arrRotor );
begin
for i := 1 to Length(arrArrayShift)-1 do
begin
tempS := arrArrayShift[i];
arrArrayShift[i] := arrArrayShift[i + 1];
arrArrayShift[i + 1] := tempS;
RichEdit1.Lines.Add(arrArrayShift[i]);
end;
RichEdit1.Lines.Add(arrArrayShift[10]);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
for i := 1 to 10 do
begin
arrTest[i]:=Chr(64+i);
end;
for i := 1 to 10 do
begin
RichEdit1.Lines.Add(arrTest[i]);
end;
ArrayShiftRowUp(arrTest);
for i := 1 to 10 do
begin
RichEdit1.Lines.Add(arrTest[i]);
end;
end;
end.
But for some reason, the actual array is not changing. The array parameter is changing (as evident of the display in the rich edit) but for some reason, arrTest is not changing.
What is the issue?

Aggregate of multiple UI controls bound to one database column

I currently have a TDBRadioGroup bound to a CHAR column on the database using the Values property to specify the value stored in the database column for each radio button. Now we have a requirement to add 2 new radio groups along side the existing one that would modify the value. In other words I basically need 3 controls on the form to act as one in a data-aware configuration for the purposes of determining the value stored. A simple example might be if each radio group has a one-character value, and in the database you concatenate them into a 3-character string. Our mapping is more complex than that but that's the general idea. What would be a good way to do this? If someone tells me live bindings would be the way to go, that would be good to know, as it would be help me to convince management to upgrade Delphi. We're stuck on XE.
Interesting q!
I hope I've understood you correctly. Suppose there is a form with 3 radiogroups, one per line, like so
*A B C
D *E F
G H *I
where the asterisks indicates the positions of the selected buttons and suppose
that these settings translate into a field value AEI in a row of a dataset.
If the G entry of the 3rd RG is clicked, the dataset field value should become
GEI. That's what I've assumed you are looking for.
The code below is a "proof of concept" which implements the above functionality.
It does so using 3 standard (non-db-aware) TRadioGroups and an adaptor class
TDBRadioGroupAdaptor which makes these operate as db-aware RadioGroups. The
TDBRadioGroupAdaptor uses a descendant, TRGFieldDataLink, of the standard TFieldDataLink (see DBCtrls.Pas)
to interface with the dataset. I've used a TClientDataSet so the project can be completely
self-contained and I've avoided the use of generices as you didn't specify a Delphi version.
Most of the db-aware functionality is contained in the TRGFieldDataLink, which
serves to interface the dataset and the RadioGroups and differs from a standard
TFieldDataLink in that it treats the field value as composed of N (3 in the code example but it supports an arbitrary number)
sub-fields which each have their own RadioGroup. As ever with implementing db-aware
functionality, the code for the TDBRadioGroupAdaptor and TRGFieldDataLink is fairly
long-winded, as most of it is necessary but not very interesting. About the only
ones that are interesting, in that they make the wheels go around, are
function TRGFieldDataLink.GetGroupString : String;
// Returns a string from the ItemIndexes of the RadioGroups
procedure TRGFieldDataLink.GetGroupValues;
// Sets the DataSet field from the RadioGroup ItemIndexes
which I hope are self-explanatory from the embedded comments.
Because it's only supposed to be a proof-of-concept, the implementation is incomplete,
lacking f.i. keyboard support, but that could easily be added by mimicking the
source code of the standard TDBRadioGroup.
Obviously, something similar could be used to treat subfields of a dataset field as
a group of strings independently editable in a group of TEdits.
Also obviously, it would be possible to turn the TDBRadioGroupAdaptor into a full-blown compound component which includes its own radiogroups, but I've left that as an exercise for the reader.
Code (warning: long!)
type
TDBRadioGroupAdaptor = class;
TRGFieldDataLink = class(TFieldDataLink)
private
FAdaptor: TDBRadioGroupAdaptor;
FRecordChanging : Boolean;
procedure GetGroupValues;
function GetGroupString: String;
procedure SetGroupValues(AValue: String);
public
constructor Create(AAdaptor : TDBRadioGroupAdaptor);
destructor Destroy; override;
procedure RecordChanged(Field : TField); override;
procedure UpdateData; override;
property Adaptor : TDBRadioGroupAdaptor read FAdaptor write FAdaptor;
end;
TDBRadioGroupAdaptor = class
private
FDataLink : TRGFieldDataLink;
FRadioGroups : TList;
procedure SetDataSource(Value : TDataSource);
function GetDataSource : TDataSource;
function GetRadioGroup(Index: Integer): TRadioGroup;
procedure ItemClicked(Sender : TObject);
procedure SetFieldName(const Value: string);
function GetFieldName : String;
public
constructor Create;
destructor Destroy; override;
procedure Add(ARadioGroup : TRadioGroup);
property DataSource : TDataSource read GetDataSource write SetDataSource;
property FieldName : string read GetFieldName write SetFieldName;
property RadioGroup[Index : Integer] : TRadioGroup read GetRadioGroup;
end;
type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
DBNavigator1: TDBNavigator;
Button1: TButton;
RadioGroup1: TRadioGroup;
RadioGroup2: TRadioGroup;
RadioGroup3: TRadioGroup;
DBEdit1: TDBEdit;
DBRadioGroup1: TDBRadioGroup;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
protected
public
Adaptor : TDBRadioGroupAdaptor;
end;
[...]
{ TRGFieldDataLink }
constructor TRGFieldDataLink.Create(AAdaptor : TDBRadioGroupAdaptor);
begin
inherited Create;
Adaptor := AAdaptor;
end;
destructor TRGFieldDataLink.Destroy;
begin
inherited;
end;
procedure TRGFieldDataLink.SetGroupValues(AValue : String);
// Sets the ItemIndexes of the RadioGroups by matching each character of AValue
// to the contents of their Items
var
i,
Index : Integer;
S : String;
begin
if AValue = '' then Exit; // To avoid error when CreateDataSet is called
for i := 0 to Adaptor.FRadioGroups.Count - 1 do begin
S := AValue[i + 1];
Index := Adaptor.RadioGroup[i].Items.IndexOf(S);
Adaptor.RadioGroup[i].ItemIndex := Index;
end;
end;
procedure TRGFieldDataLink.RecordChanged(Field : TField);
// called when the dataset goes to a new record, e.g. during scrolling
// and sets the RadioGroups' ItemIndexes from the dataset data
var
FieldValue : String;
begin
Assert(DataSet <> Nil);
if FRecordChanging then exit; // just in case, avoid re-entrancy
try
FRecordChanging := True;
if Field = Nil then
Field := DataSet.FieldByName(FieldName); // Yukky way of setting Field
FieldValue := Field.AsString;
SetGroupValues(FieldValue);
finally
FRecordChanging := False;
end;
end;
function TRGFieldDataLink.GetGroupString : String;
// Returns a string from the ItemIndexes of the RadioGroups
var
i : Integer;
S : String;
begin
Result := '';
for i := 0 to Adaptor.FRadioGroups.Count - 1 do begin
S := Adaptor.RadioGroup[i].Items[Adaptor.RadioGroup[i].ItemIndex];
Result := Result + S [1];
end;
end;
procedure TRGFieldDataLink.GetGroupValues;
// Sets the DataSet field from the RadioGroup ItemIndexes
var
FieldValue,
S : String;
begin
Assert(DataSet <> Nil);
S := Field.AsString;
FieldValue := GetGroupString;
Field.AsString := FieldValue;
end;
procedure TRGFieldDataLink.UpdateData;
// Called by RTL to update the dataset record from the RadioGroups
begin
GetGroupValues;
end;
{ TDBRadioGroupAdaptor }
procedure TDBRadioGroupAdaptor.Add(ARadioGroup: TRadioGroup);
begin
FRadioGroups.Add(ARadioGroup);
ARadioGroup.OnClick := ItemClicked;
end;
constructor TDBRadioGroupAdaptor.Create;
begin
inherited;
FRadioGroups := TList.Create;
FDataLink := TRGFieldDataLink.Create(Self);
end;
destructor TDBRadioGroupAdaptor.Destroy;
begin
FDataLink.Free;
FRadioGroups.Free;
inherited Destroy;
end;
function TDBRadioGroupAdaptor.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
procedure TDBRadioGroupAdaptor.SetDataSource(Value: TDataSource);
begin
FDataLink.DataSource := Value;
end;
function TDBRadioGroupAdaptor.GetRadioGroup(Index: Integer): TRadioGroup;
begin
Result := TRadioGroup(FRadioGroups[Index]);
end;
procedure TDBRadioGroupAdaptor.ItemClicked(Sender: TObject);
// Responds to one of the RadioGroups being clicked to put the DataSet into Edit state
// and updates the DataSet field from the ItemIndexes of the RadioGroups
var
S : String;
begin
if not FDataLink.FRecordChanging then begin
S := FDataLink.GetGroupString;
FDataLink.Edit;
FDataLink.SetGroupValues(S);
end;
end;
procedure TDBRadioGroupAdaptor.SetFieldName(const Value: string);
begin
FDataLink.FieldName := Value;
end;
function TDBRadioGroupAdaptor.GetFieldName: string;
begin
Result := FDataLink.FieldName;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Field : TField;
begin
// Create 2 fields in the CDS
Field := TIntegerField.Create(Self);
Field.FieldName := 'ID';
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
Field := TStringField.Create(Self);
Field.FieldName := 'Value';
Field.Size := 40;
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
RadioGroup1.Items.Add('A');
RadioGroup1.Items.Add('B');
RadioGroup1.Items.Add('C');
RadioGroup2.Items.Add('D');
RadioGroup2.Items.Add('E');
RadioGroup2.Items.Add('F');
RadioGroup3.Items.Add('G');
RadioGroup3.Items.Add('H');
RadioGroup3.Items.Add('I');
Adaptor := TDBRadioGroupAdaptor.Create;
Adaptor.Add(RadioGroup1);
Adaptor.Add(RadioGroup2);
Adaptor.Add(RadioGroup3);
Adaptor.DataSource := DataSource1;
Adaptor.FieldName := 'Value';
// Next, set up the CDS
ClientDataSet1.CreateDataSet;
ClientDataSet1.InsertRecord([1, 'AEI']);
ClientDataSet1.InsertRecord([2, 'BDG']);
ClientDataSet1.InsertRecord([3, 'ADG']);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Adaptor.Free;
end;

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

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?

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.

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