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.

17 comments:

Anonymous said...

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

Christian Resma Helle said...

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));

Anonymous said...

Thanks Christian, this was very useful to me!

Anonymous said...

Thanks a lot! Very useful code!

Anonymous said...

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

Anonymous said...

Thanks Christian ! The article is very helpful.

Samantha.

Eric said...

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

Eric

Christian Resma Helle said...

Hi Eric,

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

Christian Resma Helle said...

Hi Eric,

Check out my post on copying files from the desktop to the device:
http://christian-helle.blogspot.com/2010/08/copying-files-from-desktop-to-device.html

Anonymous said...

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

tikkay said...

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

Anonymous said...

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

Unknown said...

Please help me Christian...

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

Thanks...

Testman Lin said...

Thanks for your sharing...

Fahad said...

I Tried your code and Found
CeCreateFile return -1

What is wrong please help me out

Fahad said...

How To Set File Permission of Device ?

Lisa Nek said...

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.