File Conversion (File Filters)

File Filters are COM-based objects that reside on the desktop and that ActiveSync uses when converting files between a PC and mobile file format. When a file is copied to or from a connected Pocket PC device via the Mobile Device folder, ActiveSync checks whether a file filter is available for the type of file that is being transferred. If one exists, ActiveSync will then load the filter into memory and call into its methods for converting the file. For example, if you copy an Excel spreadsheet from the desktop to a device, ActiveSync will load in the Excel file filter and convert it to the Pocket Excel format, which can be read by a Pocket PC device. Likewise, if you copy a Pocket PC Excel file from a device to the desktop, it will be converted to the standard desktop Excel format.

Note that only files copied through ActiveSync will use the File Filters and be converted. A file filter lives on the desktop, so if your device downloads a file directly from a Web site or FTP server, it will not go through ActiveSync and thus will not be converted.

The interfaces that you use to create your own filter objects are defined in the replfilt.h header file.

Registering a File Filter

You need to configure your file filter in several locations in the desktop registry in order for ActiveSync to be able to use it when copying files to or from a device.

First, you need to register the file extension by adding an entry in the HKEY_CLASSES_ROOT\{New File Extension} key. The key only needs to have its default value set to the class name for the extension, as shown in Table 9.10.

Table 9.10. Registry Settings for a File Filter Extension

Key\Value Name

Description

Default

{Extension Class Name}

Under the HKEY_CLASSES_ROOT\{Extension Class Name} key, set the values shown in Table 9.11.

Table 9.11. Registry Settings for a File Filter Type

Key\Value Name

Description

Default

Description of the file type that will be displayed in the Type column in Windows Explorer

DefaultIcon

Filename or index of the icon for this file type

Second, you have to register the COM object that implements your file filter, under the HKEY_CLASSES_ROOT\CLSID\{GUID} key. The object needs to have the keys and values shown in Table 9.12 set.

Table 9.12. Registry Settings for the File Filter COM Object

Key\Value Name

Description

Default

Description of the file type. This will be displayed in the Type column of Windows Explorer.

DefaultIcon

Filename or index of the icon for this file type.

InProcServer32

Full filename and path to the DLL that the object is in.

InProcServer32\ThreadingModel

Should be set to "Apartment".

PegasusFilter\Description

String to be displayed when the conversion dialog box is shown.

PegasusFilter\NewExtension

Extension for the newly converted file.

PegasusFilter\Import

If it exists, then the conversion type is for importing from computer to device; otherwise, assume export.

PegasusFilter\HasOptions

If it exists, then the ICeFileFilter::FilterOption() method is supported.

Figure 9.4 shows the registry settings for a COM file filter.

Figure 9.4. Registry settings for a file filter COM object

graphics/09fig04.gif

Now that you have registered the COM object for your file filter, you need to let ActiveSync know about it. To do so, you will have to create a new entry under the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services\Filters\{New File Extension} key. Table 9.13 describes the values that need to be configured.

Table 9.13. File Filter Registry Settings

Key\Value Name

Description

DefaultImport

{GUID} or "Binary Copy"

DefaultExport

{GUID} or "Binary Copy"

InstalledFilters\{GUID}

One or more GUIDs for various filters for the file type

The DefaultImport and DefaultExport values are used to determine which objects are used when copying a file to or from the device. These values can be set either to the GUID of the COM object that implements the conversion interface, or to the string "Binary Copy". Setting either one to perform a "Binary Copy" tells ActiveSync that no conversion is necessary when a file of this type is copied. For example, a text (.txt) file will use "Binary Copy" for both its import and export modes because no conversion is necessary.

Because a file (such as a Word document) can have one or more filters associated with it, you can use the InstalledFilters key to specify the GUID for each filter that is installed.

Finally, you can register a file filter with an individual device by setting the HKEY_CURRENT_USER\Software\Microsoft\Windows CE Services\Partners\{Partner ID}\Filters\{New File Extension} key. The device partnership registry setting takes the same entries as the global key, as defined in Table 9.14.

Table 9.14. Registry Settings for a File Filter on a Specific Partner ID

Key\Value Name

Description

DefaultImport

{GUID} or "Binary Copy"

DefaultExport

{GUID} or "Binary Copy"

InstalledFilters\{GUID}

One or more GUIDs for various filters for the file type

Figure 9.5 shows the registry values for a file filter.

Figure 9.5. Registry settings for a file filter for a partnered device

graphics/09fig05.gif

To get a better idea of all of the settings that are required for a file filter, let's take a look at an export of the keys required for the Pocket Word filter:

 // Registry entries for the file type // [HKEY_CLASSES_ROOT\.pwd] @="pwdfile" [HKEY_CLASSES_ROOT\pwdfile] @="Pocket Word Document-H/PC" [HKEY_CLASSES_ROOT\pwdfile\DefaultIcon] @="C:\\Program Files\\Microsoft ActiveSync\\minshell.dll,-    2004" [HKEY_CLASSES_ROOT\pwdfile\Shell\Open\command] @="\"D:\\OfficeXP\\Office10\\WINWORD.EXE\" /n /dde \"%1\"" // // Registry entries for the COM object // [HKEY_CLASSES_ROOT\CLSID\    {4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}] @="Word 97/2000" [HKEY_CLASSES_ROOT\CLSID\    {4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}\DefaultIcon] @="C:\\Program Files\\Microsoft ActiveSync\\pwdcnv.dll,0" [HKEY_CLASSES_ROOT\CLSID\    {4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}\InProcServer32] @="C:\\Program Files\\Microsoft ActiveSync\\pwdcnv.dll" "ThreadingModel"="Apartment" [HKEY_CLASSES_ROOT\CLSID\    {4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}\PegasusFilter] "Description"="Word 97/2000 Document" "NewExtension"="doc" // // Registry entries for the File Filter // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE    Services\Filters\.pwd] "DefaultImport"="Binary Copy" "DefaultExport"="{4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE    Services\Filters\.pwd\InstalledFilters] "{4D3E2CF9-9B22-11D0-82A3-    00AA00C267C1}"="Software\\Microsoft\\Windows CE    Services\\Filters\\PWordData\\Export" "{4D3E2CFA-9B22-11D0-82A3-    00AA00C267C1}"="Software\\Microsoft\\Windows CE    Services\\Filters\\PWordData\\Export" "{4D3E2CFB-9B22-11D0-82A3-00AA00C267C1}"="" "{4D3E2CFC-9B22-11D0-82A3-00AA00C267C1}"="" // // Registry entries for a Partnered Device // [HKEY_CURRENT_USER\Software\Microsoft\Windows CE    Services\Partners\11111111\Filters\.pwd] "DefaultImport"="Binary Copy" "DefaultExport"="{4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}" [HKEY_CURRENT_USER\Software\Microsoft\Windows    CEServices\Partners\11111111\    Filters\.pwd\InstalledFilters] "{4D3E2CF9-9B22-11D0-82A3-00AA00C267C1}"="    Software\\Microsoft\\Windows CE    Services\\Filters\\PWordData\\Export" "{4D3E2CFA-9B22-11D0-82A3-00AA00C267C1}"="    Software\\Microsoft\\Windows CE    Services\\Filters\\PWordData\\Export" "{4D3E2CFB-9B22-11D0-82A3-00AA00C267C1}"="" "{4D3E2CFC-9B22-11D0-82A3-00AA00C267C1}"="" 

As you can see, a lot of information needs to be set up in the registry before your filter can be used by ActiveSync. Fortunately, you can also use the CEUtil helper APIs (discussed in the previous section) to help register a custom filter.

Using the CEUtil Helper APIs to Register a File Filter

To help you register your file filter using the CEUtil library functions, you can do the following:

 // Register a custom filter DEVICEID devId = 0; HRESULT hr = S_OK; HCESVC hCustomFilter = NULL, hInsFilters = NULL,    hCeDevices = NULL, hDevRoot = NULL, hCustomFilterRoot = NULL; // Set up the new filter information TCHAR tchExtension[32] = TEXT("\0"); TCHAR tchFilterGUID[128] = TEXT("\0"); wsprintf(tchExtension, TEXT(".txl")); wsprintf(tchFilterGUID,    TEXT("{85A31804-3251-4d13-9405-AA4C86F1C58A}")); // Open/create the new filter key // First, create the generic ActiveSync keys hr = CeSvcOpen(CESVC_FILTERS, tchExtension, TRUE,   &hCustomFilter); if(FAILED(hr))    return 0; // Set up the entry values CeSvcSetString(hCustomFilter, TEXT("DefaultImport"),   tchFilterGUID); CeSvcSetString(hCustomFilter, TEXT("DefaultExport"),    TEXT("Binary Copy")); hr = CeSvcOpenEx(hCustomFilter, TEXT("InstalledFilters"),    TRUE, &hInsFilters); if(FAILED(hr)) {    CeSvcClose(hCustomFilter);    return -1; } CeSvcSetString(hInsFilters, tchFilterGUID, TEXT("")); CeSvcClose(hInsFilters); CeSvcClose(hCustomFilter); // Next, add it to each partnered device LONG lDeviceProfile = 0; DEVICEID dwDeviceId = 0; do {    hr = CeSvcEnumProfiles(&hCeDevices, lDeviceProfile,       &dwDeviceId);    if(FAILED(hr))       break;    // Ok, we have the device ID, register the filter for it    // Start by opening up the device key    hr = CeSvcOpen(CESVC_DEVICEX, (LPTSTR)dwDeviceId, FALSE,       &hDevRoot);    if(FAILED(hr))    break;    // Next, get the handle to the Filters key    hr = CeSvcOpenEx(hDevRoot, TEXT("Filters"), FALSE,       &hCustomFilterRoot);    if(FAILED(hr)) {       CeSvcClose(hDevRoot);       break; } // Create the new key hr = CeSvcOpenEx(hCustomFilterRoot, tchExtension, TRUE,    &hCustomFilter); if(FAILED(hr)) {    CeSvcClose(hCustomFilterRoot);        CeSvcClose(hDevRoot);        break;     }     // Set up the entry values     CeSvcSetString(hCustomFilter, TEXT("DefaultImport"),        tchFilterGUID);     CeSvcSetString(hCustomFilter, TEXT("DefaultExport"),        TEXT("Binary Copy"));     hr = CeSvcOpenEx(hCustomFilter, TEXT("InstalledFilters"),        TRUE, &hInsFilters);     if(FAILED(hr)) {        CeSvcClose(hCustomFilter);        CeSvcClose(hCustomFilterRoot);        CeSvcClose(hDevRoot);        return -1;     }     CeSvcSetString(hInsFilters, tchFilterGUID, TEXT(""));     // Close the handles     CeSvcClose(hInsFilters);     CeSvcClose(hCustomFilter);     CeSvcClose(hCustomFilterRoot);     CeSvcClose(hDevRoot);    // Increment the profile ID for enumeration    lDeviceProfile++;    dwDeviceId = 0; } while(1); 

File Filter Interfaces Overview

Now that you've seen how a file filter is configured on the desktop, let's see what is required to build your own custom filter. ActiveSync uses three COM interfaces when performing a conversion of a file between a desktop and a device:

  • The ICeFileFilter interface is used to perform the actual file conversion. It is the only interface that you are required to implement.

  • The ICeFileFilterOptions interface is an optional interface that is used to set additional filter options.

  • A pointer to an ICeFileFilterSite interface will be passed to you when the ICeFileFilter::NextConvertFile() method is called. This interface is already implemented by ActiveSync, and contains functions to open and close the files involved with the conversion, as well as to send status information back to ActiveSync.

Implementing ICeFileFilter

The ICeFileFilter interface is the only interface you are required to implement in order for ActiveSync to call into your object when converting a file from one format to another. ICeFileFilter has three methods (outside of the standard IUnknown methods), described in Table 9.15.

Table 9.15. ICEFileFilter Methods

Method

Description

FilterOptions()

Used to display a dialog box for any filter-specific conversion options.

FormatMessage()

Formats a message from an error code.

NextConvertFile()

This is where the bulk of the conversion work is done.

 

This method is called whenever ActiveSync needs to convert a file.

The NextConvertFile() method performs the actual file conversion when your filter is called by ActiveSync. Note that it will be called repeatedly (so that you can create multiple destination files) until you return a value of ERROR_NO_MORE_ITEMS. The function is defined as follows:

 HRESULT ICeFileFilter::NextConvertFile(int nConversion,    CFF_CONVERTINFO *pci, CFF_SOURCEFILE *psf,    CFF_DESTINATIONFILE *pdf, BOOL *pbCancel, CF_ERROR *perr); 

The first parameter, nConversion, is incremented every time the function is called for the same source file, enabling you to create multiple destination files. ICeFileFilter::NextConvertFile() will continue to be called with the same source file until you return the ERROR_NO_MORE_VALUES return code.

Next, the pci parameter is a pointer to a CFF_CONVERTINFO structure that you can use to get some general information about the conversion. The structure also contains the pointer to an ICeFileFilterSite object that you will use to open and close the source and destination files involved in the conversion, as well as pass information back to ActiveSync.

The CFF_CONVERTINFO structure looks like the following:

 typedef struct tagCFF_CONVERTINFO {    BOOL bImport;    HWND hwndParent;    BOOL bYesToAll;    ICeFileFilterSite *pffs; } CFF_CONVERTINFO; 

The bImport field specifies the direction of the file conversion. If this is set to TRUE, then you are converting a file from the desktop to the Pocket PC device. If FALSE, then you are exporting a file from the device to the PC. The hwndParent field is a handle to a parent window that you can use for any dialog boxes or messages that need to be displayed during the conversion process. The bYesToAll field is used in conjunction with the ActiveSync dialog box that is presented to an end user when overwriting files; if it is set to TRUE, then the Yes To All button will be included on the warning dialog box that is displayed. The last field, pffs, points to the ICeFileFilterSite interface, which you can use to open and close files.

The next parameter, psf, points to a CFF_SOURCEFILE structure that contains information about the source file being used for the conversion. It contains details about the file's source path, file size, creation time, and so on, and is defined as follows:

 typedef struct tagCFF_SOURCEFILE {    TCHAR szFullpath[_MAX_PATH];    TCHAR szPath[_MAX_PATH];    TCHAR szFilename[_MAX_FNAME];    TCHAR szExtension[_MAX_EXT];    DWORD cbSize;    FILETIME ftCreated;    FILETIME ftModified; } CFF_SOURCEFILE; 

The pdf parameter points to a CFF_DESTINATIONFILE structure, which contains details about the file you are converting to. The CFF_DESTINATIONFILE structure is defined as follows:

 typedef struct tagCFF_DESTINATIONFILE {    TCHAR szFullpath[_MAX_PATH];    TCHAR szPath[_MAX_PATH];    TCHAR szFilename[_MAX_FNAME];    TCHAR szExtension[_MAX_EXT]; } CFF_DESTINATIONFILE; 

Next, pbCancel will be set to TRUE if the user presses the Cancel button at any time while the conversion process is taking place. Because this value can be changed by another thread while a file conversion is in progress, you need to check the value regularly to determine whether the user has canceled out. If pbCancel is set to TRUE, then you need to return the ERROR_CANCELLED return code from the function.

The last parameter, perr, should be set with an error code if something goes wrong during the conversion. This error will then be sent to the ICeFileFilter::FormatMessage() function for processing. If you do use perr, you should set the return value to E_FAIL.

NOTE:

If you need to call any of the Remote API (RAPI) functions while performing a conversion, you do not need to call the CeRapiInit() or CeRapiInitEx() functions, as a connection to the device is already established when ICeFileFilter::NextConvertFile() is called. Calling either of the RAPI initialization functions from NextConvertFile() will result in an error.


The ICeFileFilter::FilterOptions() method provides you with a handle to the parent window, which you can use for a dialog box when the Options button is pressed in ActiveSync. The function has the following prototype:

 HRESULT ICeFileFilter::FilterOptions(HWND hwndParent); 

The only parameter is the handle to the ActiveSync parent window. Remember that the Options button is enabled in ActiveSync only if the HasOptions value has been set in the HKEY_CLASSES_ROOT\CLSID\{GUID}\PegasusFilter registry key.

The last method that you need to implement in ICeFileFilter is the FormatMessage() function. FormatMessage() is used to convert an error code to a string that ActiveSync can display. It is defined as follows:

 HRESULT ICeFileFilter::FormatMessage(DWORD dwFlags, DWORD    dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize,    va_list *Arguments, DWORD *pcb); 

The parameters that ICeFileFilter::FormatMessage() uses are almost exactly the same as what the standard WinCE API's FormatMessage() function takes. In fact, unless you are specifying some user-defined errors, you can use this function to process the request, as shown in the following example:

 STDMETHODIMP CTextFileFilter::FormatMessage(DWORD dwFlags,    DWORD dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer,    DWORD nSize, va_list *Arguments, DWORD *pcb) {    DWORD dwMsgLength = 0;    dwMsgLength = ::FormatMessage(dwFlags, 0, dwMessageId,       dwLanguageId, lpBuffer, nSize, Arguments);    if(dwMsgLength)       *pcb = dwMsgLength;    else       return E_FAIL;    return S_OK; } 

The only additional parameter you need to worry about is pcb, which should be set to the length of the string that you are returning from the function.

Implementing ICeFileFilterOptions

The ICeFileFilterOptions interface is optional, i.e., its implementation is not required in order for a filter to function properly. The only method that is currently supported is the SetFilterOptions() function, which is defined as follows:

 HRESULT ICeFileFilterOptions::SetFilterOptions   (CFF_CONVERTOPTIONS *pco); 

The only parameter that the function takes is pco, which is a pointer to a CFF_CONVERTOPTIONS structure. The structure defines whether or not ActiveSync should prevent any modal user interface interaction during the conversion process. The structure is defined as follows:

 typedef struct tagCFF_CONVERTOPTIONS {    ULONG cbSize;    BOOL bNoModalUI; } CFF_CONVERTOPTIONS; 

The cbSize field should be set to the size of CFF_CONVERTOPTIONS, and bNoModalUI should be set to TRUE if you want to prevent modal interaction.

The ICeFileFilterSite Interface

The ICeFileFilterSite interface is used to open and close files and communicate the status of a file conversion back to ActiveSync. A pointer to the interface is passed when the ICeFileFilter::NextConvertFile() method is called. Table 9.16 lists the methods that are implemented in the ICeFileFilterSite object.

Let's take a look at the available functions you can call in ICeFileFilterSite during the conversion process in more detail.

To open a source file for conversion, you can use the OpenSourceFile() function, which is defined as follows:

 HRESULT ICeFileFilterSite::OpenSourceFile(int   nHowToOpenFile, LPVOID *ppObj); 

The first parameter, nHowToOpenFile, must be set to the constant CF_OPENFLAT. After calling the function, the ppObj parameter will receive a pointer to an IStream COM object that can be used to read from the source file. You must remember to call the ICeFileFilterSite::CloseSourceFile() function when you are finished reading information from the stream in order to release it properly.

Table 9.16. ICeFileFilterSite Methods

Method

Description

CloseDestinationFile()

Closes the destination file used for conversion

CloseSourceFile()

Closes the source file used for conversion

OpenDestinationFile()

Opens a destination file for conversion

OpenSourceFile()

Opens a source file for conversion

ReportLoss()

Reports any information about discarded data during the conversion

ReportProgress()

Reports the progress of a file conversion to ActiveSync

The ICeFileFilterSite::OpenDestinationFile() function is defined as follows:

 HRESULT ICeFileFilterSite::OpenDestinationFile(int nHowToOpenFile,    LPCTSTR pszFullpath, LPVOID *ppObj); 

The first parameter, nHowToOpenFile, must be set with the constant CF_OPENFLAT. Next, pszFullPath should point to a null-terminated string that should be used for the full name and path of the destination file. This parameter is optional, and can be set to NULL to use the default destination name. The last parameter, ppObj, will point to an IStream object for the destination file once the function returns successfully. You can then use this object to output the converted file data. Remember that you must call the ICeFileFilterSite::CloseDestinationFile() function to properly close the file when you are finished converting it.

To close the source file, you can call the following:

 HRESULT ICeFileFilterSite::CloseSourceFile(LPUNKNOWN pObj); 

The only parameter the function requires is the pointer to the IStream interface that you received from your call to ICeFileFilterSite::OpenSourceFile().

To close the destination file, you can use the following function:

 HRESULT ICeFileFilterSite::CloseDestinationFile(BOOL bKeepFile,    LPUNKNOWN pObj); 

The first parameter, bKeepFile, determines whether or not ActiveSync should delete the destination file. If this is set to TRUE, then the file that you have been writing to will be saved when it is closed. You should set this to FALSE when an error occurs during the conversion process and you do not want to keep the file you have been writing to. The pObj parameter should point to the IStream object that was returned from your original call to ICeFileFileter::OpenDestinationFile().

The ICeFileFilterSite::ReportProgress() function is used to send the ActiveSync window a message regarding how much of the conversion process has been completed. The function is defined as follows:

 HRESULT ICeFileFilterSite::ReportProgress(UINT nPercent); 

The only parameter that the function takes is an unsigned-integer value that specifies the percentage of the file that has been converted. This value is then displayed on the progress bar in the ActiveSync window as the conversion occurs.

If you have intentionally decided to discard any data during a file conversion (for example, removing comments from a text file), you can call the ICeFileFilterSite::ReportLoss() function to explain the data loss to the end user:

 HRESULT ICeFileFilterSite::ReportLoss(DWORD dw, LPCTSTR psz,   va_list args); 

The first parameter, dw, specifies a DWORD value that will be passed to the ICeFileFilter::FormatMessage() method and should contain an error code to be formatted by the function. If you would rather set the description immediately, use the psz parameter to point to a null-terminated string that describes the error. You cannot, however, use both the dw and psz parameters at the same time. If you set one, the other should be set to a NULL value. The last parameter, args, can be used as a pointer to an array of values that will be inserted into the formatted string.

Writing a Custom File Filter

Now that we've examined the interfaces that are involved with the file conversion process, let's examine how the conversion process works, and how you can actually build your own file filter.

Whenever a file is dragged into or from the Mobile Device folder, ActiveSync checks the registry for the file type based on its extension. If a filter is properly registered for the file type, ActiveSync will then call into your COM object using the IUnknown::QueryInterface() function to get a pointer to your ICeFileFilterOptions and ICeFileFilter objects. If ICeFileFilterOptions exists, it will then call into the ICeFileFilterOptions::SetFilterOptions() function to set the modality of the conversion operation.

The conversion process begins when ActiveSync starts to call into your ICeFileFilter::NextConvertFile() method for each file you are working with.

The following example builds a simple file filter that converts a text file in lowercase into a text file in uppercase. Your lowercase text file will have a .txl extension, and your destination uppercase file will have the extension .txu.

For this example, let's put the code for registering your filter (including using the CEUtil helper library) into the DllRegisterServer() function that you export from your DLL (as well as the code to remove it in DllUnregisterServer()). As with any COM object, these functions are called whenever regsvr32.exe is used to register a COM server object. After the filter has registered, the registry will look as follows:

 // Registry entries for file type [HKEY_CLASSES_ROOT\.txl] @="textlower" [HKEY_CLASSES_ROOT\textlower] @="Lowercase text file" // // COM object registry settings // [HKEY_CLASSES_ROOT\CLSID\   {087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}] @="Lowercase text file" [HKEY_CLASSES_ROOT\CLSID\   {087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}\DefaultIcon] @="MyTextFilter.dll, 0" [HKEY_CLASSES_ROOT\CLSID\   {087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}\InProcServer32] @="MyTextFilter.dll" "ThreadingModel"="Apartment" [HKEY_CLASSES_ROOT\CLSID\   {087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}\PegasusFilter] "Description"="Lowercase text file" "NewExtension"="txu" "Import"="" "HasOptions"="" // // Registry entries for the file filter // [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE   Services\Filters\.txl] "DefaultImport"="{087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE   Services\Filters\.txl\InstalledFilters] "{087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}"="" // // Registry entries for a partnered device // [HKEY_CURRENT_USER\Software\Microsoft\Windows CE   Services\Partners\009d05d6\Filters\.txl] "DefaultImport"="{087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}" [HKEY_CURRENT_USER\Software\Microsoft\Windows CE   Services\Partners\009d05d6\Filters\.txl\InstalledFilters] "{087CCBD5-BAB3-46c0-9AE6-E8CC68397FB8}"="" 

Besides handling all of the standard COM "goo" (I use the term goo to specify all of the code for your class factory and IUnknown interface), you need to implement the ICeFileFilter methods. The most interesting part of your object is how you handle the ICeFileFilter::NextConvertFile() function:

 STDMETHODIMP CTextFileFilter::NextConvertFile(int nConversion,    CFF_CONVERTINFO *pci, CFF_SOURCEFILE *psf,    CFF_DESTINATIONFILE *pdf,    volatile BOOL *pbCancel, CF_ERROR *perr) {    ICeFileFilterSite *pFilterSite = NULL;    IStream *pSourceStream = NULL;    IStream *pDestStream = NULL;    HRESULT hr = S_OK;    // We need to do only one conversion    if(nConversion != 0)       return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);    // Since this is an import filter, make sure we're    // copying from the desktop to device    if(!pci->bImport)       return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);    pFilterSite = pci->pffs;    if(!pFilterSite)       return E_FAIL;    // Create buffers to use to read in    LPBYTE lpBuffer = NULL;    lpBuffer = (LPBYTE)LocalAlloc(LPTR, BUFFER_SIZE+1);    if(!lpBuffer)       return E_OUTOFMEMORY;    // Open the source file    hr = pFilterSite->OpenSourceFile(PF_OPENFLAT,       (LPVOID*)&pSourceStream);    if(FAILED(hr)) {       LocalFree(lpBuffer);       return E_FAIL;    }    // Open the destination file    hr = pFilterSite->OpenDestinationFile(PF_OPENFLAT, NULL,       (LPVOID *)&pDestStream);    if(FAILED(hr)) {       LocalFree(lpBuffer);       pFilterSite->CloseSourceFile(pSourceStream);       return E_FAIL;    }    // Read in a chunk from the source    ULONG ulBytesRead = 0, ulBytesWritten = 0,       ulTotalBytes = 0;    pSourceStream->Read(lpBuffer, BUFFER_SIZE, &ulBytesRead);    while(ulBytesRead > 0) {       // Check to see if it was cancelled       if(*pbCancel == TRUE) {          LocalFree(lpBuffer);          pFilterSite->CloseSourceFile(pSourceStream);          pFilterSite->CloseDestinationFile(FALSE,              pDestStream);          return HRESULT_FROM_WIN32 (ERROR_CANCELLED);       }       // Convert it       _strupr((char *)lpBuffer);       // Write it       pDestStream->Write(lpBuffer, ulBytesRead,          &ulBytesWritten);       ulTotalBytes += ulBytesWritten;       // Update progress       pFilterSite->ReportProgress(ulTotalBytes/psf->cbSize          * 100);       // Clean up       ulBytesRead = 0;       ulBytesWritten = 0;       memset(lpBuffer, 0, BUFFER_SIZE+1);       // Read next blob       hr = pSourceStream->Read(lpBuffer, BUFFER_SIZE,          &ulBytesRead);       if(FAILED(hr))             break;       }       // Close files and clean up       pFilterSite->CloseSourceFile(pSourceStream);       pFilterSite->CloseDestinationFile(TRUE, pDestStream);       LocalFree(lpBuffer);       return NOERROR; } 

To complete your filter, you also need to implement the ICeFileFilter::FormatMessage() and ICeFileFilter::FilterOptions() functions:

 STDMETHODIMP CTextFileFilter::FilterOptions       (HWND hwndParent) {    MessageBox(hwndParent, TEXT("Filter Options go here"),       TEXT("Filter Options"), MB_OK|MB_ICONINFORMATION);    return S_OK; } STDMETHODIMP CTextFileFilter::FormatMessage(DWORD dwFlags,   DWORD dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer, DWORD   nSize, va_list *Arguments, DWORD *pcb) {    DWORD dwMsgLength = 0;    dwMsgLength = ::FormatMessage(dwFlags, 0, dwMessageId,       dwLanguageId, lpBuffer, nSize, Arguments);    if(dwMsgLength)       *pcb = dwMsgLength;    else       return E_FAIL;    return S_OK; } 


Pocket PC Network Programming
Pocket PC Network Programming
ISBN: 0321133528
EAN: 2147483647
Year: 2005
Pages: 90

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net