Posts filed under 'Programming'

MS Access: upsizing to SQL Server 2008

Microsoft Access I’m currently researching ways to move my main MS Access application from a simple local network client/backend setup to a global, multiple remote sites configuration using SQL Server.

One of the challenges is to upsize the current MS Access accdb backend database to SQL Server 2008. If you try it from Access itself using the Upsizing Wizard, you may end up getting this error message:

The Upsizing Wizard only works with Microsoft SQL Server (Versions 6.50 SP5 or higher). Please log in to a SQL Server data source.

The Upsizing Wizard only works with Microsoft SQL Server (Versions 6.50 SP5 or higher). Please log in to a SQL Server data source.

After spending some time fiddling around with SQL Server settings I couldn’t understand why I was still getting this error.
Turns out that the upsizing wizard is apparently sensitive to the version of SQL Server you’re using and it doesn’t consider SQL Server v10 (2008) as being later than v6.50…

This issue is in fact a blessing.
Microsoft provides a migration tool for upsizing MS Access database to SQL Server 2008 that’s orders of magnitude better than anything the basic wizard can do: the SQL Migration Assistant for Access, or SSMA.

Migrate from Access to SQL ServerSSMA lets you take a bunch of Access databases and move the tables and queries you choose to SQL Server, automatically linking them in your original database if you want.
It’s not just a one-off thing either: SSMA keeps track of the objects that where transferred and allows you to synchronise both schema and data as often as you need.

So here you are: do not use the basic MS Access Upsizing Wizard, download and use SSMA instead.

Strange COM Error

While SSMA works perfectly fine on my Windows 2008 x64 laptop, on my main Windows XP desktop it throws an exception when trying to load an Access database:

Unable to cast COM object of type ‘Microsoft.Office.Interop.Access.Dao.DBEngineClass’ to interface type ‘Microsoft.Office.Interop.Access.Dao._DBEngine’
… {00000021-0000-0010-8000-00AA006D2EA4}…

It was a COM error saying that the library for DAO couldn’t be loaded.

I couldn’t find any relevant information on the web.
After a while, I had a look at the DAO driver in
C:\Program Files\Common Files\Microsoft Shared\DAO\dao360.dll
and I noticed that the filename was in blue: that reminded me that I had set compression on the filesystem.

I disabled compression for the file and, magically, SSMA worked again…

Moral of the story: be careful about compressing your filesystem, some registered libraries and system files may work in unpredictable ways…



  • 23FEB2012: Added link to SSMA Team blog and updated download link to current version (5.2)
  • 17MAR2009: Added section on the strange COM error.
  • 14MAR2009: Added links to Tony’s Access MVP website.
  • 05JAN2009: Original publication.

30 comments March 17th, 2009

.Net: Working with OpenOffice 3

technology02.png(Updated Wednesday 30JAN2009.) Developing applications that manipulate OpenOffice documents has always been rather tricky; not very difficult, but just tricky to get right.

With OpenOffice 3, things got trickier and applications that used to work will now fail. I’ve just spend nearly a day trying to get a mail-merge application I built to work again with the new version of OO.

Changes and limitations

Developing .Net applications with OpenOffice 3 now requires that the .Net framework 3.5 be installed.
Only one version of OpenOffice/StarOffice must be installed.

Getting the CLI assemblies in your project

OpenOffice used to ship with a bunch of CLI DLLs for manipulating Open Documents from .Net. With OpenOffice version 3, these DLLs are directly installed in the GAC and not available in the Program installation folder any longer, making them unavailable when you need to add them as references to your Visual Studio project.

The trick is to extract the DLLs from the installation CAB, then reference those and make sure you exclude them in your setup file so only the latest version installed on the user’s computer’s GAC will get used.

  1. Open the installation folder where the installation files are extracted (usually left on your desktop under a folder like 3.0 (en-US) Installation Files during the OO installation process.
  2. Open the file (using a utility like 7zip if necessary) and extract the files matching cli_*.dl.
  3. Add the ones you need to your VS Project’s references and make sure the properties of these references have their Copy Local and Specific Version properties set to false.
  4. If you have a setup project that added these references to the list of files, right-click each of them and select Exclude to make sure they won’t be packaged.

The reason for not deploying the DLLs is that they are very specific to a given OpenOffice version and the latest assemblies relevant to the user’s OpenOffice installation will already be deployed to the target machine’s GAC.
When .Net loads assemblies, if they are not included in the path of the application being launched, they will be loaded from the GAC if they can be found there.

Path and environment setup

Before being able to call OpenOffice from your application you now must set your application’s environment variables to the correct paths so the assemblies can find the actual OpenOffice library and program files.

Basically, you need to add to your PATH the path to the folder where the UNO java libraries reside. You also need to add a UNO_PATH environment variable that points to the program folder of OpenOffice. Basically, before any call to OpenOffice functions you must:

  • Append to PATH something like C:\Program Files\ 3\URE\bin.
  • Create a UNO_PATH variable set to C:\Program Files\ 3\program.

Because there is no guarantee that these paths will not change or are valid for all systems you must get them from specific keys located in the Registry:

  • PATH is appended with the vaue of HKLM\SOFTWARE\\Layers\URE\1\UREINSTALLLOCATION to which you must append the bin directory.
  • UNO_PATH is set to the content of the HKLM\SOFTWARE\\UNO\InstallPath key.

See the C# and VB.Net code below for working examples.

Special Considerations for x64 systems

My development machine runs Windows Server 2008 x64 and I’ve ran into some specific issues that you’re likely to encounter when deploying to a 64 bits OS.

OpenOffice is 32 bits only
That means that your .Net project must be set to target x86 systems only:
Open your Solution’s Configuration Manager and under Active solution platform click New… then:


Make sure you repeat this for both the Debug and Release configurations.

Registry keys are elsewhere
32 bit applications see their registry keys normally expected under:
HKEY_LOCAL_MACHINE\Software moved to:
HKEY_LOCAL_MACHINE\Software\Wow6432Node instead.
This of course creates issues when you’re trying to read a registry key that’s not where it should be…

The Code

The code below will allow you to correctly connect to OpenOffice 3 under 32 or 64 bit systems.
It reads the registry to find the proper paths and appends the PATH and creates the UNO_PATH environment variables expected by the the bootstrapper to find the OpenOffice program and libraries.

The code is built upon information and a test program made available by Marten Feldtmann on his blog (more information, in English, is available on OOoForum ).

Please let me know if this works for you or if you have any corrections.

private void InitOpenOfficeEnvironment() {
  string baseKey;
  // OpenOffice being a 32 bit app, its registry location is different in a 64 bit OS
  if (Marshal.SizeOf(typeof(IntPtr)) == 8)
      baseKey = @"SOFTWARE\Wow6432Node\\";    
      baseKey = @"SOFTWARE\\";  

  // Get the URE directory
  string key = baseKey + @"Layers\URE\1";
  RegistryKey reg = Registry.CurrentUser.OpenSubKey(key);
  if (reg==null) reg = Registry.LocalMachine.OpenSubKey(key);
  string urePath = reg.GetValue("UREINSTALLLOCATION") as string;
  urePath = Path.Combine(urePath, "bin");

  // Get the UNO Path
  key = baseKey + @"UNO\InstallPath";
  reg = Registry.CurrentUser.OpenSubKey(key);
  if (reg==null) reg = Registry.LocalMachine.OpenSubKey(key);
  string unoPath = reg.GetValue(null) as string;

  string path;
  path = string.Format ("{0};{1}", System.Environment.GetEnvironmentVariable("PATH"), urePath);
  System.Environment.SetEnvironmentVariable("PATH", path);
  System.Environment.SetEnvironmentVariable("UNO_PATH", unoPath);

In VB.Net:

Private Sub InitOpenOfficeEnvironment()
  Dim baseKey As String
  ' OpenOffice being a 32 bit app, its registry location is different in a 64 bit OS
  If (Marshal.SizeOf(GetType(IntPtr)) = 8) Then
    baseKey = "SOFTWARE\Wow6432Node\\"
    baseKey = "SOFTWARE\\"
  End If

  ' Get the URE directory
  Dim key As String = (baseKey + "Layers\URE\1")
  Dim reg As RegistryKey = Microsoft.Win32.egistry.CurrentUser.OpenSubKey(key)
  If (reg Is Nothing) Then
    reg = Registry.LocalMachine.OpenSubKey(key)
  End If
  Dim urePath As String = CType(reg.GetValue("UREINSTALLLOCATION"),String)
  urePath = Path.Combine(urePath, "bin")

  ' Get the UNO Path
  key = (baseKey + "UNO\InstallPath")
  reg = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(key)
  If (reg Is Nothing) Then
    reg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(key)
  End If
  Dim unoPath As String = CType(reg.GetValue(Nothing),String)

  Dim path As String
  path = String.Format ("{0};{1}",System.Environment.GetEnvironmentVariable("PATH"),urePath)
  System.Environment.SetEnvironmentVariable("PATH", path)
  System.Environment.SetEnvironmentVariable("UNO_PATH", unoPath)
End Sub


07MAY2009 – Added reference link to OOo documentation.
03DEC2008 – Added VB.Net translation. Thanks to Stefan for suggesting it.
30JAN2009 – Added reference to Aleksandr Sazonov’s article on CodeProject (thanks for the the article).


32 comments November 6th, 2008

Windows 2008 / Windows 7 x64: The ‘Microsoft.Jet.OLEDB.4.0’ provider is not registered on the local machine.

TechnologyThere are times when the coexistence of 64 and 32 bit code on the same machine can cause all sorts of seemingly strange issues.
One of them just occurred to me while trying to run the ASPx demos from Developer Express, my main provider of .Net components (the best supplier I’ve ever been able to find).
I was getting the following error:

The ‘Microsoft.Jet.OLEDB.4.0’ provider is not registered on the local machine:

Server Error

It may look otherwise, but this error is generally due to either of two thing:

  • you don’t have Office 2007/2010 Jet drivers installed
  • or you are running a 32 bit application in a default x64 environment.

The first issue is easy to solve, just download the Access 2010 Database Engine from Microsoft (works with Access 2007 databases as well).

For the second one, the fix is also easy enough:

  • For Windows 2008: Navigate to Server Manager > Roles > Web Server (IIS) > Internet Information Services (IIS) Manager, then look under your machine name > Application Pool.
  • For Windows 7: Navigate to Programs > Administrative Tools > Internet Information Services (IIS) Manager, then look under your machine name > Application Pool.

Under there you can call the DefaultAppPool’s advanced settings to change Enable 32-Bits Applications to True:

Advanced Settings

You may have to restart the service for it to take effect but it should work.



  • 10DEC2011: Updated driver link to use the Access 2010 engine.
  • 03APR2010: Added instructions for Windows 7
  • 12FEB2009: Added reference to Scott’s article.
  • 28OCT2008: Original version

100 comments October 28th, 2008

SysAdmin: Installing Windows Server 2008 x64 on a Macbook Pro

security01.pngMy trusty old gigantic Sony Vaio is about 4 years old. It served me well and still works but it’s about to become my main development machine for the next couple of months and I can’t afford to have it die on me during that time.
It was time to get something as gigantic and more up-to-date in terms of technology.

I use VMware on my main desktop to keep multiple OS setups that match typical configurations of my customer’s machines.
This allows me to test my software before deployment and make sure everything works as expected. It saved me many times from strange bugs and I would consider these final tests to be a mandatory step before deployment.
My old trusty vaio would be hard pressed to run any of these without slowing down to a crawl.

I looked at some possible replacements. Initially I checked Lenovo’s offerings but they don’t seem to offer anything in large screen size (WUXGA 1920×1200) (Note, actually, they have, but not really for me).
Dito for Dell, not counting their humongous XPS M1730 luggable gaming machine that was wayyy over the top as a work computer, not to mention probably heavier than its volume in pure gold.

On a hint from a friend I checked out Apple’s online store and saw they had a nice Macbook Pro configuration. I went to check it out in the retail store close to my office and they had that exact specification in stock, so, in what must have been the highest rated expense/time-to-think ratio of any decision I ever took, well, I bought it…

The spec, some bragging rights:

  • Macbook Pro 17″
  • Core Duo T9500 2.6GHz processor
  • nVidia 8600M GT 512MB graphics card
  • 200GB 7200rpm drive
  • Kingston 4GB DDR2 667MHz RAM
  • Hi Resolution 17″ 1920×1200 glossy screen

It’s a very nice machine, Apple knows how to make nice hardware, there is no question there.
OSX has some cool features, some of them still a bit foreign to me and some minor annoyances are creeping up, like Thunderbird’s not picking up my system date and time settings and displaying the date in the wrong format (a pet peeve of me), probably not Apple’s fault but annoying nonetheless.
So far so good and while I don’t mind using OSX for my browsing, email and creative stuff, that machine is meant to be running Windows Server 2008 x64 as a development platform.

Why Windows Server 2008 x64?

Well, it has some excellent features, a smaller footprint than Vista, all the aero eye candy, is apparently noticeably faster than Vista and has none of the nagging security prompt (you are considered administrator though, so keeping safe is entirely up to you).
The 64 bit version can also address the full 4GB of RAM without limitation and all server features are optionally installable.
By default, the installation is actually pretty minimal and you have to set services and options to get Windows configured as a proper workstation. It is after all, meant to be a server.
Oh, I almost forgot that there is also support for HyperV, although you must make sure you download the right version (if you list all available downloads in your MSDN subscription, you’ll see some that are explicitly without that technology).

Installing Windows Server 2008 x64 is remarkably easy.

  • Get your hands on the ISO from your MSDN subscription or an install DVD from somewhere else (like a MS event, or even as a free 240 days download from Microsoft).
  • You’ll need to repackage the ISO as it won’t work properly (something to do with non-standard file naming options).
    It’s fairly easy if you follow the instructions from Jowie’s website (cached version): you can get the ImgBurn software for free as well, which is a good find in itself. It should’t take more than 30 minutes to repackage the DVD.
  • In OSX, go to Applications > Utilities > Boot camp and follow the instructions on screen.
    You will be able to resize the default partition by just moving the slider. I left 60GB for OSX and allocated the rest to Windows. The good thing is that OSX can read Windows partitions, so you can always store data there. Windows however, can’t read the HFS+ mac file system, although there are some third-party tools that can do it [1] [2] [3].
  • Insert your repackaged DVD and Bootcamp will have rebooted the machine.
    After a few minutes of blank screen (and no HDD activity light to let you know something is happening), windows setup launches.
  • You will be then prompted with the choice of partition to install to.
    Select the one named BOOTCAMP, then click the advanced options button and click format. From there one, windows will install everything, then reboot, then carry on installing, then reboot one last time.
  • Now, insert your OSX recovery CD 1. It should automatically launch the driver installation.
    Once done, you’ll reboot to a nice, full-resolution windows prompt.
  • All drivers will have been installed correctly except the one for Bluetooth. To easily solve that issue, just go to Spencer Harbar’s website and read how to install the Bluetooth drivers. Takes 5 minutes tops.

The final touches

A few notes to quickly get things running as expected.

  • Get the most of your configuration by following the list of tweaks from Vijayshinva Karnure from Microsoft India.
  • There are more tweaks, and even more tweaks available as well (don’t forget to enable Superfetch).
  • Microsoft has a whole KB entry on enabling user experience.
  • In the Control Panel > System > Advanced System Settings > Advanced > Settings > Advanced > Processor scheduling, set to Programs instead of Background services.
  • Activate your copy of Windows using Control Panel > System.
    I was getting an error code 0x8007232B DNS name does not exist error. To force activation, just click on the Change Product Key button and re-enter the same key you used during install.
    Windows will activate straight away.
  • When booting your Macbook, press the Option key and you will be presented a list of boot choices.
  • You can check on Apple’s Bootcamp webpage other information about how to use the track pad, keyboard layouts etc,


22 comments August 31st, 2008

Linux: AutoCAD DWG to SVG preview conversion.

Linux Years ago I did a small utility to convert DWG or DXF files into a vector-graphic, zoomable, SVG preview. The Linux command-line utility was used in a larger drawing management application that I had built before I left that company for greener pastures.

The Linux server-based application would scan the vast (100,000s of CAD drawings) and, depending on their format, attempt to create thumbnails and extract textual information from them and populate a database so we could easily find related drawings through a simple web-interface.

CAD File The software would present thumbnails of the drawings; when the drawing was in DWG or DXF format, the thumbnail would be an SVG vector representation of the original drawing instead of an image. That made the preview much more useful as you could zoom in and still retain enough detail to ascertain if the drawings was actually what you were looking for.

Other file formats (TIFF, HPGL plots, etc) were transformed into PNG image previews that were saved into 2 size: a small thumbnail, good enough to be displayed in a list, and a larger one that would show more details.

The server software was written in Perl, with some of the converters in C. Every night, the server would go through the whole tree of drawings on the filesystem, looking for drawings it hadn’t seen before and it would pass them to the appropriate plug-in for extracting text -where possible- and create thumbnails.

Memories of fun projects…

Download links:

4 comments July 18th, 2008

MS Access: checking network paths without freezing your application

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:

Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync("\\\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:

Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync("\\\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

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:

IF NOT EXIST "\\\going nowhere" GOTO NOTEXIST
echo 1 > "C:\DOCUME~1\Renaud\LOCALS~1\Temp\463C7367D8329BD6209A65A70A7DA08C"
echo 0 > "C:\DOCUME~1\Renaud\LOCALS~1\Temp\463C7367D8329BD6209A65A70A7DA08C"
DEL %0

The Batch file name is 463C7367D8329BD6209A65A70A7DA08C.bat where the long number is actually the MD5 hash of the path we’re checking \\\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 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.

Sample database

DownloadDownload the (67KB) containing the Access 2007 ACCDB database.

DownloadDownload the (121KB) containing the MDB database1 (untested as I only have Access 2007).

Test 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.

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


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

7 comments June 20th, 2008

MS Access: Enhanced Message Box Replacement

Microsoft Access This project provides a custom and enhanced message box replacement for the default MsgBoxfound in Access. A Test database containing all the code for Access 2007/2010/2013 is available at the bottom of this post.
(UPDATED Saturday 21OCT2014 to VERSION 1.10.)

What’s wrong with the default MsgBox

The default message box in Access is sometimes useful to warn, inform or ask confirmation from the user.

Standard MsgBox

It has, however, a few drawbacks:

  • It is bland: the standard message box does not even follow the currently selected Office colour scheme.
  • The amount of text it can display is limited: if you try to display too much text it will be truncated.
  • You can’t copy or save the content of the message.
  • Because popup boxes are viewed as intrusive, people tend not to read them and end-up closing message boxes before they realize they may have contained useful information.
  • They only displays plain text: you cannot format the message to draw attention to the key points.
  • They are blocking, meaning that nothing can happen in the main application while the box is displayed (it can’t even shut down).
  • It will only appear on the monitor that has the main Access application window, even though the message box may have been opened from a form on another monitor.

Sometimes you need to display an important message or require users to make take a decision.
Message boxes are not to be abused but they serve a useful purpose.

An enhanced message box

Rather than using the bland standard message box you can now have something a bit more customized.

Plain Text version of the enhanced custom message box under the Office Blue Colour Scheme:

Plaintex Enhanced Message Box

RichText version of the enhanced custom message box under the Office Black Colour Scheme:

RichText Enhanced Message Box

Here are the features of the enhanced message box:

  • It is entirely compatible with the standard one: just change MsgBox to Box using find and replace should be enough (see tip below to avoid getting strange errors).
  • It allows the user to simply click on a button to copy the content of the message to the clipboard or save it to a text file to a configurable default location.
  • It looks and feels like it belongs to the main application, following its colour scheme.
  • It attempts to prevent users from blindly closing the modal box reading the message: buttons will first be inactive for a configurable amount of time. It’s not a perfect solution, but it is quite effective.
  • There is a RichBox version that can display rich HTML content, not just plain text, so important parts of the message can be formatted in a useful way.
  • It is able to display large amount of data. While it’s not something you usually want, it may be useful for the message box to display more text in some situations (log or tracing information, legal documentation, etc).
  • Rather than sprinkling your code with “& vbCrLf & _” uglies, you can embed newlines in the text itself by using C-style “\n” escape sequences that will automatically be transformed into the appropriate newlines. Makes for clearer code and less typing.
  • Because you get the source, you can easily customise the message box with new icons and colours to better match your overall application’s personality.
  • It is non-blocking: if your application forces users to log-off after a certain amount of inactivity, the enhanced message box will just close rather than prevent Access from shutting down like the standard MsgBox does. Of course, it’s up to you to decide how to handle that gracefully, if at all.
  • It properly displays the expected button captions based on the language of the operating system, so it behaves very much like the default MsgBox (for instance, it will properly display “Cancel” on English systems and “Annuler” on French ones).
  • It also properly plays the system sounds associated with the type of message. You can also enable or disable the sound effect as needed.
  • From of version 1.4, it will display on the correct monitor in a multi-monitor environment.
  • Version 1.7 adds support for Unicode escape sequences within strings to display Unicode characters in the dialog box. This was added following the publication of this article about .Net Strings in VBA.
  • Version 1.10 adds a feature that allows users to dismiss a particular message so it doesn’t appear again.

How to use it

Download the demo database below and copy (drag & drop) the following into your application:

  • the FormDialog form,
  • the Dialog module.

If you rename the FormDialog, make sure you replace any occurrence to it in the code, in particular in the Dialog module.

Since the enhanced message box is just a replacement for the standard one, you just use it like you would use the MsgBox.

    ' Simple use of the Plaintext box
    ' Note the use of n that will be converted into a newline
    Dialog.Box "This is a plaintext message.\nClick OK to dismiss",
               vbOKOnly + vbinformation, _
               "Message Title"

    ' Getting the result back
    Dim dr As vbMsgBoxresult
    dr = Dialog.Box("Are you sure you want to delete?", _
                    vbYesNoCancel + vbQuestion, "Confirm action")
    If (dr = vbYes) Then DeleteRecords

    ' Using named parameters
    Dialog.Box Prompt:="All your bases are belong to us", _
               Buttons:=(vbOkOnly + vbCritical), _
               Title:="Bad error"

    ' Using the RichBox to display simple HTML
    ' The first line will be bold, then the word 'button' will be printed in red
    ' Here the \n will be escaped to '<br/>' tags to simulate newlines.
    Dialog.RichBox "<strong>This is a bold message</strong>.\n" & _
                   "Click the <font color=""#FF0000"">button</font> to dismiss.", 
                   vbOKOnly + vbInformation, _
                   "RichText Message Title"


There are a few additional settings that can be used to change the behaviour of the enhanced message boxes.

Custom buttons

You can customise the button labels instead of using the default ones (thanks to Kristjan for the suggestion):

Screenshot of dialog box with custom button labels

    ' Use custom labels. Buttons that are not labelled will not be displayed
    ' The returned value is either vbBt1, vbBt2 or vbBt3
    Dim dr As vbMsgBoxresultEx
    dr = Dialog.Box (Prompt:="This is a custom button label test.", 
                     Buttons:=vbCustom + vbInformation, _
                     Title:="A custom message", _
                     LabelButton1:="Hit Button 1!", _
                     LabelButton2:="No!, Me! Me!", _
                     LabelButton3:="Forget it!")

    If (dr = vbBt1) Then Debug.Print "Button 1 pressed!"
    ElseIf (dr = vbBt2) Then Debug.Print "Button 2 pressed!"
    ElseIf (dr = vbBt3) Then Debug.Print "Button 3 pressed!"
Button delay

One is that you can adjust the delay before the buttons become activated.

    ' Use the ButtonDelay to specify the time in seconds before the buttons become activated
    ' The default is 2s. Use 0 to activate the buttons immediately.
    Dialog.Box Prompt:="All your bases are belong to us", _
               Buttons:=(vbOkOnly + vbCritical), _
               Title:="Bad error", _

    ' Change the default delay value.
    ' To disable the activation delay
    Dialog.DefaultButtonDelay = 0    
    ' To make the user wait 3 seconds before they can press any button
    Dialog.DefaultButtonDelay = 3

Another one is that you can enable or disable whether beeps should be played or not.

    ' Use AllowBeep to specify whether beeps should be played when the message box opens
    ' By default, they are.
    Dialog.Box Prompt:="All your bases are belong to us", _
               Buttons:=(vbOkOnly + vbCritical), _
               Title:="Bad error", _

    ' Change the default behaviour. This is True by default.
    Dialog.DefaultAllowBeep = False
Hide Buttons

You can also hide the Copy to clipboard and save to File buttons which are normally visible by default.

    ' Use AllowCopyToClipboard and AllowSaveToFile to specify whether to display 
    ' the copy to clipboard and save to file buttons. 
    ' By default, they are visible, but here we hide them.
    Dialog.Box Prompt:="All your bases are belong to us", _
               Buttons:=(vbOkOnly + vbCritical), _
               Title:="Bad error", _
               AllowCopyToClipboard:=False, _

    ' Change the default behaviour. This is True by default.
    Dialog.DefaultCopyToClipboardAllowed = False
    Dialog.DefaultSaveToFileAllowed = False
Save Folder

It is recommended to set the the folder where we should save the content of the message when the user clicks the Save button on the message box.

    ' Change the save folder.
    ' By default, the text messages will be saved in the same directory as the database.
    ' Here we want them to be saved to a temp directory
    Dialog.DefaultSavedTextFileFolder = "C\:temp"

These few settings make the enhanced message box more customizable.

Raw text and paths

By default, the enhanced dialog box will escape certain sequences in the message to convert them to their printable version:

  • Escape sequences like \n and \t are converted to newlines and tabs spaces
  • Unicode sequences are converted to their symbol: \u20ac is converted to the euro symbol .

If you do not want this behaviour (for instance you need to display data that contains lots of \ characters), use the NoStrEsc option:

    ' By default, all messages are unescaped.
    ' Here however, we want to disable that so we can display 
    Dialog.Box Prompt:="A path c:\my\doc\file.doc", _

    ' Change the default behaviour. This is False by default.
    Dialog.DefaultNoStrEsc = True

Alternatively, you can use the helper function dialog.EscBackslash():

    ' Use EscBackslash() when you only want some portion of text
    ' to display '\' correctly, like paths.
    ' Here however, we want to disable that so we can display 
    Dialog.Box Prompt:="A path " & EscBackslash("c:\my\doc\file.doc")
Don’t display this message again

Based on suggestions (and on a feature I wanted to implement for a while), I added a way to allow the user to choose not to display a particular message again.

Note that this feature will only work for dialog boxes displaying a single vbOKOnly button. It makes some sense since if you ask the user to choose between multiple actions, you can’t really expect their choice to be the same every time the message is displayed.

To make the dialog box dismissable, you only need to provide it with a unique ID for the message, using the DismissID option:

    ' Use DismissID to allow the user to never show the message again.
    Dialog.Box Prompt:="An annoying warning message", _
               Buttons:= vbOKOnly + vbExclamation


The user can then tick the box and this particular message will never be shown again (unless we reset the setting for it).

To ensure that the user’s choice is remembered even if the Access application is updated, the message’s DismissID is stored in the registry under:
HKCU\Software\VB and VBA Program Settings\<AppFileName>\DialogBox, where <AppFileName> is simply the name of your Access file (without the path).

You can easily re-enable a particular message or all messages from your code:

    ' Re-enable the display of a previously dismissed message:
    Dialog.ResetDismissID "1234ABC"

    ' Re-enable the display of all messages:

Large text

The standard MsgBox cannot display much text. On the other hand, there is no real limitation to the amount of text the Box and RichBox can display.
When the amount of information is too much to fit the maximum allowed size for the message box the text will overflow and can be scrolled up/down as necessary.

Limitations of the RichBox

The RichBox version relies on the normal TextBox control’s ability under Access 2007 to display RichText wich is nothing more than lightweight HTML.
Because font size may be varying a lot in the message, it becomes very difficult to accurately predict the size of the box needed to display the whole message.
Short of implementing a complete HTML engine, we have to rely on some assumptions to display HTML.
The risk is that sometimes the content may not properly fit the TextBox control in some circumstances.
If you use the RichBox, thoroughly try displaying your messages and tweak the HTML as necessary to include additional lines or non-breaking spaces to ensure that the result looks good.
If you don’t overuse font size and don’t display in multiple fonts the RichBox should do the right thing most of the time.
Don’t overuse the RichBox to display colourful messages. There is a fine line between being informative and tasteless. Keep colours and formatting where it is useful.
I think that in most cases, the plain text version Box is more than enough.

How it works

The code makes extensive use of Win32 API calls.
Most of the hard work is done in the FomDialog class form. There is too much there to really go into the details but you are welcome to have a look at the commented code.
The code relies also on a utility function from Stephen Lebans used to calculate the size of of text. I have made some minor modification to that code so I would refer you to his original implementation if you are interested in calculating TextBox sizes for forms or reports.

In the code for the FormDialog, I re-implement some of the expected functionalities of the MsgBox: proper arrangement of the buttons, displaying of the appropriate icon, etc.
Once this is done, we calculate the size of the textbox needed to display the whole of the message.
In the case of RichText, we first use Application.PlainText() to convert the HTML into properly formatted plain text. We then calculate the Textbox size using a slightly larger font than needed as a way to ensure that the content of the RichText message will fit the box in most cases.
Once we know the size of the TextBox, we can easily resize the form to properly display the TextBox.
If there is too much text, we resize the form to its maximum permissible (70% of screen width and 90% of screen height) and change some of the visual cues to let the user know the text is overflowing.

One thing of note is the way the form is kept modal.
Rather than using DoCmd.OpenForm and DoCmd.Close I use the form as a class and create an instance manually (see the code in Dialog.Box and Dialog.Richbox). I keep this instance alive until I got the form’s result back.
If you are interested in knowing how the form is made modal, this is the code in FormDialog.ShowModal() what keeps the form open until the user clicks a button:

    Public Function ShowModal() As VbMsgBoxResult
        ' Here we reset the result for the clicked button such as vbOK, vbYes, etc
        ' This is set in each Button's Click event
        m_Result = -1
        ' Wait for the user to click a button
        Do While (m_Result = -1)
            Sleep 50
        ShowModal = m_Result
    End Function

The Sleep() function is a Win32 API that stops the current process for the given number of milliseconds. This in effects hands back the control to the Operating System for a short time. That way the system is still responsive and does not consume resources when it’s just waiting for user input.

Replacing MsgBox in existing code

As I said above, replacing the standard MsgBox is easy but you need to make sure your search and replace parameters are configured correctly:

Search and replace options

If you’re getting strange compile errors, it may be because you forgot to tick the Find Whole Word Only and some of the strings containing the letter sequence “msgbox” were replaced in the process.

If that’s the case, you can revert the damage by simply doing a search and replace across the whole project on:
VbboxStyle or VbDialog.BoxStyle to be replaced with VbMsgBoxStyle
VbboxResult or VbDialog.BoxResultto be replaced with VbMsgBoxResult

Upgrading from an older version

If you are already using the enhanced DialogBox, upgrading to the newest version is simple.

In your Access application:

  • delete the FormDialog form,
  • delete the Dialog module.
  • delete the API_GetTextMetrics module if you have it (used in versions before 1.5)

Download the new version of the demo database below and open it.

  • drag and drop the `FormDialog to your application
  • drag and drop the Dialog module to your application

That’s all you need to do.

Code and demo database

You can download a database containing all the necessary code as well as a number of tests.
This version contains the database in Microsoft Access accdb format (the code relies on features that don’t exist in pre-2007 versions of Access).


Download Download the (177KB), version 1.10 – 21OCT2014 containing the ACCDB database.

Code Updates

v1.10: 21OCT2014
Corrected minor bugs and added new features:

  • Added dialog.EscPath() to escape paths in your message and display them correctly, as suggested by Mark Singer in comment 115.
  • Added option NoStrEsc to display raw text when you don’t want escape and unicode sequences like ‘\n’ and ‘\u20ac’ to be converted at all in your whole message.
  • Modified the code for FileExists() to avoid the issue raised by Matthias Kläy in comment 116
  • Added option DismissID to allow the user to choose to prevent a message from displaying again (suggested by David Dewick in comment 110).

v1.9: 03FEB2014
Corrected some bugs and added some options:

  • Corrected bugs that would throw exceptions when a message would contain some file path whose ‘\’ would be wrongly interpreted as an escape sequence.
  • Added options to show the buttons for copying the message to the clipboard or saving it to file.

v1.8: 28SEP2013
Resolved some Unicode-related bugs:

  • Corrected bugs that would truncate the strings in the dialog box when they contain some Unicode characters.
  • Corrected bug with copy-to-clipboard that was not copying Unicode text.
  • Corrected bug with copy-to-file that was not saving Unicode text properly.

v1.7: 13SEP2013
Added support for character literals in strings and Unicode escape sequences as supported in .Net strings.
See using .Net strings in VBA for fun an profit for details.

v1.6: 29JUN2013
Corrected issues pointed out by Joseph Strickland (thanks) when running under Office 2010 x64.
Code updated and tested under a Virtual Machine running Win8 x64 and Office 2010 x64.

v1.5: 23JUN2013
Many thanks to contributors Steve Spiller, Jiriki and Kytu for improving and pointing out issues. See details below.

  • Moved the code from the API_GetTextMetrics module into the FormDialog class to reduce the number of necessary files (now only FormDialog and Dialog objects are necessary).
  • Corrected bugs on the test form (button delay and beep options on the form were not wired up correctly in the test form)
  • RichBox was not initialising its buttonDelay correctly, resulting in the first call to use a 0 delay instead of the DefaultButtonDelay value.
  • Corrected bug reported by Jiriki on 06JUN2013 (when the ButtonDelay was set to 0, the dialog would just close the first time the dialog was opened).
  • Focus issues should be solved: the buttons are now properly focused and will behave as the standard dialog box (you can hit ENTER or ESC on the keyboard once the buttons are visible to confirm the default dialog action or cancel it).
  • Addressed parent form focus issue mentioned by KyTu on 19JUN2013: when closing the dialog, the parent form will be properly focused instead of the Navigation Panel.
  • Now supports both x86 and x64 Office systems (32 and 64 bits versions of MSAccess). Many thanks to Steve Spiller for sending me the updated database.

v1.4: 01APR2013
It’s been a while, but at last some improvements and bug fixes!

  • As per Julie B’s comment, updated code to properly display the dialog on the proper monitor in multi-monitor environments.
    The dialog box will open in front of the Access window that currently has focus (assumed to be the one that opened the dialog), so if your application has forms on different monitors, the dialog should open on the right one. If we can’t determine the active window, the dialog box will open in the middle of the monitor containing the main Access application window.
  • Implemented Kristjan’s suggestion regarding the use of custom button labels. See updated description above.
  • Corrected background colours for the dialog box so they correctly match the MS Office theme.
  • Corrected a bug in the code that decided of the correct sound to play.

v1.3: 17MAR2009
Thanks to Henry of for proposing a correction to the default buttons behaviour.

  • Updated behaviour for the default buttons. They are now focused in a way that matches that of the standard msgbox.
  • Reversed the naming of the buttons on the form to make it a bit more consistent with the standard box.

v1.2: 07SEP2008
Thanks to Andy Colonna ( for uncovering the following bugs (check out his free Spell Checker with source code!):

  • Corrected bug in Form_FormDialog.FilenameSanitize() function that would fail to remove all invalid characters for a file name.
  • File name for the saved text message will be truncated to first 32 characters of message box title in Form_FormDialog.MakeFriendlyFileName().
  • Changed the use of FollowHyperlink to ShellExecute to avoid security warning in some instances in Form_FormDialog.btCopyToFile_Click()
  • Corrected twips to pixel conversion bug in API_GetTextMetrics.fTextWidthOrHeight() that would result in an improperly sized dialog box when the text message was too wide.

v1.1: 08AUG2008

  • Corrected code for DefaultButtonDelay (thanks to Geoffrey) (was referencing wrong variable, causing self-referencing code).
  • Corrected code for Box and RichBox to take the DefaultSavedTextFileFolder into account (the path was previously not passed onto the dialog boxes and the text file would always be created in the application folder instead of the one specified by DefaultSavedTextFileFolder)
  • Added license notice at top of source code.

v1.0: 20MAY2008

  • Original version


Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
Free for re-use in any application or tutorial providing clear credit is made about the origin of the code and a link to this site is prominently displayed where end-users can easily access it.

161 comments May 20th, 2008

MS Access: Restarting and compacting the database programmatically

Microsoft Access In my previous article about changing the MS Access colour scheme I had the need to allow the user to restart the database after the colour scheme was changed. (Article and Code Updated 13FEB2009.)

Being able to cleanly restart and compact the application is also useful in other instances:

  • Changes made to the environment
  • Recovering from errors (for instance after a network disconnection)
  • Forcing the user to re-log cleanly into the application
  • Automatically restarting a long-running application (for instance so that it may automatically compact on close and restart afresh with or without user intervention).

The problem is that you cannot -to the best of my knowledge- close and open again the same database from within MS Access itself. Most executables cannot do that and the way to solve the issue is usually to pass the control to another boostrap programme, close the main application and let the bootstrap programme re-open the main application again. I wanted a simple and clean way of using it. One that would not require shipping external dependencies.

How to use it

Download the sample database below, copy the Utilities module or just the Restart sub defined in it into your own application.

To use it, just call the Restart sub and the application will close and re-open. If you supply the optional Compact:=true parameter, the database will also be compacted during the restart process. This will work for normal databases (mdb/accdb) and also compiled (mde/accde) and runtime (accdr) databases as well.

Important note

If you want to use this code do not enable the Compact on Close option in Access for your database as the code doesn’t pick that up yet. Instead, you can either simply call restart Compact:=true on user action (for instance from a menu) or on other triggers, for instance when the database is being open and hasn’t been compacted for more than a week.

How it works

If you’re curious about the technical details, here is how it was put together. The main idea is that the MS Access database application has to be self-sufficient and restart itself by performing these steps:

  • create a small batch file
  • run the batch file, passing the path and extension of our database
  • close the main application
  • the running batch file would wait for the MS Access lock file to be removed
  • once the lock file disappears, we open the database after compacting it if required.

The key point here is that the batch file cannot just reopen the database right away: if the application is big or if it’s compacting on close for instance, it may take several seconds to actually close. The only moment we can be pretty sure that the database is effectively closed is when the lock file is deleted by MS Access.

The batch file is hard-wired in the Restart sub that does all the work:

SET /a counter=0
ping -n 1 -w 100 &gt; nul
SET /a counter+=1
IF "!counter!"=="60" GOTO CLEANUP
"%~f1" "%~f2.%3" /compact
start " " "%~f2.%3"
del %0

When the application runs the batch file, it passes 4 arguments:

  • the full path to the MSAccess.exe executable (used for compacting the database)
  • the full path to the database without the extension
  • the database file extension without the leading “.”
  • the appropriate database lock file extension (laccdb or ldb).

This allows us to easily construct the path to either the database or the lock file at line 07 and 09. Line 08 is actually only inserted if we need to compact the database: it simply launches MSAccess.exe with the /compact command line switch.

The funny use of PING is actually a simple way to wait for some time before we check if the lock file is still there or not. There is no SLEEP or WAIT function provided by default in Windows so we have to be a bit creative and use the time-out option of the PING command trying to ping an nonexistent, but valid, IP address. Once the lock file has disappeared, we open the database at line 09 and then delete the batch file itself so we leave no leftovers.

The other thing of note is that we now use a counter to keep track of the number of times we checked the existence of the lock file. Once this counter reaches a pre-determined amount (60 by default, ~ 45 seconds) we consider that there is a problem and the database application didn’t close, so we just exit and delete the batch file.

DownloadDownload the (48KB) containing both an Access 2007 ACCDB and Access 2000 MDB test databases.

Other implementations

Code Updates

v1.2: 13FEB2009

  • Added optional parameter to compact the database during restart.

v1.1: 09AUG2008

  • Now a separate test database (used to be bundled with the Colour Scheme sample).
  • Added support for older Access versions (an Access2000 MDB is now included).
  • Corrected wrong lock file extension for accd* files.
  • Added a time-out feature after which the batch file will delete itself after a while if the Access lock file wasn’t released (for instance following a crash).
  • Added checks to delete the batch file if it has not deleted itself for some reason (for instance after a reboot).
  • The batch file now has a unique name based on the name of the database, allowing multiple databases to be restarted from the same directory.
  • Added license notice at top of source code.
  • Updated the article to reflect the changes.

v1.0: 06MAY2008

  • Original version

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

40 comments May 6th, 2008

MS Access: Changing the Color Scheme programmatically

Microsoft Access Microsoft Office 2007/2010 comes with 3 colour (color) schemes. Users can easily change it but when you deploy an Access application under the Runtime your users have no way to set the colour scheme as the application’s options are not available.
(Article and Code Updated 01DEC2014.)

Luckily for us, the global colour scheme setting in the registry under the Key:

  • Office 2007: HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\Theme
  • Office 2010: HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\Theme
  • Office 2013: HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\UI Theme

The values being stored under that key are, For Office 2007 and Office 2010:

  • 1: Blue
  • 2: Silver
  • 3: Black

For Office 2013:

  • 0: White
  • 1: Light Gray
  • 2: Dark Gray

With this information, we can easily both read and set the colour scheme.
The only caveat is that I could not find a way to notify Access to reload the setting automatically once it is changed, so users will have to restart the application before the change becomes active. A small price to pay but if anyone has a better idea, please let me know.

To write the new value to the registry I use a set of WIN 32 APIs that are more flexible than the default ones provided in VBA.

Office 2007 Colour Schemes

You can download the sample database as it contains all necessary files, including the definition for the Win32 API functions.

DownloadDownload the (46KB) containing the ACCDB database.

The sample also contains some code to restart the database. This is the subject of another post: Restarting and compacting the database programmatically.


  • Find a way for Access to reload the settings without having to restart the application.
  • Use the knowledge about the current scheme to change the other colour settings in the application (or even adapt the form’s theme).


v1.4: 01DEC2014

  • Updated code for Office 2013 support.
  • Updated Registry API functions for compatibility with 64 bit Office
  • Combined all modules into OfficeScheme
  • Added new function to return the name of a Theme/current Theme in plain English.

v1.3: 31MAY2011

  • Updated code for Office 2010 support.
  • Autodetect correct registry key for Office 2007/2010

v1.2: 13FEB2009

  • Updated code for restarting the database.
    See exact changes at:

v1.1: 09AUG2008

  • Updated code for restarting the database.
    See exact changes at:
  • Added license notice to code.

v1.0: 03MAY2008

  • Original version


14 comments May 3rd, 2008

MS Access: Modal Dialogs with Transparent Backgrounds (redux)

Microsoft Access Microsoft Access Team made an interesting post and a follow-up on how to add a transparent layer that cover the screen to focus the attention of the user to a login form or other important popup window.

The trick is to use some WIN 32 API calls to modify the transparency of a standard MS Access form made to cover the screen.

The effect is quite neat and I thought I would try it and make a sample database for others to tinker with it.
My version allows you to chose between covering the whole screen or just the main Access window and it will test if it’s running under a Remote Desktop Terminal and disable the layer in that case.

The transparent layer covering the main Window onlyThe transparent layer covering the full screen

Update 07MAY2008

Following Rob’s improvements I made another sample database that incorporates his code with a few improvements:

  • I added the LightBoxForm.LayerToFullScreen property so users can choose explicitly how they want the layer to be shown.
  • I moved the code to hide the layer into a Hide() sub so you can just show/hide the layer using LightboxForm.Show and LightboxForm.Hide.
  • I changed the Form’s Resize event code in the LightBoxForm class to avoid flickering: resizing the form within its Resize event actually trigger the Resize event again a second time which causes flickering.
    I simply modified the code to make the form totally transparent (opacity of 0) the first time the event is fired and assign it the expected opacity when the event handler in re-entered.
' Handle the Layer Form Resize event
Private Sub m_objForm_Resize()
    Static busyResizing As Boolean
    Dim lngStyle As Long
    Dim r        As RECT

    ' disable screen updates
    m_objForm.Painting = False

    ' When the form opens initially, we make it totally transparent to avoid flickering
    lngStyle = GetWindowLong(m_objForm.hWnd, GWL_EXSTYLE)
    SetWindowLong m_objForm.hWnd, GWL_EXSTYLE, lngStyle Or WS_EX_LAYERED
    SetLayeredWindowAttributes m_objForm.hWnd, 0, 0, LWA_ALPHA

    ' If the Access window is maximized, then maximize the lightbox form.
    ' If the Access window is not maximized, then
    ' position the lightbox form so that it covers the Access window
    If IsZoomed(hWndAccessApp()) Or m_bLayerToFullScreen Then
        GetWindowRect Application.hWndAccessApp(), r
        MoveWindow m_objForm.hWnd, r.x1, r.y1, (r.x2 - r.x1), (r.y2 - r.y1), True
    End If

    If busyResizing Then
        ' Get the current window style, then set transparency
        lngStyle = GetWindowLong(m_objForm.hWnd, GWL_EXSTYLE)
        SetWindowLong m_objForm.hWnd, GWL_EXSTYLE, lngStyle Or WS_EX_LAYERED
        SetLayeredWindowAttributes m_objForm.hWnd, 0, (m_sngOpacity * 255), LWA_ALPHA
        ' enable screen updates
        m_objForm.Painting = True
        ' Back to normal
        busyResizing = False
        busyResizing = True
    End If
End Sub


There are now 2 sample databases. Ech zip contains a Microsoft Access 2007 ACCDB file and its conversion to Access 20001 and Access 2002-2003 MDB but please note that I have not been been able to test those in older version of Access and that form transparency doesn’t work in Operating Systems older than Windows 2000.

DownloadDownload (138KB), recommended version
(improved, more flexible version, based on Rob’s updated article).

DownloadDownload (122KB), original version
(simple code, based Rob’s original article).


  • If you are getting security warnings: make sure that you open the database from a Trusted Location or you will receive a security prompt. If you don’t know how to do that, check these steps.
  • If the layer appears on top of the login form instead of behind: make sure that the top-most form has ist Modal properties set to Yes and the frmLightBox form has its modal property set to No.
    If you improve on it, please let me know and I’ll post it here for all to find.

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

21 comments May 1st, 2008

Next Posts Previous Posts

Most Recent Posts



Posts by Month