Welcome to MSDN Blogs Sign in | Join | Help

Microsoft SQL Server 2005 Mobile Edition (SQL Server Mobile) maintains two versions of the product side-by-side. The two versions of SQL Server Mobile can coexist on the same computer. This enables a simple and gradual upgrade process.
The documentation on how this can be done can be found here:
http://whidbey.msdn.microsoft.com/library/default.asp?url=/library/en-us/ssmmain3/html/36976e7b-39f3-4837-891e-cd16300ffa5e.asp

However when I attempted to perform this task on my Pocket PC 2003 device, I got the following error:

Connecting to source database
Creating destination database
Unable to instantiate SQL CE 3.0 provider
 
HRESULT: 0x80040154
Database upgrade failed

After doing some further investigation, I discovered that it was unable to instantiate the SQL Server Mobile OLE DB Provider (sqloledb30.dll). Some further search through the documentation revealed that this dll resides in sqlce30.repl.platform.processor.cab.
See the following link http://whidbey.msdn.microsoft.com/library/default.asp?url=/library/en-us/ssmmain3/html/15cf980e-4230-4629-acd7-33817c670f51.asp
 
So in order for me to successfully upgrade my database (I’m assuming here that the device already has SQL Server CE 2.0 installed) I need to ensure that I have deployed and installed the following cabs:
1. sqlce30.platform.processor.cab
2. sqlce30.repl.platform.processor.cab
3. sqlce30.dev.lang.platform.processor.cab (useful for getting error descriptions).

I erroneously forgot to install sqlce30.repl.platform.processor.cab. Once I installed this, the upgrade process was successfully completed.

I believe this might be helpful someday just in case someone out there attempts to upgrade their database and runs into the same issue.

Yet another pretty cool application built with the .NET Compact Framework.

 

http://www.microsoft.com/presspass/features/2005/jun05/06-07BattleBot.mspx

 

Excerpt from the link above:

"I'm living proof that developers can use the .NET Compact Framework to communicate with and power machinery - from BattleBots to industrial-type machines to control systems in cars, whether locally powered or controlled over the Internet."

   This is the last post in this mini-series on implementing client side NTLM authentication within a NETCF Version 1.0 application. In this code snippet, the class that performs the actual authentication process is intialized and registered with the AuthenticationManager class. The web service can then be invoked after passing the required credentials. Authentication will be performed using NTLM.

   using Microsoft.Authentication.NTLM;

   //During Initialization Register the class that implements
   //IAuthenticationModule
   //This needs to be done only once.
   AuthenticationManager.Register(new NTLMAuth());
   //Initialize the Web Service
   SecureWS service = new SecureWS();
   
   //Supply the credentials
   System.Net.NetworkCredential Creds = new NetworkCredential("MyName", "MyPassWord", "DOMAIN");
   service.Credentials = Creds;

   //Call WebMethods from the WebService.

Here is the class that implements IAuthenticationModule and P/Invokes into the native dll created in the previous post.

using System;
using System.IO;
using System.Net;
using System.Data;
using System.Text;
using System.Runtime.InteropServices;

namespace Microsoft.Authentication.NTLM
{
 /// <summary>
 /// Summary description for Class1.
 /// </summary>
 public class NTLMAuth : IAuthenticationModule
 {
  [DllImport("NTLMAuthEx.dll")]
  public static extern IntPtr Initialize(String pszUserID, String pszPassword, String pszDomain);

  [DllImport("NTLMAuthEx.dll")]
  public static extern IntPtr Authenicate(byte[] pszResponse, byte[] pbBuffer, ref IntPtr nBufferSize);

  public NTLMAuth()
  {
  }

  public Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
  {
   NetworkCredential nc = (NetworkCredential)credentials;

   IntPtr dwBufferSize = IntPtr.Zero;
   byte[] bChallenge = null;
   bool fAuthFinished = false;

   if (challenge.StartsWith("NTLM"))
   {
    fAuthFinished = true;
    bChallenge =  Encoding.ASCII.GetBytes(challenge.Substring(5));
    dwBufferSize = (IntPtr)1029;
   }
   else
   {
    dwBufferSize = Initialize(nc.UserName, nc.Password, nc.Domain);
   }

   if ((int)dwBufferSize == 0)
   {
    throw new Exception("NTLM Authenticate Error: Could not allocate buffer");
   }

   byte[] bBuffer = new byte[(int)dwBufferSize];
   IntPtr dwReturn = Authenicate(bChallenge, bBuffer, ref dwBufferSize);

   if ((int)dwReturn != 0)
   {
    throw new Exception(String.Format("NTLM Authenticate Error: {0}", dwReturn));
   }

   string sToken = "NTLM " +  Encoding.ASCII.GetString(bBuffer, 0, (int)dwBufferSize);

   return new Authorization(sToken, fAuthFinished);
  }

  public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
  {
   return Authenticate("NTLM", request, credentials);
  }

  public string AuthenticationType
  {
   get
   {
    return "NTLM";
   }
  }

  public bool CanPreAuthenticate
  {
   get
   {
    return true;
   }
  }
 }
}

Here is the c++ code for the native library NTLMAuthEx.cpp, this is compiled into a native dll NTLMAuthEx.dll

// NTLMAuthEx.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "security.h"
#include "sspi.h"

#define SEC_SUCCESS(Status) ((Status) >= 0)

// structure storing the state of the authentication sequence
typedef struct _AUTH_SEQ {
    BOOL _fInitialized;
    CredHandle _hcred;
    BOOL _fHaveCredHandle;
    DWORD _cbMaxToken;
    BOOL _fHaveCtxtHandle;
    struct _SecHandle  _hctxt;
    BOOL _fUUEncodeData;
} AUTH_SEQ;


AUTH_SEQ g_authseq;

/************************************************************
 *    uuencode/decode functions
 ************************************************************/

//
//  Taken from NCSA HTTP and wwwlib.
//
//  NOTE: These conform to RFC1113, which is slightly different then the Unix
//        uuencode and uudecode!
//

const int pr2six[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

char six2pr[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
    '0','1','2','3','4','5','6','7','8','9','+','/'
};


BOOL uuencode(BYTE *bufin, DWORD nbytes, BYTE *pbOutData)
{
   unsigned int i;

   BYTE *outptr = pbOutData;

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
      *(outptr++) = six2pr[bufin[2] & 077];         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }

   *outptr = '\0';

   return TRUE;
}

BOOL uudecode(char *bufcoded, BYTE *pbuffdecoded, DWORD *pcbDecoded)
{
    int nbytesdecoded;
    char *bufin = bufcoded;
    BYTE *bufout;
    int nprbytes;

    /* Strip leading whitespace. */

    while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

    /* Figure out how many characters are in the input buffer.
     * If this would decode into more bytes than would fit into
     * the output buffer, adjust the number of input bytes downwards.
     */
    bufin = bufcoded;
    while(pr2six[*(bufin++)] <= 63);
    nprbytes = (int)(INT_PTR)(bufin - bufcoded - 1);
    nbytesdecoded = ((nprbytes+3)/4) * 3;

    bufout = pbuffdecoded;
    bufin = bufcoded;

    while (nprbytes > 0) {
        *(bufout++) =
            (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
        *(bufout++) =
            (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
        bufin += 4;
        nprbytes -= 4;
    }

    if(nprbytes & 03) {
        if(pr2six[bufin[-2]] > 63)
            nbytesdecoded -= 2;
        else
            nbytesdecoded -= 1;
    }

    pbuffdecoded[nbytesdecoded] = '\0';

    return TRUE;
}


BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    return TRUE;
}

extern "C" DWORD __declspec(dllexport) FAR PASCAL Initialize(LPTSTR pszUserID, LPTSTR pszPassword, LPTSTR pszDomain)
{
 SECURITY_STATUS ss;
 TimeStamp  Lifetime;

 ZeroMemory(&g_authseq, sizeof(_AUTH_SEQ));


 SEC_WINNT_AUTH_IDENTITY ident = {0};  
 SecPkgInfo *pspkg;

 ident.User = (unsigned short*)pszUserID;
 ident.Password = (unsigned short*)pszPassword;
 ident.Domain = (unsigned short*)pszDomain;

 ss = AcquireCredentialsHandle(NULL, L"NTLM", SECPKG_CRED_OUTBOUND, NULL, &ident, NULL, NULL, &g_authseq._hcred, &Lifetime);

 if (ss != STATUS_SUCCESS)
  return 0;

 ss = QuerySecurityPackageInfo(L"NTLM", &pspkg);
 if (ss != STATUS_SUCCESS)
  return 0;

 g_authseq._cbMaxToken = pspkg->cbMaxToken;

 FreeContextBuffer(pspkg);

 // Because the the result will be uuencoded, request the buffer to be 133%
 DWORD cbBufferSize = g_authseq._cbMaxToken + ((g_authseq._cbMaxToken + 3) / 3) + 4;

 g_authseq._fInitialized = TRUE;

 return cbBufferSize;
}

extern "C" DWORD __declspec(dllexport) FAR PASCAL Authenicate(BYTE *pbResponse, BYTE *pbBuffer, DWORD *pnBufferSize)
{
 DWORD   dwReturn = -1;
 BYTE   *pbuff = NULL;
 BYTE   *pBuffIn = NULL;
 DWORD   dwBuffInLen = 0;

 SECURITY_STATUS ss;
 TimeStamp  Lifetime;

 SecBufferDesc OutBuffDesc;
 SecBuffer  OutSecBuff;
    SecBufferDesc   InBuffDesc;
    SecBuffer       InSecBuff;
 ULONG   ContextAttributes;

 PCredHandle  phCredential = NULL;
 PSecBufferDesc pInput = NULL;


 if (!g_authseq._fInitialized)
 {
  goto exit;
 }

 ZeroMemory(pbBuffer, *pnBufferSize);

 if (pbResponse)
 {
  dwBuffInLen = strlen((char*)pbResponse);

  pBuffIn = new BYTE[dwBuffInLen];
  uudecode((char*)pbResponse, pBuffIn, &dwBuffInLen);
 }

 pbuff = new BYTE[*pnBufferSize];

 OutBuffDesc.ulVersion = 0;
    OutBuffDesc.cBuffers  = 1;
    OutBuffDesc.pBuffers  = &OutSecBuff;

    OutSecBuff.cbBuffer   = g_authseq._cbMaxToken;
    OutSecBuff.BufferType = SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer   = pbuff;

 if (pBuffIn)
    {
        InBuffDesc.ulVersion = 0;
        InBuffDesc.cBuffers  = 1;
        InBuffDesc.pBuffers  = &InSecBuff;

        InSecBuff.cbBuffer   = dwBuffInLen;
        InSecBuff.BufferType = SECBUFFER_TOKEN;
  InSecBuff.pvBuffer   = pBuffIn;

  phCredential = &g_authseq._hctxt;
  pInput = &InBuffDesc;
 }

 ss = InitializeSecurityContext(
  &g_authseq._hcred, phCredential, L"InetSvcs", 0, 0, SECURITY_NATIVE_DREP, pInput, 0, &g_authseq._hctxt,
  &OutBuffDesc, &ContextAttributes, &Lifetime);

 if (!SEC_SUCCESS(ss)) 
 {
  dwReturn = GetLastError();
  goto exit;
 }

 uuencode((BYTE*)OutSecBuff.pvBuffer, OutSecBuff.cbBuffer, pbBuffer);

 *pnBufferSize = strlen((char*)pbBuffer);

 dwReturn = 0;

exit:

 if (pbuff)
  delete [] pbuff;

 if (pBuffIn)
  delete [] pBuffIn;

 return dwReturn;
}

So I recently had to help one of my friends who had some problems calling a web method on a secure web service. He was running on NETCF 1.0 and kept getting a “401, Unauthorized error” even though he supplied the right credentials. It did not take us long to figure out that the web service required clients to be authenticated using the NTLM authentication mechanism. As most folks are probably aware, the .NET Compact Framework version 1.0 http client only supported Basic and Digest authentication (this limitation is no longer present in Version 2.0 of the compact framework) and if your web service required either NTLM (Windows authentication) or Kerberos authentication, it always resulted in a “401, Unauthorized” error at run time. You can work around this problem in 2 ways:

  •  Enable digest or basic authentication on the server as both are supported by NETCF. This assumes you have control over the server.

  • Implement the client side authentication code within your application. This involves the use of the AuthenticationManager to register a custom module that implements the IAuthenticationModule interface. I do not think there are many examples that show how this can be done.

Well so in my friend’s case he did not have any control over the server so there was no way for him to change the authentication mechanism required on the server so we had to resort to the second work around. I dug up some old emails and I saw some sample code that demonstrate how this can be done.
I will like to give credit to Jeffery Paul from Microsoft for the code I use in this series.

So basically here is the gist of how this is accomplished:
We have a native dll that performs the NTLM authentication. A class that implements the IAuthenticationModule P/Invokes to this native dll.
Using the AuthenticationManager class I can then register my class which performs the actual authentication.

Since there is quite a bit of code necessary to accomplish this I will break this task into 3 posts

  •  I will provide the C++ code for the native dll in part 2 of this post.

  • The class that implements IAuthenticationModule and P/Invokes into the native dll in part 3 of this post

  • And finally a example that demonstrate this in action in part 4 of this post.

After a long time off from blogging, I have decided to resume. I will try to be more consistent with my posts over the next few months. I attended MEDC and it was very fascinating to see what people are building on the .NET Compact Framework. I met quite a number of people who were interested in the managed support we have provided for MSMQ on devices. It was also very interesting to be able to match faces with a lot of familiar names.

There were some questions asked about what level of transaction support was built into CE MSMQ which I would attempt to answer in this blog post. As a backgrounder, it is helpful to know what transactions mean in the context of Message Queuing. Basically, transactional messages are used to pair the sending or receiving of any message with an action in another operation. Using transactional messages you can ensure that the unit of work is carried out as an atomic operation—that is, the operation succeeds or fails as a whole. Transactional messages can also be used to ensure that a message is delivered only once, and to ensure that all messages sent from one computer to another are delivered in order. Positive and negative acknowledgments can be used to confirm that messages reached, or were retrieved from, the destination queue. 

As I mentioned in previous posts transaction support on Windows CE MSMQ is limited to a single message transaction. So the question then arises:

What is a single message transaction and when do I need to use it?

A single message transaction implicitly uses the internal transaction support provided by Message Queuing and does not require the use of an external distributed transaction coordinator (DTC). The destination queue for a single message transaction must be a transactional queue. It is important to differentiate between exact once delivery as guaranteed by single message transactions (SMT) and single message coherency.

In MSMQ (on CE and on the desktop), A message gets sent/received in its entirety or it does not get received at all. As an example if you are attempting to send a message over a flaky wireless connection where the TCP connection breaks from time to time, the single message is guaranteed to be delivered intact or not delivered at all. This is the concept of single message coherency.  You do not need transactions to achieve single message coherency in MSMQ. You are guaranteed that out of the box.

Single Message Transactions on MSMQ CE allows you to communicate with a transactional queue. Lets assume for a second that you attempt to send a message to a transactional queue and you do not know that the queue is transactional as such you send your message in a non-transactional manner; Because Active Directory lookup is not supported in CE MSMQ there is no way to check if the remote queue is transactional as such MSMQ proceeds to attempt to open a connection to the remote queue so that it can send the message. Attempting to open the connection will be unsuccessful so it will just keep retrying indefinitely (unless you specify a TimeToReachQueue or TimeToBeReceived property). This is similar to what will happen if you attempt to send a message to a non-existent queue (MSMQ will assume that the destination queue is offline and will keep retrying the send operation in an outgoing queue). However it is important to know that this is an Operating System service, so this is abstracted from the sending application. As far as the application is concerned the message has been sent. Under the hood however the message remains in the outgoing queue and keeps attempting to connect so that the message can be sent.

On NETCF, you will have to do 2 things to send a message to a transactional queue.

·        Append the XACTONLY flag to the queue’s format name

·         Send the message with the overload that accepts a MessageQueueTransactionType parameter.

If one of the above is missing, a MessageQueueException with error code TransactionUsage and message “The transaction usage is invalid” is thrown.

The correct way to do that is highlighted in the sample code below.

So why do I need to perform a transactional send operation then?

You might be in a situation where the remote queue is transactional and you want to be able to put a message into that queue (from your device) and have it read it in a transactional manner on the receiving side. Messages sent to a transactional queue have to be transactional.

I believe transactions should be used sparingly on device MSMQ applications and the scenario should be well planned. This is because MSMQ automatically sets the delivery mode of the transactional message to recoverable (the default delivery mode is express). This could potential cause a reduction in throughput but this way you are guaranteed that the messages will be delivered, even if a computer crashes while the message is en route to the queue.

So how does guaranteed delivery differ for transactional and non-transactional messages?

For a scenario in which there is no routing server, the story is exactly the same. A message is either delivered in its entirety or not delivered at all.

However routing adds a slightly different twist to it. Take for instance a case where you are using a routing server (e.g OutFRS queue that points to a Falcon Routing Server - which enables you to send messages to a public queue) if a non transactional message is sent and the routing server cannot deliver the message, the message is saved on the MSMQ server that cannot deliver the message, however if you use a transactional message and the message cannot be delivered the message is saved on the source machine where it originated and not on the machine that fails to deliver the message. This way transactional messaging can guarantee the delivery of routed messages.

Please let me know via your comments if there is any other aspect of MSMQ programming on NETCF you will like to see covered in this blog posts.

Now some bit of code:

using System;
using System.Messaging;

public class TransactionTest
{
  public static void Main()
  {
    string strDestQ = @"FormatName:Direct=OS:MyServerName\Private$\transq;XACTONLY";
     try
     {

         MessageQueue messageQueue = new MessageQueue();
         messageQueue.Path = strDestQ;

         Message m = new Message();
         m.Label = "Sample Message";
         m.Body = "Hello World";

         messageQueue.Send(m,MessageQueueTransactionType.Single);
         messageQueue.Close();
         Console.WriteLine("First Transactional Message sent!");
     
}
     catch(MessageQueueException mex)
     {
         Console.WriteLine("Message Queue Exception");
         Console.WriteLine("Error Code is {0}",mex.MessageQueueErrorCode);
         Console.WriteLine("Exception is {0}",mex.ToString());
     }
     catch (Exception ex)
     {
         Console.WriteLine(ex.ToString());
     }

   }
}

So I was surfing the web for some technical articles recently (which BTW happens to be one of my favorite pastimes) and I ran into this article by Peter A. Bromberg. In this article he covers a sample MSMQ implementation that sends a message (MSMQ message) over the Internet via HTTP to a remote queue and having a trigger on the remote queue spawn an instance of an Executable that handles some required business logic in the enterprise. It’s quite an interesting read and I think this would be one of the many usage scenarios we would see in CE MSMQ. However one of the limitations of the Pocket PC is that it does not support HTTP-based protocol SRMP (i.e. Soap Reliable Messaging Protocol) as a result you’ll have to use the proprietary MSMQ protocol for sending messages. A good way to look at this limitation is to consider the fact that with the SRMP protocol you incur an additional overhead which increases size and may not be a cost effective implementation for connections that charge by size of packets sent (e.g. GPRS).

There has been a lot of excitement about the publicly available Beta of VS.NET 2005. This includes NETCF version 2.0 with all the exciting new features. We’ll be monitoring the newsgroup for feedback, questions and bugs.

 

Exactly what can be sent in a message? Literally anything …Well not exactly anything but any object can be sent as the body of a message. This is made possible by a mechanism known as Serialization.

Serialization is the process of taking objects and converting their state information into a form that can be stored or transported. The basic idea of serialization is that an object writes its current state, usually indicated by the value of its member variables, to persistent storage. Later, the object can be re-created by reading, or deserializing, the object's state from the storage. Serialization handles all the details of object pointers and circular object references that are used when you serialize an object.

In MSMQ, this process refers specifically to converting an object or set of data into a message that can be sent to a queue and then converting messages retrieved from the queue back into objects or data that the receiving application can process. The component that controls this process is the Formatter. The Formatter property of a MessageQueue contains an instance of a Formatter object which transforms messages when your application reads or writes to the queue. In other words when the application sends messages to the queue, the formatter serializes the object into a stream and inserts it into the message body. When reading from a queue, the formatter deserializes the message data into the Body property of a Message.

On the full .NET Framework there are 3 Formatters provided namely, the ActiveXMessageFormatter, BinaryMessageFormatter and XmlMessageFormatter.

However on NETCF the only Formatter provided is the XmlMessageFormatter (Which uses our new publicly exposed XmlSerializer J - this is my pet feature). You can however write your own custom Formatter that implements the IMessageFormatter. In your custom Formatter for instance, you could encrypt and decrypt your message body and you could also do compression. This would be relatively easy to do as the IMessageFormatter has only 3 public methods. This article details how this can be done in C#.  

The XmlMessageFormatter is the default formatter that an instance of MessageQueue uses to serialize messages written to the queue. Note however that a queue’s default XmlMessageFormatter can be used to write to the queue but it cannot be used to read from the queue until the TargetTypes of TargetTypeNames property on the Formatter is set. This is because the TargetTypes (or TargetTypeNames) property tells the Formatter what schemas to attempt to match when deserializing a message thence allowing the Formatter to interpret the message body. It is recommended that you set the TargetTypes property (instead of the TargetTypeNames property) as type existence will be checked at compile time rather than run time thence reducing the possibility for error.

The following code snippets demonstrates how to use the XmlMessageFormatter to receive an instance of a user defined type (i.e. BookOrder) sent as the body of a message. In this code I assume an instance of a BookOrder has been sent to the private queue and we attempt to retrieve it asynchronously.

  

string qName = @".\private$\mqdemoAck";

 

                  if(!MessageQueue.Exists(qName))

{

                        ackQueue = MessageQueue.Create(qName);

                  }

                  else

                  {

                        ackQueue = new MessageQueue(qName);

                  }

 

                  System.Type[] types = new Type[1];

                  types[0] = typeof(BookOrder);

 

ackQueue.Formatter = new XmlMessageFormatter(types);

      ackQueue.ReceiveCompleted += new

ReceiveCompletedEventHandler(MyReceiveCompleted);

                  ackQueue.BeginReceive();       

                 }

private void MyReceiveCompleted(Object source, ReceiveCompletedEventArgs

asyncResult)

            {

                  try

                  {

                        System.Messaging.Message message =

this.ackQueue.EndReceive(asyncResult.AsyncResult);   

                        if(message.Body is BookOrder)

                        {

                              BookOrder bookOrder = message.Body as BookOrder;

                              //Process bookOrder

                  }

                        else

                        {

                              MessageBox.Show("Message in Queue is not a book

Order");

                        }

                  }

                  catch(MessageQueueException e)

                  {

                        //handle Exception

                  }

                  this.ackQueue.BeginReceive();

            }

    

 

Please see the following documentation for more information on message serialization.

Message Serialization (MSDN)

Formatter Property (MSDN)

Well I think it’s time to get our hands dirty with some code. In this simple application, I intend to demonstrate how to get MSMQ up and running on the device and send our first “Hello World” message to a local queue on the device. We’ll also receive the message from the queue and display it in a message box. While this is not terribly fanciful it provides the foundation required to utilize this technology on the device. My primary aim in this program is to show how to install and start MSMQ service on the device from managed code.

You’ll have to grab the following components from the SDK and add them to the project as type content (msmqadm.exe,msmqadmext.dll,msmqd.dll and msmqrt.dll)

using System;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.IO;

using System.Data;

using System.Messaging; //You’ll have to use Add Reference to add the

//System.Messaging dll to your Project

using System.Runtime.InteropServices;

 

namespace SimpleMessagingApp

{

    public partial class Form1 : System.Windows.Forms.Form

    {

        private System.Windows.Forms.MainMenu mainMenu1;

       

 

  public class ProcessInfo

        {

            public IntPtr hProcess;

            public IntPtr hThread;

            public Int32 ProcessId;

            public Int32 ThreadId;

        }

 

        [DllImport("CoreDll.DLL", SetLastError = true)]

        private extern static

            int CreateProcess(String imageName,

            String cmdLine,

            IntPtr lpProcessAttributes,

            IntPtr lpThreadAttributes,

            Int32 boolInheritHandles,

            Int32 dwCreationFlags,

            IntPtr lpEnvironment,

            IntPtr lpszCurrentDir,

            IntPtr lpsiStartInfo,

            ProcessInfo pi);

 

        [DllImport("CoreDll.dll")]

        private extern static

            Int32 GetLastError();

        [DllImport("CoreDll.dll")]

        private extern static

             Int32 GetExitCodeProcess(IntPtr hProcess, out Int32 exitcode);

        [DllImport("CoreDll.dll")]

        private extern static

             Int32 CloseHandle(IntPtr hProcess);

        [DllImport("CoreDll.dll")]

        private extern static

            IntPtr ActivateDevice(

              string lpszDevKey,

                Int32 dwClientInfo);

 

 

        [DllImport("CoreDll.dll")]

        private extern static

            Int32 WaitForSingleObject(IntPtr Handle,

            Int32 Wait);

 

        public static bool CreateProcess(String ExeName, String CmdLine)

        {

            Int32 INFINITE;

            unchecked { INFINITE = (int)0xFFFFFFFF; }

 

            ProcessInfo pi = new ProcessInfo();

          

            if (CreateProcess(ExeName, CmdLine, IntPtr.Zero, IntPtr.Zero,

                0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, pi) == 0)

            {

                return false;

            }

           

            WaitForSingleObject(pi.hProcess, INFINITE);

 

            Int32 exitCode;

            if (GetExitCodeProcess(pi.hProcess, out exitCode) == 0)

            {

                MessageBox.Show("Failure in GetExitCodeProcess");

                CloseHandle(pi.hThread);                        // Free handles

                CloseHandle(pi.hProcess);

                return false;

            }

 

            CloseHandle(pi.hThread);                        // Free handles

            CloseHandle(pi.hProcess);

 

            if (exitCode != 0)

                return false;

            else

                return true;

 

        }

    

        //This is the MSMQAdm.exe utility we’ll be using

        private const String MSMQ_ADM = @"\windows\msmqadm.exe";

        private const String MSMQ_DRIVER_REG = @"Drivers\BuiltIn\MSMQD";

        public Form1()

        {

            InitializeComponent();

 

            //We need to move the MSMQ components deployed to the system folder

CopyFilesRequired();

            //Check status of MSMQ (is it installed and running yet?

 

            if (!(CreateProcess(MSMQ_ADM, "status")))

            {

               

                //Deletes the MSMQ registry key and store directory.

    //All messages are lost.

                CreateProcess(MSMQ_ADM, "register cleanup");

                 

    //Installs MSMQ as device drivcers.

    if (CreateProcess(MSMQ_ADM, "register install"))

                {

                    MessageBox.Show("Registered Drivers Successfully");

                }

                else

                {

                    MessageBox.Show("Failed to install MSMQ! System Error = " + GetLastError().ToString());

                    return;

                }

   //Creates the MSMQ Configuration in Registry

                if (CreateProcess(MSMQ_ADM, "register"))

                {

                    MessageBox.Show("Registered MSMQ Successfully");

                }

                else

                {

                    MessageBox.Show("Failed to Register MSMQ! System Error = " + GetLastError().ToString());

                    //  return;

                }

    //Enables the native MSMQ protocol

                if (CreateProcess(MSMQ_ADM, "enable binary"))

                {

                    MessageBox.Show("Enable Binary Successfully");

                }

                else

                {

                    MessageBox.Show("Failed to enable Binary! System Error = " + GetLastError().ToString());

                    return;

                }

    //Starts the MSMQ service

                if (CreateProcess(MSMQ_ADM, "start"))

                {

                    MessageBox.Show("Started MSMQ Successfully");

                }

                else

                {

  //This is one additional step that is needed for PocketPCs

  //The Device Drivers have to be loaded before the service

  //can be started

                    //ActivateDevice will load the device drivers

                    IntPtr handle = ActivateDevice(MSMQ_DRIVER_REG, 0);

                    CloseHandle(handle);

                    //Let us check if MSMQ is running

                    if (CreateProcess(MSMQ_ADM, "status"))

                    {

                        MessageBox.Show("MSMQ is up and running");

                    }

                    else

                    {

                        MessageBox.Show("Failed to start MSMQ! System Error = " + GetLastError().ToString());

                    }

                  }

            }

            else

                MessageBox.Show("MSMQ is already up and running");

 

        }

        private static void CopyFilesRequired()

        {

            string _path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);

            if (!File.Exists(@"\windows\msmqadm.exe"))

                File.Copy(_path+"\\msmqadm.exe", @"\windows\msmqadm.exe");

            if (!File.Exists(@"\windows\msmqadmext.dll"))

                File.Copy(_path + "\\msmqadmext.dll", @"\windows\msmqadmext.dll");

            if (!File.Exists(@"\windows\msmqd.dll"))

                File.Copy(_path + "\\msmqd.dll", @"\windows\msmqd.dll");

            if (!File.Exists(@"\windows\msmqrt.dll"))

                File.Copy(_path + "\\msmqrt.dll", @"\windows\msmqrt.dll");

 

            File.Delete(_path + "\\msmqadm.exe");

            File.Delete(_path + "\\msmqadmext.dll");

            File.Delete(_path + "\\msmqd.dll");

            File.Delete(_path + "\\msmqrt.dll");

        }

      

       //We will send and receive a message in button1’s click event handler

      private void button1_Click(object sender, EventArgs e)

        {

//The Queue on the device we will be sending messages to

            string strDestQ = @".\private$\destq";

            try

            {

                if (!MessageQueue.Exists(strDestQ))

                    MessageQueue.Create(strDestQ);

                MessageQueue mq = new MessageQueue(strDestQ);

                Message m = new Message();

      

                m.Label = "Hello World";

                m.Body = "Hello World";

                mq.Send(m);

                mq.Close();

                MessageBox.Show("We sent our First Message!!");

              

               mq.Formatter = new XmlMessageFormatter(new Type[]

                {typeof(String)});

               Message messageReceived = mq.Receive();

 

            MessageBox.Show((string)messageReceived.Body);

           }

            catch (Exception ex)

            {

                MessageBox.Show(ex.ToString());

            }

        }

    }

}

I will commence this sequence of posts on MSMQ support on CF version 2.0 by giving some pointers on how to get the Microsoft Message Queuing service up and running on your device. The reason for this is quite obvious, if the Queuing service is not up and running on the device then calling any of the functions to administer or monitor a queue will prove to be an exercise in futility. As a matter of fact if you try to call any of the methods that interact with a message queue when MSMQ is not installed the method will throw an InvalidOperationException with the message “Message Queuing has not been installed on this computer”. On the other hand if the Message Queuing components are present but the service is not running, the call fails with a MessageQueueException with the message “Message Queuing service is not available”.

As can be seen from this article, MSMQ can be built into some custom Windows CE systems (e.g. selecting the MSMQ component in Platform Builder), or rolling up the required MSMQ drivers (you can find them in the SDK) into the cab. Ken Rabold has a good example (complete with sources) on how this can be done for the Pocket PC in his article on devbuzz. Note however that Ken’s example is targeted towards PPC 2000 and PPC 2002. A few things have changed in PPC 2003 (and beyond). The netregd option is no longer needed and the device does not have to be rebooted to get the Messaging Queuing service running. However his example shows how you can install and configure the Messaging Queuing service as part of the application installation process.

I will show how this can be done from managed code (See Part III) by P/Invoking the CreateProcess API, an alternative would be to use the Process class provided in the OpenNETCF libraries. See this article for more information on how to P/Invoke into the CreateProcess API. The msmqadm.exe utility (deployed as part of the MSMQ components) is used to administer the installation process.

You’ll need to run the following to get MSMQ service installed and running

msmqadm.exe register install

msmqadm.exe register

msmqadm.exe enable binary (Enable the proprietary MSMQ binary protocol to send messages to remote queues)

msmqadm.exe enable srmp (Enable the SRMP (SOAP Reliable Messaging Protocol) for sending messages to remote queues over HTTP)

msmqadm.exe start

The following articles would prove to be very useful in providing answers to your device MSMQ installation problems.

MSMQ Installation on Custom Platforms

Programming MSMQ on the PocketPC

Using the MSMQAdm Utility

So one of the new features that’s going to be introduced in V2.0 of the .NET Compact Framework (NETCF) is support for Message Queuing. I feel it is a much anticipated feature and there is quite some excitement about this feature in the developer community.

 

A very high level intro…

The System.Messaging namespace provides classes that allow you to connect to, monitor, and administer message queues on the network and send, receive, or peek messages. Message Queuing can enable things like Inter-application communication without the need for a live connections and it is especially well suited for environments where multiple applications communicate over unreliable connections.

 

The mechanism is quite simple

A sending application’s message is accepted and placed in a queue that is accessible by the receiving application.

A receiving application can connect to the queue at any time to retrieve messages that have been left for it, and send back messages through same mechanism

 

You can find more background information about Managed support for System.Messaging here.

 

Since we are a subset of an existing technology, I think it would be valuable in the next series of posts to let you guys know upfront what is not supported on the NETCF implementation of System.Messaging.

Well since the classes provided in the System.Messaging.dll are essentially managed classes that wrap the underlying native Message Queuing APIs much of what we can do is limited by the capabilities of Windows CE MSMQ. The following features are not available on NETCF System.Messaging (due to limitations of CE MSMQ):

 

n      Remote Queue Read.

n      Encryption.

n      Security based on Access Control List (ACL)

n      MQMail.

n      Transaction support limited to single message transaction.

n      Public Queues based on Active Directory.

 

Support for Message Queuing on the Compact Framework eases application development for some pretty interesting “occasionally connected device” scenarios which embodies majority of mobile applications being developed today. A typical scenario is a sales inventory application where, before leaving the office, the user synchronizes the PDA with Data (either using MSMQ or via a Web Service) getting an updated list of items available in the inventory store. While at the customer site, no reliable connection exists and Orders can be taken. The application sends the order back to the server in the office
(by simply putting this into an outgoing queue). As soon as the sales person gets back to the office (or anywhere network connectivity exists), the order is transferred to the tracking system in the office. Since Message Queuing is an Operating System service, this last step is independent of the application (It does not have to be running), the OS will ensure that the messages in the Queue get sent as soon as a network connection is available.

  

Over the next series of posts I’ll be showing some sample NETCF System.Messaging code snippets that demonstrate some of this newly introduced functionality.

Continuing from where I stopped last week…

Feature Number 4 is Easier XPath Queries with Namespaces. This feature is dependent on the availability of the XPathDocument which currently is not present in our Beta1 release. So the current status of NETCF with regard to this is “Maybe”.

Feature Number 3 is The XPathDocument as a Faster DOM. Just as mentioned in previous posts this is not a Beta1 feature nevertheless it is being considered for V2.

Feature Number 2 is the XPathEditableNavigator, an updatable cursor (See above comments).

Feature Number 1 is Performance. You bet!! Our SP2 release featured some XmlTextReader performance improvements however we’re much more excited about our gains in performance in V2. The XmlTextReader performance has stepped up another notch (compared to the SP2 release). In V2 The XmlTextReader and XmlTextWriter performance (similar to Desktop performance improvement) are on the average twice as fast as SP2!!

This definitely makes V2 of NETCF something to look forward to.

My next post is going to feature System.Messaging on the Compact Framework.

As always feedback and comments are most welcome.              

 

 

   

I've been caught up in a bunch of other activites since my last post and did not think I will have to wait this long before making my next post... well...

So I'll be continuing from where I stopped last time...

Coming in at Number 9 is XML Standards Support by Default. As mentioned in the previous post none of the readers will have DTD support and as such we will not be able to resolve entity references however just like the full Desktop Framework you will be able to set the ConformanceLevel of the XmlReader as demonstrated in the code snippet. Pretty much everything covered in this feature also applies on NETCF with the exception of DTD support.

The Number 8 feature is Universal Type Support and Conversion. Yes in NETCF V2.0 the XmlReader and XmlWriter classes have also become type aware. In the upcoming Beta1 release we do not have the XPathNavigator class (it is a feature we are considering for V2.0 but it’s not going to be in Beta1). Just as Mark explained you will be able to use the ReadValueAsXXX methods on the XmlReader class to read and convert a CLR value in a single method call, you will also be able to use the WriteValue methods to write CLR values.

The Number 7 feature is XmlReader and XmlWriter Usability. I will not repeat all that he mentioned. I think it will be sufficient to state that all the new helper methods are fully supported on NETCF with the exception of the WriteNode method (this is dependent on the availability of the XPathNavigator in V2.0).

The Number 6 feature is The XQuery Language and the answer to this is a polite No. We do not plan to make this available in V2.0 of NETCF. I’m sure you already know that System.Xml in V2.0 of NETCF comes with XPath 1.0 support exposed via the SelectNodes and SelectSingleNode methods of the XmlNode class.

The Number 5 feature is Security.

Mark discusses the use of the Evidence property of the XmlReader – this is not available on NETCF Beta1. The ProhibitDtd property is also not supported on the XmlReaderSettings class because we always throws an exception whenever any DTD is encountered in the XML.

I plan to discuss the remaining 4 features in my next post.

As always feedback and comments are most welcome.

 

 

 

Mark Fussell recently authored a very comprehensive article on the MSDN XML developer center that details the improvements to the XML APIs in System.Xml and the .NET Framework for version 2.0. This is the first in a series of articles that will provide an in-depth review of the forthcoming V2.0 release for the System.Xml namespace and the XML support in ADO.NET. Mark did a terrific job with this and I think reading it is definitely worth your time (along with going thru the XML gems that are up on the developer center). Well the question, I bet that many .NET Compact Framework (CF) developers are asking is “exactly what will be supported on NETCF and what will not be available?” I thought it will be a good idea to review his article from a CF standpoint and let you guys know ahead of time what functionality we plan to provide (at least in Beta 1) and receive feedback from you guys. 

Mark structures the article by doing a top ten feature count down for System.Xml. Over the next few day's I'll be going through each of the features and give you a Yes (It's in CF Beta 1), No (We do not plan to support it) and a Maybe (We are considering it for v2.0 but it did not make it into Beta).

Coming in at No 10 feature is Static Creation Methods on XmlReader and XmlWriter 
The answer to this is “YES” .... you bet... in keeping with our model of maintaining consistency between the development story on both desktop and device we definitely thought this was one area we could not afford to deviate from the desktop story.

Yes this means (with our schema validation support introduced in V2.0) creating a reader that performs validation and strips insignificant whitespace from the document at the same time becomes as simple as this code snippet (which I pulled from the article).

XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add("books.xsd");
settings.XsdValidate = true;
settings.IgnoreWhitespace = true;
XmlReader reader = XmlReader.Create("books.xml",settings);
while(reader.Read()) {}

 Those of you who played with our V1 product remember that we did not have support for the XmlValidatingReader thus you could not perform run time data validation (against a schema) with your reader. While we do not provide a publicly available XmlValidatingReader the piece of code above does exactly just what you'll like an XmlValidatingReader to do. The caveat here though is that CF only supports Xsd Validation (no we do not plan to support DTD or XDR validation). As a matter of fact if you attempt to create a Reader over an XML document that contains a DTD you'll get a NotSupportedException (just like the V1 behavior). One thing to note is that in V1 the reader simply ignored entity references but in V2.0 a NotSupportedException will be thrown each time we encounter an entity reference.

I'll be going through the remaining 9 features over the next few days and I hope to get feedback from you guys. I highly recommend that you read the article on the developer center as these posts will only make sense if you've read the article.

More Posts Next page »
 
Page view tracker