A customer came to me for assistance in formatting an SD card with TFAT. I did a quick look in the control panel code to find how it formats disks as TFAT and told the customer to use FormatVolume() and set the FORMAT_OPTIONS dwFlags FATUTIL_FORMAT_TFAT flag.   
I assumed that it was that easy and left the customer to write the code. A few weeks later the customer came back to me and said that they just couldn’t get FormatVolume() to work, let alone format the disk as TFAT. So this time I dug deeper and write some code to test it out before getting back to the customer. The following is what I figured out including the code to format a disk as TFAT.
What went wrong? When I first looked at how to format TFAT, I looked to see what happens when the Format button is selected. What I didn’t look for is what has to happen before the Format button is selectable. It turns out that before the Format button is selectable, the partition must first be dismounted.
__declspec(dllexport) BOOL APIENTRY FormatDiskTFAT( TCHAR *StoreName, TCHAR *PartitionName )
{
                HANDLE hStore;
                HANDLE hPartition;
 
                hStore = OpenStore(StoreName);          
 
                if( INVALID_HANDLE_VALUE != hStore )
                {
                                hPartition = OpenPartition( hStore, PartitionName );
                                if( INVALID_HANDLE_VALUE != hPartition )
                                {
                                                if( DismountPartition(hPartition) )
                                                {   
                                                                FORMAT_OPTIONS fo;
 
                                                                // Get formating options
                                                                fo.dwClusSize = 4 * 1024;
                                                                fo.dwFatVersion = 32;
                                                                fo.dwNumFats = 2;
                                                                fo.dwRootEntries = 512;
 
                                                                fo.dwFlags = 0;
                                                                //fo.dwFlags |= FATUTIL_FULL_FORMAT;
                                                                fo.dwFlags |= FATUTIL_FORMAT_TFAT;
 
                                                                if( ERROR_SUCCESS != pfnFormatVolume( hPartition, NULL, &fo, HandleProgress, HandleMessage ))
                                                                                RETAILMSG( 1, (TEXT("Format Failed\n")));
                                                                MountPartition(hPartition);
                                                }
                                                else
                                                                RETAILMSG( 1, (TEXT("Failed to dismount Partition\n")));
 
                                                CloseHandle( hPartition );
                                }
                                else
                                                RETAILMSG(1, (TEXT("OpenPartition failed\n")));
 
                                CloseHandle(hStore);
                }
                else
                                RETAILMSG(1, (TEXT("OpenSelectedStore failed\n")));
 
                return TRUE;
}
FormatDiskTFAT() takes two strings as arguments:
·         StoreName which is the szDeviceName in the STOREINFO structure returned from FindFirstStore()/FindNextStore() or the szStoreName in the CE_VOLUME_INFO structure returned from CeGetVolumeInfo(). The value of the string will be somethings like “DSK1:”.
·         PartitionName which is the szPartionName in PARTINFO and CE_VOLUME_INFO returned by FindFirstPartition()/FindNextPartition() and CeGetVolumeInfo(). The value of the string will be something like “PART00”.
FormatDiskTFAT() uses the strings to open the store and the partition. Then it dismounts the partition, formats the disk and re-mounts the partition.
I choose the function signature because I was thinking about using FindFirstStore()/FindNextStore(), so the device name and the partition name would be readily available. As an afterthought, I decided that it might be handy to have a function that simply takes the folder name of the disk, so I came up with FormatDiskTFATEx():
__declspec(dllexport) BOOL APIENTRY FormatDiskTFATEx( TCHAR *FolderName )
{
                CE_VOLUME_INFO VolInfo;
                VolInfo.cbSize = sizeof( CE_VOLUME_INFO );
 
                CeGetVolumeInfo(FolderName, CeVolumeInfoLevelStandard, &VolInfo );
                return FormatDiskTFAT( VolInfo.szStoreName, VolInfo.szPartitionName );
}
FormatDiskTFATEx() takes one string as an argument which is the name of the disk folder. This will be something like “Storage Card” or “\Storage Card” depending on how you obtain the string.
One of the interesting problems that I ran into is that building this for Windows CE 5.0 (I have not tested yet with CE 6.0) the call to CeGetVolumeInfo() causes a linker error because CeGetVolumeInfoW() does not exist. The solution to this problem is to undefine CeGetVolumeInfo(). So I added:
#ifdef CeGetVolumeInfo
#undef CeGetVolumeInfo
#endif
Then next problem that I needed to solve is that while I can easily compile this code using Platform Builder, my customer who is using an SDK that I provided cannot. The problem is that FATUTIL.DLL is included in the OS, but FATUTIL.LIB is not included in the SDK and FormatVolume() is in FATUTIL. It would be easy enough to create a new SDK and release it, but that may be overkill for one function. So the solution that I came up with is to use LoadLibrary() and GetProcAddress().   I am including these functions in a DLL, so I did the mapping in DllEntry():
HMODULE hFatUtil = NULL;
DWORD (*pfnFormatVolume)(HANDLE hVolume,void *pdi,FORMAT_OPTIONS *pfo,void *pfnProgress,void *pfnMessage);
 
static void *GetFormatVolume(HMODULE *hFatUtil);
 
 
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD ul_reason_for_call,
                       LPVOID lpReserved
                                                                                 )
{
                switch( ul_reason_for_call )
                {
                                case DLL_PROCESS_ATTACH:
                                                pfnFormatVolume = GetFormatVolume(&hFatUtil);
                                                break;
                                case DLL_PROCESS_DETACH:
                                                FreeLibrary(hFatUtil);
                                                break;
                }
    return TRUE;
}
The last problem is that FORMAT_OPTIONS needed by FormatVolume() is also not available in the SDK, or more accuratly the header file that they are included in doesn’t compile cleanly, so I included them in the C code:
#define FATUTIL_FULL_FORMAT                 0x1
#define FATUTIL_FORMAT_TFAT                 0x2
 
typedef struct _FORMAT_OPTIONS {
    DWORD   dwClusSize;
    DWORD   dwRootEntries;
    DWORD   dwFatVersion;
    DWORD   dwNumFats;
    DWORD   dwFlags;
} FORMAT_OPTIONS;
In my next article (see Windows CE: C# Application to Format TFAT), I will show how to use these functions from C#. As for using them from C/C++, you may want to review Windows CE: Displaying Disk Information for example code that uses FindFirstStore()/FindNextStore() and FindFirstPartition()/FindNextPartition() to enumerate the mounted disks.
NOTE 1: In FormatDiskTFAT() I have commented out the line “//fo.dwFlags |= FATUTIL_FULL_FORMAT;” because in my testing with both this code and the Storage Manager control panel applet, the full format would not complete.
NOTE 2: I made a conscious choice to hardcode the formatting options in FormatDiskTFAT() because while these options are interesting, they just shouldn’t be user options in an embedded system. I could have pushed them up to the caller function, but that opens the door for a confusing user interface.
Copyright © 2010 – Bruce Eitman
All Rights Reserved