Friday, April 13, 2007

XML-based device configuration is a powerful mechanism of configuring Windows Mobile 2003 and newer devices. There are four ways to provision your device with new settings:

  1. Call DMProcessConfigXML on device. It can be done from native or managed code.
  2. Build a CAB file containing _setup.xml with provisioning XML and run it on the device
  3. Send the xml configuration via WAP push
  4. Use RapiConfig tool shipped with WIndows Mobile SDK (under Tools)

But what if you wanted to perform configuration from your own, PC-based installer? Granted, you could use method 2 and copy the cab over to the device, then invoke wceload etc. Or you could write your own RAPI dll and use CeRapiInvoke. Or perhaps even launch RapiConfig to do this for you. Fortunately there is an easier way. As one could suspect, RapiConfig does not use a complex approach. Rather it benefits from an undocumented but handy RAPI call that is the desktop version of DMProcessConfigXML. It is exported by ordinal (25) and has the same signature as DMProcessConfigXML. Unlike DMProcessConfigXML, the pointer returned by it in the 3rd parameter needs to be freed using CeRapiFreeBuffer.

#pragma comment(lib, "rapi")

STDAPI CeRapiInit();
STDAPI CeRapiUninit();
STDAPI CeRapiFreeBuffer(LPVOID);
typedef HRESULT (__stdcall *CeProcessConfigType)(LPCWSTR pszConfig, DWORD dwFlags, LPCWSTR* ppszConfigOut);

int _tmain(int argc, _TCHAR* argv[])
{
 if ( argc != 3 )
 {
  _tprintf(_T("Usage: rapiconfig <config file> <out file>\n"));
  return 1;
 }

 CeRapiInit();
 
 CeProcessConfigType CeProcessConfig =
   (CeProcessConfigType)GetProcAddress(GetModuleHandle(_T("rapi.dll")), (LPCSTR)25);

 HANDLE hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
 if ( hFile == INVALID_HANDLE_VALUE )
 {
  _tprintf(_T("Unable to open %s\n"), argv[1]);
  goto Exit;
 }

 DWORD cbConfig = GetFileSize(hFile, NULL);
 BYTE* pBuffer = new BYTE[cbConfig];
 if ( !pBuffer )
  goto Exit;

 ReadFile(hFile, pBuffer, cbConfig, &cbConfig, NULL);
 
 LPCWSTR pOut;
 LPWSTR pIn;
 pIn = new WCHAR[cbConfig + 1];
 ZeroMemory(pIn, (cbConfig+1) * 2);
 mbstowcs(pIn, (LPCSTR)pBuffer, cbConfig);
 HRESULT hr;
 if ( FAILED(hr = CeProcessConfig(pIn, 1, &pOut) ))
 {
  _tprintf(_T("Unable to process: %d\n"), hr);
 }

 HANDLE hFileOut = CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
 if ( hFileOut == INVALID_HANDLE_VALUE )
 {
  _tprintf(_T("Unable to create %s\n"), argv[2]);
  goto Exit;
 }

 WriteFile(hFileOut, pOut, (wcslen(pOut) + 1 )* 2, &cbConfig, NULL);

 CloseHandle(hFileOut);
 CeRapiFreeBuffer((LPVOID)pOut);

Exit:
 if ( hFile != INVALID_HANDLE_VALUE )
  CloseHandle(hFile);

 CeRapiUninit();
 return 0;
}

 

4/13/2007 5:43:46 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [1]  |