Communicating with a connected device is quite distinct from communicating with other desktop macines or a server. Considering that PDA's use a different OS and a different processor, this revelation is hardly surprising. In this regard, there's some good news and some bad news. The bad news is that it takes a little more work than you may acustomed to in order to communicate with a device. The good news is that 1) it's possible 2) Once you understand what's going on, it's fairly straightforward.
The primary mechanism for desktop to device (d to d) communication is knows as RAPI . RAPI is code for using any of the functionality exposed in RAPI.dll which resides on the desktop machine. Currently, there's no native managed provider to access RAPI, so you are going to have to delve into the unmanaged world. Opennetcf.org has an implementation which wraps the functionality of RAPI in an easy-to-use library and if you aren't familiar with their work, I highly encourage your to visit their Site . But how does one use RAPI and what can you do with it?
Let's say that you wanted to programatically activate myProgram which resides on the PDA from the desktop. A natural assumption might be that you somehow make a connection with the PDA and then use a mechanism like Shell.Execute(@"MyPath\MyApp") or Process.Start or some similar mechanism. When I first started CF programming, this was my assumption. But I quickly learned that there is no Shell.Execute functionality provided in the CF. As such, it idn't take long for me to realize that any career in Compact Framework will be riddled with P/Invoke. So before I go any further into the mechanics of RAPI, let's take a look at how we'd start an application on a PDA.
The first thing you'll need to do is include a reference to System.Runtime.InteropServices. This is the namespace that provides the fundamental interface for dealing with unmanaged code. And without striking up the all too belabored debate on VB.NET vs C#, I actively use both C# and VB.NET but greatly prefer C# when dealing with unmanaged code. C# Supports execution of unsafe code blocks and VB.NET doesn't (which isn't due to the language as much as the implementation) hence making it much better suited for Interop tasks (at least from my point of view). Next, we need to take a look at the CreateProcess API. A typical invokation looks something like the following:
| [DllImport("coredll.dll")] public static Int32( string lpApplicationName, string lpCmdLine, IntPtr lpProcessAttribute, IntPtr lpThreadAttributes, Int32 boolInheritHandles, Int32 dwCreationFlags, IntPtr lpEnvironment, IntPtr lpCurrentDir, Byte[] si, ProcessInfo pi); |
| DllImport("rapi.dll", CharSet=CharSet.Unicode)] public static extern Int32 CeCreateProcess(string lpApplicationName, string lpCmdLine, Int32 lpProcessAttribute, Int32 lpThreadAttribute, Int32 boolInheritsHandles, Int32 lwCreationFlags, Int32 lpEnvironment, Int32 lpCurrentDir, Int32 misc, ref ProcessInfo lpProcessInfo); |
| [DllImport("rapi.dll", CharSet=CharSet.Unicode)] public static extern Int32 CeRapiInit(); [DllImport("rapi.dll", CharSet=CharSet.Unicode)] public static extern Int32 CeRapiUninit(); [DllImport("rapi.dll", CharSet=CharSet.Unicode)] public static extern int CeCloseHandle(IntPtr Handle); public enum SafeToContinue: uint{ Yes = 0 ,No = 1 } |
| [StructLayout(LayoutKind.Sequential, Pack=4)] public struct ProcessInfo{ public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } |
| const string ProgramName = @"\Program Files\TargetProgram.exe"; [STAThread] static void Main(string[] args){ ProcessInfo Information = new ProcessInfo(); try{ if(CeRapiInit() == SafeToContinue.Yes){ CeCreateProcess(ProgramName, string.Empty, 0,0,0,0,0,0,0, ref Information); } } catch(System.Exception ex){ Debug.Assert(false, ex.ToString()); } finally{ CeCloseHandle(Information.hProcess); CeCloseHandle(Information.hThread); CeRapiUninit(); } } |