🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #11746

Repost of ReadDirectoryChanges question...

#11746
From: Daryl
Date: Fri, 23 Jan 2004 06:14
322 lines
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-mail

Show 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