Page 1
Page 2

With the application running, soft-reset
your device. When the OS is back start the application again
and this time just tap the third button to show the data
in the table. You'll notice that the table is empty. In
fact, if you didn't exit the application after creating
the database and table, the table doesn't even exist anymore.
This same phenomenon occurs with the Recordset object.
This can be a big problem for a deployed
application. What if the user works for a few hours adding,
modifying and deleting data, then something happens and
the device locks up and they have to reset? All of their
work is gone and you, at the very least, are getting a tech
support call from an understandably upset user.
So what exactly happened that caused
our data loss? To understand that we need a basic understanding
of how CE uses its databases. When you open a connection
to a database through ADOCE, the database is "mounted,"
meaning that a counter is updated that an application is
using the database, and memory is allocated for database
modifications. All of the changes are then made in this
memory space, not to the actual database itself. If you've
done much work with SQL Server, think of it as a transaction.
When the data needs to be committed,
the client, in this case your application, must Flush the
memory space which causes the system to commit all of the
changes. ADOCE has a bug in that a Flush only occurs when
the ADOCE object is destroyed. Closing the connection does
not force the Flush and therefore when the device is reset
all information in volatile memory, including the user's
database changes, is lost.
So how do we avoid this? There are two
ways. The first, which seems to be the easiest, is to simply
set the Connection (or Recordset) equal to Nothing after
making your data changes. But that means that every time
you need to access a database, you need to call CreateObject,
which causes a memory leak in CE. This approach forces the
user to reset the device regularly to free the memory eaten
by the CreateObject leak.
The second method, which avoids the
memory leak and allows you to leave your Recordset or Connection
open is to manually flush the database. eVB doesn't have
a native method for doing this, but a few simple API calls
can achieve it.
The APIs we're interested in are, not
surprisingly, CeMountDBVol, CEFlushDBVol and
CEUnmountDBVol. Add the following declarations to
the General Declarations section of Form1's code
page:
Declare Function CeMountDBVol Lib "Coredll"
_
(ByVal pGUID As String, _
ByVal lpszVol As String, _
ByVal dwFlags As Long) As Boolean
Declare Function CeFlushDBVol Lib "Coredll" _
(ByVal pGUID As String) As Boolean
Declare Function CeUnmountDBVol Lib "Coredll"
_
(ByVal pGUID As String) As Boolean
If you do some searching through eMbedded
Visual Tools' Help, you'll see that these declarations are
slightly different than the Help definitions. The pGUID
parameter in all three is actually a data structure pointer,
but eVB doesn't support structures. Fortunately, you don't
need to modify the pGUID. CeMountDBVol populates it and
you just pass it on to CEFlushDBVol and CEUnmountDBVol,
so a String datatype will work fine for holding the pointer.
Now you need a method that wraps these
three API calls; call it ADOCECommit. To make it
reusable, add the database path as a parameter.
Private Sub ADOCECommit(DatabasePath As
String)
Dim s As String
' create memory space for the GUID structure
s = String(8, Chr(0))
' Mount the volume
Call CeMountDBVol(s, DatabasePath, 3)
' Flush the volume - forces changes to commit
Call CeFlushDBVol(s)
' Unmount the volume
Call CeUnmountDBVol(s)
End Sub
The function simply creates enough memory
space for the GUID structure, then mounts, flushes and unmounts
the database.
The only item left to do is to call
ADOCECommit after you insert records. Add a call to ADOCECommit
in Command2's click event handler between the data inserts
and the Connection close calls. It should now look like
this:
Private Sub Command2_Click()
cn.ConnectionString = "\ADOCETest.cdb"
cn.Open
' Insert data
cn.Execute "INSERT INTO TestTable " _
& "VALUES('Fred')"
cn.Execute "INSERT INTO TestTable " _
& "VALUES('Wilma')"
cn.Execute "INSERT INTO TestTable " _
& "VALUES('Barney')"
cn.Execute "INSERT INTO TestTable " _
& "VALUES('Betty')"
' Force the data changes to commit
ADOCECommit "\ADOCETest.cdb"
cn.Close
End Sub
Re-run the earlier test by launching
the modified application, inserting data, viewing the data
and the resetting the device without exiting the application.
Again restart the application and tap the 'Show Data' button.
Notice the data is now still there.
You've now seen one of the largest problems
in ADOCE and developed a good workaround for it. Using it
will not only make your application more solid, it will
also prevent some maintenance and support headaches and
help keep your application's users happy.
Previous Page