HOWTO: Using MSMQ C API Inside an MTS Component

Last reviewed: November 11, 1997
Article ID: Q175725
The information in this article applies to:
  • Microsoft Message Queue Server version 1.0
  • Microsoft Transaction Server 1.0
  • Microsoft Visual Studio 97

SUMMARY

This article describes using the Microsoft Message Queue Server (MSMQ) C API from within Microsoft Transaction Server (MTS) components written with Visual C++. The two MSMQ transactional operations available are the message Send and Receive operations.

You can include MSMQ Send operations in Microsoft Transaction Server (MTS) transactions. This causes the send operation to be either committed or rolled back along with the MTS transaction. As a result, the message is not actually sent until the transaction commits. To include the send operation in a transaction, use the MQ_MTS_TRANSACTION constant in the pTransaction parameter of the MQSendMessage function from within a transactional MTS component. MSMQ enlists the send operation in the MTS transaction. The destination queue must be a transactional queue.

If the MTS component is not participating in a transaction, the send operation described above fails with an MQ_ERROR_TRANSACTION_USAGE error. This is because you cannot send a nontransactional message to a transactional queue. You cannot always predict whether a component will participate in a transaction. The component may be marked as "Does Not Support Transactions" or "Supports Transactions" (in which case it may or may not participate in a transaction). Therefore, it is important to verify that the component is participating in the transaction and to use MQ_MTS_TRANSACTION in the pTransaction parameter of MQSendMessage call to a transactional queue only if it is participating. If it is not participating, use the MQ_NO_TRANSACTION or MQ_SINGLE_MESSAGE constant with the appropriate queue type.

For a local MSMQ transactional queue receive operation, specifying MQ_MTS_TRANSACTION works regardless of whether or not the component is transactional. The receive operation is included in a transaction only if the component is transactional.

MORE INFORMATION

The following code demonstrates sending an MSMQ message containing a BSTR from within an MTS component written using ActiveX Template Library (ATL). The MTS context object is used to commit or abort the transaction. The context object's IsInTransaction() method is used to determine whether or not the component is transactional.

   STDMETHODIMP CMsmqSample::MQSend(BSTR bstrSend)
   {
      IObjectContext *pContext = NULL ;  // The MTS context.
      HRESULT Hr  ;

      // The usual MSMQ structures.
      MQMSGPROPS MsgProps ;
      DWORD PropCnt = 0 ;
      MSGPROPID PropIDs[4] ;
      PROPVARIANT  PropVar[4] ;

      QUEUEHANDLE  hQueue = NULL ;
      ITransaction *pTransaction  ;

      try
   {
      // Get the MTS context object.
      Hr = GetObjectContext (&pContext) ;
      if (FAILED (Hr)) throw Hr ;

      // Add code for database updates if needed.

      // Get the queue format name & open the queue.
      WCHAR szwFormatName[256] ;
      DWORD dwFormatNameSize = 256 ;
      Hr = MQPathNameToFormatName (
             L"myMachine\\localx",
             szwFormatName,
             &dwFormatNameSize) ;
      if (FAILED (Hr) ) throw Hr ;

      Hr = MQOpenQueue (
             szwFormatName,
             MQ_SEND_ACCESS,
             0,
             &hQueue) ;
      if (FAILED (Hr) ) throw Hr ;

      // Assemble the message properties.
      PropIDs[PropCnt] = PROPID_M_BODY ;
      PropVar[PropCnt].vt = VT_VECTOR | VT_UI1 ;
      PropVar[PropCnt].caub.cElems = (SysStringLen (bstrSend) + 1) *
                                      sizeof (OLECHAR);
      PropVar[PropCnt].caub.pElems = (UCHAR *) bstrSend ;
      PropCnt++ ;

      PropIDs[PropCnt] = PROPID_M_LABEL ;
      PropVar[PropCnt].vt = VT_LPWSTR ;
      PropVar[PropCnt].pwszVal = L"C++ Transactional Message" ;
      PropCnt++ ;

      MsgProps.cProp = PropCnt;      //Number of properties.
      MsgProps.aPropID = PropIDs;    //Id of properties.
      MsgProps.aPropVar = PropVar;   //Value of properties.
      MsgProps.aStatus = NULL;       //Number of error reports.

      // Determine transaction type.
      // Since you are sending to a transactional queue, you must specify
      // MQ_SINGLE_MESSAGE if the object isn't in a transaction.
      pTransaction = MQ_MTS_TRANSACTION ;
      if ( ! pContext -> IsInTransaction () )
         pTransaction = MQ_SINGLE_MESSAGE ;

      // Send the message.
      Hr = MQSendMessage(
              hQueue,            // Handle of queue.
              &MsgProps,         // Message property structure.
              pTransaction) ;    // Transaction type.
      if (FAILED (Hr) ) throw Hr ;
  }
   catch (HRESULT hr)         // A failure occurred.
  {
      if (hQueue != NULL)
         MQCloseQueue (hQueue) ;

      if (pContext != NULL)
      {
         pContext -> SetAbort () ;  // Abort the transaction.
         pContext -> Release () ;
      }
      return hr ;
   }

   MQCloseQueue (hQueue) ;     // Close the queue.
   pContext -> SetComplete () ;  // Commit the transaction.
   pContext -> Release () ;    // Release the context object.

   return S_OK;
   }

The following code demonstrates receiving an MSMQ message containing a BSTR from within an MTS component written using ATL. The MTS context object is used to commit or abort the transaction.

   STDMETHODIMP CMsmqSample::MqReceive(BSTR * pbstrRcvMsg)
   {
      IObjectContext *pContext = NULL ;  // The MTS context.
      HRESULT Hr  ;

      // The usual MSMQ structures.
      MQMSGPROPS MsgProps ;
      DWORD PropCnt = 0 ;
      MSGPROPID PropIDs[4] ;
      PROPVARIANT  PropVar[4] ;

      QUEUEHANDLE  hQueue = NULL ;

      try
      {

      // Get the MTS context object.
      Hr = GetObjectContext (&pContext) ;
      if (FAILED (Hr)) throw Hr ;

      // Add code for database updates if needed.

      // Get the queue format name & open the queue.
      WCHAR szwFormatName[256] ;
      DWORD dwFormatNameSize = 256 ;
      Hr = MQPathNameToFormatName (
             L"myMachine\\localx",
             szwFormatName,
             &dwFormatNameSize) ;
      if (FAILED (Hr) ) throw Hr ;

      Hr = MQOpenQueue (
             szwFormatName,
             MQ_RECEIVE_ACCESS,
             0,
             &hQueue) ;
      if (FAILED (Hr) ) throw Hr ;

      // Assemble the message properties.
      #define MSG_BODY_LEN 500
      WCHAR wcBodyBuffer[MSG_BODY_LEN] = L"";

      PropIDs[PropCnt] = PROPID_M_BODY ;
      PropVar[PropCnt].vt = VT_VECTOR | VT_UI1 ;
      PropVar[PropCnt].caub.cElems = sizeof (wcBodyBuffer);
      PropVar[PropCnt].caub.pElems = (UCHAR *)wcBodyBuffer ;
      PropCnt++ ;

      MsgProps.cProp = PropCnt;      //No. of properties.
      MsgProps.aPropID = PropIDs;    //Id of properties.
      MsgProps.aPropVar = PropVar;   //Value of properties.
      MsgProps.aStatus = NULL;       //No. error reports.

      // Receive the message.
      Hr = MQReceiveMessage(
              hQueue,                // Handle of queue.
              0,                     // Timeout.
              MQ_ACTION_RECEIVE,     // Action.
              &MsgProps,             // Message property structure.
              NULL,                  // OVERLAPPED structure.
              NULL,                  // Receive callback.
              NULL,                  // Cursor.
              MQ_MTS_TRANSACTION) ;  // Part of MTS Transaction.

      // For this application, return a blank string if there was no
      // message in the queue.
      if (Hr == MQ_ERROR_IO_TIMEOUT)
         *pbstrRcvMsg = SysAllocString (L"") ; // Return empty string.
      else if (FAILED (Hr) )
         throw Hr ;                       // Throw error.
      else
      {
         wcBodyBuffer[MSG_BODY_LEN - 1] = 0 ;
         *pbstrRcvMsg = SysAllocString (wcBodyBuffer) ; // Return message
                                                        // body to caller.
      }
   }
   catch (HRESULT hr)         // A failure occurred.
   {
      if (hQueue != NULL)
         MQCloseQueue (hQueue) ;

      if (pContext != NULL)
      {
         pContext -> SetAbort () ;  // Abort the transaction.
         pContext -> Release () ;
      }
      return hr ;
   }

   MQCloseQueue (hQueue) ;
   pContext -> SetComplete () ;  // Commit the transaction.
   pContext -> Release () ;     //  Release the context object.
   return S_OK;
   }

REFERENCES

For more details on using a single queue for send/receive and to avoid queue and component type mismatch, see the following Knowledge Base article:

   ARTICLE-ID: Q174387
   TITLE     : "INFO: Using a Single MSMQ Queue for an MTS Component"


Additional query words: viper falcon
Keywords : kbcode MQProg MQVC
Version : WINNT:1.0,97
Platform : winnt
Issue type : kbhowto


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: November 11, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.