Saturday, June 16, 2007

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.

19 comments:

  1. Hello Christian,

    "Version of EXE on device" is the best example I have found, but I am getting an error;

    Error 1 The name 'CeFile' does not exist in the current context


    >> This is an example how to use the code I posted previously:

    Version ver = CeFile.CopyFromDevice(
    "\\Program Files\\MyApp\\MyApp.exe",
    string.Format("{0}\\MyApp.exe", Environment.CurrentDirectory));



    What do I need to do to proceed?

    tammie.morrow@yahoo.com

    ReplyDelete
  2. Try this:

    [C# Code]

    [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 uint GENERIC_READ = 0x80000000;
    const short OPEN_EXISTING = 3;
    const short INVALID_HANDLE_VALUE = -1;
    const short FILE_ATTRIBUTE_NORMAL = 0x80;

    public static Version GetAssemblyVersionFromDevice(string
    remote_file, string local_file)
    {
    bool rapi = CeRapiInit() == ERROR_SUCCESS;
    if (!rapi) {
    return null;
    }

    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 null;
    }

    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 null;
    }
    }

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

    if (!File.Exists(local_file)) {
    return null;
    }

    if (rapi) {
    CeRapiUninit();
    }

    Version version =
    Assembly.LoadFrom(local_file).GetName().Version;

    return version;
    }

    Version ver = GetAssemblyVersionFromDevice("\\Program Files\\MyApp\\MyApp.exe",
    string.Format("{0}\\MyApp.exe", Environment.CurrentDirectory));

    ReplyDelete
  3. Thanks Christian, this was very useful to me!

    ReplyDelete
  4. Thanks a lot! Very useful code!

    ReplyDelete
  5. Nice story you got here. It would be great to read a bit more concerning this matter. The only thing I would like to see here is some pics of some gizmos.
    Jeff Flouee
    Phone jammer

    ReplyDelete
  6. Thanks Christian ! The article is very helpful.

    Samantha.

    ReplyDelete
  7. Thanks Christian.
    This was very useful.
    Do you have a Copy File from PC to Device version?

    Eric

    ReplyDelete
  8. Hi Eric,

    I'll post an article about copy a file from the desktop to the device tonight

    ReplyDelete
  9. Hello Chirstian,

    Thanks to share nice article,

    I am able to copy a text file but unable to copy sdf or exe from device.

    Error : CeCreateFile()=-1
    means file not found.

    Please give me solution how can i copy sdf file.

    Saurabh
    prince.saurabh@gmail.com

    ReplyDelete
  10. How can i run an application on the device to transer files to my desktop.

    ReplyDelete
  11. Hi Everybody,

    i would like to develop an application, that can connect to mobile devices. In my case it is a MDE 3090 with Windows CE5 (http://www.ics-ident.de/ICS-Produkte/IT-Logistik-Systeme/Motorola-MC3090,49,96,69.html).

    What motivates me? I got to reset and re-install the MDE and you have to pay a lot of attention, which folder you delete and create. This process cost about 20min everytime.

    Here you can see the Problem i got:
    https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash4/s720x720/376311_10151046736217432_232135247_n.jpg

    I can't mount those partitions because it would not be found in the hdd-snap-in from windows and i can't mount it via net use either.

    In C# i got a clue, that I can use API(RAPI) or I can add the ActiveSync Enviroment to work with.

    Yes, I already use the search function but i don't get the clues which I can use.

    Can you help me please?

    Best regards Vi

    ReplyDelete
  12. Please help me Christian...

    I got error said,
    "Can't find PInvoke DLL 'rapi.dll'"

    Thanks...

    ReplyDelete
  13. I Tried your code and Found
    CeCreateFile return -1

    What is wrong please help me out

    ReplyDelete
  14. How To Set File Permission of Device ?

    ReplyDelete
  15. I want to add one more things about file paths. that there are many cases in which we need to move long path files so we can try Long Path Tool. This really helpful in performing many tasks.

    ReplyDelete
  16. Thanks Christian, this was very useful to me!

    ReplyDelete
  17. Thanks a lot! Very useful code!

    ReplyDelete