Version imprimable       Envoyer     
Cliquez pour évaluer et commenter
MSDN
MSDN Library
Articles Techniques
.NET Compact Framework
Mise en route
 Envoyer des SMS à partir d'applicat...
Envoyer des SMS à partir d'applications Microsoft .NET Compact Framework

Derek Mitchell
DEVBUZZ.COM, Inc.

S'applique à :
    Microsoft® .NET Compact Framework 1.0
    Microsoft Visual Studio® .NET 2003

Résumé : Apprenez à envoyer des messages SMS (Short Message Service) à partir de votre application basée sur .NET Compact Framework.

Téléchargez SendSMS.msi ( Quitter le site MSDN France Site en anglais)

Sommaire

Introduction
Envoi de SMS en C#
Envoi de SMS en VB.NET
Utilisation du code
Conclusion

Introduction

Étant donné que Microsoft® .NET Compact Framework v1.0 a une fonctionnalité de sous-ensemble, il est parfois nécessaire de recourir aux appels traditionnels du système d'exploitation, exposés via la couche de programmation de l'API Microsoft Windows® CE. En code natif, c'est un jeu d'enfant ; en code géré en revanche, il faut effectuer quelques manipulations pour assurer la compatibilité avec les codes non gérés. Dans ce document, nous étudions un exemple spécifique où ces manipulations s'avèrent nécessaires : l'envoi de messages courts SMS (Short Message Service) à partir de code C# et de code Microsoft Visual Basic® .NET. SMS vous permet d'envoyer des messages pouvant contenir jusqu'à 160 caractères d'un appareil portable à un autre. À mesure que les appareils utilisant le système mondial de communications mobiles (réseau GSM) se multiplient, le système SMS constitue un moyen de plus en plus courant d'envoyer des messages courts. S'il est facile d'intégrer la prise en charge des SMS dans du code natif, nous essayons ici d'aller plus loin et de vous montrer comment réaliser cela dans du code géré.

Envoi de SMS en C#

Tout le code qui apparaît ci-dessous peut être téléchargé. Pour réaliser et exécuter du code, il nous faut réaliser deux tâches indispensables. Premièrement, nous devons déclarer un ensemble de constantes qui sont passées à notre opérateur GSM de communications mobiles pour qu'il puisse comprendre comment nous souhaitons que le message soit délivré. Par exemple, l'envoi du SMS doit-il être réitéré jusqu'à ce que la transmission aboutisse ou l'opération doit-elle être interrompue au premier échec ? Pour des raisons pratiques, l'ensemble du programme sera associé à l'espace de noms Microsoft.Wireless. Cet espace de noms a une grande capacité d'extension dans d'autres domaines de fonctionnalités sans fil. Tout d'abord, nous allons définir une énumération indiquant ce que le numéro de téléphone de notre destinataire représente :



public enum AddressType
 {
 Unknown,
  International,
  National,
  NetworkSpecific,
  Subscriber,
  Alphanumeric,
  Abbreviated
 }

Ensuite, nous définirons en quoi consiste une adresse téléphonique. Nous appelons ce type PhoneAddress. Ici, nous indiquons qu'il s'agit d'un AddressType accompagné d'une chaîne représentant les chiffres du numéro. Nous écrivons ce qui suit :


public struct PhoneAddress
 {
  public AddressType AddressType;
  public String Address;
 }

Maintenant, nous pouvons commencer à définir les comportements souhaités pour cet exemple en encapsulant les opérations SMS de base (ouvrir, envoyer et fermer) dans une classe C# que nous appelons SMS. La structure SMS requiert plusieurs constantes. Nous les définissons au début de notre classe :

private static string SMS_MSGTYPE_TEXT   = "Microsoft
  Text SMS Protocol";
  
private static long 
 SMS_MODE_SEND    = 0x00000002;
  
private static long 
 SMS_OPTION_DELIVERY_NONE = 0x00000000;
  
private static long 
 SMS_OPTION_DELIVERY_NO_RETRY = 0x00000001;
  
private static long 
 PS_MESSAGE_OPTION_NONE  = 0x00000000;

SMS_MSGTYPE_TEXT indique que le protocole utilisé est Microsoft Text SMS Protocol. SMS_MODE_SEND indique le mode envoi. SMS_OPTION_DELIVERY_NONE indique qu'aucune option particulière n'est définie tandis que SMS_OPTION_DELIVERY_NO_RETRY demande que la livraison du message ne soit pas reprogrammée selon les politiques du routeur. Sans cette option, le SMS serait renvoyé jusqu'à abandon du routeur. PS_MESSAGE_OPTION_NONE indique qu'aucune information de notification ne sera renvoyée à l'application. Ensuite, nous incluons plusieurs énumérations qui décrivent également divers comportements prescrit pour le message SMS.

private enum SMS_DATA_ENCODING
  {
   SMSDE_OPTIMAL=0,
   SMSDE_GSM,
   SMSDE_UCS2,
  }

SMSDE_OPTIMAL prend la méthode de codage des données qui représente le mieux l'intégralité du message avec la taille de transmission minimum. SMSDE_GSM oblige à utiliser un codage 7 bits comme indiqué dans la spécification GSM. Les caractères qui ne peuvent pas être codés comme il se doit ne pourront pas être traités correctement. Enfin, SMSDE_UCS2 utilise le codage UCS2.

private enum PROVIDER_SPECIFIC_MESSAGE_CLASS

{

PS_MESSAGE_CLASS0 = 0,

PS_MESSAGE_CLASS1,

PS_MESSAGE_CLASS2,

PS_MESSAGE_CLASS3,

}

PS_MESSAGE_CLASS0 indique que le message doit être affiché immédiatement sans être stocké dans le module d'identification de l'abonné (Subscriber Identity Module, SIM). PS_MESSAGE_CLASS1 indique que le centre de services qui gère le message doit être averti de la transmission du message et que celui-ci doit être stocké. PS_MESSAGE_CLASS2 indique que le message doit être d'abord envoyé au champ de données SMS dans la carte SIM de l'utilisateur, avant que la notification soit envoyée au centre de services gérant le message. Si la carte SIM est remplie, un message d'erreur est envoyé au centre de services. PS_MESSAGE_CLASS3 indique que le message est arrivé à destination et peut être stocké sur la carte SIM ; le centre de services recevra une notification.

private enum PROVIDER_SPECIFIC_REPLACE_OPTION
  {
   PSRO_NONE = 0,
   PSRO_REPLACE_TYPE1,
   PSRO_REPLACE_TYPE2,
   PSRO_REPLACE_TYPE3,
   PSRO_REPLACE_TYPE4,
   PSRO_REPLACE_TYPE5,
   PSRO_REPLACE_TYPE6,
   PSRO_REPLACE_TYPE7,
   PSRO_RETURN_CALL,
   PSRO_DEPERSONALIZATION,
  }

Les options mentionnées ci-dessus spécifient (le cas échéant) quelle est, parmi les notifications précédentes, celle qui doit être remplacée par le message en cours. Le type spécifié par l'option garantit que toutes les notifications précédentes de ce type seront remplacées par la notification en cours. Par exemple, si PSRO_REPLACE_TYPE3 est défini, toutes les notifications de type 3 seront remplacées par la notification actuelle. PSRO_RETURN_CALL indique que l'adresse d'origine prendra en charge le rappel téléphonique.

private struct TEXT_PROVIDER_SPECIFIC_DATA

{

public IntPtr dwMessageOptions;

public PROVIDER_SPECIFIC_MESSAGE_CLASS psMessageClass;

public PROVIDER_SPECIFIC_REPLACE_OPTION psReplaceOption;

}

Ces champs utilisent tous les énumérations que nous avons définies ci-dessus. Leur transmission se fait lors de l'envoi/réception d'un SMS sous forme d'une chaîne. dwMessageOptions, sous forme d'un entier, spécifie divers détails décrits dans la spécification 3.40. Pour plus de simplicité, nous n'avons pas attaché d'options. Dans la plupart des cas, elles ne sont pas nécessaires. Dans les deux champs suivants, nous nous contentons de spécifier les options à partir des énumérations précédentes.

Nous pouvons enfin tirer parti de P/Invoke et appeler les fonctions de l'API Win32. Nous gérons d'abord SmsOpen, qui ouvre le composant de messagerie SMS pour permettre l'envoi et la réception des messages. Sa signature est la suivante :



HRESULT SmsOpen (
const LPCTSTR ptsMessageProtocol,
const DWORD dwMessageModes,
SMS_HANDLE* const psmshHandle,
HANDLE* const phMessageAvailableEvent);



ptsMessageProtocol est une chaîne qui indique qu'il faut utiliser le protocole SMS. dwMessageModes spécifie si nous voulons être en mode envoi ou en mode réception. psmshHandle est un pointeur vers le handle de la session SMS. Il n'est valide que si le renvoi de la fonction se fait de façon correcte. phMessageAvailableEvent est le handle qui pointe vers un handle d'événement Win32 pouvant être utilisé pour déterminer quand le prochain message pourra être lu. Il n'est pas nécessaire de fermer ce handle car SmsClose le fait automatiquement. Nous utilisons DllImport pour rendre cette fonction disponible dans notre code géré.

 [DllImport("sms.dll")]

private static extern IntPtr SmsOpen(String ptsMessageProtocol,
  IntPtr dwMessageModes,
ref
 IntPtr psmshHandle, IntPtr
  phMessageAvailableEvent);

Ensuite nous nous intéressons à SmsSendMessage. Dans son formulaire d'API Win32, sa signature est la suivante.



HRESULT SmsSendMessage (
const SMS_HANDLE smshHandle,
const SMS_ADDRESS * const psmsaSMSCAddress,
const SMS_ADDRESS * const psmsaDestinationAddress,
const SYSTEMTIME * const pstValidityPeriod,
const BYTE * const pbData,
const DWORD dwDataSize,
const BYTE * const pbProviderSpecificData,
const DWORD dwProviderSpecificDataSize,
const SMS_DATA_ENCODING smsdeDataEncoding,
const DWORD dwOptions,
SMS_MESSAGE_ID * psmsmidMessageID);



smshHandle est le handle renvoyé dans psmshHandle par SmsOpen. psmsaSMSCAddress est un paramètre optionnel qui indique le centre de messages SMS à utiliser. Si NULL est spécifié, c'est le centre de messages SMS par défaut de l'utilisateur qui sera utilisé. psmsaDestinationAddress indique où le message doit être délivré. pstValidityPeriod se distingue de la structure standard SYSTEMTIME dans la mesure où il s'agit du laps de temps nécessaire à l'envoi d'un SMS durant lequel le message est encore considéré comme valide. pbData est la représentation en octets de la portion de données du message. Elle peut être définie sur NULL. dwDataSize est la taille en octets de la portion de données du message. pbProviderSpecificData correspond aux informations supplémentaires exigées par certains fournisseurs pour permettre la transmission correcte d'un SMS. dwProviderSpecificDataSize est la taille en octets du champ précédemment mentionné. smsdeDataEncoding est une option qui se trouve dans l'énumération SMS_DATA_ENCODING décrite précédemment. dwOptions correspond (actuellement) à deux indicateurs qui indiquent si le SMS doit échouer après une tentative ou si l'envoi doit être réitéré jusqu'à abandon du routeur. Enfin, psmsmidMessageID est non nul lorsque le traitement de la fonction réussit. Là encore, c'est en utilisant DllImport que nous pouvons accéder à SmsSendMessage via notre code géré :

 [DllImport("sms.dll")]

private static extern IntPtr SmsSendMessage(IntPtr smshHandle, IntPtr
 psmsaSMSCAddress, IntPtr psmsaDestinationAddress, IntPtr
 pstValidityPeriod, 
byte
[] pbData, IntPtr dwDataSize, 
byte
[]
 pbProviderSpecificData, IntPtr dwProviderSpecificDataSize,
 SMS_DATA_ENCODING smsdeDataEncoding, IntPtr dwOptions, IntPtr
 psmsmidMessageID);

La dernière fonction que nous utiliserons pour l'envoi de SMS est SmsClose. Cette fonction tente de fermer proprement les requêtes de services de messages SMS. Nous faisons passer notre smshHandle présenté ci-dessus et nous obtenons en retour une valeur indiquant les éventuelles erreurs rencontrées lors de l'envoi.



HRESULT SmsClose (
const SMS_HANDLE oCommandBarPopup);



Dans notre code, nous déclarons cela par

[DllImport("sms.dll")]

private static extern IntPtr SmsClose(IntPtr smshHandle);

Enfin, nous pouvons fouiller dans notre première implémentation de fonction, SendMessage. Pour commencer, nous déclarons cette fonction comme non sécurisée parce qu'elle utilise les pointeurs. Nous devons d'abord appeler SmsOpen pour informer le système d'exploitation que nous souhaitons accéder à la carte SIM, et nous gérons le cas où cette opération échoue.

IntPtr res = SmsOpen(SMS_MSGTYPE_TEXT, (IntPtr)SMS_MODE_SEND, ref hSms,
 IntPtr.Zero);
if (res != IntPtr.Zero)
  throw new Exception("Could not open SMS.");

Nous créons ensuite la représentation en octets du numéro de téléphone de destination (passé dans une chaîne). Nous déclarons un pointeur pAddr (qui pointe à l'origine vers la même adresse que bDest) ; nous faisons en sorte que le nettoyage de la mémoire ne déplace pas cette variable de façon imprévisible. En dehors des parenthèses qui l'enferment, pAddr n'est plus fixe et son accès n'est pas fiable.

 Byte[] bDest =
new Byte[516];
  
fixed (byte*
 pAddr = bDest)

Nous écrivons maintenant la première partie de PhoneAddress dans notre IntPtr en indiquant qu'il s'agit d'un format inconnu (le système d'exploitation décidera pour nous). Nous utilisons WriteInt32 parce que nous accédons directement à la mémoire.

byte *pCurrent = pAddr;
  Marshal.WriteInt32((IntPtr)pCurrent, (
int
)AddressType.Unknown);
  pCurrent +=4;

À présent, nous segmentons en octets la chaîne de représentation de notre numéro de destination et nous écrivons ces octets dans notre structure de type PhoneAddress, mais en incluant le décalage de 4 octets nécessaire à l'alignement de la structure.

foreach (byte b 
in
 Encoding.Unicode.GetBytes(sPhoneNumber))
  {
   Marshal.WriteByte((IntPtr)pCurrent, b);
   pCurrent++;
  }

Comme nous ne souhaitons pas placer d'option sur la structure de données du fournisseur, nous allouons de l'espace pour sa structure de représentation et gardons ces champs vides.

 Byte[] ProvData =
new Byte[12];

Puis nous segmentons notre message SMS (passé dans une chaîne) en octets que nous pouvons transmettre.

byte[] bMessage = Encoding.Unicode.GetBytes(sMessage);
  
int
 nMsgSize = bMessage.Length;

Ensuite, nous appelons la fonction qui utilise les variables que nous avons créées précédemment.

 res = SmsSendMessage(hSms, IntPtr.Zero, (IntPtr)pAddr, IntPtr.Zero,
  bMessage, (IntPtr)nMsgSize, ProvData, (IntPtr)ProvData.Length,
  SMS_DATA_ENCODING.SMSDE_OPTIMAL, (IntPtr)SMS_OPTION_DELIVERY_NONE,
  IntPtr.Zero);

Si cet appel renvoie autre chose que zéro, nous savons que nous avons rencontré des problèmes. Enfin, nous appelons SmsClose pour clore correctement cette session.

Envoi de SMS en VB.NET

Notre code pour VB.NET est un portage direct de ce que nous avons fait en C# ; par conséquent, nous nous contenterons de souligner les différences de syntaxe importantes. Toutes nos énumérations peuvent être copiées sur C#, sauf la syntaxe propre à VB.NET. Nous utilisons des variables IntPtr pour définir la valeur de la plupart de nos fonctions. Nous appelons SmsOpen comme précédemment et recherchons les erreurs.

retVal = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, smsHandle, IntPtr.Zero)

If retVal.ToInt32 <> 0 
Then


Throw New
 Exception("Could not open SMS.")
End If

Ensuite, nous mettons les représentations d'AddressType et d'address sous forme d'octets et remplissons la variable smsAddressTag comme s'il s'agissait de notre structure PhoneAddress.



Dim
 smsatAddressType 
As Byte
() =
 BitConverter.GetBytes(SMS_ADDRESS_TYPE.SMSAT_UNKNOWN)

Dim
 ptsAddress 
As Byte
() = 
 System.Text.Encoding.Unicode.GetBytes(sPhoneNumber)

Dim
 smsAddressTag(smsatAddressType.Length + ptsAddress.Length) 
As Byte

Array.Copy(smsatAddressType, 0, smsAddressTag, 0, smsatAddressType.Length)
Array.Copy(ptsAddress, 0, smsAddressTag, smsatAddressType.Length,
 ptsAddress.Length)

Dim
 smsAddress 
As
 IntPtr = Marshal.AllocHLocal(smsAddressTag.Length)
System.Runtime.InteropServices.Marshal.Copy(smsAddressTag, 0, smsAddress,
 smsAddressTag.Length)

Puis nous segmentons notre chaîne de message en octets et remplissons un tableau d'octets avec ces éléments. Nous pouvons alors transmettre toutes nos variables au travers de SmsSendMessage.



Dim
 smsMessageTag 
As Byte
() =
 System.Text.Encoding.Unicode.GetBytes(sMessage)
smsMessage = Marshal.AllocHLocal(smsMessageTag.Length)
System.Runtime.InteropServices.Marshal.Copy(smsMessageTag, 0, smsMessage,
 smsMessageTag.Length)

retVal = SmsSendMessage(smsHandle, 0, smsAddress, 0, smsMessage,
 smsMessageTag.Length, ProvData, 12, SMS_DATA_ENCODING.SMSDE_OPTIMAL,
 SMS_OPTION_DELIVERY_NONE, 0)

Nous vérifions les erreurs puis effectuons le renvoi.

Utilisation du code

Pour envoyer un SMS à partir de votre code C#, appelez notre code de la façon suivante :

Microsoft.Wireless.SMS.SendMessage("Phone Number","Message");
To send an SMS from within your VB.NET code, call our code by:

Conclusion

Cet article avait pour intention de montrer avec quelle simplicité vous pouvez appeler des API Win32 natives importantes à partir de C# et de VB.NET. De plus, en modifiant très légèrement la syntaxe, il est possible d'effectuer un « portage » de C# vers VB.NET en un temps record. Notons que la portabilité souligne l'une des caractéristiques les plus séduisantes de .NET Compact Framework : sa capacité à créer des objets dans un langage et de les utiliser dans un autre langage compatible avec .NET. Nous aurions pu créer notre code principal en C# et l'envelopper dans un Windows Form créé en VB.NET.



Dernière mise à jour le lundi 5 mai 2003



© 2009 Microsoft Corporation. Tous droits réservés. Conditions d'utilisation | Marques | Confidentialité
Page view tracker