C-C++ Code Example: Matching Acknowledgment Messages

 

Applies To: Windows 10, Windows 7, Windows 8, Windows 8.1, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, Windows Server 2012 R2, Windows Server Technical Preview, Windows Vista

This example provides an application-defined function that searches an administration queue for the acknowledgment message that was returned as a result of sending a specific message.

This function assumes the message was sent to a single destination queue, so it compares only the returned message identifier in the PROPID_M_CORRELATIONID property of the acknowledgment message to the cached identifier of the message that was sent. For information on other information that is returned in the message properties of an acknowledgment message, see Acknowledgment Message Properties.

Note

For cases where the original message was sent to multiple destination queues, this function would need to be modified so that it compares the returned message identifier (PROPID_M_CORRELATIONID) and the returned destination queue (PROPID_M_RESP_QUEUE) to the cached message identifier and destination queue respectfully. Both pieces of information need to be compared because messages sent to multiple destination queues all have the same message identifier.

To match acknowledgment messages

  1. Define the maximum number of queue properties to be specified and the queue property counter.

  2. Define a MQMSGPROPS structure.

  3. Specify PROPID_M_CORRELATIONID, PROPID_M_LABEL_LEN, and PROPID_M_LABEL as properties to be retrieved. The maximum label length, including the string-terminating character, is MQ_MAX_MSG_LABEL_LEN (250 Unicode characters).

  4. Initialize the MQMSGPROPS structure.

  5. Generate the format name required for opening the administration queue from the computer name and queue name supplied by the caller, and then call MQOpenQueue to open the queue with receive access. Receive access allows the application to peek at or remove the messages in the queue.

Note

wcslen properly handles only null-terminated strings. This code example does not verify that the strings passed to it are null-terminated. It is the responsibility of the caller to ensure that the strings passed are null-terminated.

  1. Call MQCreateCursor to create the cursor for navigating the administration queue. The returned cursor handle is used in the calls to peek at the messages.

  2. Call MQReceiveMessage with peek access to peek at the first message in the administration queue. This call also initializes the cursor so that the cursor points at the first message in the queue.

  3. In a loop structure, compare the PROPID_M_CORRELATIONID property of the last message peeked with the message ID supplied by the caller and continue to call MQReceiveMessage to peek at the messages in the queue.

    If the correlation ID of a message in the administration queue matches the message ID supplied by the caller, display the label of the matching acknowledgment message.

  4. Call MQCloseQueue and MQCloseCursor to free the resources.

Code Example

The following code example can be run on all versions of Message Queuing.

HRESULT MatchAck(  
                 LPCWSTR wszAdminQueueName,   
                 LPCWSTR wszComputerName,  
                 UCHAR * rgucMsgID                    //Array of bytes  
                 )  
{  
  
  // Validate the input strings.  
  if (wszAdminQueueName == NULL || wszComputerName == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  //  Define the required constants and variables.  
  const int NUMBEROFPROPERTIES = 3;                   // Number of properties  
  DWORD cPropId = 0;                                  // Property counter  
  WCHAR wszLabelBuffer[MQ_MAX_MSG_LABEL_LEN];         // Message label buffer  
  
  // Define an MQMSGPROPS structure.  
  MQMSGPROPS msgprops;  
  MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];  
  PROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];  
  HRESULT aMsgStatus[NUMBEROFPROPERTIES];  
  
  HANDLE hQueue = NULL;                               // Queue handle  
  HANDLE hCursor = NULL;                              // Cursor handle  
  HRESULT hr = MQ_OK;                                 // Return code  
  int i = 0;                                          // Array index  
  
  // Create a buffer of unsigned characters of length   
  // PROPID_M_CORRELATIONID_SIZE, which is equal to 20, and specify   
  // PROPID_M_CORRELATIONID as a message property to be retrieved.  
  UCHAR rgucCorrelationID[PROPID_M_CORRELATIONID_SIZE];  
  
  aMsgPropId[cPropId] = PROPID_M_CORRELATIONID;       // Property ID  
  aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1 ;      // Type indicator  
  aMsgPropVar[cPropId].caub.pElems = rgucCorrelationID;  
  aMsgPropVar[cPropId].caub.cElems = PROPID_M_CORRELATIONID_SIZE;  
  cPropId++;  
  
  // Specify the message label and its length as message properties to be retrieved.  
  aMsgPropId[cPropId] = PROPID_M_LABEL_LEN;           // Property ID  
  aMsgPropVar[cPropId].vt = VT_UI4;                   // Type indicator  
  aMsgPropVar[cPropId].ulVal = MQ_MAX_MSG_LABEL_LEN;  // Label buffer size  
  cPropId++;  
  
  aMsgPropId[cPropId] = PROPID_M_LABEL;               // Property ID  
  aMsgPropVar[cPropId].vt = VT_LPWSTR;                // Type indicator  
  aMsgPropVar[cPropId].pwszVal = wszLabelBuffer;      // Label buffer  
  cPropId++;  
  
  // Initialize the MQMSGPROPS structure.  
  msgprops.cProp = cPropId;                           // Number of message properties  
  msgprops.aPropID = aMsgPropId;                      // IDs of the message properties  
  msgprops.aPropVar = aMsgPropVar;                    // Values of the message properties  
  msgprops.aStatus  = aMsgStatus;                     // Error reports  
  
  // Generate the format name required for opening the administration queue.  
  WCHAR * wszAdminFormatName = NULL;  
  DWORD dwFormatNameLength = 0;  
  dwFormatNameLength = wcslen(wszAdminQueueName) + wcslen(wszComputerName) + 12;  
  wszAdminFormatName = new WCHAR[dwFormatNameLength];  
  if (wszAdminFormatName == NULL)  
  {  
    return MQ_ERROR_INSUFFICIENT_RESOURCES;  
  }  
  memset(wszAdminFormatName, 0, dwFormatNameLength*sizeof(WCHAR));  
  
  // ************************************  
  // You must concatenate "DIRECT=OS:", wszComputerName, "\",        
  // and wszAdminQueueName into the wszAdminFormatName buffer.  
  // wszAdminFormatName = "DIRECT=OS:" + wszComputerName + "\" +        
  // wszAdminQueueName  
  // If the format name is too long for the buffer,  
  // return FALSE.  
  // ************************************  
  
  // Open the administration queue to peek at all the messages.  
  hr = MQOpenQueue(  
                   wszAdminFormatName,                // Format name of the administration queue  
                   MQ_RECEIVE_ACCESS,                 // Access mode  
                   MQ_DENY_RECEIVE_SHARE,             // Share mode  
                   &hQueue                            // OUT: Queue handle  
                   );  
  
  // Free the memory that was allocated for the format name string.  
  delete [] wszAdminFormatName;  
  
  // Handle any error returned by MQOpenQueue.  
  if (FAILED(hr))  
  {  
    return hr;  
  }  
  
  // Create the cursor used to navigate through the administration queue.  
  hr = MQCreateCursor(  
                      hQueue,                         // Queue handle  
                      &hCursor                        // OUT: Cursor handle  
                      );  
  if (FAILED(hr))  
  {  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  
  // Peek at the first message in the administration queue.  
  hr = MQReceiveMessage(  
                        hQueue,                    // Queue handle  
                        0,                         // Maximum time (msec)  
                        MQ_ACTION_PEEK_CURRENT,    // Receive action  
                        &msgprops,                 // Message property structure  
                        NULL,                      // No OVERLAPPED structure  
                        NULL,                      // No callback function  
                        hCursor,                   // Cursor handle  
                        MQ_NO_TRANSACTION          // Not in a transaction  
                        );  
  if (FAILED(hr))  
  {  
    MQCloseCursor(hCursor);  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  
  // Find the matching message in the administration queue.  
  do  
  {  
    for (i = 0; (rgucMsgID[i] == rgucCorrelationID[i]) && (i < 20); i++);  
    if (i == 20)                                   // The message and correlation IDs match.  
    {  
  
      // Display the label of the message.  
      wprintf(L"Label of the matching message: %s\n", msgprops.aPropVar[2].pwszVal);   
  
    }  
  
    //Peek at the next message in the administration queue.  
    hr = MQReceiveMessage(  
                          hQueue,                 // Queue handle  
                          0,                      // Maximum time (msec)  
                          MQ_ACTION_PEEK_NEXT,    // Receive action set to peek at the next message  
                          &msgprops,              // Message property structure  
                          NULL,                   // No OVERLAPPED structure  
                          NULL,                   // No callback function  
                          hCursor,                // Cursor handle  
                          NULL                    // No transaction  
                          );  
    if (FAILED(hr))  
    {  
      break;  
    }  
  
  } while (SUCCEEDED(hr));  
  
  // Close the cursor and queue to free resources.  
  hr = MQCloseCursor(hCursor);  
  if (FAILED(hr))  
  {  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  
  hr = MQCloseQueue(hQueue);  
  if (FAILED(hr))  
  {  
    return hr;  
  }  
  wprintf(L"Peeking completed! Label of the last message: %s\n", msgprops.aPropVar[2].pwszVal);   
  return hr;  
}