Friday, June 20, 2008
Access programming is inherently single-threaded. That’s usually OK as most operations are sequential anyway and it keeps things simple at the programming level.
There are times though where the lack of ability to run code on another thread is sorely missing: anything that takes a long time to run will just freeze the application, making it unresponsive and appearing to be locked and about to crash to the user.
Checking for the existence of network paths
Checking for the existence of network paths (directories or files) is one of these issues that can freeze an application for 30 seconds or more if the folder is not accessible.
This is a type of problem that benefits greatly from running in a separate thread: it can take such a long time that the best way to check for these remote paths is to launch the verification for their existence outside of Access and somehow get the result back and cache it for the current session so we don’t have to suffer these delays again every time we check for that path’s existence.
One easy way to do achieve that goal is to create plain DOS batch files that execute hidden from view, create a result file when they complete their task and delete themselves automatically when they are finished.
How to use it
Download the sample database below then just add the
MD5 modules to your project and you can use the code as such:
Dim status As AsyncDirectoryStatus status = FileUtilities.PathExistAsync("\\188.8.131.52\shared folder")
status variable will return either of the following values:
AsyncDirectoryStatus.OKif the path was found.
AsyncDirectoryStatus.NotFoundif the path was not found (either because it doesn’t exist or you don’t have the rights to access it).
AsyncDirectoryStatus.Checkingif the verification is in progress and we haven’t received a definite answer yet.
It’s up to you to decide how you want to handle that case. You could periodically check it, like I did in the example database, or you could disable the controls until you’re getting a confirmed result (by checking every time the user performs some action, like moving from record to record in a datasheet for instance).
You can call
PathExistAsync as often as you want to check the status: it will not slow down your application (read the optional arguments section below though).
The result of the verification is cached, so querying the existence of the path is actually only done once; the result of subsequent queries for the same path is just instantly retrieved from memory.
If you want to force the actual re-checking of a path without using the cached value, you can simply pass the
ForceCheck optional parameter:
Dim status As AsyncDirectoryStatus status = FileUtilities.PathExistAsync("\\184.108.40.206\shared folder", ForceCheck:=true)
The first time you query for a path (or force it to be rechecked) there will be a short 150ms delay to give a chance to the function to return its result straight away (in case the path can be resolved quickly).
This may not be desirable if you’re checking a bunch of directories at a time. For instance, this is what I do when my application launches:
' Check a bunch of paths in parallel PathExistAsync strPathToQualityDocuments, NoDelay:=true PathExistAsync strPathToFinancialDocuments, NoDelay:=true PathExistAsync strPathToShippingDocuments, NoDelay:=true PathExistAsync strPathToPurchasingDocuments, NoDelay:=true
By querying the existence of all these paths as soon as my application launches, I am starting the verification process without introducing delays in the application itself: each verification will start in its own process, in parallel to the main application.
Later in the application, when I need to actually use these paths, their result is likely to be known.
How it works
FileUtilities module contains the main code.
In it, the
PathExistAsync function works in slightly different ways depending on whether it’s the first time it is being called for a particular path or not.
The first time
The first time the function is called for a given path, we create in the user’s temporary folder the small batch file whose name is simply a MD5 hash (see below) of the path with
.bat appended to it.
This batch file simply checks for the existence of the path and will create a small file (whose name is the MD5 hash of the path) with either
1 in it depending on the result of the verification.
We initially cache the status of the verification for the Path into the
AsyncDirectories hashtable (see below) as
Example of batch file automatically created to verify a path:
IF NOT EXIST "\\220.127.116.11\going nowhere" GOTO NOTEXIST echo 1 > "C:\DOCUME~1\Renaud\LOCALS~1\Temp\463C7367D8329BD6209A65A70A7DA08C" GOTO END :NOTEXIST echo 0 > "C:\DOCUME~1\Renaud\LOCALS~1\Temp\463C7367D8329BD6209A65A70A7DA08C" :END DEL %0
The Batch file name is
463C7367D8329BD6209A65A70A7DA08C.bat where the long number is actually the MD5 hash of the path we’re checking
Getting back the result
PathExistAsync function is called, we check the currently cached result from the
If it is still
Checking then we try to verify if we the result file has been created from the running batch. If not, we just return the same status, if yes, we read the result from the file, save it in the hashtable and delete the result file.
The code makes use of 2 extremely useful libraries that I end up using quite often:
a HashTable implementation.
It makes it easy to create hashtable objects (otherwise known as Associative Arrays) to store and retrieve key/value pairs quickly.
Hashtables are often used to cache data and can be thought of arrays where the index is a string value instead of an number.
Here I use a hashtable to keep track of the paths we’ve checked and their result.
a MD5 hash implementation.
MD5 is a way to get a somewhat unique fixed-length value from a chunk of data.
It’s a mathematical function that guarantees that a small change in input (say a single bit in the input data) has a large effect on the output value (a totally different number will be generated) and that you can’t reverse the function (you can’t obtain the input just by looking at the output).
It is often used in applications to transform sensitive data like passwords into unique values that can be (somewhat) safely stored because you can’t easily reverse a md5.
Well, MD5 are not secure any longer but here we just use their ability to transform our path into a unique number that we can easily use as a filename and a key for our hash to retrieve the current status of the path being checked.
Download the PathExistAsync01.zip (67KB) containing the Access 2007 ACCDB database.
Please refer to the source code in the database for the exact licensing terms.
Note that the license only refers to code by me. When code from other sources is used, you will have to conform to their own licensing terms.
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
A specific version for Access 2000 now included in the archive (updated 25JUL2008). ↩