The CERunApp application can be downloaded from: 
Note: in Part 3 I have included an updated version of this exe
 
 The CERunApp source code can be downloaded from: 
Note: in Part 3 I have included an updated version of the source code
 
In Windows CE: Using RAPI to Run Applications (Part 1) I wrote about starting this project to create a copy of the Windows Mobile Developer Power Toys application RAPIStart. In this article I will continue by creating an slight twist on RAPIStart.
As I discussed in Part 1, I decided that simply reproducing RAPIStart wasn’t all that exciting, since the source code for a similar app is available in the Windows Mobile SDK. So in this article I will develop a GUI based application that not only starts an app on a Windows CE device from a connected workstation, but will also transfer the app to the device before running it. I will show the interesting code here, but the link at the top of this page leads you to the download of the code and the app itself.
Since this is a GUI based app, I developed it using C#.   This choice meant that I spent very little effort on the GUI and was able to focus on the RAPI pieces.
The user interface looks like this:
This simple dialog contains:
1.       A text box to enter the application to run on the Windows CE device
2.       A browse button that opens a System.Windows.Forms.OpenFileDialog to select a file
3.       A text box to enter command line options
4.       A button to request that the app be downloaded to and run on the Windows CE device
Simple enough, and it requires very little code to back it up if we ignore the RAPI bits. Basically there are two functions, one each for the buttons. That code is:
        private void FileBrowseButton_Click(object sender, EventArgs e)
        {
 
            if (DialogResult.OK == openFileDialog1.ShowDialog())
           {
                AppName.Text = openFileDialog1.FileName;
            }
        }
 
        private void RunApplicationButton_Click(object sender, EventArgs e)
        {
            CRapi RAPI = new CRapi();
 
            if(RAPI.CeCopyToDevice(AppName.Text, "\\" + openFileDialog1.SafeFileName))
                RAPI.CeRunApplication("\\" + openFileDialog1.SafeFileName, CommandLineOptions.Text);
            else
                System.Windows.Forms.MessageBox.Show("Unable to copy file to the Windows CE");
        }
The browse button handler, FileBrowseButton_Click(), simply calls on the OpenFileDialog class to show the dialog. If it succeeds, then fill in the text box with the file path and name.
The run applciation handler, RunApplicationButton_Click() uses a CRapi class that I will show next to copy the file to the Windows CE Device and then start the application.
The CRapi class is the meat of this application. It knows how to make a connection to the Windows CE device and then uses the connection to perform actions. To make a connection, I ported the code from the MSDN documentation for CeRapiInitEx(). The MSDN example includes a function named TryRapiConnect()which attempts to make a connection to the device. The ported code is:
    // Code from MSDN
    // Copied from C++ examle and converted to C#
    private uint TryRapiConnect(uint dwTimeOut)
    {
        uint hr = E_FAIL;
        bool fInitialized = false;
 
        RAPIINIT riCopy = new RAPIINIT();
        riCopy.cbsize = Marshal.SizeOf(riCopy);
        hr = CeRapiInitEx(ref riCopy);
 
        if (SUCCEEDED(hr))
        {
            uint dwRapiInit = 0;
            fInitialized = true;
 
            dwRapiInit = WaitForSingleObject(
                        riCopy.heRapiInit,
                        dwTimeOut);
            if (WAIT_OBJECT_0 == dwRapiInit)
            {
                // heRapiInit signaled:
                // set return error code to return value of RAPI Init function
                hr = riCopy.hrRapiInit;
            }
            else if (WAIT_TIMEOUT == dwRapiInit)
            {
                // timed out: device is probably not connected
                // or not responding
                hr = ERROR_TIMEOUT;
            }
            else
            {
                // WaitForSingleObject failed
                hr = (uint)Marshal.GetLastWin32Error();
            }
        }
 
        if (fInitialized && FAILED(hr))
        {
            CeRapiUninit();
        }
        if (!fInitialized || FAILED(hr))
            System.Windows.Forms.MessageBox.Show("Windows CE device does not appear to be connected");
 
        return hr;
    }
This function is a attempts to make a connection. It may not be able to because it either times out or an error occurs.
The code for copying the file uses the System.IO.BinaryReader class to read the application on the device and copies it to the device using CeCreateFile() and CeWriteFile().
    public bool CeCopyToDevice(string AppName, string FileName)
    {
        bool ReturnValue = false;
        uint RapiResult;
 
        if (!File.Exists(AppName))
            return false;
 
        RapiResult = TryRapiConnect(5000);
        if (SUCCEEDED(RapiResult))
        {
            BinaryReader DTFile =
                new BinaryReader(File.Open(AppName, FileMode.Open));
            System.IntPtr RemoteFile = CeCreateFile(FileName, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
            try
            {
                byte[] Buffer = new byte[1024];
                bool Done = false;
                int BytesWritten;
                int BytesRead;
                while (!Done)
                {
                    BytesRead = DTFile.Read(Buffer, 0, 1024);
                    if (BytesRead == 0)
                        Done = true;
                    else
                    {
                        CeWriteFile(RemoteFile, Buffer, BytesRead, out BytesWritten, 0);
                    }
                }
                ReturnValue = true;
            }
            finally
            {
                CeCloseHandle(RemoteFile);
                DTFile.Close();
            }
            CeRapiUninit();
        }
        return ReturnValue;
    }
You will notice that I chose to call TryRapiConnect()from this function rather than from the CRapi constructor. I can’t say that it is absolutely the right choice, but I did it because I didn’t want to hold the connection open any longer than necessary. ActiveSync connects can come and go, so my thinking here is the minimize the risk of lost connecting by just having it open when it is being used. This choice means that the code will need to open it again to start the application.
CeRunApplication()is used to start the application on the device. This function also calls TryRapiConnect()to make the connection, then uses CeCreateProcess() to start the application and pass in the command line parameters.
    public bool CeRunApplication(string AppName, string CommandLine)
    {
        bool ReturnValue = true;
        uint RapiResult;
 
        RapiResult = TryRapiConnect(5000);
        if (SUCCEEDED(RapiResult))
        {
            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
            if(CeCreateProcess(AppName, CommandLine, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref pi))
            {
                CeCloseHandle(pi.hProcess);
                CeCloseHandle(pi.hThread);
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Failed to start application. Errorcode = " + CeGetLastError());
            }
            CeRapiUninit();
        }
        return ReturnValue;
    }
That’s it, well not completely because there are some supporting functions, data and structures. The rest of the code can be found in the code download at the top of this page.
This app does have some downsides. To use it to run an app on the device, the app must be on your workstation and must be downloaded first. As you can see in the picture of the dialog, I used it to run ipconfig within cmd.exe.   Cmd.exe had to be downloaded. The workaround for this is to use RAPIStart, or one of you enterprising developers may want to extend this to select an application that exists on the device instead of the workstation.
 

NOTE
There seems to be some question about how to run this application.  If you download the executable file, you will run it on your Windows XP/Vista/7 computer.  To do so, you will need the .NET Framework 2.0 or newer and have ActiveSync running and connected to your device.
 
If you download the source, you can of course change the .NET Framework version, but you will still run the app on your Windows XP/Vista/7 computer.
 
 
 
Copyright © 2009 – Bruce Eitman
All Rights Reserved