Copying files from the Desktop to the Device using .NET

Someone asked me recently how to copy files from the desktop to the device after reading my old article called How to copy files from the Device to the Desktop using .NET. Well here’s how to do it:

First, we’ll need our P/Invokes to rapi.dll

[DllImport("rapi.dll")]
static extern int CeRapiInit();
 
[DllImport("rapi.dll")]
static extern int CeRapiUninit();
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CeCreateFile(
  string lpFileName,
  uint dwDesiredAccess,
  int dwShareMode,
  int lpSecurityAttributes,
  int dwCreationDisposition,
  int dwFlagsAndAttributes,
  int hTemplateFile);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
internal static extern int CeWriteFile(
    IntPtr hFile,
    byte[] lpBuffer,
    int nNumberOfbytesToWrite,
    ref int lpNumberOfbytesWritten,
    int lpOverlapped);
 
[DllImport("rapi.dll", CharSet = CharSet.Unicode)]
internal static extern int CeSetFileTime(
    IntPtr hFile,
    ref long lpCreationTime,
    ref long lpLastAccessTime,
    ref long lpLastWriteTime);
 
const int BUFFER_SIZE = 1024 * 5; // 5k transfer buffer
const int CREATE_ALWAYS = 2;
const int ERROR_SUCCESS = 0;
const int FILE_ATTRIBUTE_NORMAL = 0x80;
const uint GENERIC_WRITE = 0x40000000;
const int INVALID_HANDLE_VALUE = -1;

Now let’s wrap all those in a method called CopyToDevice(string localFile, string remoteFile). The localFile is the file located on the desktop and the remoteFile is the destination filename on the device.

void CopyToDevice(string localFile, string remoteFile)
{
    var rapi = CeRapiInit() == ERROR_SUCCESS;
    if (!rapi)
        return;
 
    try
    {
        var filePtr = CeCreateFile(remoteFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (filePtr == new IntPtr(INVALID_HANDLE_VALUE))
            return;
 
        using (var localFileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read))
        {
            var byteswritten = 0;
            var position = 0;
            var buffer = new byte[BUFFER_SIZE];
 
            var bytesread = localFileStream.Read(buffer, position, BUFFER_SIZE);
            while (bytesread > 0)
            {
                position += bytesread;
                if (CeWriteFile(filePtr, buffer, bytesread, ref byteswritten, 0) == ERROR_SUCCESS)
                    return;
 
                try
                {
                    bytesread = localFileStream.Read(buffer, 0, BUFFER_SIZE);
                }
                catch
                {
                    bytesread = 0;
                }
            }
        }
    }
    finally
    {
        CeRapiUninit();
    }
}

To use the code above you will have to know the full path of the file on the desktop.
I hope you found this useful.

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.

Copying Files from the Device to the Desktop using .NET

Recently, I’ve been working on a tool that creates a backup of a SQL Server Compact Edition database on the device to the desktop. To accomplish this, I used the Remote API (RAPI). Unfortunately, the Remote API is not yet available in managed code. In this article I would like to demonstrate how to P/Invoke methods from the Remote API for copying files from the device to the desktop using managed code.

First, we’ll need some P/Invokes to rapi.dll

[DllImport(“rapi.dll”)]
static extern int CeRapiInit();

[DllImport(“rapi.dll”)]
static extern int CeRapiUninit();

[DllImport(“rapi.dll”)]
static extern int CeCloseHandle(IntPtr hObject);

[DllImport(“rapi.dll”, CharSet = CharSet.Unicode)]
static extern IntPtr CeCreateFile(
  string lpFileName,
  uint dwDesiredAccess,
  int dwShareMode,
  int lpSecurityAttributes,
  int dwCreationDisposition,
  int dwFlagsAndAttributes,
  int hTemplateFile);

[DllImport(“rapi.dll”, CharSet = CharSet.Unicode)]
static extern int CeReadFile(
  IntPtr hFile,
  byte[] lpBuffer,
  int nNumberOfbytesToRead,
  ref int lpNumberOfbytesRead,
  int lpOverlapped);

const int ERROR_SUCCESS = 0;
const int OPEN_EXISTING = 3;
const int INVALID_HANDLE_VALUE = -1;
const int FILE_ATTRIBUTE_NORMAL = 0x80;
const uint GENERIC_READ = 0x80000000;

Now let’s create a method called CopyFromDevice(string remote_file, string local_file). The remote_file parameter is the source file on the device that you wish to copy. The local_file parameter is the destination filename on the desktop.

public static void CopyFromDevice(string remote_file, string local_file)
{
  bool rapi = CeRapiInit() == ERROR_SUCCESS;
  if (!rapi) {
    return;
  }

  IntPtr remote_file_ptr = CeCreateFile(
    remote_file,
    GENERIC_READ,
    0,
    0,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);

  if (remote_file_ptr.ToInt32() == INVALID_HANDLE_VALUE) {
    return;
  }

  FileStream local_file_stream = new FileStream(
    local_file,
    FileMode.Create,
    FileAccess.Write);

  int read = 0;
  int size = 1024 * 4;
  byte[] data = new byte[size];

  CeReadFile(remote_file_ptr, data, size, ref read, 0);

  while (read > 0) {
    local_file_stream.Write(data, 0, read);
    if (CeReadFile(remote_file_ptr, data, size, ref read, 0) == 0) {
      CeCloseHandle(remote_file_ptr);
      local_file_stream.Close();
      return;
    }
  }

  CeCloseHandle(remote_file_ptr);
  local_file_stream.Flush();
  local_file_stream.Close();

  if (rapi) {
    CeRapiUninit();
  }

  if (!File.Exists(local_file)) {
    throw new FileNotFoundException(“The file was not copied to the desktop”);
  }
}

To use the code above you will have to know the full path of the file on the device. The way I did it was to read the registry on the device and check if my application was installed, if it was then I get the path of the application and pass as the path in my remote_file parameter.