Pocket PC and P/Invoke
The last topic we are going to cover regarding the .NET Compact Framework is its ability to call into unmanaged code using Platform Invoke (P/Invoke). As you have seen throughout this book, most of the APIs that are supported on a Pocket PC platform are exported by using dynamic link libraries (DLLs) that your application imports. By using the P/Invoke service, you can also access the same API functions from within a .NET application. This enables you to integrate much of the functionality that is native to the Pocket PC, and not natively supported by the Compact Framework. For example, the Pocket PC Phone Edition supports the capability to send and receive SMS messages (see Chapter 8). Although the Compact Framework does not come with any classes to support this, you can use P/Invoke to enable your managed code to call into the unmanaged SMS API found in the cellcore.dll library.
To declare within your application a method that will use P/Invoke, you need to use the DllImport attribute, which supports the fields described in Table 12.16.
Table 12.16 DllImport Attributes
| Field |
Description |
| EntryPoint |
The function name that you want to call into |
| CharSet |
Specifies how the string arguments should be marshaled |
| CallingConvention |
Specifies the calling convention to use when passing arguments |
| SetLastError |
Specifies the calling convention to use when passing arguments |
Set this value to TRUE to enable calling the Marshal.GetLastWin32Error method to check if an error occurred when invoking this method
For example, the following code shows how you can use the Message Box() function from a managed application by using P/Invoke:
using System;
using System.Data;
using System.Runtime.InteropServices;
namespace invokeTest {
class Class1 {
// Hook up Windows API methods
[DllImport("coredll.dll", EntryPoint="MessageBox",
CharSet=CharSet.Unicode, SetLastError=true)]
static extern Int32 MessageBox(Int32 hWnd, string stText,
string stCaption, Int32 mbType);
static void Main(string[] args) {
// Call into the MessageBox function
MessageBox(0, "MessageText", "MessageCaption", 0);
}
}
}
Once a function has been declared with the DllImport attribute, you can then call it in the same manner as any other managed function.
Note a few minor differences regarding P/Invoke on the .NET Compact Framework when comparing it to its desktop counterpart:
- There is no Unicode-to-ANSI string conversion. All string pointers are passed to an unmanaged function as a Unicode string.
- There is no marshaling of objects contained within structures.
- If a function returns a pointer to a structure, it is not marshaled to a managed structure. You need to create a wrapper function that handles simple data types.
- Platform Invoke services does not support COM interoperability with the Compact Framework. If you wish to call into COM objects, you need to create a wrapper DLL that exports non-COM-based functions.
- The DllImport attribute supports only the CharSet.Unicode and CharSet.Auto character sets.
- The DllImport attribute supports only the CallingConvention.Winapi calling convention.
Sending an SMS Message from .NET
The following example shows a slightly more complicated way of using the Platform Invoke services. Because the Compact Framework does not support the marshaling of objects that are contained within a structure, you need to create a C++ "wrapper" library in order to call the Pocket PC Phone Edition's SMS API functions (see Chapter 8).
First, create the wrapper library using Embedded Visual C++ 3.0. The code for the library will look as follows:
// First is the definition file for the DLL
// smsinvoke.def
LIBRARY SMSINVOKE
EXPORTS
SendSMSInvokeMsg ((Content component not found.))
// Here is the wrapper DLL
// smsinvoke.cpp
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) BOOL SendSMSInvokeMsg(TCHAR
*tchPhoneNumber, TCHAR *tchMessage);
#ifdef __cplusplus
}
#endif
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason,
LPVOID lpvReserved)
{
return TRUE;
}
BOOL SendSMSInvokeMsg(TCHAR *tchPhoneNumber, TCHAR
*tchMessage)
{
SMS_HANDLE hSms = NULL;
HANDLE hSmsEvent = NULL;
HRESULT hr = S_OK;
BOOL fReturn = FALSE;
// Make sure we have a number and a message
if(!tchPhoneNumber || !tchMessage)
return fReturn;
// Open up SMS
hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &hSms,
&hSmsEvent);
if(FAILED(hr)) {
OutputDebugString(TEXT("Could not open a handle to
the SMS text message service."));
return fReturn;
}
// Wait for SMS to become signaled as ready
DWORD dwReturn = 0;
dwReturn = WaitForSingleObject(hSmsEvent, INFINITE);
// SMS Event has become signaled
if(dwReturn == WAIT_ABANDONED || dwReturn ==
WAIT_TIMEOUT) {OutputDebugString(TEXT("No longer waiting for
a message"));
SmsClose(hSms);
return fReturn;
}
// Send an SMS Message through default SMSC
SMS_ADDRESS smsDestination;
SMS_MESSAGE_ID smsMsgId = 0;
// Set the destination address for the message
memset(&smsDestination, 0, sizeof(SMS_ADDRESS));
smsDestination.smsatAddressType = SMSAT_INTERNATIONAL;
_tcsncpy(smsDestination.ptsAddress, tchPhoneNumber,
SMS_MAX_ADDRESS_LENGTH);
// Create the message
DWORD dwMessageLength = 0;
dwMessageLength = lstrlen(tchMessage)*sizeof(TCHAR);
// Configure the Text Provider
TEXT_PROVIDER_SPECIFIC_DATA txtProviderData;
DWORD dwProviderLength = 0;
memset(&txtProviderData, 0, sizeof(TEXT_PROVIDER_
SPECIFIC_DATA));
txtProviderData.dwMessageOptions =
PS_MESSAGE_OPTION_STATUSREPORT;
txtProviderData.psMessageClass = PS_MESSAGE_CLASS0;
txtProviderData.psReplaceOption = PSRO_NONE;
dwProviderLength = sizeof(TEXT_PROVIDER_SPECIFIC_DATA);
// Send the message
hr = SmsSendMessage(hSms, NULL, &smsDestination, NULL,
(BYTE *)tchMessage, dwMessageLength, (LPBYTE)&txtProviderData,
dwProviderLength, SMSDE_OPTIMAL, SMS_OPTION_DELIVERY_NONE,
&smsMsgId);
if(FAILED(hr))
OutputDebugString(TEXT("Could not send SMS Text
Message."));
else {
OutputDebugString(TEXT("Message has been sent."));
fReturn = TRUE;
}
SmsClose(hSms);
return fReturn;
}
Second, use P/Invoke from C# to send an SMS by calling into the wrapper function, as follows:
using System;
using System.Data;
using System.Runtime.InteropServices;
namespace SmsInvokeTest {
class Class1 {
// Hook up to wrapper function
[DllImport("smsinvoke.dll", EntryPoint=
"SendSMSInvokeMsg", CharSet=CharSet.Unicode,
SetLastError=true)]
static extern Int32 SendSmsMessage(string
stPhoneNumber, string stMessage);
static void Main(string[] args) {
// Create a message, and send it via SMS
string stPhone = "4254432273";
string stMessage = "Hi there from the Compact
Framework!";
int nResult = 0;
nResult = SendSmsMessage(stPhone, stMessage);
}
}
}
Use the following table of contents to navigate to chapter excerpts, or click here to view Chapter 12 in its entirety.
| ABOUT THE BOOK: |
|
| Pocket PC Network Programming is a comprehensive tutorial and reference for writing network applications on Pocket PC 2002 and Pocket PC 2002 Phone Edition devices. It explains how the Pocket PC communicates with the Internet, with other mobile devices, and with networks. Click here to purchase the book from Addison-Wesley. |
| ABOUT THE AUTHOR: |
|
| Steve Makofsky is a software design engineer on Microsoft's .NET XML Messaging team. In addition to having been a Microsoft Embedded MVP, he has worked on several commercial Windows CE products, including the award-winning bUSEFUL Utilities 1.0 and 2.0 (Best of Comdex Utility 1998/1999). Steve coauthored Teach Yourself Windows CE Programming in 24 Hours (Sams, 1999) and has published several magazine articles on .NET and mobile device development. When not working on cool embedded projects, Steve likes to drink lattes and hike on Mt. Everest. |
|