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:
An example of a format name to be used with Windows CE would be the following: DIRECT=OS:mycomputer\Private$\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: mycomputer\Private$\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);
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:
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.
|