Sending Messages from Windows CE

Windows CE does not provide an object model for accessing MSMQ, so API calls need to be used. To use these API functions you will need to include mq.h and add the library msmqrt.lib to your project. A queue must first be opened, and then messages can be added to the queue. Finally, you will need to close the queue.

There are two ways in which a queue's name and location can be specified:

  • Format name— A string that contains information about how the queue is named, the DNS computer on which it is located, the type of queue (for example, private or public), and the queue's name.

  • Path name— A string that specifies the queue based only on the DNS, the queue type (private or public), and the queue's name.

An example of a format name to be used with Windows CE would be the following:

DIRECT=OS:mycomputerPrivate$WinCEQueue

This specifies a private queue on a computer with the DNS name "mycomputer", and with the queue name "WinCEQueue". The "DIRECT=OS" specification indicates that the computer should be resolved using DNS. Other "DIRECT" options allow the computer to be specified by IP address, but this is not supported in Windows CE. The "mycomputer" DNS name is the name of the Windows 2000 computer used when creating the queue, as described in the previous section, "Reading Messages from a Queue in Windows 2000."

An example of a path name to be used with Windows CE would be as follows:

mycomputerPrivate$WinCEQueue

This specifies the same queue as the format name example above. The MQOpenQueue function used to open a queue requires a format name, but it is often easier to work with path names. Therefore, the MQPathNameToFormatName function can be used to provide a conversion:

TCHAR wszFormatName[256];
DWORD dwFormatNameLength = 256;
hr = MQPathNameToFormatName
    (_T("nickdell\Private$\WinCEQueue"),
    wszFormatName,
    &dwFormatNameLength);

The function MQPathNameToFormatName is passed the path name to convert (the computer "nickdell" in this case is a Windows 2000 computer) and a string buffer in which the format name will be returned. The third parameter is a DWORD that contains the size of the buffer on calling the function and the number of characters in the format name on return.

Once the format name for the queue is obtained, the function MQOpenQueue (Table 15.1) can be called to open the queue. When opening a queue you must specify the type of access you need. For example, if you are reviewing the messages but not removing them, you can use MQ_PEEK_ACCESS. Otherwise, you may use MQ_SEND_ACCESS or MQ_RECEIVE_ACCESS to send and receive messages. A handle to the open queue is returned in a QUEUEHANDLE variable. The following code opens a queue for send access and does not deny other applications access to the queue:

HRESULT hr;
QUEUEHANDLE hq;
hr = MQOpenQueue(wszFormatName,
    MQ_SEND_ACCESS,
    MQ_DENY_NONE,
    &hq);

Table 15.1. MQOpenQueue—Opens queue on local or remote computer
MQOpenQueue
LPCWSTR lpwcsFormatNameFormat name of the queue.
DWORD dwAccessType of access required to queue:
 MQ_PEEK_ACCESS—Messages will be read but not removed from queue.
 MQ_SEND_ACCESS—Messages will be sent to the queue.
 MQ_RECEIVE_ACCESS—Messages will be read and removed from the queue.
DWORD dwShareModeAccess allowed to other applications using the queue:
 MQ_DENY_NONE—Allow other applications full access to the queue.
 MQ_DENY_RECEIVE_SHARE—Only allow other applications reading messages to access the queue.
LPQUEUEHANDLE phQueuePointer to a queue handle variable in which the queue handle will be returned.
HRESULT Return ValueMQ_OK on success, otherwise an error code.

The function MQSendMessage is used to send messages to an open queue. The function is passed a handle to an open queue, a pointer to a MQMSGPROPS structure describing the message to be sent, and a constant describing the transaction options to be used. NULL for the last parameter specifies that no transactions will be used.

hr = MQSendMessage(hq,
    &msgprops,
    NULL);

Most of the work in sending messages involves forming the MQMSGPROPS structure that describes the message options and data to be sent. To send a message you will need to provide the following properties:

  • PROPID_M_LABEL— A textual description describing the message. You are free to provide any textual label. This can be used by the recipient application to decide how to process the message.

  • PROPID_M_BODY_TYPE— A property describing the type of data contained in the message, for example, VT_BSTR for BSTR data.

  • PROPID_M_BODY— A property describing the message's data and the data itself.

To create and initialize a MQMSGPROPS, you will first need to declare a MQMSGPROPS structure. You will then need to declare a MSGPROPID array to store the property identifiers (such as PROPID_M_LABEL), a PROPVARIANT array that will contain the property data, and an optional HRESULT array used forreturning error information associated with a property. The PROPVARIANT structure is used like the VARIANT structure described in Chapter 14 (COM and ActiveX). The "vt" member contains a constant that describes the data type (such as VT_LPWSTR for a null-terminated string), and a union member that refers to the data (such as pwszVal that points to a string). In the following code, a MQMSGPROPS structure is initialized ready to store information on three properties.

MQMSGPROPS msgprops;
MSGPROPID aMsgPropId[3];
MQPROPVARIANT aMsgPropVar[3];
HRESULT aMsgStatus[3];
msgprops.cProp = 3;             // Number of properties
msgprops.aPropID = aMsgPropId;  // Ids of properties
msgprops.aPropVar = aMsgPropVar;// Values of properties
msgprops.aStatus = aMsgStatus;  // Error reports

The PROPID_M_LABEL property contains a string for the message's label. The data type for the data stored in the MQPROPVARIANT element is therefore VT_LPWSTR, and the pwszVal member points to the string data.

aMsgPropId[0] = PROPID_M_LABEL;
aMsgPropVar[0].vt = VT_LPWSTR;
aMsgPropVar[0].pwszVal = _T("Test Message");

The PROPID_M_BODY_TYPE property describes the data type for the message's body data. The data associated with this property is an unsigned 4-byte integer (VT_UI4), and the data in the ulVal member contains a constant describing the data type. The following code describes a message where the body data is a BSTR:

aMsgPropId[1] = PROPID_M_BODY_TYPE;
aMsgPropVar[1].vt = VT_UI4;
aMsgPropVar[1].ulVal = VT_BSTR;

Finally, the PROPID_M_BODY property describes the data for the message. The initialization depends on the type of data to be sent. The following code allocates a BSTR and sets the property to use this BSTR as the message's data:

BSTR bStr =
  SysAllocString(_T("Body text for the message"));
aMsgPropId[2] = PROPID_M_BODY;
aMsgPropVar[2].vt = VT_VECTOR|VT_UI1;
aMsgPropVar[2].caub.pElems = (LPBYTE)bStr;
aMsgPropVar[2].caub.cElems =
        SysStringByteLen(bStr);

The data type VT_VECTOR | VT_UI1 specifies that the data is to be passed as a counted array (that is, an array with a given size). The caub.pElems member describes the length of the data, and caub.cElems points to the data itself. The code in Listing 15.1 shows opening a queue, initializing the properties, and sending the message using MQSendMessage. Finally, the queue is closed through a call to MQCloseQueue—this function takes a single parameter that is the handle to the queue to close. This code will send a message to a queue that can be read by the Visual Basic code described in the section "Reading Messages from a Queue in Windows 2000."

Listing 15.1. Opening queue and sending a message
#include <mq.h>
// Add MSMQRT.LIB to project
void DisplayOpenError(HRESULT hr)
{
  if(hr == MQ_ERROR_ACCESS_DENIED)
        cout ≪ _T("Don't have access rights") ≪ endl;
  else if(hr == MQ_ERROR_ILLEGAL_FORMATNAME)
         cout ≪ _T("Illegal Format Name") ≪ endl;
  else if(hr == MQ_ERROR_QUEUE_NOT_FOUND )
        cout ≪ _T("Queue not found") ≪ endl;
  else if(hr == MQ_ERROR_SERVICE_NOT_AVAILABLE )
        cout ≪ _T("Cannot connect to queue mgr")
             ≪ endl;
  else if(hr == MQ_ERROR_INVALID_PARAMETER )
        cout ≪ _T("Invalid Parameter") ≪ endl;
  else if(hr == MQ_ERROR_SHARING_VIOLATION )
        cout ≪ _T("Sharing violation") ≪ endl;
  else if(hr == MQ_ERROR_UNSUPPORTED_ACCESS_MODE )
        cout ≪ _T("Invalid access mode") ≪ endl;
  else if(hr ==
        MQ_ERROR_UNSUPPORTED_FORMATNAME_OPERATION)
        cout ≪ _T("Invalid format name") ≪ endl;
  else
        cout ≪ _T("Unexpected Error") ≪ endl;
}
void Listing15_1()
{
  HRESULT hr;
  QUEUEHANDLE hq;
  TCHAR wszFormatName[256];
  DWORD dwFormatNameLength = 256;
  hr = MQPathNameToFormatName
      (_T("nickdell\Private$\WinCEQueue"),
      wszFormatName,
      &dwFormatNameLength);
  cout ≪ wszFormatName ≪ endl;
  hr = MQOpenQueue(wszFormatName,
      MQ_SEND_ACCESS,
      MQ_DENY_NONE,
      &hq);
  if(hr == MQ_OK)
    cout ≪ _T("Opened queue") ≪ endl;
  else
  {
    DisplayOpenError(hr);
    return;
  }
  DWORD cPropId = 0;
  MQMSGPROPS msgprops;
  MSGPROPID aMsgPropId[4];
  MQPROPVARIANT aMsgPropVar[4];
  HRESULT aMsgStatus[4];
  aMsgPropId[cPropId] = PROPID_M_LABEL;
  aMsgPropVar[cPropId].vt = VT_LPWSTR;
  aMsgPropVar[cPropId].pwszVal = _T("Test Message");
  cPropId++;
  aMsgPropId[cPropId] = PROPID_M_BODY_TYPE;
  aMsgPropVar[cPropId].vt = VT_UI4;
  aMsgPropVar[cPropId].bVal = VT_BSTR;
  cPropId++;
  BSTR bStr = SysAllocString(
      _T("Body text for the message"));
  aMsgPropId[cPropId] = PROPID_M_BODY;
  aMsgPropVar[cPropId].vt = VT_VECTOR|VT_UI1;
  aMsgPropVar[cPropId].caub.pElems = (LPBYTE)bStr;
  aMsgPropVar[cPropId].caub.cElems =
      SysStringByteLen(bStr);
  cPropId++;
  msgprops.cProp = cPropId;
  msgprops.aPropID = aMsgPropId;
  msgprops.aPropVar = aMsgPropVar;
  msgprops.aStatus = aMsgStatus;
  hr = MQSendMessage(hq,
        &msgprops,
        NULL);
  if (FAILED(hr))
    cout ≪ _T("Could not send message") ≪ endl;
  else
    cout ≪ _T("Message queued") ≪ endl;
  MQCloseQueue(hq);
}

If the Windows CE device on which this code runs cannot access the Windows 2000 machine where WinCEQueue is located, the messages will be stored in a local temporary queue. When the queue can next be accessed (for example, when the Windows CE device connects using RAS), MSMQ will automatically transfer the messages to the queue.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.202.187