Wednesday, September 13, 2006
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
Tuesday, December 27, 2005
One application, two database engines
When Microsoft shipped SQL Mobile, we got a new header file to include in our applications: ssceoledb30.h. Unfortunately, this means that you cannot have both the old include file and the new include file on the same exe. You can use the preprocessor to include either one, but this is not what I was looking for: I wanted an integrated solution that would allow me to consume any database (2.0 or 3.0) from the very same exe.
The solution is to merge both header files into just one. While I wait for Microsoft to tell me if I can publish the one I wrote (due to copyright issues, of course) you can have a go at it yourself by diff'ing the two files and figuring out the differences. Not many, actually.
Friday, December 23, 2005
Monday, December 05, 2005
Microsoft SQL Server 2005 Mobile Edition Device SDK
Friday, December 02, 2005
New site, new forum
Wednesday, November 30, 2005
WTL 7.5 Released
Thursday, November 24, 2005
SQL Server Mobile 2005 on MSDN
SQL Mobile has a new home page on the MSDN Mobile Developer Center. There, you can find articles, downloads and other resources on SQL Mobile. Cool stuff!
Wednesday, November 23, 2005
Localized .NET Compact Framework 2.0 Redistributable
Tuesday, November 22, 2005
Saturday, November 19, 2005
Sunday, November 13, 2005
Post-TechDays interview
Ricardo Figueira's Blog (no, we are not related)
Friday, November 11, 2005
RAPI and Windows Mobile 5
Setting this registry key corresponds to the following provisioning script:
<wap-provisioningdoc>
<characteristic type="SecurityPolicy">
<parm name="4097" value="1">
</characteristic>
</wap-provisioningdoc>
Read all about this at its original source.
Thursday, November 10, 2005
TechDays and RAPI speed
In one of the recesses, I was approached by one of my customers complaining about DesktopSqlCe performance when inserting data in bulk. After comparing the insertion performance of Data Port Wizard, DekstopSqlCe was an order of magnitude slower...
My customer's code also needed to be optimized, but that was not the root of the problem. It was in my code. How could that be if both products share most of the base code? On one hand DPW was blasting 11,000 rows in less than 30 seconds over RAPI, while DesktopSqlCe was performing in the tens of minutes range. But wait, I thought, I've seen this before.
When DPW was being developed, I soon realized that synchronous messaging was very slow over RAPI. If after inserting a row in a table (even if the cursor remains open) and you wait for the device to send an acknowledgement message, then you are going to be very slow. So slow as to justify the tens of minutes scenario.
To solve this, DPW uses an asynchronous method of communicating and that is why it does not stop when it finds an error - all errors are reported at the end and are collected asynchronously. This is fast, very fast indeed.
When I wrote DesktopSqlCe I wrongly assumed that my customers would make occasional and rare direct table inserts and updates, so inserts and updates were being delivered in a synchronous fashion, i.e. waiting for a confirmation after insertion or update. This situation is now solved by adding a new attribute to the SqlCeRowset class (similar to the CF 2.0 new SqlCeResultSet class): ReportError. This attribute is of type SqlCeReportError and has two values: Sync (default) and Async. Now you know why.
Saturday, October 29, 2005
Friday, October 28, 2005
Tools support matrix
Just to remind everyone of the Tools support matrix. Actually, this is a funny way of bookmarking this page, huh?
It reminds us that we have to keep all tools under our belt, from eVC3 to VS 2005 (if we are targetting all sorts of devices, that is...).
Waiter! Another Pilsener, please!
Dekuji!
Saturday, October 22, 2005
The IDENTITY issue on SQL Mobile is solved!
Most databases that use IDENTITY columns will do so for primary key or index support, meaning that these values are generally laid out in row creation order. For a table with few deletions, it is very likely that you can find large continuous ranges for which you can let the IDENTITY column do its job - generate the next value.
I write this after finishing my first tests on Data Port Wizard and a new version of the device component. For "well behaved" databases such as the Northwind sample, the code only needs to change the IDENTITY seed twice per table and performance does not seem to be an issue. On databases with sparse tables (where the number of gaps in IDENTITY values is large) performance may be a concern because the base table cursor must be closed before the seed is changed, and reopen again to continue data insertion.
Friday, October 21, 2005
ActiveSync 4.0 Issue Alert
From: Windows Mobile Update
Subject: ActiveSync 4.0 Issue Alert
Microsoft wants to alert you to an issue regarding ActiveSync 4.0 and the immediate steps the company is taking to resolve it and meet customer needs.
Specifically, some users of ActiveSync® 4.0 have reported challenges when using the USB connection of their personal computer to connect to a device with Windows Mobile 5.0. In many cases, these challenges are likely caused by interoperability with desktop firewall applications or applications that manage network traffic. These applications appear to conflict with the TCP traffic between a device and the PC.
An update for ActiveSync is scheduled to be available in late November, but customers may also resolve the issue manually immediately by visiting visiting http://www.microsoft.com/windowsmobile.
This issue only affects people with new devices just coming to market and running Windows Mobile 5.0 software, and only when synchronizing directly to a PC (wireless synchronization via Bluetooth, infra-red or directly to a server or other services are not affected).
- In the U.S., these devices include the Sprint PPC-6700, iMate JasJar, iMate KJam, iMate SP5, Dell Axim X51v,HP iPAQ rx1955 Pocket PC, HP iPAQ hx2495 Pocket PC, and the HP hx2795 Pocket PC.
- In Europe, these devices include the Orange SPV M5000, iMate JasJar, iMate K-Jam, T-Mobile Vario, T-Mobile MDA Pro, O2 XDA Exec, O2 XDA Mini S, O2 XDA Phone, FSC Pocket LOOX N500, Dell Axim X51, Dell Axim X51v, HP PPC series (rx1950, hx2190, hx2490, hx2790).
- In Asia, the devices affected are the iMate JasJar, iMate KJam, iMate SP5 and iMate SP5m.
Microsoft is committed to solving this issue for its customers and its partners.
For more information, please contact the Windows Mobile PR team at 425.452.5400 or usmedteam@webershandwick.com.
Sunday, October 16, 2005
Fast row counting with the ATL OLE DB Consumer Templates
The answer? OLE DB, of course!
Saturday, October 15, 2005
PRIMARY KEY bliss
Now let's wait for some good news on the IDENTITY column issue.
Wednesday, October 12, 2005
Now, it bites...
SQL CE 2.0 allowed consumers to write on IDENTITY columns, a feature that enables copying desktop databases. The table is created on the device with the IDENTITY column set at the given seed and increment. When copying the table data, the column values are forced (the OLE DB provider complains mildly) and the resulting table behaves exactly like the source one. Interestingly, both Jet and SQL Server allow this so why wouldn't SQL Mobile allow it as well?
Where is MSysConstraints?
Monday, October 10, 2005
Installing SQL Mobile Beta 2
Note: If you are developing against SQL Mobile, you might want to install the error messages DLL - sqlceer30EN.dll.
Sunday, October 09, 2005
RemSqlCe.dll for SQL Mobile
First, the whole project was ported from eVC3 to eVC4. This was not a strict requirement, but since I was targeting a Pocket PC 2003 device I decided to do so. The result was interesting because I ended up dropping some dead code that still hangs around on the SQL CE 2.0 version... Now I have a smaller DLL (87 KB).
When porting to eVC4, I had to drop Giuseppe Govi's STL port and use the one that ships with the Pocket PC 2003 SDK. Apparently the old STL code will create a number of compile-time conflicts with the XML headers...
After changing a few constants and applying the small correction I mentioned on my last post, the code started to work - in less than one hour (most of this time was spent with the eVC3 to eVC4 migration).
The SQL Mobile component can be downloded from here. Please note that this will retain a Beta status while SQL Mobile is also a Beta product. To install it, just copy it to your device's \Windows directory and you are done.
Saturday, October 08, 2005
Supporting SQL Mobile
Bookmarks are now unsigned integers (DBTYPE_UI4) when they were integers in SQL CE 2.0.
Another interesting difference occurs when you open a table with an index. With SQL CE 2.0, using an empty string for the index name would be interpreted as no index selection. No so with SQL Mobile - an empty string seems to be a valid index name and the table will refuse to open. Here's some code I had to change in my previously published CIndexTable class template (in bold):
if(szIndexName && wcslen(szIndexName))
{
idIndex.eKind = DBKIND_NAME;
idIndex.uName.pwszName = (LPOLESTR)T2COLE(szIndexName);
pIndex = &idIndex;
}
I am testing the whole code against SQL Mobile and will post my findings as they come up.
Tuesday, October 04, 2005
Back to Lisbon
Jet lag was one of my biggest problems. It was so big that when I met Alex Feinman, I thought I was talking to Andy Wigley and complimented him on his book... Foot: meet mouth! (Sorry Alex!)
Tuesday, September 27, 2005
Off to Seattle
Sunday, September 25, 2005
Supporting ActiveSync 4.0
My first approach to consuming RAPI was quite simple: a static link to rapi.lib. This meant that all the products were tied to a particular version of ActiveSync and would not run in another. By linking to a specific version's lib file we are actually precluding the possibility of linking to other versions, both older or newer.
It so happens that util recently all applciations were statically linked to ActiveSync 3.8, meaning that you had to install this particular version in order for the applications to run. Incidentally, I got a request from two different customers, one asking for ActiveSync 4.0 support, and the other was asking for version 3.6 support. Now what?
Publishing different versions of the applications was obviously out of the question. There had to be a better way of supporting mutiple versions of RAPI. Then I recalled something I read in one of Doug Boling's books: use dynamic linking. The idea is to dynamically load the RAPI.DLL file and retrieve the entry points to the functions you need by using GetProcAddress. To make things easier, I wrote a very simple helper class, of which I'm highlighting a few snippets.
First, the class declaration:
#pragma once
#include <rapi.h>
class CRemoteAPI
{
public:
CRemoteAPI(void);
~CRemoteAPI(void);
HRESULT InitEx(RAPIINIT* pRapiInit);
HRESULT Uninit();
HRESULT Invoke(LPCWSTR pszDLL,
LPCWSTR pszFUnction,
DWORD cbInput,
BYTE *pInput,
DWORD *pcbOutput,
BYTE **ppOutput,
IRAPIStream **ppIRAPIStream,
DWORD dwReserved);
protected:
HMODULE m_hDll;
};
I'm only including a few methods to illustrate the technique. You should be able to extend your own version with the calls you need.
Let's look at the constructor and destructor:
CRemoteAPI::CRemoteAPI(void)
: m_hDll(NULL)
{
m_hDll = LoadLibrary(_T("RAPI.DLL"));
}
CRemoteAPI::~CRemoteAPI(void)
{
if(m_hDll)
FreeLibrary(m_hDll);
}
As you can see, we dynamically load and unload the RAPI.DLL on the constructor and destructor. You should have no problems in nesting these - you will only be incrementing or decrementing the module load count. Now, let's see how to implement one of the methods:
typedef HRESULT (__stdcall *CERAPIINITEX)(RAPIINIT*);
HRESULT CRemoteAPI::InitEx(RAPIINIT *pRapiInit)
{
HRESULT hr = E_NOTIMPL;
CERAPIINITEX pfnCeRapiInitEx;
if(!m_hDll)
return hr;
pfnCeRapiInitEx =
(CERAPIINITEX)GetProcAddress(m_hDll,
"CeRapiInitEx");
if(pfnCeRapiInitEx)
hr = pfnCeRapiInitEx(pRapiInit);
return hr;
}
You now get the idea on how to implement all other methods. The beauty of this is that by using this approach, you can support all versions of ActiveSync (provided that the target version does implement the function call you need...).
Thursday, September 22, 2005
Data Port products updated
I'm still recovering from the shock of realizing that all desktop code was being linked to the sinlge threaded versions of the runtime libraries. At least two threads are used in all products (DPW uses more) so you may now understand why some of my users were getting random memory addressing crashes...
Wednesday, September 21, 2005
The GetTableDefinition memory leak
This explains why RemSqlCe.dll leaks memory (arrgggh!). Now, I have to roll up my sleeves and produce new releases of all my products...
Wednesday, September 14, 2005
New C# sample for Data Port Component
P.S.: The new version is now 1.0.30.
Monday, September 12, 2005
Data Port Component updated to 1.1.29
Monday, September 05, 2005
Update CCommand, please!
There are two different kinds of SQL commands: the ones that return something and the ones that return nothing. The first class of commands are the classical SELECT commands - they always return something, even if it's only a single row count. On the other hand INSERT, UPDATE and DELETE commands (as well as all the DDL commands) do not return rows for you to process.
Making this distinction is very important when you use CCommand: what accessor and rowset types will you use? All non-SELECT commands typically use a CDynamicAccessor and a CRowset where all others may get away with CNoAccessor and CNoRowset. This means that you need to know what kind of SQL command to execute before instantiating the proper template parameters in CCommand. This is a nuisance when running generic SQL commands. Why?
Let's look at the code in CCommand::Open. Somewhere near the bottom of the method, you can see (ATL 3):
if( bBind &
_OutputColumnsClass::HasOutputColumns())
return Bind();
else
return hr;
This is bad. By default, bBind is true (default function parameter) but the HasOutputColumns function is defined by the accessor class you provide as a template parameter for CCommand. Gotcha - you need to now if the command returns rows before instantiating CCommand. So how do you go around this? How do you run generic SQL commands without having errors thrown at you, no matter what accessor you use?
Easy. Just change the last code snippet so it reads:
if (bBind && GetInterface() != NULL)
return Bind();
else
return hr;
Now all commands will run without having assertions thrown at your face. To see if you actually have an accessor and a rowset, just test GetInterface(). If it's NULL then your command returned nothing.
Thursday, September 01, 2005
Why? Because I Can!
One guy, though, kept it cool. In his note on Pocket PC Thoughts, Ed Hansberry said:
I am sure someone will fine a practical application for this, but for now, it falls under the W?BIC category.
Actually we started this stunt with a challenge from César. Why don't we try and connect?
Ed has a good point. The practical implications of this are still a bit limited by the technology. On one hand, the client must know the IP address of the server PDA. This will change very often due to the connection methods available. Not everybody will be able to have a fixed IP address to use on the PDA. So one may wonder if the a half-a-world-away scenario is feasible, but on a WiFi scenario this may actually happen.
On the other hand, while the server is actually busy serving data, the PDA user cannot edit his data. This is obviously a limitation of the current SQL CE 2.0 engine that will be lifted with SQL Mobile. When it becomes mainstream, one may see real-world applications of this technology, where the backoffice accesses the mobile database to update it while the user continues to work.
So that's right, Ed: Why did we do it? Because we could, and was a lot of fun! ;)
Tuesday, August 30, 2005
Extreme database editing
I don't know how, but he did manage to get his PDA connected to the Internet with a fixed IP address, which is all that my Pocket PC server software needs to start serving a remote client. On my end, I used SQL CE Console to edit his data. We were using ADSL on both ends, which means that our real bandwidth was limited by the upload bandwith which is typically 128K.
It took us some time to get the connection settings right (especially with César because the server PDA was on his end), but we finally managed to make it work. I could successfully connect to César's PDA, open the Northwind database, insert some new records, and disconnect without a hitch. No data were lost.
Cool stuff!
New Site
A simpler site was in order and, not being an HTML geek, I turned to FogCreek's CityDesk to help me in publishing content on the site. Interestingly, I'm using it exclusively to manage the entire site. A final note: it took me one day to rewrite the whole site. Not bad, huh?
Check the new lean and mean Primeworks website.
Thursday, August 25, 2005
IColumnsRowset
Such was the case when I started to implement SqlCeDataReader.GetSchemaTable. This one was a bitch to do. One of the best clues I actually got from the MSDN documentation:
The GetSchemaTable method maps to the OLE DB IColumnsRowset::GetColumnsRowset method (...)
It's not enough, but it's a big step forward. After implementing this using the Consumer Templates, I deceided to write a small article on this subject:
Using IColumnsRowset with the ATL OLE DB Consumer Templates
Enjoy!
Friday, August 19, 2005
BLOBs and SQL CE Command Parameters
You might already have read this on the MSDN help page for SqlCeCommand.Prepare. Have you ever wondered why you need to set the size of variable-length data types before preparing the command? The answer is easy: you cannot use a BLOB in a parameter. I have a strong belief that this is not necessarily so but, at most, you could use one BLOB type as a parameter.
This happens due to a limitation of SQL CE 2.0 and of OLE DB. As you may know, SQL CE 2.0 can only handle one storage object at a time. Storage objects such as ISequentialStream are used to copy BLOB data between the provider and the consumer. One of the workarounds for this is to use more than one accessor handle per row. I described this in two articles:
Managing Blobs Using the ATL OLE DB Consumer Templates
and
Inserting non-nullable BLOB data in SQL CE using the OLE DB Consumer Templates
This solution breaks down with the current OLE DB implementation. As it stands, ICommand::Execute only takes one accessor handle for all parameters in the DBPARAMS structure. So this means no BLOB command parameters.
The workaround Microsoft devised is actually the only one possible - preset the parameter size before preparing the command.
Monday, August 15, 2005
Data Port Command Royalty-Free
Thursday, August 11, 2005
Corrected CRowsetIndex
Friday, August 05, 2005
The Garden of Arcane Delights
It fails. Yes, it fails. Works wonderfully when one assumes that BLOB columns are meant to be nullable (i.e., to accept null values). And then suddenly here comes a customer who thinks otherwise. He likes his NTEXT columns NOT NULL. Gosh: the whole thing crumbled.
If you are using my ATL OLE DB C++ code, heed these words of advice, so you can merrily trod in the garden of arcane delights.
By the way, DCD rocks (or rocked)...
Thursday, August 04, 2005
Product Updates
Another subtle bug has also been corrected: importing empty NTEXT fields into SQL Server. Apparently the SQL Server provider does not like to be given an empty stream object - you must really pass a NULL pointer as the parameter value.
Wednesday, August 03, 2005
dplib.dll updated
Tuesday, August 02, 2005
RemSqlCe.dll updated
Sunday, July 31, 2005
Back from holidays
Sunday, June 26, 2005
Why WTL?
WTL rocks!
Wednesday, June 22, 2005
Another bug in SQL CE Console (cont.)
Another bug in SQL CE Console
SQL commands (SqlCeCmd objects) may be executed either as queries (when they return a SqlCeRowset object) or as simple commands with no data returned (only status info). Interestingly, version 1.0.17 is incorrectly reporting an out of memory condition after a non-query SQL command is executed. Brilliant. So how useful is a tool like SCC when you cannot even create a table???
The version 1.0.18 is out now, with my apologies.
Friday, June 17, 2005
Diving into WTL
Too bad we don't get all the code-generation support that eVC has for MFC, but I'm getting over it pretty fast.
Monday, June 13, 2005
MEDC UK - The aftermath
On another note: London rocks!
Thursday, June 09, 2005
Friday, June 03, 2005
Why I still use eVC3
eVC3 rocks!
Wednesday, June 01, 2005
SQL CE Console 1.0.17 Released
Monday, May 30, 2005
Pocket Access - V
The CEDB code implements a record containing a collection of properties. Each property maps to a CEPROPVAL structure directly, meaning that the contained value is accessed through bit conversion methods. This is pretty stupid. My early reasoning was that this would make it so much easier to read and write the binary info to and from the database.
When I tried to fill a DataTable object using this approach, I was surprised with the poor speed. At first I blamed the DataGrid but realised that, although it is a slow loader, the bulk of the time was being spent loading the DataTable. I turned my attention to the DataTable and applied all the tricks of the trade to make it load faster. Nah... no way.
But wait! Could it be...? Was it in my code to load the records? Like Michael Abrash used to say: "Assume nothing: Test everything".
Bingo! As any Scorpio worth his salt, I rewrote the whole thing. Upon loading, the record now converts all its properties to boxed values or string references (blobs are just byte arrays, but references as well). These are all packed into an object array which makes it a breeze to load into a DataRow. Buffers are reused to avoid heap fragmentation and the undesirable visit of our friend the garbage collector.
The Suppliers table of the converted Northwind database now gets loaded into a brand-new DataTable object in under 200 ticks on my iPAQ 3850 (I cannot get enough of this machine...), down from almost 600 ticks using the old code. Impressive, huh?
Optimization rocks!
Tuesday, May 24, 2005
SqlCeSpy .NET
Before you deploy the project to the device, make sure that the fssce.dll file is copied to either the target project folder or to the \Windows folder.
Functionality is now limited to browsing the database structure and to renaming tables, something that seems to be deprecated on SQL Mobile. By the way - what does "deprecated" mean? I'm feeling linguistically challenged here: does it mean that the feature is not supported or that it is supported but should not be used? Comments are welcome.
Monday, May 23, 2005
Debugging over WiFi in VS2005
Friday, May 20, 2005
Installing a MAPI filter
Uninstalling calls for a different approach. First, you have to make sure that the DLL is not being used by the SMS-handling process. My answer to this is to shut down the tmail.exe process. Here is some code:
BOOL KillProcess(LPCTSTR pszProcName)
{
HANDLE hSnapshot;
BOOL bRet = FALSE;
hSnapshot = CreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, 0);
if((int)hSnapshot != -1)
{
BOOL bOk;
PROCESSENTRY32 pe;
DWORD dwID = 0;
HANDLE hProcess;
pe.dwSize = sizeof(PROCESSENTRY32);
for(bOk = Process32First(hSnapshot, &pe);
bOk;
bOk = Process32Next(hSnapshot, &pe))
{
if(!wcsicmp(pe.szExeFile, pszProcName))
dwID = pe.th32ProcessID;
}
CloseToolhelp32Snapshot(hSnapshot);
if(dwID != 0)
{
// Kill the process
hProcess = OpenProcess(0, FALSE, dwID);
if(hProcess != INVALID_HANDLE_VALUE)
{
bRet = TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
}
}
}
return bRet;
}
After killing the process, you have to unregister the DLL (sample code here) and finally soft-reset the device.
A sample setup dll project is available on demand.
Tuesday, May 17, 2005
The New Emulator
Monday, May 16, 2005
Native is Faster
A .NET Loss
What is strange is presumably they had tried out .NET 2 which would seem to have a more compelling story than .NET 1, but found that lacking as well.
I wonder why...
Data Port ActiveX
This project shows how the Data Port Component must be used from VB6 and uses new code (the dplib.dll is now on version 1.1.25) that is not yet compatible with the published C# wrapper. The C# wrapper will be updated to comply with the changes and will also be published with the source code (it's a very thin wrapper, really).
To make things work reliably, you should put the dplib.dll in the search path or you risk getting a run-time error from VB...
Sunday, May 15, 2005
Windows Mobile Platform Migration FAQ for Developers
Check out the "Recent Updates" list next to the May 2005 entry: they're about Windows Mobile 5.
Saturday, May 14, 2005
CeDbgView32
Friday, May 13, 2005
Robert Burdick's Blog
Wednesday, May 11, 2005
News for us, Native Fellas
There is also another one, also by Nisham, on Migrating Microsoft eMbedded Visual C++ Projects to Visual Studio 2005 where he discusses the Upgrade Wizard and other issues.
Data Port and VB6
At first it seemed quite straightforward. First, I had to change all __cdecl exported functions to __stdcall. The need for the second change was very hard to detect. The supporting DLL was designed to either be P/Invoked from .NET or to be directly consumed by a C++ application. This meant shielding all database-related functions with COM initialization code. This also meant exit exceptions when the DLL was consumed by VB6, either on the IDE or on the standalone runtime. After discovering this, there was the threading issue. Apparently one cannot safely call back into VB6 from an external thread, something that is safe in C++ and even on the .NET framework (that is how the C# sample works). The final solution is to start a timer to poll the import or export progress data. A kludge, but it works.
The nice thing about the whole process is that I had to instrument the whole code just to make sure there were no memory leaks or invalid pointers. A typical case of spring cleaning...
Monday, May 09, 2005
MEDC UK
Friday, May 06, 2005
Vibrating a Pocket PC
To use this functionality in your applications, use the following code:
extern "C"
{
BOOL NLedGetDeviceInfo(INT nID, PVOID pOutput);
BOOL NLedSetDevice(INT nID, PVOID pOutput);
};
void SetLedStatus(int wLed, int wStatus)
{
NLED_SETTINGS_INFO nsi;
nsi.LedNum = (INT) wLed;
nsi.OffOnBlink = (INT) wStatus;
NLedSetDevice(NLED_SETTINGS_INFO_ID, &nsi);
}
Simple, right?
Wednesday, May 04, 2005
First Customers
Now you know why I write in English.
Sunday, May 01, 2005
New Projects
Meanwhile, I published a small QA on Pocket PC Developer Network based on a year-old piece of code that shows you how to turn on the WiFi radio on a Dell Axim X30. There was some detective work involved in writing this one, especially using Remote Spy++. Fun stuff!