{"id":161,"date":"2007-05-21T00:48:18","date_gmt":"2007-05-21T00:48:18","guid":{"rendered":"http:\/\/dalelane.co.uk\/blog\/?p=161"},"modified":"2007-05-21T00:48:18","modified_gmt":"2007-05-21T00:48:18","slug":"running-windows-mobile-apps-from-a-pc-using-rapi-start","status":"publish","type":"post","link":"https:\/\/dalelane.co.uk\/blog\/?p=161","title":{"rendered":"Running Windows Mobile apps from a PC using RAPI Start"},"content":{"rendered":"<p>People keeping half an eye on my <a href=\"http:\/\/wakoopa.com\/dalelane\" target=\"_blank\">Wakoopa feed<\/a> might have noticed that I started playing with <a title=\"microsoft.com\" href=\"http:\/\/www.microsoft.com\/downloads\/details.aspx?FamilyId=74473FD6-1DCC-47AA-AB28-6A2B006EDFE9&#038;displaylang=en\" target=\"_blank\">RAPI Start<\/a> this weekend. <\/p>\n<p>RAPI Start is a command-line tool that lets you remotely run commands on a Windows Mobile device from a connected desktop. It&#8217;s quite neat, so I want to see what sort of things I can do with it. <\/p>\n<p>Here is noddy first attempt number 1 \ud83d\ude42<\/p>\n<p><strong>The problem:<\/strong><br \/>\nI get a text message while I&#8217;m sat at my desk with my mobile connected to my computer. (<em>For a mobile device, it spends a large amount of it&#8217;s life tethered to a desktop &#8211; but that&#8217;s a discussion for another time!<\/em>) <\/p>\n<p>I want to reply, but writing on a mobile device is fiddly. I&#8217;m sat at a full-sized keyboard, so why can&#8217;t I just use that instead?<\/p>\n<p><!--more--><strong>The solution:<\/strong><br \/>\nTwo quick .NET applications:<\/p>\n<ul>\n<li>Windows Mobile app &#8211; taking a couple of command-line arguments (phone number, and a message) and using them to send a text message<\/li>\n<li>Desktop app &#8211; something that uses RAPI Start to kick off the Windows Mobile app<\/li>\n<\/ul>\n<p><strong>Mobile<\/strong><\/p>\n<p>Let&#8217;s start with the really easy code first:<\/p>\n<pre><code>using System;\r\nusing System.Text;\r\nusing Microsoft.WindowsMobile.PocketOutlook;\r\n\r\nnamespace SmsSender\r\n{\r\n  class Program\r\n  {\r\n    static void Main(string[] args)\r\n    {\r\n      if (args.Length >= 2)\r\n      {\r\n        try\r\n        {\r\n          string phonenumber = args[0];\r\n          string messageBody = args[1];\r\n\r\n          \/\/ the first command-line argument is the phone number\r\n          \/\/           to send the message to\r\n          \/\/ everything else is the message - so we concatenate \r\n          \/\/   all of the arguments after the number into a single\r\n          \/\/   messageBody string\r\n          for (int i = 2; i < args.Length; i++)\r\n          {\r\n            messageBody = messageBody + \" \" + args[i];\r\n          }\r\n\r\n          \/\/ Create and send the text message\r\n          SmsMessage newMessage = new SmsMessage(phonenumber, messageBody);\r\n          newMessage.Send();\r\n        }\r\n        catch (Exception e)\r\n        {\r\n          \/\/ RAPI Start doesn't return anything from the \r\n          \/\/  mobile app to the desktop command-line that\r\n          \/\/  invoked it\r\n          \/\/ So I don't bother doing anything clever or \r\n          \/\/  useful like error-handling \r\n        }\r\n      }\r\n    }\r\n  }\r\n}<\/code><\/code><\/pre>\n<p>Well, that was easy \ud83d\ude42<\/p>\n<p><strong>Desktop<\/strong><\/p>\n<p>This is slightly more complicated. I need to send a phone number to the mobile, so I look up a name using the desktop Outlook. <\/p>\n<p><code>C:\\> sms dale Hello, this is a text message<br \/>\n -> sent to 078888777888<\/code><\/p>\n<p>or <\/p>\n<p><code>C:\\> sms smith Hello, I am being a bit vague with names<br \/>\n Identify who to send text message to:<br \/>\n  (1) John Smith<br \/>\n  (2) Will Smith<br \/>\n  (3) David Smithson<\/code><\/p>\n<p>This makes the desktop code a little longer, getting entries from the desktop Outlook Address Book, and looking through them for a name (or names) that matches. <\/p>\n<pre><code>using System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Text;\r\nusing Outlook;\r\n\r\nnamespace SmsDesktop\r\n{\r\n  class Program\r\n  {\r\n    static void Main(string[] args)\r\n    {\r\n      \/\/----------------------------------------------\r\n      \/\/ TWO ARGUMENTS\r\n      \/\/\r\n      \/\/ the first command-line argument is the phone number\r\n      \/\/           to send the message to\r\n      \/\/ everything else is the message - so we concatenate \r\n      \/\/   all of the arguments after the number into a single\r\n      \/\/   messageBody string\r\n      \/\/----------------------------------------------\r\n      if (args.Length >= 2)\r\n      {\r\n        string contact   = args[0];\r\n        string messageBody = args[1];\r\n\r\n        for (int i = 2; i < args.Length; i++)\r\n        {\r\n          messageBody = messageBody + \" \" + args[i];\r\n        }\r\n\r\n        \/\/ find the person to send the message to\r\n        string numberToSendTo = contact;\r\n\r\n        try\r\n        {\r\n          \/\/ is this a number? if it is, then \r\n          \/\/  this function should probably work fine\r\n          Convert.ToInt64(contact);\r\n        }\r\n        catch (FormatException e)\r\n        {\r\n          \/\/ Convert failed - throwing an exception\r\n          \/\/ So the argument provided is not a number\r\n          \/\/ Hopefully it is a name, instead.\r\n          \/\/ So we look for a match in the Outlook Address Book\r\n          ContactItem personToSendTo = SearchInOutlookAddressBook(contact);\r\n\r\n          if (personToSendTo != null)\r\n          {\r\n            \/\/ if we found a matching entry in the Address Book\r\n            \/\/  then we grab the mobile number out of it\r\n            numberToSendTo = personToSendTo.MobileTelephoneNumber;\r\n          }\r\n        }\r\n\r\n        string cmd = \"C:\\\\Program Files\\\\Windows Mobile Developer Power Toys\\\\RAPI_Start\\\\rapistart.exe\";\r\n        string arg = \"bLADE_SMS \" + numberToSendTo + \" \" + messageBody;\r\n\r\n        ProcessStartInfo proc = new ProcessStartInfo();\r\n        proc.WindowStyle = ProcessWindowStyle.Minimized;\r\n        proc.FileName = cmd;\r\n        proc.Arguments = arg;\r\n        Process.Start(proc);\r\n        \r\n        Console.WriteLine(\" -> sent to \" + numberToSendTo);\r\n      }\r\n    }\r\n\r\n\r\n\r\n    static ContactItem SearchInOutlookAddressBook(string contactDescription)\r\n    {\r\n      \/\/ who are we looking for? \r\n      \/\/  convert the name we are searching for to lower-case (to let us\r\n      \/\/   do case-insensitive searching) \r\n      \/\/  and break it apart into separate names to search for individually\r\n      string[] searchNames = contactDescription.ToLowerInvariant().Split();\r\n\r\n      \/\/ get handle to Outlook \r\n      Application myOutlookHandle = new Application();\r\n      NameSpace myOutlookNS = myOutlookHandle.GetNamespace(\"MAPI\");\r\n      MAPIFolder outlookContacts = myOutlookNS.GetDefaultFolder(OlDefaultFolders.olFolderContacts);\r\n      Items contactEntries = outlookContacts.Items;\r\n\r\n      \/\/ prepare a space to store address-book items that match\r\n      List<contactitem> possibleMatches = new List<\/contactitem><contactitem>();\r\n\r\n      \/\/ look through every entry in the address-book\r\n      for (int i = 1; i < = contactEntries.Count; i++)\r\n      {\r\n        if (contactEntries.Item(i) is ContactItem)\r\n        {\r\n          \/\/ get the next person from the Address Book\r\n          ContactItem nxtContact = (ContactItem)(contactEntries.Item(i));\r\n\r\n          \/\/ searching in first and last name\r\n          \/\/  could expand this to look in other fields like NickName\r\n          string contactName = nxtContact.LastNameAndFirstName;\r\n\r\n          if (contactName != null)\r\n          {\r\n            \/\/ convert to lowercase as we did with the searchstring\r\n            \/\/  as we want to do case-insensitive searching\r\n            contactName = contactName.ToLowerInvariant();\r\n\r\n            \/\/ look for each bit of the name we are searching for \r\n            bool match = true;\r\n            foreach (string searchName in searchNames)\r\n            {\r\n              if (contactName.Contains(searchName) == false)\r\n              {\r\n                match = false;\r\n                break;\r\n              }\r\n            }\r\n\r\n            \/\/ if we found a match, we add it to the \r\n            \/\/  list of possible possibleMatches\r\n            if (match)\r\n            {\r\n              possibleMatches.Add(nxtContact);\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      \/\/ prepare a space to store the person identified as a match\r\n      ContactItem identifiedMatch = null;\r\n\r\n      switch (possibleMatches.Count)\r\n      {\r\n        case 0:\r\n          Console.WriteLine(\"No matches found.\");\r\n          break;\r\n        case 1:\r\n          identifiedMatch = possibleMatches[0];\r\n          break;\r\n        default:\r\n          \/\/ found more than one person with a name that matches\r\n          \/\/ \r\n          \/\/ so we display them in a list and get the user to \r\n          \/\/  choose one\r\n\r\n          Console.WriteLine(\"Identify who to send text message to:\");\r\n          for (int i = 0; i < possibleMatches.Count; i++)\r\n          {\r\n            ContactItem nxtMatch = possibleMatches[i];\r\n            Console.WriteLine(\"(\" + (i + 1) + \")  \" + nxtMatch.FirstName + \" \" + nxtMatch.LastName);\r\n          }\r\n          string selected = \"\";\r\n          int selIdx = 0;\r\n\r\n          while (selIdx == 0)\r\n          {\r\n            selected = Console.ReadLine();\r\n\r\n            try\r\n            {\r\n              selIdx = Convert.ToInt16(selected);\r\n            }\r\n            catch (FormatException ee)\r\n            {\r\n              Console.WriteLine(\"Enter a number.\");\r\n            }\r\n\r\n\r\n            if ((selIdx > possibleMatches.Count) && (selIdx < = 0))\r\n            {\r\n              Console.WriteLine(\"Invalid choice. Enter again.\");\r\n              selIdx = 0;\r\n            }\r\n          }\r\n\r\n          identifiedMatch = possibleMatches[selIdx - 1];\r\n          break;\r\n      }\r\n\r\n      return identifiedMatch;\r\n    }\r\n  }\r\n}<\/code><\/contactitem><\/code><\/pre>\n<p><strong>Okay&#8230; so &#8216;solution&#8217; might be overstating things&#8230;<\/strong><\/p>\n<p>There are probably better ways to do this. <a href=\"http:\/\/www.jeyo.com\/extender.asp\">Jeyo<\/a>, for example, have a couple of neat apps that provide a desktop interface to SMS in Windows Mobile. <\/p>\n<p>Or something like <a href=\"http:\/\/www.microsoft.com\/downloads\/details.aspx?FamilyId=74473FD6-1DCC-47AA-AB28-6A2B006EDFE9&#038;displaylang=en\">ActiveSync Remote Display<\/a> which lets you display Windows Mobile applications on the desktop &#8211; letting me use the Windows Mobile Text Messages app directly.  <\/p>\n<p>But I like using the command-line &#8211; it&#8217;s certainly a very quick way to send an SMS. And it was a fun thing to tinker with while watching Doctor Who \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p>People keeping half an eye on my Wakoopa feed might have noticed that I started playing with RAPI Start this weekend. RAPI Start is a command-line tool that lets you remotely run commands on a Windows Mobile device from a connected desktop. It&#8217;s quite neat, so I want to see what sort of things I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-161","post","type-post","status-publish","format-standard","hentry","category-code"],"_links":{"self":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/161","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=161"}],"version-history":[{"count":0,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/161\/revisions"}],"wp:attachment":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=161"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=161"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=161"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}