Table of Content

Microsoft Access
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 `FileUtilities`, `HashTable` and `MD5` modules to your project and you can use the code as such:

~~~~brush:vb
Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync(“\\123.45.67.89\shared folder”)
~~~~
The `status` variable will return either of the following values:

* `AsyncDirectoryStatus.OK` if the path was found.
* `AsyncDirectoryStatus.NotFound` if the path was not found (either because it doesn’t exist or you don’t have the rights to access it).
* `AsyncDirectoryStatus.Checking` if 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.

### Optional arguments ###
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:
~~~~brush:vb
Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync(“\\123.45.67.89\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:
~~~~brush:vb
‘ 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 ###
The `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 `0` or `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 `Checking`.

Example of batch file automatically created to verify a path:
~~~~brush:bash
IF NOT EXIST “\\123.56.78.9\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 `\\123.56.78.9\going nowhere`.

**Getting back the result**
Whenever the `PathExistAsync` function is called, we check the currently cached result from the `AsyncDirectories` hastable.
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.

### Useful libraries ###
The code makes use of 2 extremely useful libraries that I end up using quite often:

* __a [HashTable][3] 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][4] 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][5] 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.

### Sample database ###
![Download][D]Download the PathExistAsync01.zip (67KB) containing the **Access 2007 ACCDB** database.

![Download][D]Download the PathExistAsync02b.zip (121KB) containing the **MDB** database[^1] (untested as I only have Access 2007).

![Test Database][TDB]

### License ###
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.


Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

### References ###
* [Hashtable implementation in VB][1]
* [Cryptography Software Code][2] (for the MD5 hash implementation in VB)

[1]: http://www.bytemycode.com/snippets/snippet/251/119/
[2]: http://www.di-mgt.com.au/crypto.html
[3]: http://en.wikipedia.org/wiki/Hash_table
[4]: http://en.wikipedia.org/wiki/Md5
[5]: http://en.wikipedia.org/wiki/Md5#Vulnerability
[D]: /wp-content/uploads/2008/05/download.png
[TDB]: /wp-content/uploads/2008/06/sshot-112.png

[^1]: A specific version for Access 2000 now included in the archive (updated 25JUL2008).

Last modified: Sunday, 18 April 2021

Author

Comments

Great !! I have been looking for answers like this and never found one really this good. This forces me to ask another question. . . Can you create code like this to see if a SQL server is found, and to see if the database is also found, or maybe retrieve a server list and database list from SQL. That will be really great to have. . . Imagine the code can say, looking, maybe even with a progress bar, and not hang the application. The access somtimes says not responding, misleading users, this wasy it will be more proffesional. . . Good Idea, inst it?

Hi Edwin, Thanks for the comment. One way to make a non-freezing database check could be to create a small database from code and launch it. That small database would simply attempt to reach the the main server. Once successful, it would write a flag in its ‘Result’ table. The main application could simply check regularly the external ‘Result’ table and display a permanent progress bar until a result is returned. I’ll think about this and try to implement it some time. Don’t have the time just now though ;-( Another, more simple solution would require a an external program or Access database already containing the necessary code and shipped with the application. Until now I’ve always tried to make my utilities from the principle that they should be created from within my main application, but it may be smarter to just create these ad-hoc tools separately and ship them with the app. Using external programs is actually an easy way to deal with blocking code. For more general asynchronous coding it’s actually more useful to use .Net or addins.

karel van der kaaden 

really usefull !!! I deal a long time with the problem of freezing the application when using Dir() or FileSystem object. Thanks. from the netherlands

Thank you Karel 🙂

thanks for your sample database module and class really works.

Howdy just wanted to give you a quick heads up and let you know a few of the images aren’t loading correctly. I’m not sure why but I think its a linking issue. I’ve tried it in two different web browsers and both show the same outcome.

@Rati Murty: Thanks for reporting the issue, I’m not sure what could be wrong as I can see the images loading properly. Maybe it was just a temporary problem. Could you be so kind as to double-check and let me know if you still have this issue. Thanks.

Comments are closed.