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.