Intercept incoming SMS message on HTC Phones with HTC Sense
In my previous post I have explained the problem with MessageInterceptor on new HTC Phones and how to temporary fix it it by installing the patch. Although this allows existing applications to work without any code changes, user will have to use Pocket Outlook, which has an inferior user interface compared to HTC Messages, to send SMS. MMS are not supported until the Arcsoft MMS client is installed. HTC Sense will not be able to show new incoming text messages, which is a major limitation.
Luckily there is a way to intercept incoming text messages without installing any patch. However, you will have to make some code changes to your application. The idea is that, although the .NET MessageInterceptor class does not work, the SDK MapiRule sample (C:Program FilesWindows Mobile 6.5.3 DTKSamplesCommonCPPWin32MapiRule) works just fine. This post shows you how to modify the MAPIRule sample code to integrate it with your .NET project
Modifying MAPIRule.dll
You will not have to touch most of the MAPIRule code, except for modifying the GUID (important!) and the ProcessMessage() function (the sample just displays the incoming text message received in a MessageBox). Although you can parse the incoming message and process it directly inside ProcessMessage(), I decided on the following to make things clearer:
- When MapiRule.dll received a new SMS message in ProcessMessage(), it simply send a Win32 message via SendMessage to the host application.
- Upon received the specific Win32 message, host application, which is a .NET application, will decide on whether or not to intercept the new SMS message (it will not appear in phone Inbox) or let the default messaging application handle it.
- Based on the result of SendMessage(), MapiRule.dll will act accordingly.
This interprocess communication can be easily done by using the Win32 API SendMessage() and the .NET Compact Framework’s MessageWindow class. Although you can specify your own message ID, I have chosen WM_COPYDATA since MapiRule.dll needs to send the SMS text and sender number to the host application, otherwise string pointers to message text and sender numbers cannot be shared. The sample code is below. Notice that newMsgWin is the handle to the host application’s window. The sender number and message text is concatenated into a single string to be sent out.
HRESULT CMailRuleClient::ProcessMessage(IMsgStore *pMsgStore, ULONG cbMsg, LPENTRYID lpMsg, ULONG cbDestFolder, LPENTRYID lpDestFolder, ULONG *pulEventType, MRCHANDLED *pHandled)
{
…………
TCHAR msgInfo[500];
COPYDATASTRUCT cds;
// we concatenate the sender number and the message text, to be sent to the host application
memset(msgInfo, 0, sizeof(msgInfo));
wcscpy(msgInfo, pspvEmail->Value.lpszW);
wcscat(msgInfo, L”n”);
wcscat(msgInfo, pspvSubject->Value.lpszW);
//length for the recipient to know
cds.dwData = wcslen(pspvEmail->Value.lpszW) + wcslen(pspvSubject->Value.lpszW) + 1;
cds.cbData = sizeof(msgInfo); //msg size in bytes
cds.lpData = &msgInfo; //pointer to the information to be sent
// tell the main app about the text message
if (SendMessage(newMsgWin, WM_COPYDATA, 0, (LPARAM) &cds) == (LRESULT) 1)
{
// a LRESULT of 1 means that the message was processed by parent application
// so we delete the message and mark it as handled so it won’t show up in Inbox
hr = DeleteMessage(pMsgStore, pMsg, cbMsg, lpMsg, cbDestFolder, lpDestFolder, pulEventType, pHandled);
}
else
{
// other LRESULT means message not handled by main app, pass it on
*pHandled = MRC_NOT_HANDLED;
}
…………
Everything is straightforward, except that the lpData member of COPYDATASTRUCT structure has to be part of the structure memory area. This explains why an array of TCHAR, an not LPWSTR is used. If this is not followed, application may work or crash randomly without any indication why.
UPDATE (26 May 2012): As suggested by a reader, the above code may have problem with Unicode messages since sizeof(wchar_t) is 2 on Windows, resulting in incorrect value for the data length, dwData. If you have problems with truncated messages, try this:
cds.dwData = 2*wcslen(pspvEmail->Value.lpszW) + 2*wcslen(pspvSubject->Value.lpszW) + 1;
The updated MAPI DLL with the fix can be downloaded from here
The .NET code is as follows:
private struct CopyDataStruct
{
public int IntData;
public int Length;
[MarshalAs(UnmanagedType.LPWStr)]
public string Data;
}
{
{
if (m.Msg == WM_COPYDATA) //we received a message telling us that there is an incoming SMS
{
CopyDataStruct cs = (CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(CopyDataStruct));
if (cs.Data.Contains(delim))
{
string sender = cs.Data.Split(delim)[0];
string messageText = cs.Data.Split(delim)[1];
if (true)
{
// a LRESULT of 1 marks message as processed.
// and native msg app will not show msg in Inbox
m.Result = new IntPtr(1);
}
else
// Other LRESULT indicates we did not intercept the message
// and the message will be passed on to native messaging application.
m.Result = new IntPtr(0);
}
}
…….
}
The challenge is to receive the WM_COPYDATA message and marshal it back to a string. Most sample codes use GetLParam(), but as this is not supported by .NET CF, we have to use Marshal.PtrToStructure(). As commented, the .NET code will response with a result of either 1 or 0.
Source code
The sample code is here. Some part of the code was taken from the Remote Tracker open source project which also intercepts incoming text messages. Remote Tracker source code, however, parses the text message directly inside MapiRule.dll.
Setting up
Some registry keys need to be modified for MapiRule.dll to be used. This can either be done via a CAB file, or via the CreateInterceptorMethod2() method in the code. A reboot is needed for changes to take effect. If you need to change MapiRule.dll, you will have to remove the registry keys via RemoveInterceptorMethod2(), reboot the device, update the dll, call CreateInterceptorMethod2() and reboot again! Terminating poutlook.exe and tmail.exe may also help to reduce the number of restarts on some devices. This makes the development process very time consuming. Debugging MapiRule.dll is possible by attaching the debugger to tmail.exe.
Issues
There are still some minor issues yet to be solved. On HTC HD2, for some reasons, all text messages received will have the string ” – GSM” appended at the end (refer to this discussion). Also, the total length of the sender name and the message text cannot exceed 500 since the TCHAR array length is hard coded. If you need to receive longer text message, the interprocess communication algorithm has to be modified to send and receive the text message part by part. You cannot simply increase the array length, as it will cause a stack fault.
Last but not least, the approach described here works on all devices, including HTC Devices. So it can be used as a replacement for the MessageInterceptor class. In a sense, it’s better as you can decide, at the point of receiving, which message to be processed, instead of pre-creating a set of MessageInterceptor conditions and re-creating them should the interception conditions change.
Hi, Can you advise me please?
Maybe I am doing something wrong.
1) From solution I removed C++ project (because I do not want to recompile a DLL, I want to use original one.)
2) Than I opened solution (only C# project TestMsgInterceptor) and added MessageBox.Show(“I am here”) to method msgWin_OnNewTextMessage() . Than I rebuild project.
3)I copied TestMsgInterceptor.exe and RTRule.dll to one (new) folder on my HTC HD2.
4) Thank I executed TestMsgInterceptor.exe and "Start Intercept" button. After reboot of my mobile I started TestMsgInterceptor.exe again and waited for incoming SMS …. SMS received by HTC HD2 standard service, the MessageBox.Show(“I am here”) has not been Shown.
Can you advise me why? What can be wrong?
Thanks,
Martin
BTW: please aswer
Hi, when I tested this on a HTC HD2 a few years back, all worked.
Checked if the Start Intercept button did what it is supposed to do, that is, to set up the required registry keys for the DLL. Make sure that the path to the DLL is correct. The DLL is a COM API for the Messaging API (MAPI). It needs to be COM-registered for things to work – which is why you need a reboot.
Try to make the project work as-is first before attempting to modify it.
Hope this helps.
Hi,
yes, that it is a reason why I did not want to recompile DLL
If I would like to use original TestMsgInterceptor.exe than please, how (in HTC HD2) I can see outcome of Debug.WriteLine(sender) ….as it is used in msgWin_OnNewTextMessage()?
Thanks so much.
Martin
Hi, you can run it in Debug Mode and see the error in debug output. Or you can change it to MessageBox.Show() and view it without the debugger.
Take note that there is no way for it to work if the DLL is not COM-registered properly. Try to make the original source code work first.
Hi. I played with code and problem is here:
The WndProc() method in C# code is been called when SMS received, but cs.Data contains always half of text of original SMS.
Susspicious is that cs.Length value is same as number of chars which *should be* stored in cs.Data So cs.Length is 40 but in cs.Data are only 20 chars.
And now say me why?
Thanks,
Martin
Hi Martin,
Do you mean that the message sender was displayed properly and the body cut into half? The design was to merge both into a single cs.data string, separated by "\n".
Do you also happen to have Unicode characters in the message text? Take note that sizeof(wchar_t) on Windows is 2 bytes, which may not have been handled properly (I only tested with ANSI characters). Check the ProcessMessage function in the C++ library to make sure that the message is received properly. If yes, the problem is with the passing of the message from the DLL to the C# code
I suspect the following line:
cds.dwData = wcslen(pspvEmail->Value.lpszW) + wcslen(pspvSubject->Value.lpszW) + 1;
Try this (or try some big numbers first)
cds.dwData = 2*wcslen(pspvEmail->Value.lpszW) + 2*wcslen(pspvSubject->Value.lpszW) + 1;
Let me know if you manage to fix it.
Thanks for trying
Hi.
yes, alwayes the text is cut to half. The several chars from this half is being used by phone number.
This is really same approach I wanted to use
Unfortunatelly I have no C++ compiler ;-(
Can you compile it?
BTW: For Win32 I am using MS Visual Studio Express C#, and for C# mobile development I am using free ware ShrapDevelop http://www.icsharpcode.net/opensource/sd/
Thanks,
Martin
Hi Martin,
Thanks for getting back,
Unfortunately, I do not have access to Visual Studio at the moment (on a netbook now), so I am unable to re-compile the DLL for you. There is a free express version of Visual Studio (VB, C# and C++ as well), but that doesn't support mobile development. If you don't have the full version of Visual Studio 2008 Professional or Enterprise (which is the last version to support Windows Mobile), you can search for Visual Studio 2008 Beta. The edition is free and there are some websites still offering it, legally You should also have the various Windows Mobile SDKs (available for free from Microsoft) installed in order to recompile the DLL.
Hi,
thanks for answer.
please, will you have access to VS C++ in near future? You know sometimes is better to wait few days than immediately to start installing that and that
BTW my email to simplify communication: email.pro.me@gmail.com.
Thanks, Martin
Hi,
I will try to recompile the DLL and update the article when I have the time too, hopefully within the next few days. Meanwhile, try to look for Visual Studio 2008 Beta – installing it and compiling the code should be easy. You'll learn new things too!
Hi,
finally I compiled it and it /* cds.dwData = 2*wcslen(pspvEmail->Value.lpszW) + 2*wcslen(pspvSubject->Value.lpszW) + 1; */ works
I had to find MS Visual Studion 2008 Trial on some mirror servers of Microsoft, and than compiled it.
Let me know if you want "new" DLL from me. I can send it to you.
Thanks & regards,
Martin
Hi Martin,
I am glad my suggestions work for you and thanks so much for getting back. I believe it will help other with similar problems. If you have time, upload the DLL somewhere (dropbox perhaps) and send me the link.
Good luck
Hi,
the updated DLL is here
http://meteo.webz.cz/Mobil/RTRuleWide.dll
regards,
Martin
Hi Martin,
Thank you for the DLL. I have downloaded it and updated the article accordingly.
Cheers