Book Excerpt

Pocket PC and P/Invoke

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

Set this value to TRUE to enable calling the Marshal.GetLastWin32Error method to check if an error occurred when invoking this method
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

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   @1
// 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.



.NET Compact Framework
  Home:Introduction
 Part 1:.NET Compact Framework
 Part 2: Networking with the Compact Framework
 Part 3:Winsock, .NET and the Compact Framework
 Part 4:Internet Protocols and the .NET Pluggable Protocol Mode
 Part 5:Consuming Web Services and the Handheld Device
 Part 6: Pocket PC and P/Invoke
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.

This was first published in December 2006

There are Comments. Add yours.

 
TIP: Want to include a code block in your comment? Use <pre> or <code> tags around the desired text. Ex: <code>insert code</code>

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
Sort by: OldestNewest

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to: