Copying files from Desktop to a CE device


 

So I have this problem, where I need to create a tool that creates a folder with a bunch of files on the PC and then copies all these files over to a CE device. The method of choice here was via ActiveSync over USB.  One of the design goals was to be able to copy these files to a number of devices simultaneously. However, research revealed that this is not really possible since, activesync only supports one active connection at a time. So much for the great idea.


So now we are faced with this problem of copying files over to the device over ActiveSync. Since the app had a lot of GUI, I created that in C#. The .NET framework exposes excellent API for file manipulation based on paths. The problem with ActiveSync connections is when the device is cradled, there is no drive letter associated with the connection. So all device paths are then \My Documents\ or \Program Files\ etc.


This creates a problem as far as the File manipulation API in System.IO. Since these methods accept relative as well as fully qualified paths, any path like the above, would be treated as a relative path and not a fully qualified path, thereby causing a lot of grief.


The only way to get past this so far has been to use RAPI.


RAPI stands for Remote Application Programming Interface, and was created ages ago with the first wave of activesync and windows mobile products.


RAPI has some documentation on MSDN at: http://msdn.microsoft.com/en-us/library/aa458022.aspx


However since RAPI is a native API, almost all the documentation is geared towards native implementation.


After searching the intertubes for a suitable solution, I couldn’t find one that was recent, and that was guaranteed to work.


Below is the solution:


 


 


using System;
using 
System.Threading;
using 
System.Runtime.InteropServices;
using 
COMTYPES System.Runtime.InteropServices.ComTypes;
using 
System.Collections.Generic;
using 
System.Text;
using 
System.IO;
using 
Logger Microsoft.MobileDevices.Tools.Utilities.Logger;


namespace 
Microsoft.MobileDevices.Tools.RAPILibrary
{
    
public class RAPILibrary
    {
        
# region RAPI EXTERN
        
// -------------------------------------------------------
        // RAPI.DLL Definitions
        // -------------------------------------------------------

        
public const int MAX_PATH 260;
        public const uint 
MAXDWORD 0xFFFFFFFF;
        public const uint 
S_OK 0;

        public struct 
RAPIINIT
        {
            
public int cbSize;
            public 
IntPtr heRapiInit;
            public int 
hrRapiInit;
        
};

        
// From WINBASE.H -- for CeCreateFile
        
public enum ACCESS : uint
        
{
            READ 
0x80000000,
            WRITE 
0x40000000,
        }
        
// From WINBASE.H -- for CeCreateFile
        
public enum SHARE
        {
            READ 
0x00000001,
            WRITE 
0x00000002,
        }
        
// From WINBASE.H -- for CeCreateFile
        
public enum ACTION
        {
            CREATE_NEW 
1,
            CREATE_ALWAYS 
2,
            OPEN_EXISTING 
3,
            OPEN_ALWAYS 
4,
            TRUNCATE_EXISTING 
5
        
}

         
// From WINBASE.H -- for CeCreateFile
        
public enum FILEFLAGATT : uint
        
{
            ATTRIBUTE_READONLY 
0x00000001,
            ATTRIBUTE_HIDDEN 
0x00000002,
            ATTRIBUTE_SYSTEM 
0x00000004,
            ATTRIBUTE_DIRECTORY 
0x00000010,
            ATTRIBUTE_ARCHIVE 
0x00000020,
            ATTRIBUTE_INROM 
0x00000040,
            ATTRIBUTE_ENCRYPTED 
0x00000040,
            ATTRIBUTE_NORMAL 
0x00000080,
            ATTRIBUTE_TEMPORARY 
0x00000100,
            ATTRIBUTE_SPARSE_FILE 
0x00000200,
            ATTRIBUTE_REPARSE_POINT 
0x00000400,
            ATTRIBUTE_COMPRESSED 
0x00000800,
            ATTRIBUTE_OFFLINE 
0x00001000,
            ATTRIBUTE_ROMSTATICREF 
0x00001000,
            ATTRIBUTE_NOT_CONTENT_INDEXED 
0x00002000,
            ATTRIBUTE_ROMMODULE 
0x00002000,
            FLAG_WRITE_THROUGH 
0x80000000,
            FLAG_OVERLAPPED 
0x40000000,
            FLAG_NO_BUFFERING 
0x20000000,
            FLAG_RANDOM_ACCESS 
0x10000000,
            FLAG_SEQUENTIAL_SCAN 
0x08000000,
            FLAG_DELETE_ON_CLOSE 
0x04000000,
            FLAG_BACKUP_SEMANTICS 
0x02000000,
            FLAG_POSIX_SEMANTICS 
0x01000000,
        }


        
/// <summary>
        /// Closes handle passed in
        /// </summary>
        /// <param name="hObject">handle to be closed</param>
        /// <returns></returns>
        
[DllImport("rapi.DLL", CharSet CharSet.Unicode)]
        
public static extern int CeCloseHandle(IntPtr hObject);

        
/// <summary>
        /// Creates file on device
        /// </summary>
        /// <param name="lpFileName">path of file</param>
        /// <param name="dwDesiredAccess">Read/Write</param>
        /// <param name="dwShareMode">Share mode</param>
        /// <param name="Res1"></param>
        /// <param name="dwCreationDisposition">File creation options</param>
        /// <param name="dwFlagsAndAttributes">Flags and attributes</param>
        /// <param name="Res2"></param>
        /// <returns>pointer to the created file</returns>
        
[DllImport("rapi.DLL", CharSet CharSet.Unicode)]
        
public static extern IntPtr CeCreateFile(string lpFileName, ACCESS dwDesiredAccess, SHARE dwShareMode, int Res1, ACTION dwCreationDisposition, FILEFLAGATT dwFlagsAndAttributes, int Res2);

        
/// <summary>
        /// Reads the file
        /// </summary>
        /// <param name="hFile">handle to the file</param>
        /// <param name="lpBuffer">buffer to copy data to</param>
        /// <param name="nNumberOfBytesToRead">file size to read</param>
        /// <param name="lpNumberOfBytesRead">file size read</param>
        /// <param name="Reserved"></param>
        /// <returns></returns>
        
[DllImport("rapi.DLL", CharSet CharSet.Unicode)]
        
public static extern int CeReadFile(IntPtr hFile, IntPtr lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, int Reserved);

        
/// <summary>
        /// Writes file to device
        /// </summary>
        /// <param name="hFile">handle of file to write to</param>
        /// <param name="lpBuffer">data to write</param>
        /// <param name="nNumberOfBytesToWrite">length of buffer</param>
        /// <param name="lpNumberOfBytesWritten">length of file on device</param>
        /// <param name="Reserved"></param>
        /// <returns></returns>
        
[DllImport("rapi.DLL", CharSet CharSet.Unicode)]
        
public static extern int CeWriteFile(IntPtr hFile, IntPtr lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, int Reserved);

        
/// <summary>
        /// Copies file from desktop to device
        /// </summary>
        /// <param name="deskFile">Full path of file on the desktop including filename</param>
        /// <param name="devFilePath">Path of file name on the device. '\' denotes root </param>
        
public static void CopyFiletoDevice(string deskFile, string devFilePath)
        {
            
try
            
{
                
int numTries=0;
                
// Verify file exists on desktop
                
FileInfo fInfo = new FileInfo(deskFile);
                if 
(fInfo.Exists)
                {
                    IntPtr devFileHandle
;
                    
// create a file on the device
                    
do // force valid handle in the loop
                    
{
                        devFileHandle 
CeCreateFile(devFilePath, ACCESS.READ | ACCESS.WRITE, SHARE.READ | SHARE.WRITE, 0, ACTION.CREATE_ALWAYS, FILEFLAGATT.ATTRIBUTE_NORMAL, 0);
                        if 
(devFileHandle.ToInt32() < 0)
                        {
                            CeCloseHandle(devFileHandle)
;
                         
}
                    } 
while (devFileHandle.ToInt32() < 0);


                    
// read file from desktop into a byte buffer
                    
Byte[] buffer;
                    
ReadFileAsBinary(deskFile, out buffer);

                    int 
numBytesWritten 0;
                    unsafe
                    
{
                        
fixed(byte* bufferPtr buffer)
                        {   
                            
int retVal CeWriteFile(devFileHandle, (IntPtr)(bufferPtr), (int)fInfo.Length, ref numBytesWritten, 0);
                            if 
(retVal == 0)
                            {
                                 
throw new IOException("RAPILibrary:CopyFileToDevice() Error Writing file on device. " + CeGetLastError().ToString());
                            
}
                        }
                    }

                    
// close handles
                    
CeCloseHandle(devFileHandle);
                
}
                
else
                
{
                    Logger.WriteLog(
"RAPILibrary:CopyFileToDevice() File does not exist on desktop: " + deskFile);
                
}

            }
            
catch (IOException ioEx)
            {
                Logger.WriteLog(ioEx.Message)
;
            
}
            
catch (Exception ex)
            {
                Logger.WriteLog(ex.ToString())
;
            
}
        }

        
/// <summary>
        /// Reads file and returns byte[]
        /// </summary>
        /// <param name="path">Full path of file</param>
        /// <param name="buffer">byte array for file data</param>
        
private static void ReadFileAsBinary(string path, out Byte[] buffer)
        {
            Logger.WriteLog(
"RAPILibrary:ReadFileAsBinary() Reading the file " + path);
            
FileInfo fInfo = new FileInfo(path);
            long 
fileLength fInfo.Length;
            
buffer = new Byte[fileLength];
            
FileStream fs fInfo.OpenRead();
            
fs.Read(buffer, 0, (int)fileLength);
            
fs.Close();
            
Logger.WriteLog("RAPILibrary:ReadFileAsBinary() Exit");
        
}
    }
}

Skip to main content