Friday, October 19, 2007
The edit control under Windows Mobile 2003 and 5.0
This problem was quickly solved by adding the OnKeyDown message handler to the CCeEdit class and forcing the control to set the focus to the parent window whenever an up or down navigation keys are detected. Voilá!
Thursday, October 18, 2007
A list-based form for Windows Mobile
Next two projects: port this thing to WTL and start writing an OLE DB class library for Windows Mobile. This second project is necessary because Microsoft no longer ships the atloledbcli.h file on the SDKs and this must mean something...
Thursday, October 11, 2007
Bug in the FormListSample
Solution: On the CFormListCtrl::OnDestroy method, set the m_iEdit member variable to -1 after calling ShowEditor.
Meanwhile, I added some code to handle edit control resizing when the screen is rotated. The code can be found on the same location.
Monday, October 08, 2007
The list-based form control
Soon we learned that ADOCE would be deprecated and so I started to look at the ATL OLE DB consumer templates. This is a great technology and I still fail to understand why it is not present on the WM5 and WM6 SDKs. In my code, I use an adapted version of the header file you get on the Pocket PC 2003 SDK but to be honest I don't like this approach (I will come back to this issue in a future post).
On the user interface side, we already had a very nice alternative to dialogs: the list-based form control used by the Pocket Outlook Contacts application. Not only this is an intuitive control for the user, it also promised to be easy for a developer to use: no designer needed to be involved, you just specified the list of items to edit and fired the object.
The big problem with this control was that there was no API for it so I had to write one. Back then I was using MFC big time due to the compiler support for it on eVC3 and eVC4. Now I am not so sure that MFC is a good option for native user interfaces and I'm leaning towards WTL. You now have great tool support in VS 2005 with the WTL Helper tool which I am using on a daily basis and with great results.
So I eventually wrote an MFC control to implement a list-based form and in the process I threw in couple of goodies such as grouping, context menus for managing the items, control notifications and a few other bells and whistles. After a recent request of fellow MVP Christian Forsberg for a similar control, I decided to go back to my 4 year old code, adapt it to VS 2005 and publish it. I had to do more work than expected because nowadays there are lots of devices that have a keyboard, display in landscape and even have VGA screens! Back then I was used to portrait QVGA screens and no keyboard, so some changes were in order.
Before I publish this code as an article, I decided to preview it here so you get a chance to look at it and let me know what you think. Download FormListSample.zip
Thursday, September 20, 2007
Supporting SQL Compact 3.5
DBPROP_SSCE_DBENCRYPTIONMODE. Now your header file can be used for 2.0, 3.0 and 3.5!
What this means for me is that SQL Compact 3.5 will be supported by the release version of Data Port Console and I will also issue a last version of DesktopSqlCe that will also support the new database engine. This will be the only game in town for .NET 2.0 desktop applications that must support both the 3.0 and the 3.5 database formats (without code changes, of course).
Cool!
Friday, September 07, 2007
SQL Compact 3.5 Beta 2 and VS2005
Now back to work.
Friday, August 31, 2007
Database Diagrams for Data Port Console
Tuesday, August 21, 2007
ZIP and RAPI - The article
Sunday, August 19, 2007
ZIP and RAPI - Yes, it's faster
So I decided to take a harder look at the code and found the culprit: WriteFile latency. The function was being called way too often and sometimes with small sets of data. If you look at the code, you will see that WriteFile is called when either the decompression input or output buffers are emptied. This is wrong. The call must only be made when the output buffer is empty and not when the input buffer is. When the input buffer is emptied it must be refilled from the RAPI stream and the output buffer should not be flushed, otherwise we risk flushing a small amout of data expending one WriteFile call.
The final result is now ready and is consistently faster than the PC decompression approach. Try it!
Friday, August 17, 2007
ZIP and RAPI - Is it worth it?
To make this work I changed the CeUnzip project in order to support calling a RAPI extension and added an extra mandatory command line argument: -i for expanding on the PC from a ZIP file on the device and -e for the exact opposite operation. It's here that I make the remote call to the RAPI extension DLL. Please note that the two projects use different versions of the unzip.cpp file. I will correct this before publishing an article that describes in greater detail the inner workings of the code.
How about results? Is this really faster than using an on-the-fly decompression algorithm on the PC and using CeWriteFile to write the decompressed data on the device?
My first results were very disappointing. When compared to an installer application I wrote for a customer that decompresses on the PC and writes to the device, I consistently got worse timings. The only remedy that made the device DLL run as fast as the PC was to increase the IRAPIStream read buffer size to 128 or 256 KB. As a matter of fact, RAPI read latency is a big issue here. Before giving up I looked at the particular ZIP file I was using to test: it was quite large and had a relatively small compression ratio. Maybe this explains something?
My next test was to use a very high compression ratio ZIP file and run the same tests (expanding it to the device using the two different approaces). This ZIP file was built from a very large SDF file which almost disappeared when compressed: 193 KB to only 3 KB. Now the results were a bit different...
Depending on the device, the DLL expansion was from 2 to 4.5 times faster than the PC's. This means that for compression ratios closer to 1 it is (almost) indifferent which approach to use, but when the ZIP compression ratios get larger, it's far better to use the server DLL.
Sunday, August 12, 2007
ZIP and RAPI
Recently I had to use this code to write a specialized device installer (no cabs) for a consumer application that runs on all sorts of devices. This installer was designed to extract files from a ZIP on the desktop and copy the extracted files to the device over an ActiveSync connection. I used Zip Utils to extract the data and RAPI to copy the expanded files to the device. This works but can be slow on some devices. Wouldn't it be great if I could expand the files on the device while having the source ZIP file on the PC? I would surely make the whole thing faster because the RAPI transport times are a big bottleneck in this whole process.
Due to time constraints, the installer is now shipping like this: data is expanded on the PC and copied to the device. Now I'm starting to write an application that will update the device code and data. Interestingly most of the static data is stored on ZIP files on the device and the updater must extract specific files on the ZIPs in order to know some details. The problem is that some of these ZIP files are over 2 MB and copying them to the PC just to extract one tiny file is out of the question due to slow transfer times.
No options here: I had to roll up my sleeves and started to read Lucien's code in order to see what could be done. This was no walk in the park because he uses a very packed code indentation, and understanding the code was not easy at first. Then I noted that all the ZIP file access methods were neatly encapsulated in specific functions (like lufopen, lufread and so on) so it would be quite easy to replace the desktop file access functions with RAPI's.
My approach was to expand the LUFILE structure and include a set of function pointers that would be initialized with the regular API when the ZIP file is on the PC and with RAPI's when the ZIP file is on the device. After 20 minutes the code was working and I was able to write a very simple command line unzipper (get the code here).
Now I want to solve the first problem I had: efficiently unzipping a file to a CE device. This will require a RAPI DLL on the device that will have all the decompression code. When this is ready I will publish the results in an article. Stay tuned.
Friday, August 03, 2007
Bitten by the desktop GC
While running perfectly in Debug mode, the error almost always showed up in Release mode. When exporting very short databases or single tables it might not show up. But when I tried to export the Northwind sample the Console would bomb exporting one of the largest tables (Order Details or Orders). The keyword here is random because as it turned out, the error was being caused by the garbage collector (well, my bad code was causing it - the GC just made it apparent).
The Access database code was implemented in C++ with a similar approach as DesktopSqlCe's low-level classes: there are separate classes for the table structure and the table cursor. In order to open a base table cursor and insert data on the table (no SQL is used for data manipulation) the code creates a table object and "opens" it returning a "rowset" object. My mistake was to have the table class include an ATL CTable-derived object, not a pointer to it. The CTable-derived class contains all the accessor and rowset logic that are used by the rowset object. Making it static is a bad idea because the desktop .NET GC did not know about this and kills the table object because it thinks it is no longer needed. After all the only active object is the rowset... Now you see the problem: by killing the table, the rowset is implicitly killed as well because it shares an object that is statically owned by the table. Closing the rowset means that a few internal pointers will be deleted and set to NULL. Bang! Am I smart or what? (The answer is "No").
I uploaded a revised Beta 4 that solves this issue, so if you installed the one I uploaded yesterday please replace it with the new one.
Thursday, August 02, 2007
Data Port Console 1.0 Beta 4 available for download
What's next? Primeworks.Data - the merger between DesktopSqlCe and Data Port Sync with a few "benefits" such as the ability to make P2P connections via Bluetooth or WiFi between two devices. Also I will add support for the JET (ACE) engines on the desktop server and will publish the services via a muti-threaded Windows Sockets server (I don't anticipate the need for IOCP anytime soon because there will be few concurrent connections).
The new Beta 4 can be downloaded from here.
Monday, July 30, 2007
Bluetooth Service Publishing
After being able to find a device with the service you want to consume, you can connect to that device using Windows Sockets. The reverse process involves publishing a service for other devices to consume and to serve requests using WS. Unfortunately the SDK has no means (nor explanation) on how to create an SDP record. Platform Builder users have the BTHNSCREATE sample to play with, but what about us, mere SDK users? Looks like Microsoft thinks this is something that should only concern people designing new Windows CE or Windows Mobile devices. I sincerely hope not and I'd rather think this is a design oversight. Why would I need such a thing?
Well, one of my plans is to provide P2P support for my products. If you look around the newsgroups you will see lots of people asking for an SDF-only synchronization package. This will require that two SDF files can "talk" to one another. I already support having a PC talk to an SDF file on a device, but what about having one device talk to another? A year ago I actually achieved this using WiFi, but this is not the correct protocol for ad-hoc P2P scenarios - Bluetooth is better. So if I want to support this scenario, I must publish a new Bluetooth service. Yes, of course I could use emulated COM ports and the code already supports serial communications. But I want the real thing: a dedicated service with its own GUID.
So how am I going to circumvent this? Most likely I will use Doug Boling's solution: use a templated SDP record and fill in the gaps (not nice, I know). What really bugs me is that the Widcomm stack makes it so much easier to publish a new service...
Wednesday, July 25, 2007
SHMENUBAR and MF_GRAYED
While working for the new Microsoft Bluetooth device inquiry and service discovery (these seem to be the appropriate Bluetooth terms) I found out that Sergey Solozhentsev's WTL Helper tool works like a charm in Windows Mobile projects under VS 2005. All of the message handling functions for the main frame window were generated with this tool and it makes it very easy to write all the UI code - this tool just rocks.
Today I found out about something that does not seem to be documented anywhere: when using the SHMENUBAR resource, never specify a menu resource with a grayed (MF_GRAYED) menu item - the menu will not show up. The WTL code is very straight forward and uses the SHCreateMenuBar API, so this was not where the problem lied. After removing the grayed style from the resource editor the menu magically showed up! So is this a bug on the shell or on the compiler?
Monday, July 23, 2007
Back to Bluetooth the WTL way
I'm coming back to writing about Bluetooth on Windows Mobile devices and this time I'm exploring the Microsoft stack. Using MFC code I had written almost a year ago, I'm now turning to WTL to rewrite the sample for the article. So far I can tell you that the Windows Mobile WTL wizard looks quite competent at generating the basic application code. More impressions on this experience will be published here. Stay tuned.
Tuesday, July 03, 2007
CeGetDiskFreeSpaceEx - II
typedef struct _DISKSPACE
{
ULARGE_INTEGER uliFreeUser,
uliTotalSize,
uliTotalFree;
} DISKSPACE;
__declspec(dllexport)
int DevGetDiskFreeSpace(DWORD cbInput, BYTE* pInput,
DWORD* pcbOutput, BYTE** ppOutput,
LPVOID pReserved)
{
DISKSPACE ds;
BOOL bOk;
bOk = GetDiskFreeSpaceEx((LPCTSTR)pInput, &ds.uliFreeUser,
&ds.uliTotalSize, &ds.uliTotalFree);
if(bOk)
{
*ppOutput = (BYTE*)LocalAlloc(LPTR, sizeof(ds));
memcpy(*ppOutput, &ds, sizeof(ds));
*pcbOutput = sizeof(ds);
}
return 0;
}
Of course, you need to put this in a device DLL and consume it as a blocking RAPI call.
Friday, June 29, 2007
CeGetDiskFreeSpaceEx
During my furious searches on the net, I found an obscure thread posted by someone from Microsoft stating that this API is only exposed from ActiveSync versions 4.0 onwards.
Tuesday, June 26, 2007
Data Port Console 1.0 Beta 3 available for download
Next up is to enable the serial port connectivity for Bluetooth emulated COM ports (the code is there, just needs a little work). I would like to have a native Bluetooth connectivity option in order to make it simpler for users to set up their devices, but this will imply going back to my Bluetooth code (which I started to write for this very purpose...).
Where do I want to go last? Device P2P. In a few days I will start working on the DesktopSqlCe successor that will also run on devices, so your mobile applications will be able to consume SQL Compact Edition databases locally or on a nearby device.
Back to work.
(*) This is not a real Beta. I will keep adding features to the application while it is almost ready to go live and after that as well. Beta status will stop when data can be exported to an Access database and when the number of errors is reduced to a sane minimum.
Monday, June 25, 2007
Primary Key or Index? ROWGUIDCOL or Guid.NewGuid()?
Looking for more performance material to publish about SQL Compact Edition, I found myself testing the performance of GUIDs when inserting on an indexed table. Performance will be marginally slower when you insert a GUID on a PRIMARY KEY column than when inserting it on a UNIQUE INDEX. In a 10,000 row insert sample it can be as low as half a second, but nevertheless this is an interesting result: PKs are slower.
Now, if you remove the ROWGUIDCOL property from the UNIQUEIDENTIFIER column and start generating the GUID values yourself, be prepared for a big surprise: the insertion time tripled (10.5 to 30.7 seconds on my battle-scarred JasJar). Is the Guid.NewGuid() function that slow?
Change MSI name in VS2005
The solution for this is to manually open the .vdproj file and edit it on a regular text editor (Notepad will do). Now search for the "OutputFilename" string under the configuration name of your choice (by default you will see "Debug" and "Release" under the "Configurations" group, but your project may have other configurations), and change the MSI file name. Save the file, open the project in VS2005 and rebuild.
Request for Orcas: can Microsoft please make this property available on the IDE?
EDIT: Someone smarter than me just showed me the way... Thanks, Pedro!
Tuesday, June 12, 2007
More troubles installing WTL Wizards in Vista
Then I decided to install Sergey Solozhentsev's WTL Helper tool. No issues again (nice!). This great tool requires that you download and install a new WTL Wizard from Sergey's website. Instead for using a JScript file to install everything, Sergey's Wizard uses an MSI file, and this is when I felt the iron grip of Vista security again: the MSI just would not install reporting an error number 2739. My friend Google pointed me in the right direction, first to understand what this error means, and what to do about it.
For your information, I did not remove the JScript key but merely renamed it. Running the msiexec /i command from the admin command window did the rest of the trick.
Friday, June 08, 2007
Data Port Console 1.0 Beta 2 available for download
One of the features I added for this Beta was a special request of Alberto Silva: the ability to edit the query data grid (when the user enters an "updatable" SELECT). Behind the scenes, the code just uses a SqlCeCommandBuilder to create the necessary commands (displayed on the notes tab) and these are used for updating (use the save button). You can also filter the grid by selecting the row state and the changes can be rolled back row by row (before updating, of course).
Comments and suggestions are welcome!
Monday, June 04, 2007
Tuning SQL Compact Edition Insert Performance
Wednesday, May 30, 2007
DBPROP_SSCE_MAXBUFFERSIZE
For now, keep this in your mind: increasing the value of DBPROP_SSCE_MAXBUFFERSIZE may actually help your database perform better.
Sunday, May 27, 2007
MFC Revisited
I (re)started developing for mobile devices in MFC and that was a very interesting experience. In those days the best PDA around was a Compaq iPAQ 3870 with Bluetooth and your development environment of chioce was eVC3. MFC seemed to be an obvious choice due to tool support but there were a few shortcomings. The frameworks imposes a document-view model that sometimes you have to fight against. Documentation was scarce (yes, the online help was bad) and your best resource was to read the MFC source files. This was how I got to understand how the framework was designed and why the tap-and-hold always made the dots circle twice. Interestingly you can see this behavior in a lot of the most recent devices on the Settings applets meaning that these are written in MFC (and the double circling bug is still there - sigh).
There is one good reason why you should not use MFC: it is slow. I understood this when I was working on the SqlCeSpy code and found that MFC would refresh a virtual list view noticeably slower than other applications, especially the ones written in ATL. MFC's message handling mechanism is a bit complex and convoluted meaning that when you need to handle a reflected message it gets bounced back and forth before it actually gets to where it is needed. The result for me was a slow refresh of the virtual list view. So slow in fact that it prompted me to write a grid-like control using virtual methods instead of reflected notification messages. Come to think of it, I never published this code...
So what is the best choice? As far as I can tell, when handling a user interface in native code, your best choice is to use WTL, ATL's extension now freely available from sourceforge. The thunk mechanism for handling the message maps make it very fast and efficient to process windows messages and applications tend to be both faster and smaller (compared to the statically linked MFC applications). Also, you feel like having much better control of what is going on and the templated approach is very elegant, although it requires conquering a steeper learning curve. But once you're there, you're hooked.
Thursday, May 24, 2007
Installing the WTL 7.5 Application Wizards in VS 2005 under Vista
It's not that difficult, but took some time to reverse-engineer what the script was doing. The first thing you need to do is to locate the VS application wizards directory. In my case it's in:
C:\Program Files\Microsoft Visual Studio 8\VC\vcprojects
Under this directory you must create a new one named WTL (what else?). Now go to the directory where you installed the WTL 7.5 files and copy the following files:
WTLAppWiz.ico
WTLAppWiz.vsz
to the vcprojects directory. You will need administrator clearance to do this. Now change the security settings of WTLAppWiz.vsz to allow editing. Change it so it looks like this:
VSWIZARD 7.0
Wizard=VsWizard.VsWizardEngine.8.0
Param="WIZARD_NAME = WTLAppWiz"
Param="WIZARD_VERSION = 8.0"
Param="ABSOLUTE_PATH = D:\src\WTL75\AppWiz\Files"
Param="FALLBACK_LCID = 1033"
Replace the D: path with your own WTL 7.5 directory. Finally, copy the WTLAppWiz.vsdir file to the WTL directory you created before and, going through the same security checks you did for the vsz file, change it so it reads:
..\WTLAppWiz.vsz ATL/WTL Application Wizard1An application that uses the Windows Template Library. 67774096#1154
You are done.
Tuesday, May 22, 2007
Data Port Console 1.0 Beta 1 available for download
There are some new features wothy of notice:
- Connection breakups are detected and handled. This means that if you inadvertently break the USB connection both the desktop and the device will gracefully recover (the device may take a few seconds before it becomes responsive, but this is a RAPI issue).
- The device component is now manually installed from a specific menu. I have included a very comprehensive set of device cabs: Pocket PC 2003, Windows Mobile 5 (both Pocket PC and Smartphone), Windows CE 4.2 (all CPUs) and Windows CE 5.0 (all CPUs). Hopefully the WM5 cab will install correctly on WM6 devices.
- The new version of DesktopSqlCe (1.8) is included (the console is built on top of DesktopSqlCe 1.8) and the Gold will ship with a single user license of this component. This means that if you just want to use the component in one PC (the same where the Console is installed) you will not have to pay the extra DesktopSqlCe license.
I'm really interested in reading your opinions on this product and what features you would add / remove / change.
Sunday, May 20, 2007
Diagnosing cab installations
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\AppMgr\ReportErrors = (DWORD)1
Thursday, May 17, 2007
The dangers of not terminating your strings
My first approach was to use the text conversion functions (wcstombs and mbstowcs). This way I had only one set of conversion functions (I mean converting from text to a GUID, to a DBTIMESTAMP and so forth). After the first tests the customer complained about stability: the data would be written to the database but the component DLL crashed almost consistently.
Back to the eVC4 debugger (now running on my Vista box in a Parallels virtual machine) I was confronted with an ugly beast. After successfully running the code, the whole thing would blast into hyperspace when closing the data source object (yes, that's the last thing you close). Hunting the bug forced me to look everywhere (even in the tested ATL OLE DB code). The thing was nowhere to be found. Often times I had to hard reset the debug device in order to get it back to its feet. Not a nice thing...
Bugs like these "smell" like memory issues: you write to a wild pointer and you may destroy some very fundamental data structures in your application. Needless to say that you are in for some unexpected outcomes.
I did not kill the bug directly. Instead, I rewrote the code so as to avoid text conversions. After running the new code for the first time the whole thing just behaved correctly. So should we avoid the text conversion functions altogether? Absolutely not! Just be wiser than me when you use them and make sure the converted strings are terminated. An unterminated string may cause this kind of problems in your application. So beware: you can either add one to the source string length or you can do that to the target string and make sure the buffer is zeroed before the conversion. Or you can do both.
Saturday, April 21, 2007
What was the last inserted IDENTITY value?
Wednesday, April 11, 2007
Installing CAB files from desktop .NET
The code I wrote for the installer is quite simple and I decided to write a short article on this. You can read it here.
Monday, April 09, 2007
Who put SQL CE 3.1 in my C:\Windows\System32?
Microsoft.SQLLITE.MOBILE.OLEDB.3.0
Say what!?!? SQLLITE???
P.S.: Yes, it looks like it is causing trouble: IAlterIndex does not work in my PC even after reinstalling SQL Compact Edition 3.1...
Saturday, March 24, 2007
SQL Compact Edition Insert Performance
The demo that caused most impact on my presentation was a benchmark between four different approaches to bulk data insertion on a SQL Compact Edition database. As you are probably guessing, using native code and OLE DB was the fastest scenario but the SqlCeResultSet also puts out a very decent performance.
To better illustrate this (and also to release the code to the public), I published an article that explains the benchmark and shows how wrong you can be if you don't use the correct approach...
Friday, March 02, 2007
Sunday, February 25, 2007
The Bluetooth sources
Requests for the Bluetooth code and article have not stopped, though so I decided to post links to the unedited versions of the C++ and C# sources. Before I get to fully comment the code and write a proper article, what you see is what you get. The sources are divided into 3 ZIP files:
Bluetooth.zip - Contains a sample C# WM5 application that enumerates devices and services. Also contains the Primeworks.Bluetooth class library.
msbts.zip - The low-level Microsoft Bluetooth stack wrapper.
wcbts.zip - The low-level Widcomm Bluetooth stack wrapper.
Now, I can take my time and write the full length article. All your comments and suggestions are welcome.
Sunday, February 18, 2007
Handling the Power Button
I finally got the eToken from Verisign. From now on I will be able to digitally sign my device DLL and CAB files in order to make life so much easier for my customers. I hope to have the first CAB for Windows Mobile 5 (Pocket PC) out next week.
Monday, February 05, 2007
An unexpectedly good experience
Well, I have to admit that I wasted a lot of my time procrastinating... After contacting Verisign for the first time on January the 31st, I got the full approval today. Instead of a heavy bureaucratic process, I was only asked for a telephone bill copy. After emailing it last Friday, I got the approval early this morning.
This is still not an inexpensive service, but it certainly is easy to purchase!
Tuesday, January 30, 2007
Installing a CE MAPI filter
There is a better way, though: the messaging application can be closed with a simple WM_CLOSE message sent to the right window. To find that window, use the FindWindow API with the "Inbox.MainWnd" class name. Once you close the messaging application, you can register and unregister the DLL without resetting the device. Nice.
Wednesday, January 24, 2007
Read-only SDF files
Friday, January 19, 2007
Reading the GPS port on the HTC P3300
Recently I had to tackle the built-in GPS serial port of the HTC P3300 and got a big surprise: my code wouldn't work! In fact, no characters were being buffered by the thread code meaning that the consuming application was receiving no data... After a close examination of what was going on in the thread code, I realised that no events were being generated by the serial port, namely the EV_RXCHAR that is used to signal that the serial port has characters to deliver. Interestingly, if you use the ReadFile API with a reasonable set ot timeouts you will be able to read data.
It is baffling to se how unreliably hardware support for common stuff like serial ports can be implemented. I'm still trying to figure out why HTC implemented it this way...
Friday, January 12, 2007
Recognition
Time to update eVC4?
[Edit]
Well, this was *my* error, not eVC4's... Shame on me.
Friday, January 05, 2007
RAPI Data Throttling
The connection speed can be so slow to the point where loading a very large table from the iPAQ 2210 (on a CF card) to the new Console may take several minutes. In the case of the huge table I discussed before, this means a 15 minute load time. If you think like I do, this is unnacceptable. So how can we speed up the communication between the device and the desktop? Easy: ZIP it!
In my particular case this was quite easy. All of my remote database access products use the same device DLL that communicates with the desktop via RAPI, TCP/IP or even the serial port. Data is packaged in messages of varying sizes and, depending on their semantics, they are either dispatched immediately or grouped together in a 64 KB buffer and sent when it fills up. This seems to be the optimal size for RAPI connections (according to one slide deck from MEDC 2005). So what happens if you compress this buffer before sending it? In my case the data transfer time was cut to a third. Now the behemoth table downloads to the desktop in under 5 minutes.
Finally, the ZIP library I'm using was found in CodeProject. This code allows me to compress and uncompress to a memory buffer, making it perfect for my own purposes.
Friday, December 15, 2006
1946
My apologies for the off-topic, but this flight sim just looks GREAT. I've been trying to fly IL2 FB only to find that no matter what plane I fly, I'm a damned turkey. If you are on to WW2 flight sims like me (addicted since the venerable SWOTL) 1946 has got to be your next toy - just take a look at the trailers (drool).
Thursday, December 14, 2006
How fast can you delete?
How about deleting? The same principles apply, actually. Use a base table cursor and (if possible) drop the indexes. But what if you have to selectively delete a large number of rows in a very large table? Well, you will need an index to seek the rows to delete. You pick up the next row to delete (given a unique row ID), seek it and delete it. Simple? No. This apparently simple process can become a real headache as I have just learned.
The scenario is simple: a very large database (around 50 MB) stored on a very fast (80x) CF card. I'm given a text file containing a very large set of GUID values that correspond to rows in a table (around 150K rows) to delete. The table has 6 indexes and a PK. How fast can we go on an iPAQ 2210 (my wartime machine) deleting al the rows? We cannot just do a DELETE FROM because this is a selective algorithm - it just happens to be deleting all of the rows (the same result would apply of you were deleting most of them).
Anywhere from 7 minutes to several hours... Really. Is this an issue with the CF card? No - this is repeatable on other devices with different flash cards. Is this an issue with the indexes? Partly - if you remove all indexes but the GUID you will get better performance, but this is a restriction of the problem: I cannot drop the indexes (after all, the algorithm does not know how many rows it is supposed to delete). Is this an issue with SQL CE? No - after all there is a scenario where you delete all the rows in 7 minutes (roughly the same as the DELETE FROM command takes).
As it turns out, this is an issue with the order with which you feed the GUID values to the algorithm. The 7 minutes run was achieved with an unsorted set of GUID values (randomly fed) where the several hours run (actually this is a projection because I stopped the bastard after 30 minutes and only 25K rows deleted) was ran when the GUID values were sorted. Yes, you read correctly - sorted.
So if you are in a similar scenario, please make sure you are not deleting the rows in the same order of the index you are seeking them on. It will kill your performance.
Why does this happen? B-tree algorithms, anyone?
Thursday, November 30, 2006
Renaming a SQL CE Table From a .NET CF Application
Wednesday, November 29, 2006
ALTER TABLE / ALTER COLUMN
If you compare the 2.0 BOL with the 3.0 BOL you see no difference. The same syntax is supported with the same limitations. Well, actually not.
With SQL CE 3.0 you can change a column data type using an ALTER TABLE command. The Syntax is very simple:
ALTER TABLE table ALTER COLUMN column type
I just got a confirmation from Microsoft that this is missing from the documentation so you should see it included in future versions.
Implications of this discovery are very important to me, especially for the SQL CE table editor in Data Port Console (now in Draft 11):
- Editing tables for SQL CE 3.0 just got easier. Instead of recreating a table every time a user requests a change in column data types, I only need to generate an appropriate SQL command and I'm done with it.
- Now, how do I keep backwards compatibility with SQL CE 2.0? Well, I don't. When using the SQL CE 2.0 engine the Console code will have to recreate the table (and hope the user has the missing msdadc.dll on the device - a highly unlikely scenario).
Back to the VS2005 editor...
Thursday, November 23, 2006
Biting the DB_NUMERIC bullet
Well, you avoid biting the bullet until you definitely have to do it. Right now I'm working on a native code text-to-SDF conversion tool for a customer and one of the types I need to handle is the dreaded numeric.
It's interesting that this is a very popular type due to the fact that it represents a fixed-precision number. What's more, you get to define the precision. The downside to this type is its sheer size: 19 bytes! As a matter of fact, a numeric value is represented as a DB_NUMERIC structure. If you look it up either on MSDN or on oledb.h, you will see what I'm talking about. The damned thing is a monster.
Now my problem was to deserialize a text representation of a numeric value and store it in a DB_NUMERIC structure so I could feed it to the SQL CE OLE DB provider. If you are thinking about using the IDataConvert interface then you can forget it because Microsoft does not provide the msdadc.dll file anymore, so you have to convert the beast by yourself.
Firstly I tried a two tier approach: convert the text to a double and then convert the double to a DB_NUMERIC (there's some code out there that does this). You have to be careful with rounding to get the conversion done, but it works... slowly.
My second approach was actually based on the first one, but I removed most of the floating point calculations. It is fast but limited to numbers with a precision of 18 digits. Take a look:
bool CDbNumeric::Parse(LPCTSTR pszText)
{
__int64 big = 0;
LPCTSTR pCur;
int nScale = -1;
int i;
for(pCur = pszText; *pCur == ' '; ++pCur)
;
if(*pCur == '-')
{
++pCur;
sign = 0;
}
else
sign = 1;
for(; *pCur && nScale; ++pCur, --nScale)
{
if(IsNumeric(*pCur))
{
big = big * 10 + (*pCur - '0');
}
else if(*pCur == '.')
{
if(nScale > 0)
return false;
nScale = scale + 1;
}
else
return false;
}
memset(val, 0, sizeof(val));
for(i = 0; i < 16 && big != 0; ++i)
{
val[i] = (BYTE)(big & 0xFF);
big >>= 8;
}
return true;
}
It's faster, believe me.
Tuesday, November 14, 2006
Just another database?
SQL Server Everywhere: Just Another Database?
Thursday, November 09, 2006
Thank you Rui!
Data Port Console Draft 6
Tuesday, November 07, 2006
Data Port Console Draft 5
Anybody else out there with some bright ideas?
Monday, November 06, 2006
Microsoft SQL Server 2005 Compact Edition RC1
JScript support on Windows Mobile
Thursday, November 02, 2006
Draft 3 Notes
The new RemSqlCe.dll (marked as 1.50.xxxx) now supports multiple databases per physical connection. I was looking forward to implement this new feature some time ago. It was not quite necessary for products like the Wizard where you actually process one device database per connection. With DesktopSqlCe I started to feel the need to implement this but as the sales took off I simply got too busy supporting my customers cleaning off some very stupid bugs, and this was delayed.
I finally decided to rewrite the console and implementing this feature was on the top of my to-do list. Designing the multi-database support code was quite easy: I extracted most of the code that was on a single monolithic server and implemented it in a separate class. This new server class is instantiated whenever a new database is created or an existing one is opened, and a pointer to it is stored in an internal array of 256 elements (I believe you will never need to open 256 databases on a device at the same time, will you?). When you open a database using the Draft 3 code you can see the server index in the database properties window. If you get a -1 after opening the database then you are using an older version of RemSqlCe.dll - please replace it. Note that the index value will not automatically refresh after opening the database. To do so, please select another node in the tree and select the database node again.
Please send all your comments and suggestions to this forum. The best ideas will get a free license. What do you think should be in this product?
- Integrated import / export features?
- If so, what formats should I support?
- What database editing features are the most important for you?
- Should I implement some sort of SQL Command batch processing?
- Do you want to manage other Microsoft databases from this console?
- Should I integrate the Data Port Sync features?
Let's hear it from you!
Wednesday, November 01, 2006
Data Port Console Draft 3
- Export the table data to an Excel sheet and
- Specify database open and creation properties
As always, remember that this is not a product yet. Most of the features you will see on the menus will not work. Although my tests have shown that the data handling is now stable (the underlying native code DLLs have been revamped to support multiple connections per Pipe), please refrain to use your production SDF files with this and use copies instead.
Tuesday, October 31, 2006
SQL Server Compact Edition
Looks like I will have to change all my product's documentation...
Tuesday, October 24, 2006
Data Port Console Draft 1
So why am I posting this? I want your feedback on how the product should evolve and I am ready to give away free licenses for the best feature ideas. Use the special forum to express yourself.
Wednesday, October 18, 2006
Handling multiple databases
One of the biggest improvements you will see is that the code now handles multiple databases per connection (DesktopSqlCe users - this will also apply to you). As a matter of fact, and as you should expect, the whole product is based on DesktopSqlCe and I am using this as an excuse to add more features to the component.
The versions so far used to tie up the Pipe (physical connection) to the SqlCeDatabase in a one-to-one relationship. No more! Now you can have more than one SqlCeDatabase object per Pipe even on a RAPI connection. This enables some interesting scenarios indeed, and the first consumer for this one will be the new Console. Stay tuned for the incoming Draft 1.
Monday, October 16, 2006
SQL CE Console Updated (1.3.700)
With SQL CE 2.0 there are no problems because the clever OLE DB provider allows you to overwite the generated value.
SQL Mobile, on the other hand, has a mean OLE DB engine - the only way you can influence the next IDENTITY value is by altering the column's seed through either a SQL command or a low-level OLE DB interface (my solution).
To avoid changing the IDENTITY seed for every copied row I use an optimization algorithm that keeps track of the next value and only alters the column if there is a difference between the value to write and the automatically generated one. This is actually a variation of the same algorithm found in Data Port Wizard.
For all you SQL CE Console users, here's the latest download.
Thursday, October 12, 2006
Accessing SQL Mobile-specific column properties
Wednesday, September 27, 2006
SQL Syntax Highlighting
SELECT * FROM Customers
Fonts and colors are applied as you write and now I want to extend the editor to support auto-completion. Instead of purchasing a costly component, I decided to adapt the code on this article:
Syntax highlighting textbox written in C#
The code has some bugs and gotchas, but it is easy to use and adapt to VS 2005.
Saturday, September 23, 2006
Data Port Console
Combine this with the new .NET Framework 2.0 features such as a better data grid, and I am actually getting very good results and very fast. Looks like .NET is actually delivering its promise: fast time-to-market with a solid framework to support your application.
P.S.: Yes, I know it hogs memory and the CPU with what some native code purists would call needless cycles. But I'm not sure I would be able to do this as fast as I am doing now with either MFC or WTL. Anyway, the core of the code - remote database access - is still native code. The more I think of it, the more I like this combination: native code for the performance bits and managed code for the UI and managing the user environment. A killer!
Wednesday, September 13, 2006
Enumerating devices and services with the Widcomm Bluetooth stack
Data Port Wizard crack
Let's see if I can find a crack for this product myself (hehehe).
Tuesday, September 12, 2006
Unified Bluetooth stack access for Windows Mobile
The Widcomm API is oriented to C++ developers and exposes itself as a set of C++ classes that are supposed to be consumed either as-is or derived in your application. Exposing this type of API to a .NET CF developer is nothing short of impossibe unless a C layer is developed to consume the Widcomm stack while exposing a set of simple functions.
While I was writing this piece of code, I found out that the Microsoft stack could also be subject to the same treatment. So my idea was to produce two DLL files with exactly the same function exports, one for the Widcomm stack and the other for the Microsoft stack. The obvious advantage of this is that now a .NET CF application can consume the existing Bluetooth stack without having to consider the differences between the Microsoft and the Widcomm stack.
I have to say that this was a very interesting learning project, thanks to Peter Foot's help. The resulting code will be published under a series of articles and, time permitting, I will try to integrate the code in Peter's own 32feet.NET project.
Thursday, August 31, 2006
MobileServer preview
If you have been reading these posts lately you know that I am working on getting a peer to peer scenario to work with my remote data access tools. This will allow two PDAs to synchronize data just by getting close to one another within the 32 feet required by Bluetooth.
Right now this is working over Wi-Fi and Bluetooth serial COM ports (no support for sockets in the Widcomm stack, alas).
The thing that has been missing from the picture is the server code. I started by providing a standalone native application to serve a TCP / IP sockets connection. Later I split the server feature and put it in a separate DLL. The latest change was to move all that code to the device server DLL (RemSqlCe.dll) and to expose an API for the sockets server. After adding the serial port server I started writing a very simple managed code class library for users to integrate in their own applications.
This class library allows the consuming application to create a server, configure, start, stop, query its status and dispose of it. The above picture shows an instance of the sample MobileServer application running two servers on an HP iPAQ 2210 serving two different desktop PCs, one via Wi-Fi and the other via a Bluetooth COM port.
Now I have to add Bluetooth server abilities to this little beast and this means supporting both the Microsoft stack and the popular Widcomm stack (that's why I'm using the 2210). The server will be able to expose its services (either COM or sockets) via enumeration in order to make ad-hoc device pairing easier.
I expect to release the device client very soon with some P2P samples ready to run.
Monday, August 21, 2006
SQL Mobile over Bluetooth
Nothing automatic here - I just configured a serial link between the devices and wrote the serial code to glue them both. I am ashamed to say that this was not a swift experience because my years as a mobile developer led me to overlook some of the finer points of desktop development, namely overlapped IO. Yes, porting the device code to the desktop did not quite work as I expected. Nothing at all, really. After researching into the overlapped IO mechanism, it was quite easy to set up the internal threading and queueing to simulate a RAPI Pipe over a serial port.
Next steps? 1 - Port the client code to the mobile device so you can access another devices SQL CE / Mobile databases over a Bluetooth COM port. 2 - Add support for the Microsoft stack so a device will be able to identify which partners can serve SQL Mobile data and connect via sockets. 3 - Add support for the Broadcom stack. From what I have seen, there is no sockets support in the Broadcom stack. 4 - Why not infrared?
Friday, August 18, 2006
SQL Mobile Peer to Peer
This is especially cool because I am not using revolutionary new technology - I just adapted what I had been using in my products a long time ago. Let's see a little history.
When I first shipped Data Port Wizard almost two years ago, the device component DLL (RemSqlCe.dll) was only a RAPI streamed server. Soon I understood that I could adapt the same stream code and build more transports on top of it and the TCP/IP single-threaded server came about in almost no time. In its first incarnation the server resided in a separate device DLL file, but it was not a very pratical solution. The next release of DesktopSqlCe will have a new version of RemSqlCe.dll with the socket server code included as well as a CF (1.0 and 2.0) sample that shows how to use it. And this is what I used on the K-Jam for the server.
The client uses an adapted version of the desktop's dssce.dll and Primeworks.DesktopSqlCe.dll. (In a future version, these DLL files will change their names to better reflect what they do.) This is how I was able to quickly produce pwdclice.dll (dssce.dll device version) and the new Primeworks.Data.Client.dll (Primeworks.DesktopSqlCe.dll device version). I ported the code for both DLL files in about one day (cool, huh?).
Now, besides being able to open a local device connection I can also open a TCP/IP client connection to a server device. Et voilá: P2P! Shipping in September, but I'm open to send the early builds to anyone willing to test the code.
Three final thoughts:
Why should you want to have a local connection on your device? After all, that is why you make a reference to System.Data.SqlServerCe in your application, right? Well, can you connect to either SQL CE 2.0 and SQL Mobile with the same data provider using the same application code? And can you remotely connect to another device just by changing connection properties? Now you can!
And why not add socket server code to the LocSqlCe.dll, the RemSqlCe.dll desktop counterpart? This would be a very easy way to set up a client server scenario over the internet...
On the issue of pluggable data streams, why not a virtual COM port over Bluetooth as well? Can I sync my sdf with yours via Bluetooth?
Stay tuned!
Thursday, August 10, 2006
How do I know the database version of an sdf file?
Monday, August 07, 2006
DesktopSqlCe redistributables updated
Monday, July 03, 2006
SQL CE Console Updated
Thursday, June 15, 2006
Supporting SQL Everywhere
Tuesday, June 13, 2006
Adapt your application to exotic screen resolutions
Friday, June 09, 2006
SQL Everywhere CTP
Thursday, June 01, 2006
Migrating Sync to SQL Server
Having said all this, I am not sure that this will be the final preparation code for SQL Server. As a matter of fact, with a more advanced database engine I will ba able to use triggers to flag any changes to the tracked tables (or so I hope...).
Wednesday, May 24, 2006
Data Port Sync Released
- Prepares the Access database for synchronization by adding the tracking columns and tables
- Exports the database to the device in SDF format (either SQL CE 2.0 or SQL Mobile)
- Tracks the changes on both ends and
- Merges them according to a very simple rule: the Access database always wins
There is another C# sample (Prepare) that you may use to prepare and "unprepare" Access databases and I have used it together win an end-user sample that I am also shipping (this time it's a VB sample that I will port to C#) that does steps 2 to 4 and is supposed to act as a template for a custom solution.
All thoughts and suggestions are most welcome for this product.
Friday, May 19, 2006
Thursday, May 18, 2006
Synchronization stability
There are four phases to the synchronization process: 1) preparation of the Access database; 2) exporting the Access database to the device in SDF format; 3) tracking changes on both ends and 4) merging the changes. When I designed the tracking code I assumed it would be okay to immediately mark the changes in the database, both in the user table and in the tracking table. As a matter of fact, this is a reasonable solution if you just want to track changed data, but it is not very safe if you need to process this information subsequently. If that process fails, you essentially lose the tracking information (at least with the way I was doing it).
To solve this issue, the data tracker now makes no change to the database. It generates a list of changed rows (for both databases) and the generation of this list is only affected by the posterior changes made to the database. The importance of this is that if the merge process fails, I can roll everything back and rerun the data tracker. All changes will be there.
Data Port Sync will become a product by the end of this month, before I go to MEDC Europe in Nice. Stay tuned.
Thursday, May 04, 2006
SQL Mobile DateTime parameters
Thursday, April 27, 2006
Preparing for SQL Everywhere - II
The solution I came to was quite simple: two asynchronous data buffers. The first is written to by the client code (dssce.dll) and read by the server (LocSqlCe.dll). The second data buffer is used in the exact reverse way.
When I finally managed to make the whole thing work I was amazed by how slow it was... The data streaming code was the culprit because it was chewing up too much CPU bandwidth in its own thread, especially while waiting. The solution? A criteriously inserted Sleep(0) on the reader thread.
The bottom line is that DesktopSqlCe users may now develop their applications against a local SQL Mobile database. Look Ma: no PDA!
Monday, April 24, 2006
Preparing for SQL Everywhere
Well, it's easy enough. First, make sure you have Visual Studio 2005 installed (one of the versions that carries the SQL Mobile bits). Second, go to the C:\Program Files\Microsoft Visual Studio 8\Common7\IDE directory and copy all the sqlce*.dll files to your C:\Windows\System32 directory. Third, register the sqlceoledb30.dll file using regsvr32.exe Finally, open the ssceoledb30.h file and copy the SQL Mobile definitions to a header file of their own.
After this very simple routine, I was able to quickly write a desktop C++ application to create an SDF file on my desktop using code borrowed from eVC3/4! You just need to include your new header file after atldbcli.h and you are done... I will give all the details in an upcoming article.
Ah, and don't forget: this only works on development machines with VS 2005 and Windows XP Tablet PC edition. These license limitations should be lifted when we get the Everywhere bits by the end of this calendar year.
Thursday, April 06, 2006
SQL Server Everywhere Edition
Tuesday, April 04, 2006
Synchronizing multiple PDAs - success!
My final hurdles are to implement conflict rules and constraint-directed updates. Right now, there is only one conflict rule: if both the PC and the PDA have updated the same row (as identified by a GUID), the PC wins. As for constraint-directed updates (this is a pompous term that means "insert, update and delete in foreign key order") I have already devised a means to calculate an "INSERT order", the order by which it should be safe to insert new rows without FK clashes. It so happens that it is the reverse order for DELETE.
Lots of tests ahead...
Friday, March 31, 2006
MEDC Europe 2006
Wednesday, March 29, 2006
Synchronizing multiple PDAs
The day's work was spent on calculating INSERT order for tables (reverse of DELETE) so that database structure and constraints are not violated. Also, work is under way to support multiple PDAs synchronizing against the same database.
Hope that by the end of the week I will have more to write about.
Tuesday, March 21, 2006
DataPortSync pre-released
If you want to take a look at how the code is coming, download the latest version of DesktopSqlCe (1.3.200) and the DataPortSync pre-release 1.0.200. Be prepared for a bit of a rough ride!
Wednesday, March 15, 2006
Second step to Sync
After figuring out a simple means of doing this, using additional tracking tables and columns on the tracked database, I wrote a new sample using the pre-release code of Data Port Sync and DesktopSqlCe (some minor errors were corrected meanwhile). After selecting an Access database, this sample prepares it (according to the user's selection of tables and columns) and exports it to the device. Preparation involves adding new tables to the database and new columns to the tracked tables.
After all is ready, you can start playing with both databases and the code will report what changed and in what table. Tracked rows are uniquely identified across both databases with a GUID value. I expect to upload the sample to the site very soon. Stay tuned.
Monday, March 13, 2006
First step to Sync
- A Microsoft Access data tracker - prepares a database for tracking and tracks changes made to the database. Tracking is achieved by adding some control tables and columns to the tracked tables.
- SQL CE / Mobile data tracker - uses the same data tracking algorithms to track changes made to a mobile database.
- Data transport - building on DesktopSqlCe, I'm adding some direct access features for Access databases (so that .NET developers don't have to bother with INSERT commands).
Here is the first sample: an Access to SQL CE 2.0 / SQL Mobile export tool. You will need to download the latest version of DesktopSqlCe (soon to be renamed).
Enjoy.
Thursday, March 09, 2006
Another issue with BLOBs and SQL Mobile
An .sdf database file in SQL Server Mobile is larger than the same .sdf database file in SQL Server CE 2.0 when you use the ntext data type or the image data type
SQL Mobile is a rewrite, not an upgrade of the SQL CE 2.0 code, so one should expect some differences in behavior. I am collecting a list of these, both high and low-level and will publish them soon.
Friday, February 24, 2006
The Bookmarks and BLOBs Bug
Read more about this issue here.
Wednesday, February 22, 2006
Thread synchronization errors
My last bang on the wall was quite simple. At the very heart of all my products, both on the desktop and the device sides, lies a thread that reads all the messages and stores them in a queue. The device side is simpler - the queue is simply polled. But the desktop side is a bit more complex and requires signalling events. When a properly formed message arrives and is stored in the queue, a call to SetEvent is made to signal other threads of the availability of data.
Now, when I rewrote the code for the 2.0 versions of the products, I called SetEvent before adding the message to the queue. Access to the message queue is guarded by a critical section, as it should. It so happens that adding a message to the queue is a bit lengthy (requires memory allocation) and this meant that after firing the event any thread that would test the event before the message got to the queue would be in a strange situation: there's a signal telling me there's something on the queue but the queue is still empty...
The solution is obvious - raise the event after the message is added to the queue. Humpf...
Friday, February 17, 2006
Friday, February 03, 2006
CEDB vs EDB
Tuesday, January 17, 2006
Installing SQL CE 2.0 and SQL Mobile on a Storage Card
Wednesday, January 11, 2006
Supporting Visual Basic 6
This experience has been quite easy because the .NET Framework has lots of classes and mechanisms to suport marshalling and binary data conversions, so the supporting API can really be made very basic indeed. Not so with VB6.
Visual Basic 6 is a very old progamming language and (I think) it lacks the skills required to qualify as a systems development language. My case is quite simple. After shipping the first version of DesktopSqlCe (which only supported C and .NET), I started to get a lot of requests to port the higher level code to Visual Basic 6. And that is the unfortunate work I am doing right now.
My major gripe is the apparent lack of functions to convert from the language types to binary streams (C++: why do you need functions for that ???). The .NET Frameworks has a bewildering number of conversion classes, but VB6 lacks almost everything. Bottom line: I am adding a lot more functions to the low-level API so that VB6 can talk to it...
And yes, I am adding VARIANT support to the code...
Wednesday, January 04, 2006
K-Jam and ToolHelp
To shut down a process, you must get a handle to it and this is usually done through the ToolHelp API. This API creates a static snapshot of all running processes and allows the application to enumerate them and kill these processes at will. Interestingly, this technique worked quite well until Cristiano tested it in a K-Jam. Being the fortunate owner of one, I was volunteered to help (forced, actually) and found out that the CreateToolhelp32Snapshot funtion would fail every time when called with the TH32CS_SNAPPROCESS flag.
Thanks to one very clever fellow MVP (Paul Tobey) and Microsoft employee (Ilya Tumanov), I now know that this function was failing because it was generating way too much data in the snapshot. To limit the amount of data it generates, one needs to add another flag: TH32CS_SNAPNOHEAPS. Interestingly, this flag is not defined in tlhelp32.h file, where it was supposed to be. Here it is, with my thanks to Paul and Ilya:
// optimization for text shell to not snapshot heaps
#define TH32CS_SNAPNOHEAPS 0x40000000