C-C++ COM Code Example: Reading Messages Asynchronously
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
The following example includes three application-defined functions for asynchronously reading the messages already available in a specified queue and any messages arriving in the queue until a known time period elapses without the arrival of a new message. The first function enables notification of message-arrival (MSMQEvent.Arrived) and arrival-error (MSMQEvent.ArrivedError) events. The second function handles an Arrived event by reading a single message and removing it from the queue. The third function handles an ArrivedError event by sending a WM_QUIT message to terminate the process in the first function and thereby return control to its caller.
For more information about reading messages asynchronously, see Asynchronous Reading.
This example uses smart pointers to the following Message Queuing interfaces.
MSMQQueue: Represents an open instance of the destination queue.
MSMQQueueInfo: Represents the destination queue.
MSMQMessage: Represents the message.
MSMQEvent: Provides an Arrived event that is fired when a message arrives at the associated queue and an ArrivedError event that is fired for a message expected to arrive in the queue.
To use smart pointers, your application must import Mqoa.dll. You can import this DLL using the #import directive and specify the MSMQ namespace.
#import "mqoa.dll" named_guids
using namespace MSMQ;
To use the event-handling functions, your C++ code file must also include the Atlbase.h header file, declare a COM server module (CComModule), and then include a header file that defines an application-specific CEventHandler class and is called EventHandler.h:
#include <atlbase.h>
CComModule _Module;
#include "EventHandler.h"
EventHandler.h
The following is the code contained in EventHandler.h:
#include <atlcom.h>
IMSMQEventPtr pEvent;
class CEventHandler : public IDispEventImpl<0, CEventHandler,
&DIID__DMSMQEventEvents, &LIBID_MSMQ, 3, 0>,
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<_DMSMQEventEvents,
&DIID__DMSMQEventEvents, &LIBID_MSMQ>
{
public:
STDMETHOD(Arrived)(IDispatch* Queue, long Cursor);
STDMETHOD(ArrivedError)(IDispatch* Queue, long ErrorCode, long Cursor);
BEGIN_SINK_MAP(CEventHandler)
SINK_ENTRY_EX(0, DIID__DMSMQEventEvents, 0, Arrived)
SINK_ENTRY_EX(0, DIID__DMSMQEventEvents, 1, ArrivedError)
END_SINK_MAP()
BEGIN_COM_MAP(CEventHandler)
COM_INTERFACE_ENTRY(_DMSMQEventEvents)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
};
Enable Notification of Events
Before using any smart pointer, your application must call CoInitialize or CoInitializeEx to initialize the COM library. In this example, the COM library is initialized in the single-threaded apartment (STA) model. After the COM library is no longer needed, your application must call CoUnitialize. For more information, see Using Message Queuing COM Components in Visual C++ and C.
To enable notification of events for asynchronous reading
Declare the required variables and an event handler.
Validate the input parameter.
Initialize the data members of the COM server module.
Create a smart pointer to an MSMQQueueInfo interface and set the path name property of the MSMQQueueInfo object to the path name passed.
Call MSMQQueueInfo.Open to open the queue with receive access and create an MSMQQueue smart pointer to an open instance of the queue. When opening a queue with receive access, the application can peek at or retrieve the messages in the queue.
Create an instance of the smart pointer to the MSMQEvent interface declared in EventHandler.h.
Call MSMQQueue.EnableNotification to start notification, setting the receive time-out period to 10000 milliseconds, and create an instance of the CEventHandler object.
Create a simple Windows® message loop. This is necessary because this example uses the single-thread apartment (STA) model. The STA model must be used to receive messages asynchronously because this operation is not supported in the multithreaded apartment (MTA) model.
Cal MSMQQueue.Close to close the queue.
Message-Arrival Events
To handle message-arrival events
Create a smart pointer to an MSMQQueue interface that exposes the MSMQQueue object specified in the call.
Call MSMQQueue.Receive to read one message from the queue and create a smart pointer to the MSMQMessage interface that exposes the message. Note that the values of the Transaction and ReceiveTimeout parameters are specified.
Call EnableNotification to restart notification.
Display a message indicating that a message was read. This step can be replaced by code to process the message.
To handle arrival-error events
Optional. Display a message indicating an error and the error code.
Call PostQuitMessage to send a WM_QUIT message to terminate the process in the first function and thereby return control to its caller.
Code Example
The following code example can be run on all versions of Message Queuing.
HRESULT AsyncRead(
WCHAR *wszPathName
)
{
// Declare the required variables and an event handler.
HRESULT hr = S_OK;
WCHAR wszMessage[1024] = {0};
CComObject<CEventHandler>* objEventHandler; // Event handler
// Validate the input parameter.
if (wszPathName == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
try
{
// Initialize the data members of the COM server module.
hr = _Module.Init(NULL, NULL);
if (FAILED(hr))
{
_com_issue_error(hr);
}
// Create an IMSMQQueueInfoPtr smart pointer and set the path name
// property of the MSMQQueueInfo object to the path name provided
// by the caller.
IMSMQQueueInfoPtr qinfo("MSMQ.MSMQQueueInfo");
qinfo->PathName = wszPathName;
// Open the queue.
IMSMQQueuePtr pQueue = qinfo->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
hr = pEvent.CreateInstance("MSMQ.MSMQEvent");
if (FAILED(hr))
{
_com_issue_error(hr);
}
// Enable notification by the queue and create an instance of the
// smart pointer to the IMSMQEvent interface.
hr = CComObject<CEventHandler>::CreateInstance(&objEventHandler);
if (FAILED(hr))
{
_com_issue_error(hr);
}
hr = objEventHandler->DispEventAdvise(pEvent, &DIID__DMSMQEventEvents);
if (FAILED(hr))
{
_com_issue_error(hr);
}
_variant_t vtTimeOut((long)10000);
pQueue->EnableNotification(pEvent, &vtMissing, &vtTimeOut);
// Windows message loop
MSG msgW;
while (GetMessage(&msgW, // Message structure
NULL, // Handle of window receiving message
NULL, // Lowest message value to be retrieved
NULL)) // Highest message value to be retrieved
{
TranslateMessage(&msgW); // Translate virtual key codes.
DispatchMessage(&msgW); // Dispatch message to window.
}
hr = objEventHandler->DispEventUnadvise(pEvent, &DIID__DMSMQEventEvents);
if (FAILED(hr))
{
_com_issue_error(hr);
}
pQueue->Close();
MessageBoxW(NULL, L"The queue was closed.", L"Asynchronous Receive", MB_OK);
}
catch (const _com_error& comerr)
{
// ************************************
// You must concatenate "Error Code: ". comerr.Error(),
// "Error Description: ", (WCHAR*)comerr.Description(), and "\n"
// into the wszMessage buffer.
// wszMessage = "Error Code: " + comerr.Error()+
// "Error Description: " + (WCHAR*)comerr.Description() + "\n"
// If the message is too long for the buffer, return FALSE;
// ************************************
// If the concantentaion fails
{
wprintf(L"The message is too long for the buffer specified.\n");
return FALSE;
}
else
{
wszMessage[(sizeof(wszMessage)/sizeof(wszMessage[0])) - 1] = L'\0';
MessageBoxW(NULL, wszMessage, L"Asynchronous Receive", MB_OK);
}
return comerr.Error();
}
return hr;
}
HRESULT __stdcall CEventHandler::Arrived(IDispatch* Queue, long Cursor)
{
IMSMQQueuePtr pQueue(Queue);
_variant_t vtNoTransaction((long)MQ_NO_TRANSACTION);
_variant_t vtZero((long)0);
_variant_t vtTimeOut((long)10000);
// Read one message and restart notification.
IMSMQMessagePtr pMsg = pQueue->Receive(&vtNoTransaction, &vtMissing, &vtMissing, &vtZero);
pQueue->EnableNotification(pEvent, &vtMissing, &vtTimeOut);
MessageBox(NULL, "A message was received.", "Asynchronous Receive", MB_OK);
return S_OK;
}
HRESULT __stdcall CEventHandler::ArrivedError(IDispatch* Queue, long ErrorCode, long Cursor)
{
WCHAR wszMessage[1024] = {0};
// ************************************
// You must concatenate "An error occurred.\nError Code: " and
// ErrorCode into the wszMessage buffer.
// wszMessage = "An error occurred.\nError Code: " + ErrorCode
// If the message is too long for the buffer, return FALSE;
// ************************************
// If the concantentaion fails
{
wprintf(L"The message is too long for the buffer specified.\n");
return FALSE;
}
else
{
wszMessage[(sizeof(wszMessage)/sizeof(wszMessage[0])) - 1] = L'\0';
MessageBoxW(NULL, wszMessage, L"Asynchronous Receive", MB_OK);
}
PostQuitMessage(ErrorCode);
return S_FALSE;
}