0

I have an old native application written in Borland Delphi:

enter image description here

The application interfaces with the special hardware and creates/stores its data in a BDE/Borland Paradox Database in a .db file. I am not the author of this application and the company who created it is long gone.

I need to add some custom functionality to this application and namely, to be able to read the database when a certain hardware-related event occurs. I found an old C library that allows me to read the paradox .db file. So that part is covered.

What I am trying to accomplish now is to find a way to track the moment when this application writes into its .db file. So I decided to try the following in my test app:

static void Main(string[] args)
{
    string path = "C:\\Program Files\\Company Name\\logfile.db";

    string strDirName = Path.GetDirectoryName(path);
    string strFileName = Path.GetFileName(path);

    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = strDirName;

    watcher.NotifyFilter = NotifyFilters.Attributes |
        NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastAccess |
        NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;

    watcher.Filter = strFileName;   // "*.db";

    watcher.IncludeSubdirectories = false;

    watcher.Changed += new FileSystemEventHandler(OnChanged);
    watcher.Created += new FileSystemEventHandler(OnChanged);
    watcher.Deleted += new FileSystemEventHandler(OnChanged);
    watcher.Renamed += new RenamedEventHandler(OnRenamed);
    watcher.Error += new ErrorEventHandler(OnError); 

    watcher.EnableRaisingEvents = true;

    Console.WriteLine("Starting the watch...");
    Console.WriteLine("Folder: " + watcher.Path);
    Console.WriteLine("File: " + watcher.Filter);

    while (true)
    {
        watcher.WaitForChanged(WatcherChangeTypes.All);
        Console.WriteLine("-next-");
    }
}

private static void OnChanged(object source, FileSystemEventArgs e)
{
    Console.WriteLine("-\"" + e.FullPath + "\", type=" + e.ChangeType + ", time=" + DateTime.Now);
}

private static void OnRenamed(object source, RenamedEventArgs e)
{
    Console.WriteLine("-\"{0}\" renamed to \"{1}\"", e.OldFullPath, e.FullPath);
}

private static void OnError(object source, ErrorEventArgs e)
{
    Console.WriteLine("#error: \"" + e.ToString() + "\", time=" + DateTime.Now);
}

The problem is that it doesn't seem to see any changes to the database.

If I test it with a .txt file by opening it in a Notepad and then saving it with some changes, it works fine. But not with the app I need it to.

Here's how I know it's not working:

  • The application in question is already running.

  • I start my app next to it. It shows no errors or exceptions. So I know that my watcher.WaitForChanged function had started.

  • I wait for the hardware event to be registered in the application in question.

  • My test app doesn't see any changes.

  • I then copy the logfile.db file, while both the application and my test app are still running, and then open it in my laptop with Paradox DB viewer. And it shows the new entry in the database.

So why is FileSystemWatcher not catching that logfile.db file being changed?

PS. I do all this on a 64-bit version of Windows 7 Pro.

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • This may be relevant to you: https://stackoverflow.com/questions/3540801/filesystemwatcher-does-not-report-changes-in-a-locked-file – Sam Axe May 31 '18 at 05:21
  • does the LastWrite change on the file? – TheGeneral May 31 '18 at 05:28
  • @SamAxe: Hmm. Interesting. I found [this](https://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html) and [this](https://social.msdn.microsoft.com/Forums/vstudio/en-US/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/filesystemwatcher-reliability?forum=netfxbcl) as well. I'll need to check that. I'm not sure though what is the proposed solution though? Do I have to flush the disk on a timer then, or what? – c00000fd May 31 '18 at 05:28
  • 2
    Directory entries are guaranteed updated only when the handle is closed. The FileSystemWatcher looks for directory entry changes. – Raymond Chen May 31 '18 at 06:28
  • @RaymondChen: So what's my other options to track the change in this case then? (I'd hate to just read/poll that database file on a repeating timer.) – c00000fd May 31 '18 at 17:01
  • I am not aware of any events that are raised for individual writes. I guess you could write a filter driver, but that's a pretty heavy hammer. – Raymond Chen May 31 '18 at 21:56
  • You can open the file and close it. That will [propagate the last-modified time into the directory](https://blogs.msdn.microsoft.com/oldnewthing/20111226-00/?p=8813). It's still polling, but less work than parsing the entire file. – Raymond Chen Jun 11 '18 at 04:27
  • @RaymondChen: Thanks for trying to help. I read your article and tried calling `CreateFileW` and `CloseHandle` on the file being tracked for changes from a worker thread in my process. Unfortunately that didn't change anything in this case. The change in that file was only detected when I closed it in the Paradox DB viewer. I also trying opening the volume where the file was located and flushing it (like was [suggested here](https://social.msdn.microsoft.com/Forums/vstudio/en-US/4465cafb-f4ed-434f-89d8-c85ced6ffaa8/filesystemwatcher-reliability?forum=netfxbcl)) but that also didn't help :( – c00000fd Jun 12 '18 at 00:01
  • Rats. Maybe they're updating the file via a memory-mapped file mapping (which does not affect last-modified). – Raymond Chen Jun 12 '18 at 00:45
  • @RaymondChen: I just realized that I should've tried `procmon` on it. Here's the [screenshot of the result](https://i.imgur.com/3Uc3q0i.jpg). I'm getting strange behavior though. Whenever the event that I want to trap happens it opens that `logbase.db` file several times and then closes it. So just for the heck of it I let my test app run by itself for a little while (the one with your suggestion of opening and closing the `logbase.db` file from a worker thread.) To my surprise when I checked later, I realized that it caught some events. I'm not sure why? – c00000fd Jun 12 '18 at 05:06
  • The worker thread in my test app runs every second (that does opening and immediate closing of the `logbase.db` file.) I noticed though that whenever I get multiple hardware events/logins at once (4 or 5 in a row) that generate repeated write events to the tracked file, `FileSystemWatcher` seems to catch them (along with your worker thread method.) But not the single events. – c00000fd Jun 12 '18 at 05:15
  • Sorry, my mistake. The file size in the directory entry is updated by opening/closing the file, but the timestamps are not. – Raymond Chen Jun 12 '18 at 19:37

1 Answers1

0

The BDE is a strange and frustrating piece of software to work with. I'm not surprised that although FileSystemWatcher doesn't see any changes, copying logfile.db does show the changes.

The BDE performs write caching, and it's turned on by default. To turn it off, try either one of these methods:

  1. Use the BDE Administrator applet to modify the IDAPI32.CNF or IDAPI32.CFG file, setting LOCAL SHARE to TRUE.

Or,

  1. Modify the registry for the machine running the BDE: [HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine\Settings\SYSTEM\INIT] LOCAL SHARE = TRUE
Chris R. Timmons
  • 2,187
  • 1
  • 13
  • 11
  • Are those 1 and 2 AND or OR options? I don't think I know what BDE administration applet is. I'd rather try to do it in registry. Although I would really like to solve it without applying any modifications to the target application/environment. – c00000fd May 31 '18 at 05:32
  • Thanks for clarification. I'll try it tomorrow and report back. Don't want to mess up that hardware this late at night :) – c00000fd May 31 '18 at 05:35
  • Chris, forgot to update this post. I checked the installation of BDE and the `LOCAL SHARE` value in registry was already set to `TRUE`. – c00000fd Jun 12 '18 at 05:12