0

MQSendMessage() returns value of S_OK even when the message is not sent. Is there a way to make sure the message was sent?

According to message queue storage limit on a single queue doesn't work? the function will always return S_OK (unless some obvious error, like wrong parameters) even when the message cannot be sent.

John John
  • 23
  • 5
  • I know it's only a google search away, but it's helpful to mention you're talking about the Windows MSMQ API: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/msmq/ms711382(v%3Dvs.85) – parktomatomi Nov 10 '19 at 06:50

1 Answers1

0

From the documentation for MQSendMessage:

Information on errors that occur after the message is placed in an outgoing queue is provided by acknowledgment messages.

The API expects you to request an acknowledgement message, receive it later on, and match it to the original using its PROPID_M_CORRELATIONID property. This page provides an example:

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms700122%28v%3dvs.85%29

EDIT: From the link:

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;  
}  
parktomatomi
  • 3,851
  • 1
  • 14
  • 18