CEUtil Helper APIs

When a partnership exists between a desktop computer and a Pocket PC device, ActiveSync makes extensive use of the registry to store information about that device, such as its partnership settings, synchronization objects, and other device-specific configuration information. In addition, the registry on the desktop is used to configure global ActiveSync items, such as new synchronization service providers and file filters that are installed.

This information is stored in two locations in the desktop's registry:

  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services is used to store general ActiveSync configuration information. This key is used to configure connection notifications, add new synchronization objects, set up defaults for installed file filters, and even add additional menu items for your own extensions to the ActiveSync application.

  • HKEY_CURRENT_USER\Software\Microsoft\Windows CE Services is used to store information about both the individual settings and the defaults for a device partnership. This includes how a synchronization provider works with a particular device, what file filters are enabled on it, as well as other general device configuration information for connected devices.

While you can design your desktop-side ActiveSync applications to directly manipulate information based on the current registry locations, the CEUtil helper functions facilitate development by wrapping all of the functionality that is required to navigate through the registry (i.e., it internally uses functions such as RegOpenKeyEx(), RegQueryValueEx(), etc.). Another advantage to using the CEUtil APIs is that you are guaranteed that your application will still work properly even if future versions of ActiveSync change the location in the registry where information is stored.

Note that the CEUtil helper library works only with partnerships that are already established on the local desktop PC. While it does not perform any direct communications with a Pocket PC device, applications will typically use the CEUtil library in conjunction with other APIs, such as the Remote API, to find out specific information about a device. This enables you to get to information even when a device is not currently connected to ActiveSync.

In order to use the CEUtil helper APIs in your applications, you need to include the ceutil.h header file in your project, as well as link with the ceutil.lib library (because this is a desktop library, it is located in the .\wce300\Pocket PC 2002\support\ActiveSync\lib directory in the folder where you have installed Embedded Visual C++).

Finding Partnered Devices

Every device that has a partnership with a desktop has a unique identifier known to ActiveSync as its device ID. As you may have guessed, before you can use any of the functions in the CEUtil helper library, you must first determine the device ID for the partnered device with which you want to work.

To determine the identifier of the device that currently has an established connection to ActiveSync, you can simply call the following function:

DEVICEID CeGetDeviceId(void);

This will return a DEVICEID (which is just a DWORD value) of the currently connected device, or zero if no device is connected. If you receive a value of 0xFFFFFFFF, the connected device is a guest device.

Therefore, to find out the ID of a device that is currently connected, you can do the following:

// Get the connected device ID
DEVICEID devId = 0;
devId = CeGetDeviceId();

if(devId == 0xFFFFFFFF) {
   OutputDebugString(TEXT("Guest device connected."));
   return 0;
} else
   printf("The connected device id is: 0x%x", devId);

To retrieve the ID of the device that is currently selected for display in ActiveSync, you can use the CeGetSelectedDeviceId() function, which is prototyped as follows:

DEVICEID CeGetSelectedDeviceId(void);

This will have the same return values as CeGetDeviceId().

Finally, you can use the CeSvcEnumProfiles() function to enumerate all of the devices that currently have partnerships established with the desktop PC. The function is defined as follows:

HRESULT CeSvcEnumProfiles(PHCESVC phSvc, DWORD
   lProfileIndex, PDWORD plProfile);

The first parameter, phSvc, is a pointer to an enumeration handle that will be used in additional calls to CeSvcEnumProfiles(). In order to use the function properly, you must set the variable that phSvr points to with a NULL value the first time that you use it. When CeSvcEnumProfiles() returns successfully, you will have a valid handle for future enumeration calls.

The next parameter, lProfileIndex, is the index value for the enumeration and should be set to 0 the first time the function is called. This value should be incremented every time you call CeSvcEnumProfiles().

Finally, the plProfile parameter is a pointer to a DWORD value that will receive the device ID when the function returns a value of NOERROR. This is the ID that can be used for making additional calls into the CEUtil helper functions.

If CeSvcEnumProfiles() returns a value of ERROR_NO_MORE_ITEMS, then you have reached the end of the partnered device profile enumeration.

There are two ways to properly close and release the enumeration handle that you are using: You can continue to call CeSvcEnumProfiles() until you receive an ERROR_NO_MORE_ITEMS error, or you can use the CeSvcClose() function (which is described in the next section) to close the handle. Either way, you want to make sure that the function properly closes the handle and frees the memory associated with the enumeration.

The following code shows how you can use the CeSvcEnumProfiles() function to enumerate through all of the partnerships that have been established with the desktop:

HCESVC hCeDevices = NULL;
LONG lDeviceProfile = 0;
DEVICEID dwDeviceId = 0;
HRESULT hr = S_OK;

do {
   hr = CeSvcEnumProfiles(&hCeDevices, lDeviceProfile,
      &dwDeviceId);
   if(FAILED(hr))
      break;

   // Ok, we have the device ID, do something
   // ...

   // Increment the profile ID for enumeration
   lDeviceProfile++;
   dwDeviceId = 0;
} while(1);

Working with a Partnered Device's Configuration Information

Now that you have determined the device ID that you want to work with, you can go ahead and open the registry key associated with the device by using the CeSvcOpen() function, which is defined as follows:

HRESULT CeSvcOpen(UINT uSvc, LPTSTR pszPath, BOOL fCreate,
  PHCESVC phSvc);

The first parameter, uSvc, specifies the registry key that you are interested in. Table 9.8 describes the predefined values that can be used.

What you pass in for the uSvc flag will determine what you should use for the second parameter, pszPath. If you have passed in the CESVC_DEVICEX flag, pszPath should point to a DWORD value that specifies the device ID for which you are interested in opening the registry. This is the same device ID that you were returned from a previous call to CeGetSelectedDeviceId() or CeSvcEnumProfiles().

If you use any of the other flags, then the pszPath parameter will expect a pointer to a null-terminated string that contains that path to a registry subkey value under the root that was specified by the uSvc flag.

The next parameter, fCreate, should be set to TRUE if you want CeSvcOpen() to create the subkey you have specified if it does not already exist in the desktop registry. If this is set to FALSE, the function will fail if it cannot open the key.

The phSvc parameter should point to a CESVC (which is actually an HKEY registry handle) value that will receive a handle to the opened registry key. This handle should be used for further function calls to the CEUtil helper APIs, and should be closed by using the CeSvcClose() function to free any resources associated with it.

Table 9.8. Device Configuration Registry Flags

uSvc Flag

Registry Key Root

Description

CESVC_ROOT_MACHINE

HKLM\Software\Microsoft\Windows CE Services

ActiveSync root

CESVC_FILTERS

HKLM\Software\Microsoft\Windows CE Services\Filters

Filters root

CESVC_CUSTOM_MENUS

HKLM\Software\Microsoft\Windows CE Services\Custom Menus

Custom Menu root

CESVC_SERVICES_COMMON

HKLM\Software\Microsoft\Windows CE Services\Services

Services root

CESVC_SYNC_COMMON

HKLM\Software\Microsoft\Windows CE Services\Services\Synchronization

Synchronization Provider root

CESVC_ROOT_USER

HKCU\Software\Microsoft\Windows CE Services\

ActiveSync root for current user

CESVC_DEVICES

HKCU\Software\Microsoft\Windows CE Services\Partners

Device registration root

CESVC_DEVICEX

HKCU\Software\Microsoft\Windows CE Services\Partners\(device ID)\

Root key for a specific device

CESVC_DEVICE_SELECTED

HKCU\Software\Microsoft\Windows CE Services\Partners\(selected device ID)

Root key for the selected device

CESVC_SERVICES_USER

HKCU\Software\Microsoft\Windows CE Services\Partners\(device ID)\Services

Root key for services on a specific device

CESVC_SYNC

HKCU\Software\Microsoft\Windows CE Services\Partners\(device id)\Services\Synchronization

Root key for synchronizationsettings for a specific device

The following example gets a handle for the currently selected device:

DEVICEID devId = 0;
HRESULT hr = S_OK;
HCESVC hSvcRoot = NULL;

devId = CeGetSelectedDeviceId();
if(devId == 0)
   return 0;

hr = CeSvcOpen(CESVC_DEVICEX, (LPTSTR)devId, FALSE,
   &hSvcRoot);
if(FAILED(hr))
   return 0;

// Do something here with the handle
// ...

CeSvcClose(hSvcRoot);

Once you have an open handle to the device partnership registry, you can open any additional subkeys by using the CeSvcOpenEx() function:

HRESULT CeSvcOpenEx(HCESVC hSvcRoot, LPTSTR pszPath, BOOL
   fCreate, PHCESVC phSvc);

The first parameter is the handle to the root key that was opened by your previous call to the CeSvcOpen() function. This is followed by pszPath, which should point to a null-terminated string of the sub-key that you are interested in opening. The next parameter, fCreate, should be set to TRUE if you want the function to create the key if it does not already exist. Finally, the open handle to the registry sub-key will be returned in the variable to which the phSvc parameter points.

To open a registry sub-key value, you could do the following:

HCESVC hSvcKey = NULL;
hr = CeSvcOpenEx(hSvcRoot, TEXT("Filters"), FALSE,
  &hSvcKey);
if(FAILED(hr)) {
   CeSvcClose(hSvcRoot);
   return 0;
}

// Do something with the Filters sub-key
// ...

CeSvcClose(hSvcKey);
CeSvcClose(hSvcRoot);

Remember that the handles returned by CeSvcOpen() and CeSvcOpenEx() are standard registry key handles, and need to be closed properly in order to free any memory or resources that are allocated to the handle.

To close the handle and free any resources that are allocated, you can use the following function:

HRESULT CeSvcClose(HCESVC hSvc);

The only parameter that CeSvcClose() takes is a handle to the registry entry that was opened from your call to CeSvcOpen() or CeSvcOpenEx().

Getting and Setting Values

Six functions are provided in the CEUtil helper library to read and write values in the registry. All of the functions take the same first two parameters: hSvc and pszValName. The hSvc parameter is the handle to the registry sub-key that was opened with CeSvcOpen() or CeSvcOpenEx() and is followed by the pszValName parameter. This should point to a null-terminated string specifying the name of the value that you are interested in.

String values are handled with the following functions:

HRESULT CeSvcGetString(HCESVC hSvc, LPCTSTR pszValName,
   LPTSTR pszVal, DWORD cbVal);
HRESULT CeSvcSetString(HCESVC hSvc, LPCTSTR pszValName,
   LPCTSTR pszVal);

When reading string values, the pszVal parameter should point to a buffer that will receive the registry data. The cbVal parameter specifies the size of the buffer to which pszVal points.

Setting string values requires only the pszVal parameter, which points to a null-terminated value that contains the string data. DWORD values are handled with the following:

HRESULT CeSvcGetDword(HCESVC hSvc, LPCTSTR pszValName,
   LPDWORD pdwVal);
HRESULT CeSvcSetDword(HCESVC hSvc, LPCTSTR pszValName,
   DWORD dwVal);

To get a DWORD value, the pdwVal parameter should point to a DWORD variable that will receive the registry data. Setting a DWORD value only requires you to pass in a DWORD via the dwVal parameter.

Finally, binary registry values are read and set by using the following:

HRESULT CeSvcGetBinary(HCESVC hSvc, LPCTSTR pszValName,
   LPBYTE lpData, LPDWORD pcbVal);
HRESULT CeSvcSetBinary(HCESVC hSvc, LPCTSTR pszValName,
   LPBYTE pszVal, DWORD cbVal);

Reading binary data from the registry requires a buffer, lpData. The size of the buffer should be set with the variable to which pcbVal points. When the function returns, pcbVal will be set with the actual number of bytes that were copied to the lpData buffer.

To set a binary value, lpData should point to a buffer containing the data to be written to the registry, with the size of the buffer, in bytes, specified by the cbVal parameter.

To delete a value from the registry, you can use the CeSvcDeleteVal() function, which is defined as follows:

HRESULT CeSvcDeleteVal(HCESVC hSvc, LPCTSTR pszValName);

The only parameters that the function requires are a handle to the registry sub-key and the value name to delete.

Adding a Custom Menu Item to ActiveSync

If you have developed an application on the desktop that communicates with a Pocket PC device through the Remote API, a good way to integrate it with CE Services is by adding an additional menu option to the ActiveSync Tools menu (see Figure 9.3).

Figure 9.3. Extending the ActiveSync Tools menu

graphics/09fig03.gif

To add additional menu items to ActiveSync, you need to add a new sub-key value for your menu option under the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\CustomMenus. You also need to set several additional values for your applications. Table 9.9 describes the values that you need to configure.

Table 9.9. Registry Values for Extending the ActiveSync Manager

Name

Type

Description

DisplayName

String

The name that will be displayed as the menu option. You can use an ampersand (&) to specify a menu hotkey (e.g., "&Backup").

Command

String

The full path and executable name of the application to launch when the command is selected.

CommandArguments

String

Any command-line arguments that you want to have passed to your application.

StatusHelp

String

The text that will be displayed on the status bar when the item is selected in the menu.

Version

DWORD

Application version. This value should be set to 0x00020000.

The following code sample shows how you can add your own menu items to ActiveSync by using the CEUtil helper functions:

// Add a Custom Menu entry
DEVICEID devId = 0;
HRESULT hr = S_OK;
HCESVC hCustomMenu = NULL;

// Open/create the new key
hr = CeSvcOpen(CESVC_CUSTOM_MENUS, TEXT("MyMenuItem"), TRUE,
  &hCustomMenu);
if(FAILED(hr))
   return 0;

// Add the details
DWORD dwVersion = 0x00020000;
CeSvcSetString(hCustomMenu, TEXT("DisplayName"), TEXT("&My
  Custom Menu"));
CeSvcSetString(hCustomMenu, TEXT("Command"),
   TEXT("c:\\program files\\myapp\\myexe.exe"));
CeSvcSetString(hCustomMenu, TEXT("CommandArguments"),
   TEXT("-dSomeArguments"));
CeSvcSetString(hCustomMenu, TEXT("StatusHelp"),
   TEXT("This is the details for my custom menu"));
CeSvcSetDword(hCustomMenu, TEXT("Version"), dwVersion);

// Clean up
CeSvcClose(hCustomMenu);