Pages

Thursday, November 19, 2009

RAPI: Detect Storage on Windows Mobile Device

This article on EE (edited by Mark Wills)


Progress means simplifying, not complicating.
Bruno Munari

Preface

How to detect the name of the internal storage or an SD-card on Windows Mobile device from the desktop application?
I was surprised to find when found so trivial answer.  If it was in an MSDN article, but it was not very obvious, so many people who tried to solve a problem of the coping a data from the desktop to the mobile devices connected to this desktop.

Enumerative Technique

In case you don’t have time, or you don’t want to spend your time asking people, or you didn’t get the answer from the people your asked, you will try to find a logical solution for your problem yourself and you will use the tools you have in your hands.
So the task is to copy a big amount of data on the storage (SD-card or an internal storage) of the device connected to the desktop. Both, the computer and the device, are running Windows – Mobile and XP or Vista. So let’s use RAPI.
Remote API is a small set of functions that allows to create or remove files, folders, registry keys on the windows mobile device connected to the PC via ActiveSync or Windows Device Mobile Center.
I work with the mobile devices for many years and I know that the storage card inserted to the device in the File Explorer of the device is usually shown as “Storage Card” or “SD”, the internal storage has name “Internal Storage” or “My Flash Disk” or “Resident Flash”.
If my PC application via RAPI will check the existence of one of these folders on the device and copy my data into the detected folder, it will solve my question.
I propose for your attention the following console application demonstrating this approach:
#define WIN32_LEAN_AND_MEAN

#include <rapi2.h>
#pragma comment(lib, "rapi.lib")
#pragma comment(lib, "rapiuuid.lib")

#include <cstdio>

static const DWORD s_nTime = 5000;

const static LPCWSTR s_szFolder = L"RAPITestFolder";
static const int s_nSD = 6;
const static LPCWSTR s_arrSD[s_nSD] =
{
L"Internal Storage",
L"ResidentFlash",
L"My Flash Disk",
L"Storage Card",
L"SD",
L""
};

int main()
{
RAPIINIT riCopy = { 0 };
riCopy.cbSize = sizeof(riCopy);
HRESULT hr = CeRapiInitEx(&riCopy);
if (FAILED(hr))
{
wprintf_s(L"Connection failed\n");
return 0;
}

DWORD nRapiInit = WaitForSingleObject(riCopy.heRapiInit,
s_nTime);

if (WAIT_OBJECT_0 != nRapiInit)
{
wprintf_s(L"Connection failed\n");
return 0;
}

LPCWSTR lpszSD = NULL;
int nCnt = 0;
WCHAR szDir[MAX_PATH];
BOOL bCreated = FALSE;
DWORD nError = 0;
while (nCnt < s_nSD)
{
lpszSD = s_arrSD[nCnt];
ZeroMemory(szDir, sizeof(WCHAR) * MAX_PATH);
_snwprintf_s(szDir, MAX_PATH,
L"\\%s\\%s", lpszSD, s_szFolder);
bCreated = CeCreateDirectory(szDir, NULL);
if (!bCreated)
{
nError = CeGetLastError();
if (nError == ERROR_ALREADY_EXISTS)
bCreated = TRUE;
}
if (bCreated)
{
wprintf_s(L"Found: %s\n", lpszSD);
CeRemoveDirectory(szDir);
}
nCnt++;
}

CeRapiUninit();

return 0;
}
The application screenshot is here:



I don’t know if you see the problem with this method, but our QA made few test on a Windows Mobile phone with pre-installed German support. Now you see – the name of the storage card was “Speicherkarte”. How it will be in French?
In a certain extend this method is acceptable only for the English speaking users. :)

Daemon

In my mobile application I don’t have a problem to detect my database located on the storage card. I look for a folder with the temporary attribute and check if there are my data files. I use the well-known API: FindFirstFile, FindNextFile and FindClose to enumerate all folders on the device.
So I can make an executable, download it on the device and launch it via RAPI (CeCreateProcess). The executable (the daemon) will make a text report that I can upload to the PC and read.
I think I’ve seen this approach implemented. It even worked. But it is so… unprofessional. It looks like a trick made because the laziness of a leak of time or a knowledge – a programmer knows only a few functions in Win32 API and applies them everywhere because he’s lazy enough to open the book and read something new.
Of course there is a more modern way -  make a DLL that will export a special function that can be called by CeRapiInvoke function. The example of such DLL that can be called via RAPI can be found on Native Mobile blog.
More details you can find in:
  1. MSDN: How To Use CeRapiInvoke()
  2. Dr. Dobb’s: The Windows CE 2.0 Remote API. The CeRapiInvoke API is a versatile tool

The Answer

The previous, a complicated enough method, enumerates the folders on the device and this information should be retrieved by a desktop application via RAPI. If I will decide to implement this approach, I will need to add one more project to my solution – the daemon DLL. I will have to sign this DLL in order to avoid the annoying question from Microsoft asking the user if he allows to launch this DLL from an unknown provider. It already smells bad.
Can I enumerate the folders on the device via RAPI?
There is no CeFindFirstFile function. :(
But there is CeFindAllFiles!
I made a console application to check the function:
#define WIN32_LEAN_AND_MEAN

#include <rapi2.h>
#pragma comment(lib, "rapi.lib")
#pragma comment(lib, "rapiuuid.lib")

#include <cstdio>

static const DWORD s_nTime = 5000;

int main()
{
RAPIINIT riCopy = { 0 };
riCopy.cbSize = sizeof(riCopy);
HRESULT hr = CeRapiInitEx(&riCopy);
if (FAILED(hr))
{
wprintf_s(L"Connection failed\n");
return 0;
}

DWORD nRapiInit = WaitForSingleObject(riCopy.heRapiInit,
s_nTime);

if (WAIT_OBJECT_0 != nRapiInit)
{
wprintf_s(L"Connection failed\n");
return 0;
}

LPCE_FIND_DATA pData = NULL;
LPCWSTR lpszPath = L"\\*.*";
DWORD nFlags = FAF_FOLDERS_ONLY | FAF_NAME | FAF_ATTRIBUTES;
DWORD nCount = 0;
BOOL bRetrieved = CeFindAllFiles(lpszPath, nFlags, &nCount, &pData);
if (bRetrieved)
{
DWORD nCnt = 0;
do
{
if ((pData[nCnt].dwFileAttributes &
FILE_ATTRIBUTE_TEMPORARY) == FILE_ATTRIBUTE_TEMPORARY)
wprintf_s(L"Found: \\%s\n", pData[nCnt].cFileName);

nCnt++;
} while (nCnt < nCount);
}
if (pData != NULL)
CeRapiFreeBuffer(pData);

CeRapiUninit();
return 0;
}
With my HTC Touch Pro 2 phone this application gave me this result:
screenshot1

Here is the screenshot from the phone itself:



Disclaimer

I’ve implemented the solution and have tested it on few Windows Mobile and CE devices.  I was writing this article and launched Google to find more information about PInvoke (it was a mistake, I needed CeRapiInvoke). It always happens this way – I found an example in VB that uses exactly the same method of the temporary folder detection on CodeProject:
Display device memory information using P/Invoke
Nothing is new. Soon Google will have “Generate Code” feature proving us with already implemented solutions for our development questions.

No comments:

Post a Comment