<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cypris&#039; lookout &#187; MSAccess</title>
	<atom:link href="http://blog.nkadesign.com/category/programming/database/msaccess/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.nkadesign.com</link>
	<description>Renaud Bompuis on the interwebs!</description>
	<lastBuildDate>Sat, 14 Jan 2012 06:30:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Office 2010 SP1 and Access 2010 Runtime SP1 are broken</title>
		<link>http://blog.nkadesign.com/2011/office-2010-sp1-and-access-2010-runtime-sp1-are-broken/</link>
		<comments>http://blog.nkadesign.com/2011/office-2010-sp1-and-access-2010-runtime-sp1-are-broken/#comments</comments>
		<pubDate>Wed, 13 Jul 2011 11:22:25 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=706</guid>
		<description><![CDATA[(Updated Saturday 10DEC2011.) On 28th of June 2011, Microsoft Office 2010 Service Pack 1 and the Access 2010 Runtime Service Pack 1 were issued. After upgrading my development machine (Win7 x64) and a few clients (Windows 2008R2 x64) to SP1 (x86), I started to get strange issues: I use .Net libraries from my Access application [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />  (<strong>Updated Saturday 10DEC2011.</strong>) On 28th of June 2011, Microsoft Office 2010 Service Pack 1 and the Access 2010 Runtime Service Pack 1 were issued.</p>

<p>After upgrading my development machine (Win7 x64) and a few clients (Windows 2008R2 x64) to SP1 (x86), I started to get strange issues:</p>

<ul>
<li>I use .Net libraries from my Access application and suddenly, even when not instantiating any .Net objects, Access would crash, usually on startup, but sometimes when opening the VBE.<br />
Decompiling and re-compacting the database would be OK, usually once, but the problem would reappear the next time I would restart the application.<br />
<center><img title="MSAccess error" src="/wp-content/uploads/2011/07/sshot-4.png" width=491 Height=128 style="float:center;margin:10px;" /></center></li>
<li>In the Runtime, I would get strange errors, such as <em>The setting your entered isn&#8217;t valid for this property</em>, or <em>Action Failed Error Number: 2950</em>, or <em>Runtime Error 3270 Property not found</em>.<br />
The strange thing about these errors is that they would occur in places that had not been modified for many releases of our application, parts that have been running without problem until now.<br />
<center><img title="MSAccess error" src="/wp-content/uploads/2011/07/sshot-5.png" width=327 Height=199 style="margin:10px;" /></center></li>
<li>Another weird issue was the systematic reset of our custom ribbon to its first tab. this could happen randomly, but most it could also be reproduced by simply opening a report as a tab page (that fill-in the whole MDI window). When closing that form, the first tab of the ribbon would select itself automatically. This wasn&#8217;t happening when closing pop-up windows.</li>
</ul>

<p>After removing the Office and Runtime Service Pack 1, everything went back to normal.</p>

<h3>A fix, finally!</h3>

<p>A good 6 months after SP1 was released, Microsoft finally issued <a href="http://support.microsoft.com/kb/2596585">hotfix</a> for the issue!<br />
So, <a href="http://answers.microsoft.com/en-us/office/forum/office_2010-access/access-2010-sp1-you-receive-random-crashes-in/d2bf6175-075a-4a12-a2b1-f55d40af271b?page=17">read about it on the MS Forums</a> and get your <a href="http://support.microsoft.com/kb/2596585">KB2596585</a> hotfix, then <a href="http://www.trigeminal.com/usenet/usenet004.asp">decompile</a> your database to clean it out.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2011/office-2010-sp1-and-access-2010-runtime-sp1-are-broken/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Access: checking blank variables</title>
		<link>http://blog.nkadesign.com/2009/access-checking-blank-variables/</link>
		<comments>http://blog.nkadesign.com/2009/access-checking-blank-variables/#comments</comments>
		<pubDate>Wed, 09 Sep 2009 05:54:00 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=618</guid>
		<description><![CDATA[I often have to test String, Variant or Object variables that have no content and could be considered &#8216;blank&#8217;. The problem is that testing for &#8220;blankness&#8221; can mean many different things to different types: For an Object type, the variable can be Nothing. For a String type, the string can have no content at all: [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
I often have to test String, Variant or Object variables that have no content and could be considered &#8216;blank&#8217;.</p>

<p>The problem is that testing for &#8220;blankness&#8221; can mean many different things to different types:</p>

<ul>
<li>For an <code>Object</code> type, the variable can be <code>Nothing</code>.</li>
<li>For a <code>String</code> type, the string can have no content at all: <code>""</code>, <code>vbNullString</code>.</li>
<li>For a <code>Variant</code> type, the string can have any of the following attributes or values:

<ul>
<li>it can be <code>Missing</code> if the variable is an unused optional parameter,</li>
<li>it can be <code>Empty</code> if it was never assigned,</li>
<li>it can be <code>Null</code> if, for instance it&#8217;s bound to a nullable field or unbound with no value,</li>
<li>it can be an empty string <code>""</code>, <code>vbNullString</code>.</li>
</ul></li>
</ul>

<p>When having to check these variables in code, it can be tiresome to have to go through testing some of these possibilities just to find out that your variable does or not not contains something useful, regardless of the type of variable you are using.</p>

<p>To avoid having to do all these tests, make the code a bit more tidy and allow me to move on to more important things, I use this small utility function quite often:</p>

<p><textarea name="code" class="vb:nogutter">
'-----------------------------------------------------------------------------
' True if the argument is Nothing, Null, Empty, Missing or an empty string .
'-----------------------------------------------------------------------------
Public Function IsBlank(arg As Variant) As Boolean
    Select Case VarType(arg)
        Case vbEmpty
            IsBlank = True
        Case vbNull
            IsBlank = True
        Case vbString
            IsBlank = (arg = vbNullString)
        Case vbObject
            IsBlank = (arg Is Nothing)
        Case Else
            IsBlank = IsMissing(arg)
    End Select
End Function
</textarea></p>

<p>So now I don&#8217;t have to worry so much about the type of the variable I&#8217;m testing when I want to know if it contains useful data:
<textarea name="code" class="vb:nogutter">
...
' Here assume that CustomerReference is a control on a form.
' By using IsBlank() we avoid having to test both for Null and empty string.
If IsBlank(CustomerReference) Then
   MsgBox "Customer Reference cannot be left blank."
End If
...
</textarea></p>

<p>Obviously, <code>IsBlank()</code> doesn&#8217;t replace the other tests but I found it to be more straightforward to use in most cases.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-checking-blank-variables/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Access: Run-time Error 3155 ODBC insert on a linked table failed</title>
		<link>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/</link>
		<comments>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 08:26:06 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=606</guid>
		<description><![CDATA[I have been spending a lot of time trying to find out why some of the code used to insert new records into a linked SQL Server table would systematically fail with an error: Run-time Error '3155' ODBC--insert on a linked table failed It was driving me mad. I could insert a simple record using [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
I have been spending a lot of time trying to find out why some of the code used to insert new records into a linked SQL Server table would systematically fail with an error:</p>

<pre><code>Run-time Error '3155' ODBC--insert on a linked table  failed
</code></pre>

<p>It was driving me mad.<br />
I could insert a simple record using <em>SQL Server Management Studio</em>, I could add new records to the table in datasheet mode within Access, but as soon as I tried to insert a record from code, whether using DAO recordset or executing the same SQL INSERT, it would miserably fail.</p>

<p>After a fair bit of investigation and tests, of which you can read the full account on <a href="http://stackoverflow.com/questions/979269/inserting-null-in-an-nvarchar-fails-in-msaccess/">the question I asked on StackOverflow</a>, it turns out that this is a long-standing bug in the ODBC Driver (or Access).</p>

<p><code>Memo</code> fields in Access are usually translated into <code>nvarchar(MAX)</code> in SQL Server by tools like <a href="/2009/ms-access-upsizing-to-sql-server-2008/">SSMA</a>.<br />
Unfortunately, when you link tables having these fields using the <em>SQL Server Client</em> driver, these fields get incorrectly interpreted as <code>string</code>, even though they appear ok from the table design view.<br />
It&#8217;s only if you try to insert something into the field, either text larger than 255 chars or NULL, that you get the error message.</p>

<p>So, the solution, at least in this case, is to revert to the older <em>SQL Server</em> ODBC driver instead, or use <code>varchar()</code> instead of <code>nvarchar()</code>, but if you&#8217;re dealing with Unicode, you have to stick with <code>nvarchar()</code>.</p>

<h3>References</h3>

<ul>
<li>My question <a href="http://stackoverflow.com/questions/979269/inserting-null-in-an-nvarchar-fails-in-msaccess/">Inserting NULL in an nvarchar fails in MSAccess</a> on <a href="http://stackoverflow.com/">StackOverflow</a>.</li>
<li>A reference I found on this issue: <a href="http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/c6d2466e-ecb5-4a98-963f-ae827dbf8caa">Ms Access linking table with nvarchar(max)</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-run-time-error-3155-odbc-insert-on-a-linked-table-failed/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Access vs SQL Server: some stats (part 1)</title>
		<link>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/</link>
		<comments>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 04:00:53 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=527</guid>
		<description><![CDATA[In the perspective of upsizing my current Access 2007 application, I have been trying to understand a bit more about the possible performance impact of various choices of Primary Keys. My problem is that currently, the Access application uses autoincrement numbers as surrogate Primary Keys (PK). Since I will need to synchronise the data over [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
In the perspective of upsizing my current Access 2007 application, I have been trying to understand a bit more about the possible performance impact of various choices of Primary Keys.</p>

<p>My problem is that currently, the Access application uses autoincrement numbers as surrogate Primary Keys (PK).
Since I will need to synchronise the data over multiple remote sites, including occasionally disconnected clients, I can&#8217;t use the current autoincrement PK and will need to change to GUID.</p>

<p>To see for myself what could be the impact, I made a series of benchmarks.<br />
This first part is fairly simple:</p>

<ul>
<li>Populate a Product table that contains 3 fields: <code>ID</code>, <code>SKU</code> and <code>Designation</code> with 1,000,000 records.</li>
<li>Test natively on SQL Server and Access 2007.</li>
<li>The records are inserted in transactions batches of 1000 records.</li>
<li>I collect the time taken for each of these transactions and plot it.</li>
</ul>

<h3>Test setup</h3>

<p>Nothing much to say about that:</p>

<blockquote>
  <p>All tests are performed on a dedicated <em>Windows Server 2008 x64</em> rack running <em>Access 2007</em> and <em>SQL Server 2008 Standard (SP1) x64</em>.</p>
</blockquote>

<h3>Test database</h3>

<p>In SQL Server, we created a database with two tables <code>ProductGUID</code> and <code>ProductInt</code>:</p>

<p><textarea name="code" class="sql:nogutter" cols="60" rows="10">
CREATE TABLE ProductGUID (
	ID UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID(),
	SKU NVARCHAR(32) NOT NULL,
	Description NVARCHAR(255) NULL
);
CREATE CLUSTERED INDEX ProdGUIDix ON ProductGUID(ID);
GO

CREATE TABLE ProductINT (
	ID INT IDENTITY(1,1),
	SKU NVARCHAR(32) NOT NULL,
	Description NVARCHAR(255) NULL
);
CREATE CLUSTERED INDEX ProdINTix ON ProductINT(ID);
GO
</textarea></p>

<p>For the table using a GUID, we use the <code>NewSequentialID()</code> instead of <code>NewID()</code> to create new keys. This is supposed to offer much better performance as the generated GUIDs are guaranteed to be sequential rather than random, resulting in better index performance on insertion.</p>

<p>For the Access version of the tables, we basically use the same definition, except that we used 4 tables:</p>

<ul>
<li>ProductINT: let Jet/ACE autonumbering create the sequential integer Primary Key.</li>
<li>ProductINTRandom: let Jet/ACE autonumbering create the random integer Primary Key.</li>
<li>ProductGUIDRandom: let Jet/ACE use its own internal <code>GenGUID()</code> for the key which generates random GUIDs instead of sequential ones.  </li>
<li>ProdcutGUIDSequential: call the Windows API (<a href="http://www.pinvoke.net/default.aspx/rpcrt4/UuidCreateSequential.html">UuidCreateSequential</a>) to create sequential ID instead.</li>
</ul>

<h3>SQL Server Test code</h3>

<p>Using the SQL Server Management Studio, we performed the following test once for each table (resetting the database in-between):
<textarea name="code" class="sql:nogutter" cols="60" rows="10">
SET NOCOUNT ON;
GO

DECLARE @i INT = 1;
WHILE (@i <= 1000)
BEGIN
	DECLARE @tstart DATETIME2 = SYSDATETIME();
	BEGIN TRAN
		DECLARE @a INT = 1;
		WHILE (@a <= 1000)
		BEGIN
			INSERT INTO ProductGUID (SKU,Description) 
			VALUES ('PROD' + CONVERT(CHAR,@a), 'Product number ' + CONVERT(CHAR,@a));
			SELECT @a = @a + 1;
		END;
	COMMIT TRAN;
	SELECT DATEDIFF(MILLISECOND, @tstart, SYSDATETIME()) AS timespan;
SELECT @i = @i + 1;
END;
GO
</textarea></p>

<p>Basically, we perform 1000 transactions each inserting 1000 records into the table <code>ProductGUID</code> or <code>ProductINT</code>.</p>

<h3>Access 2007 Test code</h3>

<p>To duplicate the same conditions, the following VBA code will perform 1000 transactions each inserting 1000 records.<br />
Note that the recordset is opened in Append mode only.<br />
The importance of this will be discussed in another article.</p>

<p><textarea name="code" class="vb:nogutter" cols="60" rows="10">
' Run this to inset 1,000,000 products in batches of 1000
' In the given table
Public Sub Benchmark(TableName as String, InsertSeqGUID  as Boolean)
    Dim i As Integer
    For i = 1 To 1000
        Insert1000Products TableName, InsertSeqGUID 
    Next i
End Sub

' Insert 1000 products in a table
Public Sub Insert1000Products(TableName as String, InsertSeqGUID as boolean)
    Dim i As Long
    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    Dim ws As DAO.Workspace
    Dim starttime As Long
    Dim timespan As Long
    
    Set ws = DBEngine.Workspaces(0)
    DoEvents
    starttime = GetClock ' Get the current time in ms
    ws.BeginTrans
    Set db = CurrentDb
    Set rs = db.OpenRecordset(TableName, dbOpenDynaset, dbAppendOnly)
    With rs
        For i = 1 To 1000
            .AddNew
                If InsertSeqGUID Then !ID = "{guid {" & CreateStringUUIDSeq() & "}"
                !SKU = "PROD" & i
                !Description = "Product number " & i
            .Update
        Next i
    End With
    ws.CommitTrans
    rs.Close
    timespan = GetClock() - CDbl(starttime)
    Set rs = Nothing
    Set db = Nothing
    ' Print Elapsed time in milliseconds
    Debug.Print timespan
    DoEvents
End Sub
</textarea></p>

<p>We call this code to perform inserts on each of our Access tables:</p>

<ul>
<li>ProductINT table: we just insert data in the <code>ProductINT</code> table, letting Access create autonumber IDs.</li>
<li>ProductINTRandom table: we just insert data in the <code>ProductINTRandom</code> table, letting Access create random autonumber IDs.</li>
<li>ProductGUIDRandom table: we let Access create the Random GUID for the primary key.</li>
<li>ProductGUIDSequential: we use the Windows API to create a sequential ID that we insert ourselves.</li>
</ul>

<h3>Test results</h3>

<p>Without further ado, here are the raw results, showing the number of inserted record per second that we achieve for each test over the growing size of the database (here are only shown tests comapring Sequantial GUID and Autoincrement on SQL Server and Access, see next sections for the other results):</p>

<p><img src="/wp-content/uploads/2009/04/chart04.png" alt="Inserts per second" /></p>

<p>What we clearly see here is that performance when using autoincrement and Sequential GUID stays pretty much constant over the whole test.<br />
That&#8217;s good new as it means that using Sequential GUIDs do not degrade performance over time.</p>

<p>As a side note, in this particular test, Access offers much better raw performance than SQL Server. In more complex scenarios it&#8217;s very likely that Access&#8217; performance would degrade more than SQL Server, but it&#8217;s nice to see that Access isn&#8217;t a sloth.</p>

<h3>Using Sequential GUID vs Autoincrement in Access</h3>

<p>The results show that we do take a performance hit of about 30% when inserting Sequential GUID vs just using autonumbers.<br />
We&#8217;re still getting good results, but that&#8217;s something to keep in mind.</p>

<p>In terms of CPU consumption, here is what we get:</p>

<p><img src="/wp-content/uploads/2009/04/CPUAccess.png" alt="CPU load Access" /></p>

<p>Random PK, whether they are simple integer or GUID do consume substantially more CPU resources.</p>

<h3>Using Sequential GUID vs Identity in SQL Server</h3>

<p>Out-of-the box, SQL Server performs quite well and there is not much difference whether you&#8217;re using Sequential GUIDs or autoincrement PK.</p>

<p>There is however a surprising result: using Sequential GUIDs is actually slightly <em>faster</em> than using autoincrement!</p>

<p>There is obviously an explanation for this but I&#8217;m not sure what it is so please enlighten me :-)</p>

<p>CPU Consumption:</p>

<p><img src="/wp-content/uploads/2009/04/CPUSQLServer.png" alt="CPU load SQL Server" /></p>

<h3>Using Random GUID vs Sequential GUID vs Random Autonumber in Access</h3>

<p>So, what is the impact of choosing a Sequential GUID as opposed to letting Access create its own random GUIDs?</p>

<p><img src="/wp-content/uploads/2009/04/chart05.png" alt="Inserts per second Random GUID vs Sequential GUID in Access" /></p>

<p>It&#8217;s clear that random GUIDs have a substantial performance impact: their randomness basically messes up indexing, resulting in the database engine having to do a lot more work to re-index the data on each insertion.<br />
The good thing is that this degradation is pretty logarithmic so while it degrades over time, the overall performance remains pretty decent.<br />
While GUIDs are larger than Random Integers (16 bytes vs 4 bytes) the actual performance of inserting records whose PK is a random integrer is actually worse than random GUID&#8230;</p>

<h3>Provisional conclusions</h3>

<p>Here we&#8217;ve check the baseline for our performance tests.
In the next article, we&#8217;ll look exclusively at the performance of inserting data from a remote Access 2007 front end using our VBA code.</p>

<p>Having this baseline will allow us to check the performance overhead of using ODBC and letting Jet/ACE manage the dialogue with the SQL Server backend.</p>

<p>Feel free to leave your comments below, especially if you have any resources or information that would be useful.</p>

<h3>Updates</h3>

<ul>
<li>16APR2009: added test of random autonumber as PK in Access.</li>
<li>13APR2009: Original Article</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-vs-sql-server-some-stats-part-1/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Access: building &#8216;upsizable&#8217; applications.</title>
		<link>http://blog.nkadesign.com/2009/access-building-upsizable-applications/</link>
		<comments>http://blog.nkadesign.com/2009/access-building-upsizable-applications/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 03:39:45 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=433</guid>
		<description><![CDATA[When you start building an Access application, it&#8217;s tempting to just think about today&#8217;s problem and not worry at all about the future. If your application is successful, people will want more out of it and, over time, you&#8217;ll be faced with the task of moving the back-end database to a more robust system like [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
When you start building an Access application, it&#8217;s tempting to just think about today&#8217;s problem and not worry at all about the future.<br />
If your application is successful, people will want more out of it and, over time, you&#8217;ll be faced with the task of moving the back-end database to a more robust system like SQL Server.</p>

<p>While there are <a href="/2009/ms-access-upsizing-to-sql-server-2008/">tools like SSMA that can help you move an Access database to SQL Server</a>, <strong>a lot of the problems you&#8217;ll encounter can be solved before you even have to think about upsizing</strong>.<br />
Abiding by a few simple rules will cost you nothing when creating your Access application but will save you a lot of headache if -when- the time comes to upsize.</p>

<p>So here are a few things to keep in mind.</p>

<h3>Naming conventions</h3>

<p>Access is pretty liberal about naming conventions and it will let you freely name your tables, columns indexes and queries.
When these get moved to another database you&#8217;ll most probably be faced with having to rename them.<br />
In some cases, you could actually create subtle bugs because something that used to work fine in Access may be tolerated in the new database but be interpreted differently.</p>

<ul>
<li><p><strong>Do not use spaces or special characters</strong> in your data object names.<br />
Stick to characters in the range <code>A</code> through <code>Z</code>,  <code>0</code> to <code>9</code> with maybe underscores <code>_</code> somewhere in between (but not at the start or the end).<br />
Also try to respect casing wherever you reference this name (especially for databases like MySQL which are case-sensitive if the hosted on a Linux platform for instance).<br />
eg:<br />
<code>Customer Order Lines (archive)</code> should be  <code>CustomerOrderLines_Archive</code>.<br />
<code>Query for last Year's Turnover</code> should be <code>QueryLastYearTurnover</code>.<br />
Index <code>ID+OrderDate</code> should become instead <code>ID_OrderDate</code>.</p></li>
<li><p><strong>Do not use keywords that are reserved</strong> or might mean something else whether they are SQL keywords or functions names:<br />
A column called <code>Date</code> could be renamed <code>PurchaseDate</code> for instance.<br />
Similarly, <code>OrderBy</code> could be renamed <code>SortBy</code> or <code>PurchaseBy</code> instead, depending on the context of <em>Order</em>.<br />
Failing to do so may not generate errors but could result in weird and difficult to debug behaviour.</p></li>
<li><p><strong>Do not prefix tables with <code>Sys</code>, <code>USys</code>, <code>MSys</code> or a tilde <code>~</code>.</strong><br />
Access has its own internal system tables starting with these prefixes and it&#8217;s best to stay away from these.<br />
When a table is deleted, Access will often keep it around temporarily and it will have a tilde as its prefix.</p></li>
<li><p><strong>Do not prefix Queries with a tilde <code>~</code>.</strong><br />
Access use the tilde to prefix the hidden queries kept internally as recordsource for controls and forms.</p></li>
</ul>

<h3>Database design</h3>

<ul>
<li><p><strong>Always use Primary keys.</strong><br />
Always have a non-null primary key column in every table.<br />
All my tables have an autonumber column called <code>ID</code>.  Using an automatically generated column ID guarantees that each record in a table can be uniquely identified.<br />
It&#8217;s a painless way to ensure a minimum level of data integrity.</p></li>
<li><p><strong>Do not use complex multivalue columns.</strong><br />
Access 2007 introduced <a href="http://office.microsoft.com/en-us/access/HA012337221033.aspx">complex columns that can record multiple values</a>.<br />
They are in fact fields that return whole recordset objects instead of simple scalar values.  Of course, this being an Access 2007 only feature, it&#8217;s not compatible with any other database.
Just don&#8217;t use it, however tempting and convenient it might be.<br />
Instead use a table to record Many-To-Many relationships between 2 tables or use a simple lookup to record lists of choices in a text field itself if you&#8217;re only dealing with a very limited range of multivalues that do not change.</p></li>
<li><p><strong>Do not use the Hyperlink data type.</strong><br />
Another Access exclusive that isn&#8217;t available in other databases.</p></li>
<li><p><strong>Be careful about field lookups.</strong><br />
When you create Table columns, Access allows you to define lookup values from other tables or lists of values.<br />
If you <em>manually</em> input a list of values to be presented to the user, these won&#8217;t get transferred when upsizing to SQL Server.<br />
To avoid having to maintain these lookup lists all over your app, you could create small tables for them and use them as lookup instead; that way you only need to maintain a single list of lookup values.</p></li>
<li><p><strong>Be careful about your dates.</strong><br />
Access date range is much larger than SQL Server.<br />
This has 2 side-effects:<br />
1) if your software has to deal with dates outside the range, you&#8217;ll end-up with errors.<br />
2) if your users are entering dates manually, they could have made mistakes when entering the year (like 09 instead of 2009).<br />
Ensure that user-entered dates are valid for your application.</p></li>
</ul>

<h3>VBA</h3>

<p>While most of your code will work fine, there are a few traps that will bomb your application or result in  weird errors:</p>

<ul>
<li><p><strong>Always explicitly specify options when opening recordsets or executing SQL.</strong><br />
With SQL Server, the <code>dbSeeChange</code> is mandatory whenever you open a recordset for update.<br />
I recommend using <code>dbFailOnError</code> as well as it will ensure that the changes are rolled back if an error occurs.
<textarea name="code" class="vb:nogutter">
Dim rs as DAO.RecordSet
' Open for read/write
set rs = db.OpenRecordSet("Stock", dbOpenDynaset, dbSeechanges + dbFailOnError)
' Open for read only
set rs = db.OpenRecordSet("Stock", dbOpenSnapshot)
' Direct SQL execution
CurrentDB.Execute "INSERT INTO ...",  dbSeeChanges + dbFailOnError
</textarea></p></li>
<li><p><strong>Get the new autonumbered ID <em>after</em> updating the record.</strong><br />
In Access, autonumbered fields are set as soon as the record is added even if it hasn&#8217;t been saved yet.<br />
That doesn&#8217;t work for SQL Server as autonumbered IDs are only visible after the records have been saved.
<textarea name="code" class="vb:nogutter">
' Works for Access tables only
' We can get the new autonumber ID as soon as the record is inserted
rs.AddNew
mynewid = rs!ID
...
rs.Update

' Works for ODBC and Access tables alike
' We get the new autonumber ID after the record has been updated
rs.AddNew
...
rs.Update
rs.Move 0, rs.LastModified
mynewid = rs!ID
</textarea></p></li>
<li><p><strong>Never rely on the type of your primary key.</strong><br />
This is more of a recommendation but if you use an autonumbered ID as your primary key, don&#8217;t rely in your code or you queries on the fact that it is a <em>long integer</em>.<br />
This can become important if you ever need to upsize to a replicated database and need to transform your number IDs into GUID.<br />
Just use a Variant instead.</p></li>
</ul>

<h3>Parting thoughts</h3>

<p>These simple rules will not solve all your problems but they will certainly reduce the number of issues you&#8217;ll be faced with when upsizing you Access application.<br />
Using a tool like <a href="/2009/ms-access-upsizing-to-sql-server-2008/">SSMA</a> to upsize will then be fairly painless.</p>

<p>If you have other recommendations, please don&#8217;t hesitate to leave them in the comments, I&#8217;ll regularly update this article to included them.</p>

<h3>References</h3>

<ul>
<li>You will find lots of other bits of wisdom on this page: <a href="http://www.granite.ab.ca/access/sqlserverupsizing.htm">My random thoughts on SQL Server Upsizing from Microsoft Access</a> by Tony, from Granite Consulting.</li>
<li>Martin Green&#8217;s Office tips has a series of <a href="http://www.fontstuff.com/siteindex.htm#access">articles on Access to SQL Server migration</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/access-building-upsizable-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MS Access: upsizing to SQL Server 2008</title>
		<link>http://blog.nkadesign.com/2009/ms-access-upsizing-to-sql-server-2008/</link>
		<comments>http://blog.nkadesign.com/2009/ms-access-upsizing-to-sql-server-2008/#comments</comments>
		<pubDate>Tue, 17 Mar 2009 07:00:36 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=285</guid>
		<description><![CDATA[I&#8217;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 2007 backend database to SQL Server 2008. If you try it from Access itself using the [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
I&#8217;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.</p>

<p>One of the challenges is to upsize the current MS Access 2007 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:</p>

<blockquote>
  <p><em>The Upsizing Wizard only works with Microsoft SQL Server (Versions 6.50 SP5 or higher). Please log in to a SQL Server data source.</em></p>
  
  <p><img src="/wp-content/uploads/2009/01/UpsizingErrorsm.png" alt="The Upsizing Wizard only works with Microsoft SQL Server (Versions 6.50 SP5 or higher). Please log in to a SQL Server data source." /></p>
</blockquote>

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

<p>This issue is in fact a blessing.<br />
Microsoft provides a migration tool for upsizing MS Access database to SQL Server 2008 that&#8217;s orders of magnitude better than anything the basic wizard can do: the <a href="http://www.microsoft.com/sqlserver/2008/en/us/migration.aspx">SQL Migration Assistant for Access, or SSMA</a>.</p>

<p>SSMA 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.<br />
It&#8217;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.</p>

<p>So here you are: do <strong>not</strong> use the basic <em>MS Access Upsizing Wizard</em>, download and use SSMA instead.</p>

<h3>Strange COM Error</h3>

<p>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:</p>

<blockquote>
  <p><em>Unable to cast COM object of type &#8216;Microsoft.Office.Interop.Access.Dao.DBEngineClass&#8217; to interface type &#8216;Microsoft.Office.Interop.Access.Dao._DBEngine&#8217;<br />
  &#8230; {00000021-0000-0010-8000-00AA006D2EA4}&#8230;</em></p>
</blockquote>

<p>It was a COM error saying that the library for DAO couldn&#8217;t be loaded.</p>

<p><a title="Disabling Filesystem Compression" style="border:0;" href="/wp-content/uploads/2009/01/sshot-568.png" rel="lightbox"><img title="Disabling Filesystem Compression" src="/wp-content/uploads/2009/01/sshot-568sm.png" style="float:right;margin-left:10px;margin-bottom:5px;" /></a>
I couldn&#8217;t find any relevant information on the web.<br />
After a while, I had a look at the DAO driver in<br />
<code>C:\Program Files\Common Files\Microsoft Shared\DAO\dao360.dll</code><br />
and I noticed that the filename was in blue: that reminded me that I had set compression on the filesystem.</p>

<p>I disabled compression for the file and, magically, SSMA worked again&#8230;</p>

<p>Moral of the story: be careful about compressing your filesystem, some registered libraries and system files may work in unpredictable ways&#8230;</p>

<h3>References</h3>

<ul>
<li><a href="http://www.microsoft.com/sqlserver/2008/en/us/migration.aspx">SQL Server Migration Assistant for Access</a> main page.</li>
<li><a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=133B59C2-C89C-4641-BEBB-6D04476EC1BA&amp;displaylang=en">SQL Server Migration Assistant download</a>. Note that you will have to register to get a license file that you need to save on your PC before you can use this free tool.</li>
<li><a href="http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.access.sqlupsizing&amp;tid=a1198438-f914-4710-91ca-440da168dc5d&amp;cat=&amp;lang=&amp;cr=&amp;sloc=&amp;p=1">&#8220;Access 2007 Upsizing to SQL Server 2008 Express in SQL Upsizing&#8221;</a> discussion on Microsoft groups.</li>
<li><a href="http://support.microsoft.com/kb/838594">&#8220;KB838594: Error message when you try to upsize your Access database by using the Upsizing Wizard&#8221;</a> is not the problem here, but some may find the reference useful if you&#8217;re trying to upsize your MS Access database to a SQL Server database on a network.</li>
<li><a href="http://www.granite.ab.ca/access/sqlserverupsizing.htm">My random thoughts on SQL Server Upsizing from Microsoft Access</a> from Tony&#8217;s Access MVP website, and his <a href="http://www.granite.ab.ca/access/sqlserverupsizinglinks.htm">list of links to other resources</a>.</li>
</ul>

<h3>Updates</h3>

<ul>
<li>17MAR2009: Added section on the strange COM error.</li>
<li>14MAR2009 : Added links to Tony&#8217;s Access MVP website.</li>
<li>05JAN2009 : Original publication.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2009/ms-access-upsizing-to-sql-server-2008/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>SysAdmin: Installing Windows Server 2008 x64 on a Macbook Pro</title>
		<link>http://blog.nkadesign.com/2008/mac-installing-windows-server-2008-x64-on-a-macbook-pro/</link>
		<comments>http://blog.nkadesign.com/2008/mac-installing-windows-server-2008-x64-on-a-macbook-pro/#comments</comments>
		<pubDate>Sun, 31 Aug 2008 06:04:39 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[sysadmin]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=149</guid>
		<description><![CDATA[My trusty old gigantic Sony Vaio is about 4 years old. It served me well and still works but it&#8217;s about to become my main development machine for the next couple of months and I can&#8217;t afford to have it die on me during that time. It was time to get something as gigantic and [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/apple.png" alt="security01.png" title="security01.png" align="left" width="53" height="64" hspace="5" vspace="5" border="0" />My trusty old gigantic <a href="http://etc.nkadesign.com/Linux/SonyVaio">Sony Vaio</a> is about 4 years old. It served me well and still works but it&#8217;s about to become my main development machine for the next couple of months and I can&#8217;t afford to have it die on me during that time.<br />
It was time to get something as gigantic and more up-to-date in terms of technology.</p>

<p>I use VMware on my main desktop to keep multiple OS setups that match typical configurations of my customer&#8217;s machines.<br />
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.<br />
My old trusty vaio would be hard pressed to run any of these without slowing down to a crawl.</p>

<p>I looked at some possible replacements. Initially I checked Lenovo&#8217;s offerings but they don&#8217;t seem to offer anything in large screen size (<a href="http://en.wikipedia.org/wiki/WUXGA">WUXGA</a> 1920&#215;1200) (Note, actually, <a href="http://www.tomshardware.com/news/Lenovo-W700-wacom-17-thinkpad,6117.html">they have</a>, but not really for me).<br />
Dito for Dell, not counting their humongous <a href="http://www.dell.com/content/products/productdetails.aspx/xpsnb_m1730?c=us&amp;l=en&amp;s=dhs">XPS M1730</a> luggable gaming machine that was wayyy over the top as a work computer, not to mention probably heavier than its volume in pure gold.</p>

<p><a title="Powerbook Pro" style="border:0;" href="/wp-content/uploads/images/MacBookPro_cr.png" rel="lightbox"><img title="Macbook Pro" src="/wp-content/uploads/images/MacBookPro_sm.png" width=250 Height=163 style="float:right;margin-left:10px;margin-bottom:5px;" /></a>
On a hint from a friend I checked out <a href="http://store.apple.com/hk/configure/MB166ZP/A?mco=NzUzNDY0">Apple&#8217;s online store</a> 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&#8230;</p>

<p>The spec, some bragging rights:</p>

<ul>
<li>Macbook Pro 17&#8243;</li>
<li>Core Duo T9500 2.6GHz processor</li>
<li>nVidia 8600M GT 512MB graphics card</li>
<li>200GB 7200rpm drive</li>
<li>Kingston 4GB DDR2 667MHz RAM</li>
<li>Hi Resolution 17&#8243; 1920&#215;1200 glossy screen</li>
</ul>

<p>It&#8217;s a <em>very</em> nice machine, Apple knows how to make nice hardware, there is no question there.<br />
OSX has some cool features, some of them still a bit foreign to me and some minor annoyances are creeping up, like Thunderbird&#8217;s not picking up my system date and time settings and displaying the date in the wrong format (a <a href="http://blog.nkadesign.com/2007/people-mind-your-dates-plz/">pet peeve</a> of me), probably not Apple&#8217;s fault but annoying nonetheless.<br />
So far so good and while I don&#8217;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.</p>

<h3>Why Windows Server 2008 x64?</h3>

<p>Well, it has some excellent features, a smaller footprint than Vista, all the aero eye candy, is apparently <a href="http://exo-blog.blogspot.com/2008/03/windows-2008-vista-done-right.html">noticeably faster than Vista</a> and has none of the nagging security prompt (you are considered administrator though, so keeping safe is entirely up to you).<br />
The 64 bit version can also address the full 4GB of RAM without limitation and all server features are optionally installable.<br />
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.<br />
Oh, I almost forgot that there is also support for <a href="http://en.wikipedia.org/wiki/Hyperv">HyperV</a>, although you must make sure you download the right version (if you list all available downloads in your MSDN subscription, you&#8217;ll see some that are explicitly without that technology).</p>

<h3>Installing Windows Server 2008 x64 is remarkably easy.</h3>

<ul>
<li>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 <a href="www.microsoft.com/windowsserver2008">free 240 days download from Microsoft</a>).</li>
<li>You&#8217;ll need to repackage the ISO as it won&#8217;t work properly (something to do with <a href="http://support.microsoft.com/kb/931708">non-standard file naming options</a>).<br />
It&#8217;s fairly easy if you follow the instructions from <a href="http://jowie.com/blog/post/2008/02/24/Select-CD-ROM-Boot-Type-prompt-while-trying-to-boot-from-Vista-x64-DVD-burnt-from-iso-file.aspx">Jowie&#8217;s website</a> <em>(<a href="/wp-content/uploads/2008/08/Select-CD-ROM-Boot-Type-prompt-while-trying-to-boot-from-Vista-x64-DVD-burnt-from-iso-file.aspx.htm">cached version</a>)</em>: you can get the <a href="http://www.imgburn.com/">ImgBurn</a> software for free as well, which is a good find in itself. It should&#8217;t take more than 30 minutes to repackage the DVD.</li>
<li>In OSX, go to Applications > Utilities > Boot camp and follow the instructions on screen.<br />
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&#8217;t read the <a href="http://en.wikipedia.org/wiki/HFS_Plus">HFS+</a> mac file system, although there are some third-party tools that can do it  <a href="http://www.ufsexplorer.com/">[1]</a> <a href="http://www.macdisk.com/prospen.php3">[2]</a> <a href="http://www.acutesystems.com/">[3]</a>.</li>
<li>Insert your repackaged DVD and Bootcamp will have rebooted the machine.<br />
After a few minutes of blank screen (and no HDD activity light to let you know something is happening), windows setup launches.</li>
<li>You will be then prompted with the choice of partition to install to.<br />
Select the one named BOOTCAMP, then click the <em>advanced options</em> button and click <em>format</em>.
From there one, windows will install everything, then reboot, then carry on installing, then reboot one last time.</li>
<li>Now, insert your <em>OSX recovery CD 1</em>. It should automatically launch the driver installation.<br />
Once done, you&#8217;ll reboot to a nice, full-resolution windows prompt.</li>
<li>All drivers will have been installed correctly except the one for Bluetooth. To easily solve that issue, just go to <a href="http://www.harbar.net/Default.aspx">Spencer Harbar&#8217;s website</a> and read <a href="http://www.harbar.net/archive/2008/06/13/Enabling-Bluetooth-on-MacBook-Pro-and-Windows-Server-2008-x64.aspx">how to install the Bluetooth drivers</a>. Takes 5 minutes tops.</li>
</ul>

<h3>The final touches</h3>

<p>A few notes to quickly get things running as expected.</p>

<ul>
<li>Get the most of your configuration by following the <a href="http://blogs.msdn.com/vijaysk/archive/2008/02/11/using-windows-server-2008-as-a-super-desktop-os.aspx">list of tweaks from Vijayshinva Karnure</a> from Microsoft India.</li>
<li>There are <a href="http://www.win2008workstation.com/wordpress/">more tweaks</a>, and <a href="http://weblogs.asp.net/israelio/archive/2008/02/21/windows-server-2008-as-workstation.aspx">even more tweaks</a> available as well (don&#8217;t forget to enable <a href="http://www.2008server.org/?q=SuperFetch">Superfetch</a>).</li>
<li>Microsoft has a whole KB entry on <a href="http://support.microsoft.com/kb/947036">enabling user experience</a>.</li>
<li>In the Control Panel > System > Advanced System Settings > Advanced > Settings > Advanced > Processor scheduling, set to <em>Programs</em> instead of <em>Background services</em>.</li>
<li>Activate your copy of Windows using Control Panel > System.<br />
I was getting an error code 0x8007232B <em>DNS name does not exist</em> error. To force activation, just click on the <em>Change Product Key</em> button and re-enter the same key you used during install.<br />
Windows will activate straight away.</li>
<li>When booting your Macbook, press the <em>Option</em> key and you will be presented a list of boot choices.</li>
<li>You can check on <a href="http://www.apple.com/support/bootcamp/">Apple&#8217;s Bootcamp webpage</a> other information about how to use the track pad, keyboard layouts etc,</li>
</ul>

<h3>References</h3>

<ul>
<li><a href="http://stuartd.blogspot.com/2008/04/windows-server-2008-boot-camp-and.html">http://stuartd.blogspot.com/2008/04/windows-server-2008-boot-camp-and.html</a></li>
<li><a href="http://burnetts.homeserver.com/post/Windows-Server-2008-on-the-Mac.aspx">http://burnetts.homeserver.com/post/Windows-Server-2008-on-the-Mac.aspx</a></li>
<li><a href="http://csaborio.wordpress.com/2008/03/28/installing-windows-server-2008-on-a-mac-book-pro/">http://csaborio.wordpress.com/2008/03/28/installing-windows-server-2008-on-a-mac-book-pro/</a></li>
<li><a href="http://exo-blog.blogspot.com/2008/03/windows-2008-vista-done-right.html">http://exo-blog.blogspot.com/2008/03/windows-2008-vista-done-right.html</a></li>
<li><a href="http://www.sturmnet.org/blog/archives/2008/10/02/stuff-i-use-on-the-mac/">http://www.sturmnet.org/blog/archives/2008/10/02/stuff-i-use-on-the-mac/</a> Lots of software recommendations for someone switching from Windows to the Mac.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/mac-installing-windows-server-2008-x64-on-a-macbook-pro/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>MS Access: checking network paths without freezing your application</title>
		<link>http://blog.nkadesign.com/2008/ms-access-checking-network-paths-without-freezing-your-application/</link>
		<comments>http://blog.nkadesign.com/2008/ms-access-checking-network-paths-without-freezing-your-application/#comments</comments>
		<pubDate>Fri, 20 Jun 2008 08:48:31 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=93</guid>
		<description><![CDATA[Access programming is inherently single-threaded. That&#8217;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 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
Access programming is inherently single-threaded. That&#8217;s usually OK as most operations are sequential anyway and it keeps things simple at the programming level.<br />
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.</p>

<h3>Checking for the existence of network paths</h3>

<p>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.</p>

<p>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&#8217;t have to suffer these delays again every time we check for that path&#8217;s existence.</p>

<p>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.</p>

<h3>How to use it</h3>

<p>Download the sample database below then just add the <code>FileUtilities</code>, <code>HashTable</code> and <code>MD5</code> modules to your project and you can use the code as such:</p>

<p><textarea name="code" class="vb:nogutter">
Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync("\\123.45.67.89\shared folder")
</textarea>
The <code>status</code> variable will return either of the following values:</p>

<ul>
<li><code>AsyncDirectoryStatus.OK</code> if the path was found.</li>
<li><code>AsyncDirectoryStatus.NotFound</code> if the path was not found (either because it doesn&#8217;t exist or you don&#8217;t have the rights to access it).</li>
<li><code>AsyncDirectoryStatus.Checking</code> if the verification is in progress and we haven&#8217;t received a definite answer yet.<br />
It&#8217;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&#8217;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).</li>
</ul>

<p>You can call <code>PathExistAsync</code> as often as you want to check the status: it will not slow down your application (read the <em>optional arguments</em> section below though).<br />
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.</p>

<h3>Optional arguments</h3>

<p>If you want to force the actual re-checking of a path without using the cached value, you can simply pass the <code>ForceCheck</code> optional parameter:
<textarea name="code" class="vb:nogutter">
Dim status As AsyncDirectoryStatus
status = FileUtilities.PathExistAsync("\\123.45.67.89\shared folder", ForceCheck:=true)
</textarea>
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).<br />
This may not be desirable if you&#8217;re checking a bunch of directories at a time. For instance, this is what I do when my application launches:
<textarea name="code" class="vb:nogutter">
' Check a bunch of paths in parallel
PathExistAsync strPathToQualityDocuments, NoDelay:=true
PathExistAsync strPathToFinancialDocuments, NoDelay:=true
PathExistAsync strPathToShippingDocuments, NoDelay:=true
PathExistAsync strPathToPurchasingDocuments, NoDelay:=true
</textarea>
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.<br />
Later in the application, when I need to actually use these paths, their result is likely to be known.</p>

<h3>How it works</h3>

<p>The <code>FileUtilities</code> module contains the main code.<br />
In it, the <code>PathExistAsync</code> function works in slightly different ways depending on whether it&#8217;s the first time it is being called for a particular path or not.</p>

<p><strong>The first time</strong><br />
The first time the function is called for a given path, we create in the user&#8217;s temporary folder the small batch file whose name is simply a MD5 hash (see below) of the path with <code>.bat</code> appended to it.<br />
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 <code>0</code> or <code>1</code> in it depending on the result of the verification.<br />
We initially cache the status of the verification for the Path into the <code>AsyncDirectories</code> hashtable (see below) as <code>Checking</code>.</p>

<p>Example of batch file automatically created to verify a path:
<textarea name="code" class="bat">
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</textarea>
The Batch file name is <code>463C7367D8329BD6209A65A70A7DA08C.bat</code> where the long number is actually the MD5 hash of the path we&#8217;re checking <code>\\123.56.78.9\going nowhere</code>.</p>

<p><strong>Getting back the result</strong><br />
Whenever the <code>PathExistAsync</code> function is called, we check the currently cached result from the <code>AsyncDirectories</code> hastable.<br />
If it is still <code>Checking</code> 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.</p>

<h3>Useful libraries</h3>

<p>The code makes use of 2 extremely useful libraries that I end up using quite often:</p>

<ul>
<li><p><strong>a <a href="http://en.wikipedia.org/wiki/Hash_table">HashTable</a> implementation.</strong><br />
It makes it easy to create hashtable objects (otherwise known as <em>Associative Arrays</em>) to store and retrieve key/value pairs quickly.<br />
Hashtables are often used to cache data and can be thought of arrays where the index is a string value instead of an number.<br />
Here I use a hashtable to keep track of the paths we&#8217;ve checked and their result.</p></li>
<li><p><strong>a <a href="http://en.wikipedia.org/wiki/Md5">MD5</a> hash implementation.</strong><br />
MD5 is a way to get a somewhat unique fixed-length value from a chunk of data.<br />
It&#8217;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&#8217;t reverse the function (you can&#8217;t obtain the input just by looking at the output).<br />
It is often used in security applications to transform sensitive data like passwords into unique values that can be (somewhat) safely stored because you can&#8217;t easily reverse a md5.<br />
Well, MD5 are <a href="http://en.wikipedia.org/wiki/Md5#Vulnerability">not that secure</a> 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.</p></li>
</ul>

<h3>Sample database</h3>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/06/pathexistasync01.zip' title='Sample database'><img src="/wp-content/uploads/2008/05/download.png" alt="Download" /></a>Download the <a href='http://blog.nkadesign.com/wp-content/uploads/2008/06/pathexistasync01.zip' title='Test database'>PathExistAsync01.zip (67KB)</a> containing the  <strong>Access 2007 ACCDB</strong> database.</p>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/06/pathexistasync02b.zip' title='Sample database'><img src="/wp-content/uploads/2008/05/download.png" alt="Download" /></a>Download the <a href='http://blog.nkadesign.com/wp-content/uploads/2008/06/pathexistasync02b.zip' title='Test database'>PathExistAsync02b.zip (121KB)</a> containing the <strong>MDB</strong> database<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> (untested as I only have Access 2007).</p>

<p><center><img src="/wp-content/uploads/2008/06/sshot-112.png" alt="Test Database" /></center></p>

<h3>License</h3>

<p>Please refer to the source code in the database for the exact licensing terms.<br />
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.</p>

<p><!--- Code source license -->
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/80x15.png"/></a><br/>This <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" rel="dc:type">work</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.</p>

<h3>References</h3>

<ul>
<li><a href="http://www.bytemycode.com/snippets/snippet/251/119/">Hashtable implementation in VB</a></li>
<li><a href="http://www.di-mgt.com.au/crypto.html">Cryptography Software Code</a> (for the MD5 hash implementation in VB)</li>
</ul>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>A specific version for Access 2000 now included in the archive (updated 25JUL2008).&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/ms-access-checking-network-paths-without-freezing-your-application/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MS Access: Enhanced Message Box Replacement</title>
		<link>http://blog.nkadesign.com/2008/ms-access-enhanced-message-box-replacement/</link>
		<comments>http://blog.nkadesign.com/2008/ms-access-enhanced-message-box-replacement/#comments</comments>
		<pubDate>Tue, 20 May 2008 10:39:54 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=76</guid>
		<description><![CDATA[This project provides a custom and enhanced message box replacement for the default MsgBoxfound in Access. A Test database for Access 2007 is available at the bottom of this post. (Updated Tuesday 17MAR2009.) What&#8217;s wrong with the default MsgBox The default message box in Access is sometimes useful to warn, inform or ask confirmation from [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
This project provides a custom and enhanced message box replacement for the default <code>MsgBox</code>found in Access. A Test database for Access 2007 is available at the bottom of this post. (<strong>Updated Tuesday 17MAR2009.</strong>)</p>

<h3>What&#8217;s wrong with the default MsgBox</h3>

<p>The default message box in Access is sometimes useful to warn, inform or ask confirmation from the user.
<center><img src="/wp-content/uploads/2008/05/sshot-79.png" alt="Standard MsgBox" /></center></p>

<p>It has, however, a few drawbacks:</p>

<ul>
<li>It is bland: the standard message box does not even follow the currently selected Office 2007 scheme.</li>
<li>The amount of text it can display is limited: if you try to display too much text it will be truncated.</li>
<li>You can&#8217;t copy or save the content of the message.</li>
<li>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.</li>
<li>They only displays plain text: you cannot format the message to draw attention to the key points.</li>
<li>They are blocking, meaning that nothing can happen in the main application while the box is displayed (it can&#8217;t even shut down).</li>
</ul>

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

<h3>An enhanced message box</h3>

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

<p><strong>Plain Text</strong> version of the enhanced custom message box under the Office Blue Colour Scheme:
<center><img src="/wp-content/uploads/2008/05/sshot-81.png" alt="Plaintex Enhanced Message Box" /></center></p>

<p><strong>RichText</strong> version of the enhanced custom message box under the Office Black Colour Scheme:
<center><img src="/wp-content/uploads/2008/05/sshot-78.png" alt="RichText Enhanced Message Box" /></center></p>

<p>Here are the features of the enhanced message box:</p>

<ul>
<li>It is entirely compatible with the standard one: just change <code>MsgBox</code> to <code>Box</code> 
using <em>find and replace</em> should be enough (see tip below to avoid getting strange errors).</li>
<li>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.</li>
<li>It looks and feels like it belongs to the main application, following its colour scheme.</li>
<li>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&#8217;s not a perfect solution, but it is quite effective.</li>
<li>There is a <code>RichBox</code> version that can display rich HTML content, not just plain text, so important parts of the message can be formatted in a useful way.</li>
<li>It is able to display large amount of data. While it&#8217;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).</li>
<li>Rather than sprinkling your code with &#8220;<code>&amp; vbCrLf &amp; _</code>&#8221; uglies, you can embed newlines in the text itself by using C-style &#8220;<code>\n</code>&#8221; escape sequences that will automatically be transformed into the appropriate newlines. Makes for clearer code and less typing.</li>
<li>Because you get the source, you can easily customise the message box with new icons and colours to better match your overall application&#8217;s personality.</li>
<li>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 <code>MsgBox</code> does. Of course, it&#8217;s up to you to decide how to handle that gracefully, if at all.</li>
<li>It properly displays the expected button captions based on the language of the operating system, so it behaves very much like the default <code>MsgBox</code> (for instance, it will properly display &#8220;<em>Cancel</em>&#8221; on English systems and &#8220;<em>Annuler</em>&#8221; on French ones).</li>
<li>It also properly plays the system sounds associated with the type of message. You can also enable or disable the sound effect as needed.</li>
</ul>

<h3>How to use it</h3>

<p>Download the demo below and copy (drag &amp; drop) the following into your application:</p>

<ul>
<li>the <code>FormDialog</code> form,</li>
<li>the <code>API_GetTextMetrics</code> module,</li>
<li>the <code>Dialog</code> module.</li>
</ul>

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

<p>Since the enhanced message box is just a replacement for the standard one, you just use it like you  would use the <code>MsgBox</code>.<br />
<textarea name="code" class="vb:nogutter">
'-----------------------------------------------------------------------------
' 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"
</textarea>
There are a few additional settings that can be used to change the behaviour of the enhanced message boxes.<br />
One is that you can adjust the delay before the buttons become activated.<br />
<textarea name="code" class="vb:nogutter">
'-----------------------------------------------------------------------------
' 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",
           ButtonDelay:=1

'-----------------------------------------------------------------------------
' 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</textarea>
Another one is that you can enable or disable whether beeps should be played or not.<br />
<textarea name="code" class="vb:nogutter">
'-----------------------------------------------------------------------------
' 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",
           AllowBeep:=false

'-----------------------------------------------------------------------------
' Change the default behaviour. This is true by default.
Dialog.DefaultAllowBeep = false
</textarea>The last settings is the folder where we should save the content of the message when the user clicks the Save button on the message box.<br />
<textarea name="code" class="vb:nogutter">
'-----------------------------------------------------------------------------
' 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"
</textarea>
These few settings make the enhanced message box more customizable.</p>

<h3>Large text</h3>

<p>The standard <code>MsgBox</code> cannot display much text. On the other hand, there is no real limitation to the amount of text the <code>Box</code> and <code>RichBox</code> can display.<br />
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.</p>

<h3>Limitations of the RichBox</h3>

<p>The <code>RichBox</code> version relies on the normal TextBox control&#8217;s ability under Access 2007 to display RichText wich is nothing more than lightweight HTML.<br />
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.<br />
Short of implementing a complete HTML engine, we have to rely on some assumptions to display HTML.<br />
The risk is that sometimes the content may not properly fit the TextBox control in some circumstances.<br />
If you use the <code>RichBox</code>, 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.<br />
If you don&#8217;t overuse font size and don&#8217;t display in multiple fonts the <code>RichBox</code> should do the right thing most of the time.<br />
Don&#8217;t overuse the <code>RichBox</code> to display colourful messages. There is a fine line between being informative and tasteless. Keep colours and formatting where it is useful.<br />
I think that in most cases, the plain text version <code>Box</code> is more than enough.</p>

<h3>Replacing MsgBox in existing code</h3>

<p>As I said above, replacing the standard <code>MsgBox</code> is easy but you need to make sure your search and replace parameters are configured correctly:
<center><img src="/wp-content/uploads/2008/05/sshot-407.png" alt="Search and replace options" /></center></p>

<p>If you&#8217;re getting strange compile errors, it may be because you forgot to tick the <em>Find Whole Word Only</em> and some of the strings containing the letter sequence &#8220;msgbox&#8221; were replaced in the process.</p>

<p>If that&#8217;s the case, you can revert the damage by simply doing a search and replace across the whole project on:<br />
- <code>VbboxStyle</code> or <code>VbDialog.BoxStyle</code> to be replaced with <code>VbMsgBoxStyle</code><br />
- <code>VbboxResult</code> or <code>VbDialog.BoxResult</code>to be replaced with <code>VbMsgBoxResult</code></p>

<h3>How it works</h3>

<p>The code makes extensive use of Win32 API calls.<br />
Most of the hard work is done in the <code>FomDialog</code> 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.<br />
The code relies also on a utility function from <em>Stephen Lebans</em> 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.</p>

<p>In the code for the <code>FormDialog</code>, I re-implement some of the expected functionalities of the <code>MsgBox</code>: proper arrangement of the buttons, displaying of the appropriate icon, etc.<br />
Once this is done, we calculate the size of the textbox needed to display the whole of the message.<br />
In the case of RichText, we first use <code>Application.PlainText()</code> 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.<br />
Once we know the size of the TextBox, we can easily resize the form to properly display the TextBox.<br />
If there is too much text, we resize the form to its maximum permissible (70% or screen width and 90% of screen height) and change some of the visual cues to let the user know the text is overflowing.</p>

<p>One thing of note is the way the form is kept modal.<br />
Rather than using <code>DoCmd.OpenForm</code> and <code>DoCmd.Close</code> I use the form as a class and create an instance manually (see the code in <code>Dialog.Box</code> and <code>Dialog.Richbox</code>). I keep this instance alive until I got the form&#8217;s result back.<br />
If you are interested in knowing how the form is made modal, this is the code in <code>FormDialog.ShowModal()</code> what keeps the form open until the user clicks a button:<br />
<textarea name="code" class="vb">
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)
        DoEvents
        Sleep 50
    Loop
    ShowModal = m_Result
End Function</textarea>
The <code>Sleep()</code> 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&#8217;s just waiting for user input.</p>

<h3>Sample database</h3>

<p>You can download a sample database containing all the necessary code as well as a number of tests.<br />
This version only contains the database in Microsoft Access 2007 accdb format.
<center><img src="/wp-content/uploads/2008/05/sshot-77.png" alt="Sample database testing form" /></center></p>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/EnhancedMsgBox01.zip' title='Sample database'><img src="/wp-content/uploads/2008/05/download.png" alt="Download" /></a>Download the <a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/EnhancedMsgBox01.zip' title='Test database'>EnhancedMsgBox01.zip (116KB), v1.3</a> containing the ACCDB database.</p>

<h3>Code Updates</h3>

<p><em>v1.3: 17MAR2009</em><br />
Thanks to Henry of <a href="http://www.access-pro.de/">Access-Pro.de</a> for proposing a correction to the default buttons behaviour.</p>

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

<p><em>v1.2: 07SEP2008</em><br />
Thanks to Andy Colonna (<a href="http://www.pcesoft.com">http://www.pcesoft.com</a>) for uncovering the following bugs (check out his <a href="http://www.pcesoft.com/Access-Spell-Checker-Tool-Source-Code.html">free Spell Checker with source code</a>!):</p>

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

<p><em>v1.1: 08AUG2008</em></p>

<ul>
<li>Corrected code for <code>DefaultButtonDelay</code> (thanks to Geoffrey) (was referencing
wrong variable, causing self-referencing code).</li>
<li>Corrected code for <code>Box</code> and <code>RichBox</code> to take the <code>DefaultSavedTextFileFolder</code>
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 <code>DefaultSavedTextFileFolder</code>)</li>
<li>Added license notice at top of source code.</li>
</ul>

<p><em>v1.0: 20MAY2008</em></p>

<ul>
<li>Original version</li>
</ul>

<h3>Resources</h3>

<ul>
<li><a href="http://www.codeproject.com/KB/dialog/MessageBoxEx.aspx">Dissecting the MessageBox</a> article on <a href="http://www.codeproject.com/">CodeProject</a></li>
<li><a href="http://www.codeproject.com/KB/dialog/xmessagebox.aspx">XMessageBox -- A reverse-engineered MessageBox()</a> article on <a href="http://www.codeproject.com/">CodeProject</a></li>
<li><a href="http://www.lebans.com/textwidth-height.htm">TextWidth-Height code demo</a> from <a href="http://www.lebans.com/">Stephen Lebans</a> (great resource, check it out!).</li>
<li><a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;210590">Pixel to Twips conversion</a> from MSDN.</li>
<li><a href="http://www.mvps.org/access/api/api0049.htm">Copy Text to Clipboard</a> from the excellent site <a href="http://www.mvps.org/access/">The Access Web</a>.</li>
<li><a href="http://www.thevbzone.com/l_res.htm">Getting Resource Strings and more</a> from DLLs.</li>
</ul>

<p><!--- Code source license -->
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/80x15.png"/></a><br/>This <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" rel="dc:type">work</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.<br />
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.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/ms-access-enhanced-message-box-replacement/feed/</wfw:commentRss>
		<slash:comments>64</slash:comments>
		</item>
		<item>
		<title>MS Access: Restarting and compacting the database programmatically</title>
		<link>http://blog.nkadesign.com/2008/ms-access-restarting-the-database-programmatically/</link>
		<comments>http://blog.nkadesign.com/2008/ms-access-restarting-the-database-programmatically/#comments</comments>
		<pubDate>Tue, 06 May 2008 03:06:19 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[sysadmin]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=73</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
In my previous article about <a href="http://blog.nkadesign.com/2008/ms-access-changing-the-color-scheme-programmatically/">changing the MS Access colour scheme</a> I had the need to allow the user to restart the database after the colour scheme was changed.<br />
(<strong>Article and Code Updated 13FEB2009.</strong>)</p>

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

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

<p>The problem is that you cannot -to the best of my knowledge- close and open again the same database from within MS Access itself.<br />
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.<br />
I wanted a simple and clean way of using it. One that would not require shipping external programmes.</p>

<h3>How to use it</h3>

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

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

<h3>Important note</h3>

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

<h3>How it works</h3>

<p>If you&#8217;re curious about the technical details, here is how it was put together.<br />
The main idea is that the MS Access database application has to be self-sufficient and restart itself by performing these steps:</p>

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

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

<p>The batch file is hard-wired in the <code>Restart</code> sub that does all the work:<br />
<textarea name="code" class="bat">
SETLOCAL ENABLEDELAYEDEXPANSION
SET /a counter=0
:CHECKLOCKFILE
ping 0.0.0.255 -n 1 -w 100 > nul
SET /a counter+=1
IF "!counter!"=="60" GOTO CLEANUP
IF EXIST "%~f2.%4" GOTO CHECKLOCKFILE
"%~f1" "%~f2.%3" /compact
start " " "%~f2.%3"
:CLEANUP
del %0
</textarea>
When the application runs the batch file, it passes 4 arguments:</p>

<ul>
<li>the full path to the MSAccess.exe executable (used for compacting the database)</li>
<li>the full path to the database without the extension</li>
<li>the database file extension without the leading &#8220;.&#8221;</li>
<li>the appropriate database lock file extension (<code>laccdb</code> or <code>ldb</code>).</li>
</ul>

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

<p>The funny use of <code>PING</code> is actually a simple way to wait for some time before we check if the lock file is still there or not. There is not <code>SLEEP</code> or <code>WAIT</code> function provided by default in Windows so we have to be a bit creative and use the time-out option of the <code>PING</code> command trying to ping an inexistent, but valid, IP address.<br />
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.</p>

<p>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.<br />
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&#8217;t close, so we just exit and delete the batch file.</p>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/DatabaseRestart.zip' title='Sample database'><img src='http://blog.nkadesign.com/wp-content/uploads/2008/05/download.png' align="left" alt='Download' /></a>Download the <a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/DatabaseRestart.zip' title='Test database'>DatabaseRestart.zip (48KB)</a> containing both an Access 2007 ACCDB and Access 2000 MDB test databases.</p>

<h3>Other implementations</h3>

<ul>
<li><a href="http://www.rogersaccesslibrary.com/">Roger&#8217;s Access Library</a> (MVP) has a <a href="http://www.rogersaccesslibrary.com/forum/forum_posts.asp?TID=377">different implementation on offer</a>.</li>
</ul>

<h3>Code Updates</h3>

<p><strong>v1.2: 13FEB2009</strong></p>

<ul>
<li>Added optional parameter to compact the database during restart.</li>
</ul>

<p><strong>v1.1: 09AUG2008</strong></p>

<ul>
<li>Now a separate test database (used to be bundled with the Colour Scheme sample).</li>
<li>Added support for older Access versions (an Access2000 MDB is now included).</li>
<li>Corrected wrong lock file extension for accd* files.</li>
<li>Added a time-out feature after which the batch file will delete itself 
after a while if the Access lock file wasn&#8217;t released 
(for instance following a crash).</li>
<li>Added checks to delete the batch file if it has not deleted itself for some
reason (for instance after a reboot).</li>
<li>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.</li>
<li>Added license notice at top of source code.</li>
<li>Updated the article to reflect the changes.</li>
</ul>

<p><strong>v1.0: 06MAY2008</strong></p>

<ul>
<li>Original version</li>
</ul>

<p><!--- Code source license -->
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/80x15.png"/></a><br/>This <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" rel="dc:type">work</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/ms-access-restarting-the-database-programmatically/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>MS Access: Changing the Color Scheme programmatically</title>
		<link>http://blog.nkadesign.com/2008/ms-access-changing-the-color-scheme-programmatically/</link>
		<comments>http://blog.nkadesign.com/2008/ms-access-changing-the-color-scheme-programmatically/#comments</comments>
		<pubDate>Sat, 03 May 2008 13:20:43 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/?p=72</guid>
		<description><![CDATA[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&#8217;s options are not available. (Article and Code Updated 31MAY2011.) Luckily for us, Office 2007 stores the global colour [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
Microsoft Office 2007/2010 comes with 3 colour (color) schemes.
Users can easily change it but when you deploy an Access application under the <a href="http://www.microsoft.com/downloads/en/details.aspx?familyid=57a350cd-5250-4df6-bfd1-6ced700a6715">Runtime</a> your users have no way to set the colour scheme as the application&#8217;s options are not available.<br />
(<strong>Article and Code Updated 31MAY2011.</strong>)</p>

<p>Luckily for us, Office 2007 stores the global colour scheme setting in the registry under:<br />
<code>HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\Theme</code><br />
and Office 2010 in:<br />
<code>HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\Theme</code></p>

<p>The values being stored under that key are:</p>

<ol>
<li>Blue scheme</li>
<li>Silver scheme</li>
<li>Black scheme</li>
</ol>

<p>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.</p>

<p>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.</p>

<p><img src="/wp-content/uploads/2008/05/colorschemes.png" alt="Office 2007 Colour Schemes" title="Office 2007 Colour Schemes" width="560" height="263" hspace="0" vspace="5" border="0" /></p>

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

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/ColorScheme.zip' title='Sample database'><img src='http://blog.nkadesign.com/wp-content/uploads/2008/05/download.png' align="left" alt='Download' /></a>Download the <a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/ColorSchemeV1.3.zip' title='Test database'>ColorSchemeV1.3.zip (31KB)</a> containing the ACCDB database.</p>

<p>The sample also contains some code to restart the database. This is the subject of another post: <a href="/2008/ms-access-restarting-the-database-programmatically/">Restarting and compacting the database programmatically</a>.</p>

<h3>Improvements/uses:</h3>

<ul>
<li>Find a way for Access to reload the settings without having to restart the application.</li>
<li>Use the knowledge about the current scheme to change the other colour settings in the application (or even adapt the form&#8217;s theme).</li>
</ul>

<h3>Updates:</h3>

<ul>
<li>31MAY2011: added support for Office 2010.</li>
</ul>

<h3>References:</h3>

<ul>
<li><a href="http://blogs.msdn.com/eric_carter/archive/2008/04/11/setting-the-backcolor-to-match-the-office-2007-color-scheme.aspx">Setting the BackColor to match the Office 2007 color scheme</a></li>
<li>API <a href="http://www.arcatapet.net/vbregget.cfm">Get</a> / <a href="http://www.arcatapet.net/vbregset.cfm">Set</a> Registry functions from <a href="http://www.arcatapet.net/">http://www.arcatapet.net/</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/ms-access-changing-the-color-scheme-programmatically/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>MS Access: Modal Dialogs with Transparent Backgrounds (redux)</title>
		<link>http://blog.nkadesign.com/2008/ms-access-modal-dialogs-with-transparent-backgrounds/</link>
		<comments>http://blog.nkadesign.com/2008/ms-access-modal-dialogs-with-transparent-backgrounds/#comments</comments>
		<pubDate>Thu, 01 May 2008 11:36:36 +0000</pubDate>
		<dc:creator>Renaud Bompuis</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[MSAccess]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://blog.nkadesign.com/2008/ms-access-modal-dialogs-with-transparent-backgrounds/</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/wp-content/uploads/access.png" alt="Microsoft Access" title="Microsoft Access" align="left" width="64" height="64" hspace="5" vspace="5" border="0" />
Microsoft Access Team </a>made an <a href="http://blogs.msdn.com/access/archive/2008/04/28/modal-dialogs-with-transparent-backgrounds.aspx">interesting post </a> and a <a href="http://blogs.msdn.com/access/archive/2008/05/05/follow-up-to-transparency-forms.aspx">follow-up</a> 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.</p>

<p>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.</p>

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

<p><a rel="lightbox" href="/wp-content/uploads/2008/05/sshot-70.png"><img width="250" vspace="5" hspace="5" height="189" border="0" alt="The transparent layer covering the main Window only" title="The transparent layer covering the main Window only" src="/wp-content/uploads/2008/05/sshot-70sm.png" /></a><a rel="lightbox" href="/wp-content/uploads/2008/05/sshot-73.png"><img width="250" vspace="5" hspace="5" height="189" border="0" alt="The transparent layer covering the full screen" title="The transparent layer covering the full screen" src="/wp-content/uploads/2008/05/sshot-73sm.png" /></a></p>

<h3>Update 07MAY2008</h3>

<p>Following Rob&#8217;s improvements I made another sample database that incorporates his code with a few improvements:</p>

<ul>
<li>I added the <code>LightBoxForm.LayerToFullScreen</code> property so users can choose explicitly how they want the layer to be shown.</li>
<li>I moved the code to hide the layer into a Hide() sub so you can just show/hide the layer using <code>LightboxForm.Show</code> and <code>LightboxForm.Hide</code>.</li>
<li>I changed the Form&#8217;s <code>Resize </code>event code in the <code>LightBoxForm</code> class to avoid flickering: resizing the form within its <code>Resize</code> event actually trigger the <code>Resize</code> event again a second time which causes flickering.<br />
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.  </li>
</ul>

<p><textarea name="code" class="vb">
'-----------------------------------------------------------------------------
' 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
        DoCmd.Maximize
    Else
        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
    Else
        busyResizing = True
    End If
End Sub
</textarea></p>

<h3>Samples</h3>

<p>There are now 2 sample databases. Ech zip contains a Microsoft Access 2007 ACCDB file and its conversion to Access 2000<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> 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&#8217;t work in Operating Systems older than Windows 2000.</p>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/transparentlayer02b.zip' title='Test database'><img src='http://blog.nkadesign.com/wp-content/uploads/2008/05/download.png' align="left" alt='Download' /></a>Download <a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/transparentlayer02b.zip' title='Test database'>TransparentLayer02b.zip (138KB)</a>, recommended version<br />(improved, more flexible version, based on <a href="http://blogs.msdn.com/access/archive/2008/05/05/follow-up-to-transparency-forms.aspx">Rob&#8217;s updated article</a>).</p>

<p><a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/transparentlayer01b.zip' title='Test database'><img src='http://blog.nkadesign.com/wp-content/uploads/2008/05/download.png' align="left" alt='Download' /></a>Download <a href='http://blog.nkadesign.com/wp-content/uploads/2008/05/transparentlayer01b.zip' title='Test database'>TransparentLayer01b.zip (122KB)</a>, original version<br />(simple code, based <a href="http://blogs.msdn.com/access/archive/2008/04/28/modal-dialogs-with-transparent-backgrounds.aspx">Rob&#8217;s original article</a>).</p>

<h3>Troubleshooting</h3>

<ul>
<li><em>If you are getting security warnings:</em> make sure that you open the database from a Trusted Location or you will receive a security prompt.
If you don&#8217;t know how to do that, <a href="http://office.microsoft.com/en-us/help/HA100319991033.aspx#11">check these steps</a>.</li>
<li><em>If the layer appears on top of the login form instead of behind:</em> make sure that the top-most form has ist <code>Modal </code>properties set to <code>Yes</code> and the <code>frmLightBox</code> form has its modal property set to <code>No</code>.<br />
If you improve on it, please let me know and I&#8217;ll post it here for all to find.</li>
</ul>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>A specific version for Access 2000 now included in the archive (updated 25JUL2008).&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nkadesign.com/2008/ms-access-modal-dialogs-with-transparent-backgrounds/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
	</channel>
</rss>

