creatfile('e:\aa.txt',generic_read or generic_write,0,nil,open_existing, file_attribute_normal or file_flag_overlapped,0);
to hawk_js: 我想监视对硬盘一个路径的访问,想取得该路径的文件句柄,不是打开一个文件。 代码如下: unit Unit1;interface{ ========================== DIR EVENTS MONITORING ============================= == Overview == This form demonstrates the use of ReadDirectoryChangesW API function The function can be used either in synchronous or asynchronous mode. Here I implemented the asynchronous mode (ie: function returns immediatly and you have to watch for events). There are 3 ways to get results: a. call the function with a callback proc that the system calls when an event occurs b. associate the directory with an IOCompletionPort that "traps" IO events (that's what I did) c. create an Event, wait for it and call GetOverlapped Result For more information on synchronous calls or on the 2 other asynchronous implementations refer to MSDN == Implementation notes == I assume anyone willing to use this code will have sufficient knowledge of basic API Calls so I won't comment on threads, API Structures etc... I implemented a very basic call to SHBrowseForFolder. If you're interested refer to MSDN or download Brad Stower's components at www.delphifreestuff.com OK, Now we get to the bottom of things. Like much of the APIs, Monitoring is quite simple once you know how to get it to work ! First you have to open the directory you want to monitor. Use CreateFile in FILE_LIST_DIRECTORY mode and with FILE_FLAG_BACKUP_SEMANTICS privilege. Note that you have to add FILE_FLAG_OVERLAPPED for asynchronous operations. Then create an IOCompletionPort with the directory handle. If you open multiple directories, you can reuse the same port, simply specify a different cookie for each dir. Third Call ReadDirectoryChangesW with an empty Overlapped struct and no callback proc (asynchronous b method, see overview) Then wait for events using GetQueuedCompletionStatus. Upon event fire ReadDirectoryChangesW again and loop. Here you have mulmtiple implementation choices. Either you give a TimeOut to GetQueuedCompletionStatus and check whether it returned sth or (what I did) you call it in a thread with INFINITE wait time In this alternative, post an empty completion status to stop the thread; see PostQueuedCompletionStatus call in bStopClick method When you are finished, release all dir handles and IOCompletionPort with CloseHandle Events are written as continous TFileNotifyInformation records in a buffer you provide. >>Important Note<< FBytesWritten is not updated by asynchronous calls to ReadDirectoryChangesW Thus, don't rely on it for buffer parsing. Rather use the NextEntryOffset which is set to 0 for the last record. == Release Notes == This code has been tested with delphi 3.02 running on Windows NT4 SP6 It should work on all Windows NT platforms, though I haven't tested it under Windows NT 3.51 or Windows 2000. I don't known whether it works under Win9x or not. Eventually, it may be kind of you to let me know if you run some tests You shouldn't have much trouble compiling it with Delphi 2/4/5+ and C++ port is quite easy
Damien Thouvenin (mailto:[email protected]) } uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons;type TForm1 = class(TForm) lbEvents: TListBox; Panel1: TPanel; Label1: TLabel; ePath: TEdit; CJLabel1: TLabel; bStart: TButton; bStop: TButton; ckWatchSubTree: TCheckBox; GroupBox1: TGroupBox; ckMonitorFileName: TCheckBox; ckMonitorDirName: TCheckBox; ckMonitorAttributes: TCheckBox; ckMonitorSize: TCheckBox; ckMonitorLastWrite: TCheckBox; ckMonitorSecurity: TCheckBox; ckMonitorCreationDate: TCheckBox; ckMonitorLastAccess: TCheckBox; SpeedButton1: TSpeedButton; procedure bStartClick(Sender: TObject); procedure bStopClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ePathDblClick(Sender: TObject); private { D閏larations priv閑s } FDirectoryHandle: THandle; FNotificationBuffer: array[0..4096] of Byte; FWatchThread: TThread; FNotifyFilter: DWORD; FOverlapped: TOverlapped; FPOverlapped: POverlapped; FBytesWritten: DWORD; FCompletionPort: THandle; public { D閏larations publiques } end;var Form1: TForm1;type PFileNotifyInformation = ^TFileNotifyInformation; TFileNotifyInformation = record NextEntryOffset: DWORD; Action: DWORD; FileNameLength: DWORD; FileName: array[0..0] of WideChar; end;const FILE_LIST_DIRECTORY = $0001;const SAction: array[FILE_ACTION_ADDED..FILE_ACTION_RENAMED_NEW_NAME] of String = ( 'ADDED %s', 'DELETED %s', 'MODIFIED %s', 'RENAMED %s [...]', '[...] into %s');implementation{$R *.DFM}uses ShlObj, ActiveX;type TWaitThread = class(TThread) private FForm: TForm1; procedure HandleEvent; protected procedure Execute; override; public constructor Create(Form: TForm1); end;constructor TWaitThread.Create(Form: TForm1); begin inherited Create(True); FForm := Form; FreeOnTerminate := False; end;procedure TWaitThread.HandleEvent; var FileOpNotification: PFileNotifyInformation; Offset: Longint; begin with FForm do begin Pointer(FileOpNotification) := @FNotificationBuffer[0]; repeat Offset := FileOpNotification^.NextEntryOffset; lbEvents.Items.Add(Format(SAction[FileOpNotification^.Action], [WideCharToString(@(FileOpNotification^.FileName))])); PChar(FileOpNotification) := PChar(FileOpNotification)+Offset; until Offset=0; end; end;procedure TWaitThread.Execute; var numBytes: DWORD; cbOffset: DWORD; CompletionKey: DWORD; begin while not Terminated do begin GetQueuedCompletionStatus( FForm.FCompletionPort, numBytes, CompletionKey, FForm.FPOverlapped, INFINITE); if CompletionKey <> 0 then begin Synchronize(HandleEvent); with FForm do begin FBytesWritten := 0; ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer)); ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), ckWatchSubTree.Checked, FNotifyFilter, @FBytesWritten, @FOverlapped, nil); end; end else Terminate; end; end;procedure TForm1.bStartClick(Sender: TObject); begin FNotifyFilter := 0; if ckMonitorFileName.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_FILE_NAME; if ckMonitorDirName.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_DIR_NAME; if ckMonitorAttributes.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_ATTRIBUTES; if ckMonitorSize.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SIZE; if ckMonitorLastWrite.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_WRITE; if ckMonitorLastAccess.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_ACCESS; if ckMonitorCreationDate.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_CREATION; if ckMonitorSecurity.Checked then FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SECURITY; if FNotifyFilter = 0 then begin ShowMessage('Vous devez monitorer au moins 1 関閚ement !'); exit; end; lbEvents.Clear; FDirectoryHandle := createfile('c:\a.txt',generic_read or generic_write,0,nil,open_existing, file_attribute_normal or file_flag_overlapped,0); { FDirectoryHandle := CreateFile('c:\',//PChar(ePath.Text), GENERIC_READ,// or GENERIC_WRITE,//FILE_LIST_DIRECTORY, 0,// or FILE_SHARE_WRITE,// or FILE_SHARE_DELETE, nil, OPEN_EXISTING, 0,//FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, //FILE_FLAG_BACKUP_SEMANTICS or 0); }if FDirectoryHandle = INVALID_HANDLE_VALUE then begin beep; FDirectoryHandle := 0; ShowMessage(SysErrorMessage(GetLastError)); exit; end; FCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Longint(pointer(self)), 0); ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer)); FBytesWritten := 0; if not ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), ckWatchSubTree.Checked, FNotifyFilter, @FBytesWritten, @FOverlapped, nil) then begin CloseHandle(FDirectoryHandle); FDirectoryHandle := 0; CloseHandle(FCompletionPort); FCompletionPort := 0; ShowMessage(SysErrorMessage(GetLastError)); exit; end; ePath.Enabled := False; bStart.Enabled := False; bStop.Enabled := True; FWatchThread := TWaitThread.Create(self); TWaitThread(FWatchThread).Resume; end;procedure TForm1.bStopClick(Sender: TObject); begin if FCompletionPort = 0 then exit; PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil); FWatchThread.WaitFor; FWatchThread.Free; CloseHandle(FDirectoryHandle); FDirectoryHandle := 0; CloseHandle(FCompletionPort); FCompletionPort := 0; ePath.Enabled := True; bStart.Enabled := True; bStop.Enabled := False; end;procedure TForm1.FormDestroy(Sender: TObject); begin bStop.Click; end;procedure TForm1.FormCreate(Sender: TObject); begin FCompletionPort := 0; FDirectoryHandle := 0; FPOverlapped := @FOverlapped; ZeroMemory(@FOverlapped, SizeOf(FOverlapped)); end;procedure TForm1.ePathDblClick(Sender: TObject); var SelectionPIDL: PItemIDList; BrowseInfo: TBrowseInfo; ShellAllocator: IMalloc; PathBuffer: array[0..MAX_PATH] of Char; begin // simplest implementation of BrowseForFolder // to get more information refer to MSDN Library or Check Brad Stower's excellent site www.delphifreestuff.com ZeroMemory(@BrowseInfo, SizeOf(BrowseInfo)); BrowseInfo.hwndOwner := Handle; BrowseInfo.ulFlags := BIF_RETURNONLYFSDIRS; CoInitialize(nil); try SelectionPIDL := ShBrowseForFolder(BrowseInfo); if SelectionPIDL <> nil then try ZeroMemory(@PathBuffer, SizeOf(PathBuffer)); if not SHGetPathFromIDList(SelectionPIDL, @PathBuffer) then begin beep; exit; end; ePath.Text := StrPas(@PathBuffer[0]); finally if SHGetMalloc(ShellAllocator) = 0 then begin ShellAllocator.Free(SelectionPIDL); ShellAllocator := nil; end; end; finally CoUnInitialize; end; end;end.在NT下这些代码没问题,98下通不过。
file_attribute_normal or file_flag_overlapped,0);
我想监视对硬盘一个路径的访问,想取得该路径的文件句柄,不是打开一个文件。
代码如下:
unit Unit1;interface{ ========================== DIR EVENTS MONITORING ============================= == Overview == This form demonstrates the use of ReadDirectoryChangesW API function The function can be used either in synchronous or asynchronous mode.
Here I implemented the asynchronous mode (ie: function returns immediatly
and you have to watch for events). There are 3 ways to get results:
a. call the function with a callback proc that the system calls when an event occurs
b. associate the directory with an IOCompletionPort that "traps" IO events (that's what I did)
c. create an Event, wait for it and call GetOverlapped Result For more information on synchronous calls or on the 2 other asynchronous implementations refer to MSDN == Implementation notes == I assume anyone willing to use this code will have sufficient knowledge of
basic API Calls so I won't comment on threads, API Structures etc... I implemented a very basic call to SHBrowseForFolder. If you're interested
refer to MSDN or download Brad Stower's components at www.delphifreestuff.com OK, Now we get to the bottom of things.
Like much of the APIs, Monitoring is quite simple once you know how to get it to work ! First you have to open the directory you want to monitor. Use CreateFile in
FILE_LIST_DIRECTORY mode and with FILE_FLAG_BACKUP_SEMANTICS privilege.
Note that you have to add FILE_FLAG_OVERLAPPED for asynchronous operations. Then create an IOCompletionPort with the directory handle.
If you open multiple directories, you can reuse the same port, simply
specify a different cookie for each dir. Third Call ReadDirectoryChangesW with an empty Overlapped struct and no
callback proc (asynchronous b method, see overview) Then wait for events using GetQueuedCompletionStatus. Upon event fire ReadDirectoryChangesW
again and loop. Here you have mulmtiple implementation choices. Either you give a TimeOut to GetQueuedCompletionStatus
and check whether it returned sth or (what I did) you call it in a thread with INFINITE wait time
In this alternative, post an empty completion status to stop the thread; see PostQueuedCompletionStatus
call in bStopClick method When you are finished, release all dir handles and IOCompletionPort with CloseHandle Events are written as continous TFileNotifyInformation records in a buffer you provide. >>Important Note<<
FBytesWritten is not updated by asynchronous calls to ReadDirectoryChangesW
Thus, don't rely on it for buffer parsing. Rather use the NextEntryOffset which
is set to 0 for the last record. == Release Notes == This code has been tested with delphi 3.02 running on Windows NT4 SP6
It should work on all Windows NT platforms, though I haven't tested it under
Windows NT 3.51 or Windows 2000. I don't known whether it works under Win9x or not.
Eventually, it may be kind of you to let me know if you run some tests You shouldn't have much trouble compiling it with Delphi 2/4/5+ and C++ port is quite easy
Damien Thouvenin (mailto:[email protected])
}
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Buttons;type
TForm1 = class(TForm)
lbEvents: TListBox;
Panel1: TPanel;
Label1: TLabel;
ePath: TEdit;
CJLabel1: TLabel;
bStart: TButton;
bStop: TButton;
ckWatchSubTree: TCheckBox;
GroupBox1: TGroupBox;
ckMonitorFileName: TCheckBox;
ckMonitorDirName: TCheckBox;
ckMonitorAttributes: TCheckBox;
ckMonitorSize: TCheckBox;
ckMonitorLastWrite: TCheckBox;
ckMonitorSecurity: TCheckBox;
ckMonitorCreationDate: TCheckBox;
ckMonitorLastAccess: TCheckBox;
SpeedButton1: TSpeedButton;
procedure bStartClick(Sender: TObject);
procedure bStopClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ePathDblClick(Sender: TObject);
private
{ D閏larations priv閑s }
FDirectoryHandle: THandle;
FNotificationBuffer: array[0..4096] of Byte;
FWatchThread: TThread;
FNotifyFilter: DWORD;
FOverlapped: TOverlapped;
FPOverlapped: POverlapped;
FBytesWritten: DWORD;
FCompletionPort: THandle;
public
{ D閏larations publiques }
end;var
Form1: TForm1;type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: array[0..0] of WideChar;
end;const
FILE_LIST_DIRECTORY = $0001;const
SAction: array[FILE_ACTION_ADDED..FILE_ACTION_RENAMED_NEW_NAME] of String =
( 'ADDED %s',
'DELETED %s',
'MODIFIED %s',
'RENAMED %s [...]',
'[...] into %s');implementation{$R *.DFM}uses
ShlObj, ActiveX;type
TWaitThread = class(TThread)
private
FForm: TForm1;
procedure HandleEvent;
protected
procedure Execute; override;
public
constructor Create(Form: TForm1);
end;constructor TWaitThread.Create(Form: TForm1);
begin
inherited Create(True);
FForm := Form;
FreeOnTerminate := False;
end;procedure TWaitThread.HandleEvent;
var
FileOpNotification: PFileNotifyInformation;
Offset: Longint;
begin
with FForm do
begin
Pointer(FileOpNotification) := @FNotificationBuffer[0];
repeat
Offset := FileOpNotification^.NextEntryOffset;
lbEvents.Items.Add(Format(SAction[FileOpNotification^.Action], [WideCharToString(@(FileOpNotification^.FileName))]));
PChar(FileOpNotification) := PChar(FileOpNotification)+Offset;
until Offset=0;
end;
end;procedure TWaitThread.Execute;
var
numBytes: DWORD;
cbOffset: DWORD;
CompletionKey: DWORD;
begin
while not Terminated do
begin
GetQueuedCompletionStatus( FForm.FCompletionPort, numBytes, CompletionKey, FForm.FPOverlapped, INFINITE);
if CompletionKey <> 0 then
begin
Synchronize(HandleEvent);
with FForm do
begin
FBytesWritten := 0;
ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));
ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), ckWatchSubTree.Checked, FNotifyFilter, @FBytesWritten, @FOverlapped, nil);
end;
end
else
Terminate;
end;
end;procedure TForm1.bStartClick(Sender: TObject);
begin
FNotifyFilter := 0;
if ckMonitorFileName.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_FILE_NAME;
if ckMonitorDirName.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_DIR_NAME;
if ckMonitorAttributes.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_ATTRIBUTES;
if ckMonitorSize.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SIZE;
if ckMonitorLastWrite.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_WRITE;
if ckMonitorLastAccess.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_ACCESS;
if ckMonitorCreationDate.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_CREATION;
if ckMonitorSecurity.Checked then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SECURITY;
if FNotifyFilter = 0 then
begin
ShowMessage('Vous devez monitorer au moins 1 関閚ement !');
exit;
end;
lbEvents.Clear;
FDirectoryHandle := createfile('c:\a.txt',generic_read or generic_write,0,nil,open_existing,
file_attribute_normal or file_flag_overlapped,0);
{ FDirectoryHandle := CreateFile('c:\',//PChar(ePath.Text),
GENERIC_READ,// or GENERIC_WRITE,//FILE_LIST_DIRECTORY,
0,// or FILE_SHARE_WRITE,// or FILE_SHARE_DELETE,
nil,
OPEN_EXISTING,
0,//FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, //FILE_FLAG_BACKUP_SEMANTICS or
0);
}if FDirectoryHandle = INVALID_HANDLE_VALUE then
begin
beep;
FDirectoryHandle := 0;
ShowMessage(SysErrorMessage(GetLastError));
exit;
end;
FCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Longint(pointer(self)), 0);
ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));
FBytesWritten := 0;
if not ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), ckWatchSubTree.Checked, FNotifyFilter, @FBytesWritten, @FOverlapped, nil) then
begin
CloseHandle(FDirectoryHandle);
FDirectoryHandle := 0;
CloseHandle(FCompletionPort);
FCompletionPort := 0;
ShowMessage(SysErrorMessage(GetLastError));
exit;
end;
ePath.Enabled := False;
bStart.Enabled := False;
bStop.Enabled := True;
FWatchThread := TWaitThread.Create(self);
TWaitThread(FWatchThread).Resume;
end;procedure TForm1.bStopClick(Sender: TObject);
begin
if FCompletionPort = 0 then
exit;
PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);
FWatchThread.WaitFor;
FWatchThread.Free;
CloseHandle(FDirectoryHandle);
FDirectoryHandle := 0;
CloseHandle(FCompletionPort);
FCompletionPort := 0;
ePath.Enabled := True;
bStart.Enabled := True;
bStop.Enabled := False;
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
bStop.Click;
end;procedure TForm1.FormCreate(Sender: TObject);
begin
FCompletionPort := 0;
FDirectoryHandle := 0;
FPOverlapped := @FOverlapped;
ZeroMemory(@FOverlapped, SizeOf(FOverlapped));
end;procedure TForm1.ePathDblClick(Sender: TObject);
var
SelectionPIDL: PItemIDList;
BrowseInfo: TBrowseInfo;
ShellAllocator: IMalloc;
PathBuffer: array[0..MAX_PATH] of Char;
begin
// simplest implementation of BrowseForFolder
// to get more information refer to MSDN Library or Check Brad Stower's excellent site www.delphifreestuff.com
ZeroMemory(@BrowseInfo, SizeOf(BrowseInfo));
BrowseInfo.hwndOwner := Handle;
BrowseInfo.ulFlags := BIF_RETURNONLYFSDIRS;
CoInitialize(nil);
try
SelectionPIDL := ShBrowseForFolder(BrowseInfo);
if SelectionPIDL <> nil then
try
ZeroMemory(@PathBuffer, SizeOf(PathBuffer));
if not SHGetPathFromIDList(SelectionPIDL, @PathBuffer) then
begin
beep;
exit;
end;
ePath.Text := StrPas(@PathBuffer[0]);
finally
if SHGetMalloc(ShellAllocator) = 0 then
begin
ShellAllocator.Free(SelectionPIDL);
ShellAllocator := nil;
end;
end;
finally
CoUnInitialize;
end;
end;end.在NT下这些代码没问题,98下通不过。