Detect Storage on Windows Mobile Device

Published:
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 got this question, when I was working on a PC application copying a huge amount of data on a storage card inserted into a Windows Mobile device (a phone in this case).  I found the answer, very simple answer, but on my way I tested a few interesting ideas and I'd like to show them in this article.

Remote API

So the task is to copy a big amount of data to a 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 one to create or remove files, folders, registry keys on the windows mobile device connected to the PC via ActiveSync or Windows Device Mobile Center.

Storage cards inserted into a device are usually shown in File Explorer as a folder named as "Storage Card" or "SD". Internal storage may have name "Internal Storage" or "My Flash Disk" or "Resident Flash".

My first idea was to check the existance of these names on the device and copy the data on the first detected storage. Have a look at 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 = 5;
                      const static LPCWSTR s_arrSD[s_nSD] =
                      {
                      	L"Internal Storage",
                      	L"ResidentFlash",
                      	L"My Flash Disk",
                      	L"Storage Card",
                      	L"SD"
                      };
                      
                      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;
                      }

Open in new window


The application screenshot is here:

 Enumarating

I don't know if you can see the problem with this method. Our QA did a few tests on Windows Mobile phones with pre-installed German support. Now hopefully you will see the problem. The name of the storage card was "Speicherkarte". Consider how it will be in French, or other languages ?

To a certain extent this method is acceptable only for the English speaking users. :)

A Mobile Application

Many years ago I make applications for Windows Mobile and CE devices and 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). This 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 simple... It looks like a trick made because of the laziness, or a lack of time (or knowledge) - a programmer knows only 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!

You can find a detailed explanation of this function on CodeGuru:
Nancy Nicolaisen. "Using RAPI to Find Remote Files"

I made a console application to check this 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;
                      }

Open in new window


With my HTC Touch Pro 2 phone this application gave me this result:

CeFindAllFiles.

Here is the screenshot from the phone itself:

 File Explorer
Compiling with RAPI

You can compile the source code from this article only if you have Microsft Windows Mobile SDK installed. In the SDK folder in ActiveSync\inc folder you can find rapi2.h file and rapi.lib and rapiuuid.lib file in in ActiveSync\Lib folder.
In your project you can add "$(ProgramFiles)\Windows Mobile 6 SDK\Activesync\inc" to Additional Include Directories and "$(ProgramFiles)\Windows Mobile 6 SDK\Activesync\lib" to Additional Library Directories of the project settings.

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
1
7,809 Views

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.