Article View: borland.public.cppbuilder.nativeapi
Article #11746Repost of ReadDirectoryChanges question...
From: Daryl
Date: Fri, 23 Jan 2004 06:14
Date: Fri, 23 Jan 2004 06:14
322 lines
12212 bytes
12212 bytes
This is a repost that answers a previous set of questions. I would be good
if I could get some help. The original post with the questions is below.
Thanks
Daryl
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Remy
I have copied most of the relevant methods so you can get a better idea
of what I am doing. I have added an extra check in the method
bool __fastcall TForm1::processFile(String s) to ensure the file
has been copied. Maybe it is not correct. I would be very
interested in your comments on what I have done.
In reading it would appear that the API ReadDirectoryChangesW
does not reliably monitor network drives - UNC or mapped. I would
also be interested in you comments on this.?
Outline of methods...
The form method to start the process after the user selects a directory
to monitor. The completionPort and the thread is initialised in the
form's constructor.
//--------------------------------------------------------------------------
-
void __fastcall TForm1::btnDirectoryClick(TObject *Sender)
{
// Call Directory Dialog and set path to folder to monitor
String oldPath = Edit1->Text;
Edit1->Text = "";
TDirDlg *dl = new TDirDlg(NULL);
try {
dl->Execute();
Edit1->Text = dl->SelectedFolder;
if (!setDirHandle(Edit1->Text)) {
if (oldPath.Length() != 0)
Edit1->Text = oldPath;
MessageDlg("Invalid path for folder to monitor!",
mtInformation, TMsgDlgButtons() << mbOK, 0);
}
else {
completionPort = CreateIoCompletionPort(thrd->getDirToMonitor(),
NULL, (long)this, 0);
memset(notificationBuffer, 0, sizeof(CHAR)*MAX_BUFFER);
bytesWritten = 0;
notifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME;
// If an error occurs, inform user, tidy-up and exit.
if (!ReadDirectoryChangesW(thrd->getDirToMonitor(),
notificationBuffer,
MAX_BUFFER, false, notifyFilter, &bytesWritten, &overlapped,
NULL)) {
CloseHandle(thrd->getDirToMonitor());
thrd->setDirToMonitor(NULL);
CloseHandle(completionPort);
completionPort = NULL;
MessageDlg("Error msg : " + SysErrorMessage((int)GetLastError),
mtError, TMsgDlgButtons() << mbOK, 0);
ShowMessage((int)GetLastError);
exit;
return;
}
thrd->Resume();
Application->Minimize();
}
}
__finally {
delete dl;
}
}
//--------------------------------------------------------------------------
-
This is the Execute method in the thread that monitors the directory
and the method called to synchronise to the main form.
//--------------------------------------------------------------------------
-
void __fastcall TDirectoryThread::Execute()
{
// Used to monitor the directory using API ReadDirectoryChangesW
DWORD *completionKey;
while (!Terminated && dir != NULL) {
GetQueuedCompletionStatus(Form1->completionPort, &Form1->dwBytes,
completionKey,
&Form1->lpOverlapped, INFINITE);
if (completionKey != 0) {
Synchronize(Update);
Form1->bytesWritten = 0;
memset(Form1->notificationBuffer, 0,
sizeof(Form1->notificationBuffer));
memset(&Form1->overlapped, 0, sizeof(Form1->overlapped));
ReadDirectoryChangesW(dir, &Form1->notificationBuffer,
sizeof(Form1->notificationBuffer),
false, Form1->notifyFilter,
&Form1->bytesWritten,
&Form1->overlapped, NULL);
}
else {
Terminate();
dir = NULL;
}
}
}
//--------------------------------------------------------------------------
-
void __fastcall TDirectoryThread::Update()
{
// Used to call Main thread to handle event
Form1->UpdateNotify();
}
//--------------------------------------------------------------------------
-
void __fastcall TForm1::UpdateNotify()
{
// Use this section to handle the file added to the folder being monitored
DWORD cbOffset = 0xFFFFFFFF;
FILE_NOTIFY_INFORMATION *fni = NULL;
// Make a local copy of buffer
CHAR localBuffer[MAX_BUFFER];
memcpy(localBuffer, notificationBuffer, dwBytes);
fni = (PFILE_NOTIFY_INFORMATION)localBuffer;
String full_path;
while (cbOffset > 0 ) {
full_path = Edit1->Text + String("\\") + String(fni->FileName);
switch (fni->Action) {
case FILE_ACTION_ADDED:
processFile(full_path);
break;
case FILE_ACTION_REMOVED:
WriteToLog(DateTimeToStr(Now())
+ " File " + full_path + " was deleted.\n");
break;
case FILE_ACTION_MODIFIED:
WriteToLog(DateTimeToStr(Now())
+ " File " + full_path + " was modified.\n");
break;
}
cbOffset = fni->NextEntryOffset;
fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni+cbOffset);
}
dwBytes = 0;
}
//--------------------------------------------------------------------------
-
The final method called to parse the ASCII file and populate the
database. Note: the DB is MS Access2000 and there is a DataModule
that contains the TADOConnection and the TADOCommand components.
//--------------------------------------------------------------------------
-
bool __fastcall TForm1::processFile(String s)
{
// Used to process the text passed file. File is parsed line by line
// and each line is broken into relevent field values.
if (ExtractFileExt(s) != ".aei")
return false;
// Create/Initialise file for reading and check if file exists...
ifstream ifs(s.c_str(), ios::in);
int trys = 0;
while (trys < 4 && ifs.fail()) { // Windows or other app may be using file
ifs.clear();
Sleep(250); // wait for 1/4 of a second
ifs.open(s.c_str(), ios::in);
trys++;
}
if (!ifs) {
WriteToLog(DateTimeToStr(Now()) + " File "
+ s + " could not be successfully opened.");
return false;
}
TStringList *SplitList = new TStringList;
String cmdtxt;
bool first_line = true;
int Position = 0, PKey = 0;
char buff[1024];
// Read in from ASCII file.
try {
try {
while (!ifs.eof()) {
ifs.getline(buff, 1023, '\n');
if (strlen(buff) <= 0) continue; // Do not process blank lines in
file
Split(SplitList, String(buff), Char(VK_TAB));
if (first_line) {
cmdtxt = "INSERT INTO tblReading (FileName, Site, [Date], ";
cmdtxt += "[Time], TrainID, Direction) ";
cmdtxt += "VALUES ('" + s + "', '";
cmdtxt += SplitList->Strings[0] + "', '";
cmdtxt += SplitList->Strings[1] + "', '";
cmdtxt += SplitList->Strings[2] + "', '";
cmdtxt += SplitList->Strings[4] + "', '";
cmdtxt += SplitList->Strings[5] + "')";
}
else {
Position++;
cmdtxt = "INSERT INTO tblAEIData (FileID, [Position], [Type], ";
cmdtxt += "TagClass, [Number], Track, [Length], Axles, Side, ";
cmdtxt += "Spare1, Spare2) ";
cmdtxt += "VALUES (" + IntToStr(PKey) + String(", ");
cmdtxt += IntToStr(Position) + ", '";
cmdtxt += SplitList->Strings[0] + "', '";
cmdtxt += SplitList->Strings[1] + "', '";
cmdtxt += SplitList->Strings[2] + "', '";
cmdtxt += SplitList->Strings[3] + "', '";
cmdtxt += SplitList->Strings[4] + "', ";
cmdtxt += SplitList->Strings[5] + ", '";
cmdtxt += SplitList->Strings[6] + "', '";
cmdtxt += SplitList->Strings[7] + "', '";
cmdtxt += SplitList->Strings[8] + "')";
}
DMConnection->cmdMain->CommandText = cmdtxt;
DMConnection->cmdMain->CommandType = cmdText;
DMConnection->cmdMain->Execute();
if (first_line) {
PKey = getIdentityKey("tblReading");
first_line = false;
WriteToLog(DateTimeToStr(Now()) + " File "
+ s + " was added.\n");
}
}
}
catch (Exception &E) {
//DMConnection->cmdMain->Cancel();
WriteToLog(DateTimeToStr(Now()) + " Error: " + E.Message
+ " has occured at position " + IntToStr(Position) + "\n");
return false;
}
}
__finally {
ifs.close();
delete SplitList;
}
return true;
}
//--------------------------------------------------------------------------
-
Used to CreateFile to monitor.
//--------------------------------------------------------------------------
-
bool __fastcall TForm1::setDirHandle(String path)
{
// Used to get a handle to the directory to be monitored
HANDLE _dir;
bool OK = false;
// Check if initialisation has occurred, if yes then tidy-up.
if (completionPort != NULL) {
CloseHandle(thrd->getDirToMonitor());
thrd->setDirToMonitor(NULL);
CloseHandle(completionPort);
completionPort = NULL;
}
_dir = CreateFile(Edit1->Text.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (_dir != INVALID_HANDLE_VALUE) {
thrd->setDirToMonitor(_dir);
OK = true;
}
return OK;
}
//--------------------------------------------------------------------------
-
"Remy Lebeau (TeamB)" <gambit47.no.spam@no.spam.yahoo.com> wrote in message
news:400c484b$1@newsgroups.borland.com...
>
> "Daryl" <stockd@tpg.com.au> wrote in message
> news:4009b11a@newsgroups.borland.com...
>
> > The problem is when I want to immediately use the file that
> > has been copied, if I say want to open and parse the file I
> > use the following code:
>
> Please show your actual monitoring code. You have only shown a snippet
that
> has nothing to do with the issue being asked about.
>
> > //Check to see if file exists
> > if (!ifs) {
> > WriteToLog("File " + "full_path_to_file" + " does not exist.");
> > return;
> > }
>
> You are making an assumption about the error. Opening the file could fail
> for any number of reasons, not necessarily because the file doesn't exist.
>
> > My problem is that although I get notification of every
> > file that is placed into the monitored directory (I simply
> > set the directory to monitor and then drag and drop a file
> > to it) I only get a handle to the first file (using ifstream
> > ifs("full_path_to_file", ios::in);). All subsequent files added
> > to the directory will not be processed - Why?
>
> How are you determining the filenames to begin with? Probably, a
> notification can contain multiple filenames and you are simply not looping
> through them correctly? You have not shown enough code yet to see whether
> you are handling the notifications correctly to begin with.
>
> > It appears that they do not exist in the directory (I use the
> > full path to the file). I know this by the statement if (!ifs) failing.
>
> Again, that is not an indication of the files' existance or not.
>
>
> Gambit
>
Technical Details
Message-ID:
<4010bbda@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!archiver1.google.com!news2.google.com!fu-berlin.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mailShow Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 10cc67,8564915c86bce0ef,start X-Google-Attributes: gid10cc67,public X-Google-ArrivalTime: 2004-01-22 22:39:30 PST From: "Daryl" <daryl.stock@qr.com.au> Newsgroups: borland.public.cppbuilder.nativeapi Subject: Repost of ReadDirectoryChanges question... Date: Fri, 23 Jan 2004 16:14:48 +1000 Lines: 324 X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2800.1106 X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2800.1106 NNTP-Posting-Host: 203.9.186.133 Message-ID: <4010bbda@newsgroups.borland.com> X-Trace: newsgroups.borland.com 1074838490 203.9.186.133 (22 Jan 2004 22:14:50 -0700) Path: arc1-ca.pugleaf.net!archiver1.google.com!news2.google.com!fu-berlin.de!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail