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’s quite neat, so I want to see what sort of things I can do with it.
Here is noddy first attempt number 1 🙂
The problem:
I get a text message while I’m sat at my desk with my mobile connected to my computer. (For a mobile device, it spends a large amount of it’s life tethered to a desktop – but that’s a discussion for another time!)
I want to reply, but writing on a mobile device is fiddly. I’m sat at a full-sized keyboard, so why can’t I just use that instead?
The solution:
Two quick .NET applications:
- Windows Mobile app – taking a couple of command-line arguments (phone number, and a message) and using them to send a text message
- Desktop app – something that uses RAPI Start to kick off the Windows Mobile app
Mobile
Let’s start with the really easy code first:
using System;
using System.Text;
using Microsoft.WindowsMobile.PocketOutlook;
namespace SmsSender
{
class Program
{
static void Main(string[] args)
{
if (args.Length >= 2)
{
try
{
string phonenumber = args[0];
string messageBody = args[1];
// the first command-line argument is the phone number
// to send the message to
// everything else is the message - so we concatenate
// all of the arguments after the number into a single
// messageBody string
for (int i = 2; i < args.Length; i++)
{
messageBody = messageBody + " " + args[i];
}
// Create and send the text message
SmsMessage newMessage = new SmsMessage(phonenumber, messageBody);
newMessage.Send();
}
catch (Exception e)
{
// RAPI Start doesn't return anything from the
// mobile app to the desktop command-line that
// invoked it
// So I don't bother doing anything clever or
// useful like error-handling
}
}
}
}
}
Well, that was easy 🙂
Desktop
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.
C:\> sms dale Hello, this is a text message
-> sent to 078888777888
or
C:\> sms smith Hello, I am being a bit vague with names
Identify who to send text message to:
(1) John Smith
(2) Will Smith
(3) David Smithson
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.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Outlook;
namespace SmsDesktop
{
class Program
{
static void Main(string[] args)
{
//----------------------------------------------
// TWO ARGUMENTS
//
// the first command-line argument is the phone number
// to send the message to
// everything else is the message - so we concatenate
// all of the arguments after the number into a single
// messageBody string
//----------------------------------------------
if (args.Length >= 2)
{
string contact = args[0];
string messageBody = args[1];
for (int i = 2; i < args.Length; i++)
{
messageBody = messageBody + " " + args[i];
}
// find the person to send the message to
string numberToSendTo = contact;
try
{
// is this a number? if it is, then
// this function should probably work fine
Convert.ToInt64(contact);
}
catch (FormatException e)
{
// Convert failed - throwing an exception
// So the argument provided is not a number
// Hopefully it is a name, instead.
// So we look for a match in the Outlook Address Book
ContactItem personToSendTo = SearchInOutlookAddressBook(contact);
if (personToSendTo != null)
{
// if we found a matching entry in the Address Book
// then we grab the mobile number out of it
numberToSendTo = personToSendTo.MobileTelephoneNumber;
}
}
string cmd = "C:\\Program Files\\Windows Mobile Developer Power Toys\\RAPI_Start\\rapistart.exe";
string arg = "bLADE_SMS " + numberToSendTo + " " + messageBody;
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Minimized;
proc.FileName = cmd;
proc.Arguments = arg;
Process.Start(proc);
Console.WriteLine(" -> sent to " + numberToSendTo);
}
}
static ContactItem SearchInOutlookAddressBook(string contactDescription)
{
// who are we looking for?
// convert the name we are searching for to lower-case (to let us
// do case-insensitive searching)
// and break it apart into separate names to search for individually
string[] searchNames = contactDescription.ToLowerInvariant().Split();
// get handle to Outlook
Application myOutlookHandle = new Application();
NameSpace myOutlookNS = myOutlookHandle.GetNamespace("MAPI");
MAPIFolder outlookContacts = myOutlookNS.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
Items contactEntries = outlookContacts.Items;
// prepare a space to store address-book items that match
List possibleMatches = new List ();
// look through every entry in the address-book
for (int i = 1; i < = contactEntries.Count; i++)
{
if (contactEntries.Item(i) is ContactItem)
{
// get the next person from the Address Book
ContactItem nxtContact = (ContactItem)(contactEntries.Item(i));
// searching in first and last name
// could expand this to look in other fields like NickName
string contactName = nxtContact.LastNameAndFirstName;
if (contactName != null)
{
// convert to lowercase as we did with the searchstring
// as we want to do case-insensitive searching
contactName = contactName.ToLowerInvariant();
// look for each bit of the name we are searching for
bool match = true;
foreach (string searchName in searchNames)
{
if (contactName.Contains(searchName) == false)
{
match = false;
break;
}
}
// if we found a match, we add it to the
// list of possible possibleMatches
if (match)
{
possibleMatches.Add(nxtContact);
}
}
}
}
// prepare a space to store the person identified as a match
ContactItem identifiedMatch = null;
switch (possibleMatches.Count)
{
case 0:
Console.WriteLine("No matches found.");
break;
case 1:
identifiedMatch = possibleMatches[0];
break;
default:
// found more than one person with a name that matches
//
// so we display them in a list and get the user to
// choose one
Console.WriteLine("Identify who to send text message to:");
for (int i = 0; i < possibleMatches.Count; i++)
{
ContactItem nxtMatch = possibleMatches[i];
Console.WriteLine("(" + (i + 1) + ") " + nxtMatch.FirstName + " " + nxtMatch.LastName);
}
string selected = "";
int selIdx = 0;
while (selIdx == 0)
{
selected = Console.ReadLine();
try
{
selIdx = Convert.ToInt16(selected);
}
catch (FormatException ee)
{
Console.WriteLine("Enter a number.");
}
if ((selIdx > possibleMatches.Count) && (selIdx < = 0))
{
Console.WriteLine("Invalid choice. Enter again.");
selIdx = 0;
}
}
identifiedMatch = possibleMatches[selIdx - 1];
break;
}
return identifiedMatch;
}
}
}
Okay… so ‘solution’ might be overstating things…
There are probably better ways to do this. Jeyo, for example, have a couple of neat apps that provide a desktop interface to SMS in Windows Mobile.
Or something like ActiveSync Remote Display which lets you display Windows Mobile applications on the desktop – letting me use the Windows Mobile Text Messages app directly.
But I like using the command-line – it’s certainly a very quick way to send an SMS. And it was a fun thing to tinker with while watching Doctor Who 🙂
All good code-hacking ideas should be measured in the number of Doctor Who episodes needed to finish them
Desktop SMS : 1 x Doctor Who
🙂
[…] mentioned last week that watching Doctor Who made for a good chance to absentmindedly play with some code. I seem to […]
Dale, I want to use the smsmessage send from your code in my windows form. I’m new at .net and try something simple as a form with textbox for number and textbox for message and event button to send message. I’m recieving exception error below. Is it possible to send message from a windows form? If so, do you have any way to fix this? Thanks,
System.DllNotFoundException was unhandled
Message=”Unable to load DLL ‘cemapi.dll’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)”
Source=”Microsoft.WindowsMobile.PocketOutlook”
TypeName=””
StackTrace:
at Microsoft.WindowsMobile.PocketOutlook.MAPIUtilities.SafeNativeMethods.SendSMSMessage(String RecipientAddress, String MessageText, Boolean DeliveryReportRequested)
at Microsoft.WindowsMobile.PocketOutlook.SmsMessage.Send()
at SmsSender.Form1.button1_Click(Object sender, EventArgs e) in C:\Users\Ron\Documents\Visual Studio 2008\Projects\SmsSender\SmsSender\Form1.cs:line 44
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at SmsSender.Program.Main() in C:\Users\Ron\Documents\Visual Studio 2008\Projects\SmsSender\SmsSender\Program.cs:line 18
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
The error you are getting is because your code couldn’t find cemapi.dll on your desktop. This is a Windows Mobile only DLL – designed to use the Windows Mobile interfaces to mobile phone hardware. It doesn’t make any sense to try and use it on a desktop – without a mobile phone radio, you can’t send an SMS directly from a PC. Your code will need to access some third-party (e.g. a connected mobile or a web service API) to send the SMS for you.
I created a bat file which uses rapistart
rapistart “\Storage Card\test.exe””
when I execute this in PC cmd prompt I am getting a error…
Failed to start process (2)
what wrong I am doing…any place where I get some example code to use rapistart
I met a problem like Ravi, that is “Failed to start process (2)”
I found the same problem when i’m runing my applications. Do someone know where can we find cemapi.dll?