Thursday, April 29, 2010

How to enumerate files on a Windows CE based device from the Desktop

In this article I'd like to demonstrate how to enumerate files on a Windows CE based device from the desktop.

Listing the contents of a directly on a Windows CE from the desktop is something I found to be useful every now and then. It involves using the Remote API and ActiveSync / Windows Mobile Device Center

As usual this will involve a few P/Invokes:

[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern int CeRapiInit();
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern int CeRapiUninit();
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CeFindFirstFile(string lpFileName, ref CE_FIND_DATA lpFindFileData);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern bool CeFindNextFile(IntPtr hFindFile, ref CE_FIND_DATA lpFindFileData);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern bool CeFindClose(IntPtr hFindFile);


We also need the CE_FIND_DATA structure

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CE_FIND_DATA
{
    public int dwFileAttributes;
    public FILETIME ftCreationTime;
    public FILETIME ftLastAccessTime;
    public FILETIME ftLastWriteTime;
    public int nFileSizeHigh;
    public int nFileSizeLow;
    public int dwOID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string cFileName;
};
 
[StructLayout(LayoutKind.Sequential)]
struct FILETIME
{
    public int dwLowDateTime;
    public int dwHighDateTime;
}


The CeRapiInit method has be always called before performing Remote API operations. Once done, the CeRapiUninit must be called. For listing the files in a directory, I use CeFindFirstFile, CeFindNextFile, and CeFindClose. How this works: If the file(s) exists CeFindFirstFile will return a valid handle that can be used for calling CeFindNextFile. After going through all the files CeFindNextFile will return false and a call to CeFindClose needs to be called.

public static string[] GetFiles(string remoteDirectory)
{
    try
    {
        CeRapiInit();
 
        var list = new List<string>();
        var findData = new CE_FIND_DATA();
        var hFindFile = CeFindFirstFile(remoteDirectory + "\\*", ref findData);
 
        if (hFindFile != new IntPtr(-1))
        {
            try
            {
                do
                {
                    if (findData.dwFileAttributes != (int)FileAttributes.Directory)
                        list.Add(findData.cFileName);
                } while (CeFindNextFile(hFindFile, ref findData));
            }
            finally
            {
                CeFindClose(hFindFile);
            }
        }
 
        return list.ToArray();
    }
    finally
    {
        CeRapiUninit();
    }
}


I hope you found this useful.