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;
}