Windows CE Services
As you probably very well aware, a whole bunch of data synchronization methods and techniques are bound around one common basis: Windows CE Services. This term combines ActiveSync custom service providers, file filters, connection notifications, and the CeUtil API. An upcoming article will be dedicated to the last two topics. ActiveSync service provider’s development will be described later. Most of the discussed topics below are related to the desktop side only.
So, you can start with CeUtil. This DLL exports about a dozen functions that allow you to manage ActiveSync’s Registry values. Obviously enough, you can do the same job using regular RegXXX calls. A benefit you get with CeUtil API is that it provides an abstract layer over ActiveSync registry settings, so you don’t need to bother with their exact names, which in turn may vary a bit between different OS versions and localizations. Thus far, here is what you can use:
HRESULT __stdcall CeSvcOpen( UINT uSvc, LPTSTR pszPath, BOOL fCreate, PHCESVC phSvc ); HRESULT __stdcall CeSvcOpenEx( HCESVC hSvcRoot, LPTSTR pszPath, BOOL fCreate, PHCESVC phSvc ); HRESULT __stdcall CeSvcClose( HCESVC hSvc ); HRESULT __stdcall CeSvcGetString( HCESVC hSvc, LPCTSTR pszValName, LPTSTR pszVal, DWORD cbVal ); HRESULT __stdcall CeSvcSetString( HCESVC hSvc, LPCTSTR pszValName, LPCTSTR pszVal ); HRESULT __stdcall CeSvcGetDword( HCESVC hSvc, LPCTSTR pszValName, LPDWORD pdwVal ); HRESULT __stdcall CeSvcSetDword( HCESVC hSvc, LPCTSTR pszValName, DWORD dwVal ); HRESULT __stdcall CeSvcGetBinary( HCESVC hSvc, LPCTSTR pszValName, LPBYTE pszVal, LPDWORD pcbVal ); HRESULT __stdcall CeSvcSetBinary( HCESVC hSvc, LPCTSTR pszValName, LPBYTE pszVal, DWORD cbVal ); HRESULT __stdcall CeSvcDeleteVal( HCESVC hSvc, LPCTSTR pszValName ); DEVICEID __stdcall CeGetDeviceId( void ); DEVICEID __stdcall CeGetSelectedDeviceId( void ); HRESULT __stdcall CeSvcEnumProfiles(PHCESVC phSvc, DWORD lProfileIndex, PDWORD plProfile);
As you see, there are three logical functional groups:
- To access Registry keys
- To manipulate values
- To obtain device info
CeUtil offers you a lot of predefined keys touching different aspects of ActiveSync programming. You will find an excerpt from the SDK help in the following table. But prior to this, let me highlight the following point. CeUtil Registry keys are mirrored at several locations. There is “MACHINE_ROOT”:
HKEY_LOCAL_MACHINESoftwareMicrosoftWindows CE Services—Used to address settings in general;
There is also “LOCAL_ROOT”:
HKEY_CURRENT_USERSoftwareMicrosoftWindows CE Services—Addresses specific device configuration.
So, you have some freedom to configure Windows CE Services at different levels. Having said this all, here is a table:
Registry Key | CEUTIL Constants | Description |
---|---|---|
MACHINE_ROOT | CESVC_ROOT_MACHINE | Windows CE Services root hierarchy under HKEY_LOCAL_MACHINE |
MACHINE_ROOTFilters | CESVC_FILTERS | The Filters Registry root. New filters should register themselves here |
MACHINE_ROOTCustomMenus | CESVC_CUSTOM_MENUS | Custom menus Registry root |
MACHINE_ROOTServices | CESVC_SERVICES_COMMON | Services Registry root |
MACHINE_ROOTServices Synchronization | CESVC_SYNC_COMMON | Synchronization Registry root. New sync services should register here |
LOCAL_ROOT | CESVC_ROOT_USER | Windows CE Services root hierarchy under HKEY_CURRENT_USER |
LOCAL_ROOTPartners | CESVC_DEVICES | Individual device subkeys are created under this key |
LOCAL_ROOTPartners<device id> | CESVC_DEVICEX | Particular device root subkey |
LOCAL_ROOTPartners<selected device id> | CESVC_DEVICE_SELECTED | Root subkey for the currently selected device |
LOCAL_ROOTPartners<device id>Services | CESVC_SERVICES_USER | Root Services subkey for a particular device |
LOCAL_ROOTPartners<device id>ServicesSynchronization | CESVC_SYNC | Synchronization reg root for a particular device |
Thus far, you’re getting access to desired Registry key by calling CeSvcOpen. A deeper hierarchy is achieved by subsequent CeSvcOpenEx calls. By playing with their parameters, you can control key access and creation policy.
I will skip a second group of functions dealing with key values themselves due to their simplicity and move forward to a device-related one. It isn’t complicated in usage but nevertheless may be pretty helpfull. A code snippet below shows just one simple case of it:
HCESVC hSvc; DWORD dwProfile = 0; DWORD dwProfileInx = 0; TCHAR szDevName[255] = { 0 } , szDevType[255] = { 0 }; HRESULT hr = S_OK; HCESVC hDevice; do { hr = CeSvcEnumProfiles(&hSvc,dwProfileInx,&dwProfile); if ( hr == ERROR_NO_MORE_ITEMS ) break; if ( hr != S_OK ) { // handle an error as needed break; } if ( S_OK == CeSvcOpen(CESVC_DEVICEX, (LPTSTR)dwProfile, FALSE, &hDevice) ) { CeSvcGetString(hDevice,_T("DisplayName"),szDevName, sizeof(szDevName)/sizeof(TCHAR)); CeSvcGetString(hDevice,_T("DeviceType"),szDevType, sizeof(szDevType)/sizeof(TCHAR)); // do whatever you want with this info CeSvcClose(hDevice); } dwProfileInx++; } while ( hr == S_OK );
This is actually all the story about CeUtil. Use it wisely, and leave it in peace for now.
Getting Your Application AutoStarted
If all you need is to launch some application when a PDA is connected or disconnected, this is really a piece-of-cake task. ActiveSync uses two keys in the Registry to keep a list of all programs to run upon the above events:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows CE Services AutoStartOnConnect HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows CE Services AutoStartOnDisconnect
So, to execute your own application, simply add a value as in the following sample:
; Here, put the full path to your app, possibly with a command ; line to designate that it was launched on a device connection [HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows CE Services AutoStartOnConnect] YourCompanyApp=notepad.exe
In this sample, notepad.exe will be called when the PDA connects successfully, either through its cradle or remotely. In your real environment, you then may recognize such a device by its ID and handle it somehow specifically.
Using Notifications to Control the Device’s Connection
For more complicated cases, you have another opportunity—connection notifications. Windows helps you here and implements IDccMan, one of two interfaces responsible for this task. You in turn have to implement one of additional interfaces, IDccManSink or IDccManSink2. They are identical, but IDccManSink2 also supports the IPv6 protocol.
Take a look at the usual working flow. It is really simple:
- Create an instance of IDccMan interface
- Call IDccMan::Advise method to get notified about connections
- Call IDccMan::Unadvise method when you no longer need notifications
Now let’s take a closer look at IDccManSink declarations:
DECLARE_INTERFACE_ (IDccManSink, IUnknown) { STDMETHOD(OnLogIpAddr) (THIS_ DWORD dwIpAddr) PURE; STDMETHOD(OnLogTerminated) (THIS) PURE; STDMETHOD(OnLogActive) (THIS) PURE; STDMETHOD(OnLogInactive) (THIS) PURE; STDMETHOD(OnLogAnswered) (THIS) PURE; STDMETHOD(OnLogListen) (THIS) PURE; STDMETHOD(OnLogDisconnection) (THIS) PURE; STDMETHOD(OnLogError) (THIS) PURE; }; DECLARE_INTERFACE_ (IDccManSink2, IDccManSink) { STDMETHOD(OnLogIpAddrEx) (THIS_ const SOCKADDR_STORAGE* pIpAddr) PURE; };
You will find a short description of above methods in the following table:
Method | Description |
---|---|
OnLogListen | Indicates that the connection manager listens for incoming connections |
OnLogTerminated | Indicates that the connection manager has been shut down |
OnLogAnswered | Indicates that the connection manager has detected the communications interface |
OnLogActive | Indicates that a connection is established and fully operational |
OnLogInactive | Indicates a disconnection, or disconnected state |
OnLogDisconnection | Indicates that the connection manager has terminated the connection. It may be sent several seconds after actual disconnection |
OnLogError | Indicates that the connection manager failed to start communications |
OnLogIpAddr | Indicates that an IP address has been established for communications |
OnLogIpAddrEx | Indicates that an IP address has been established for communications, IDccManSink2 only |
Now we can code our IDccManSink implementation. I left it pretty simple just to shape the idea. Full source you will find in article’s sample project. Here I want to place only main loop:
CoInitialize(0); IDccMan *pIDccMan = NULL; HRESULT hr = CoCreateInstance(CLSID_DccMan,NULL,CLSCTX_SERVER, IID_IDccMan,(void**)&pIDccMan); if ( SUCCEEDED(hr) ) { CDevComDccSync DccSync; DWORD dwContext = 0; pIDccMan->Advise(&DccSync,&dwContext); SetConsoleTitle("Developer.com IDccManSink Sample"); while (!kbhit()); pIDccMan->Unadvise(dwContext); pIDccMan->Release(); } else { cout << "Failed to create IDccMan object: " << std::hex << hr << std::dec << endl; } CoUninitialize();
Our implementation of IDccManSink interface simply fakes a regular COM object. If you need to develop separate component which will be used by other applications, then you will have to add all standard COM stuff like ClassFactory and so forth. The sample’s output is presented in Figure 1:
As you see, the connection manager informs your application about various phases of connection process. If you are going to use IP-based communications, you have PDA’s address handy. Moreover, if WinCE device is connected via a cradle, the IP will indicate “localhost;” in other words, 127.0.0.1, so you will always know how device is connected.
Conclusion
This article has discussed simple yet useful techniques of managing a connectivity between Windows CE device and a desktop computer. I still haven’t touched data synchronization itself, but anyway even now your application can use; for example, RAPI or WinSock to pass data back and forth. I’ll describe more complicated data exchange and synchronization methods in coming articles.
Download
Download the accompanying code’s zip file here
About the Author
Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.