C-C++ Code Example: Sending a Message Using an MS DTC External Transaction

 

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 sends a message within an MS DTC External Transaction. A call to the DtcGetTransactionManager function with IID_ITransactionDispenser establishes a connection to MS DTC and returns a pointer to the ITransactionDispenser on the MS DTC proxy core object. Then a call to the MQBeginTransaction method on this interface initiates a new MS DTC (external) transaction and returns a pointer to the ITransaction interface of the new transaction object, which is used to send a single message to a queue.

To use MSMQTransactionDispenser.BeginTransaction, your application must #include Transact.h.

To send a message within an MS DTC external transaction

  1. Define the total number of properties to be specified, a property counter, a queue handle, and a result variable.

  2. Define an MQMSGPROPS structure.

  3. Specify the message body and the message label as properties to be included in the message and initialize the MQMSGPROPS structure.

  4. Call MQOpenQueue to open the transactional destination queue to send the message.

  5. Call DtcGetTransactionManager to establish a connection to MS DTC and obtain a pointer to the ITransactionDispenser.

  6. Call MSMQTransactionDispenser.BeginTransaction to initiate a new MS DTC transaction and obtain a pointer to the ITransaction interface of the new transaction object.

  7. Call MQSendMessage to send the message within the external transaction initiated.

  8. Call ITransaction::Commit using the pointer to the ITransaction interface of the transaction object to commit the transaction.

  9. Release the transaction object and close the queue.

Code Example

This example can be run on all versions of Message Queuing.

HRESULT SendExternalTransMsg(  
                             LPCWSTR wszDestFormatName  
                             )  
{  
  
  // Validate the input string.  
  if (wszDestFormatName == NULL)  
  {  
    return MQ_ERROR_INVALID_PARAMETER;  
  }  
  
  // Define the required constants and variables.  
  const int NUMBEROFPROPERTIES = 2;                 // Number of properties  
  QUEUEHANDLE hQueue = NULL;                        // Queue handle  
  DWORD cPropId = 0;                                // Property counter  
  HRESULT hr = MQ_OK;                               // Define results  
  
  // Define an MQMSGPROPS structure.  
  MQMSGPROPS msgprops;  
  MSGPROPID aMsgPropId[NUMBEROFPROPERTIES];  
  PROPVARIANT aMsgPropVar[NUMBEROFPROPERTIES];  
  HRESULT aMsgStatus[NUMBEROFPROPERTIES];  
  
  ITransactionDispenser   *g_pTransactionDispenser;  
  ITransaction            *pTransaction;  
  
  //.Define the message body.  
  WCHAR wszMessageBody[] = L"Message sent in an external transaction.";  
  
  // Specify PROPID_M_BODY.  
  aMsgPropId[cPropId] = PROPID_M_BODY;  
  aMsgPropVar[cPropId].vt = VT_VECTOR | VT_UI1;   
  aMsgPropVar[cPropId].caub.pElems = (LPBYTE)wszMessageBody;  
  aMsgPropVar[cPropId].caub.cElems = sizeof(wszMessageBody);  
  cPropId++;  
  
  // Add the message properties here. When adding properties,  
  // adjust NUMBEROFPROPERTIES to reflect the total  
  // number of message properties specified.  
  
  aMsgPropId[cPropId] = PROPID_M_LABEL;  
  aMsgPropVar[cPropId].vt = VT_LPWSTR;  
  aMsgPropVar[cPropId].pwszVal = L"MS DTC transactional test message.";  
  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  
  
  // Open the transactional queue to send the message.  
  hr = MQOpenQueue(  
                   wszDestFormatName,               // Format name of the queue  
                   MQ_SEND_ACCESS,                  // Access mode  
                   MQ_DENY_NONE,                    // Share mode  
                   &hQueue                          // OUT: Handle to queue  
                   );  
  if (FAILED(hr))  
  {  
    return hr;  
  }  
  
  // Obtain a pointer to the ITransactionDispenser interface from the MS DTC proxy.  
  hr = DtcGetTransactionManager(  
                                NULL,                      // pszHost  
                                NULL,                      // pszTmName  
                                IID_ITransactionDispenser, // ID of the interface  
                                0,                         // Reserved: must be null  
                                0,                         // Reserved: must be null  
                                0,                         // Reserved: must be null  
                               (void **)&g_pTransactionDispenser  // pointer to pointer to requested interface  
                               );  
  
  if (FAILED(hr))  
  {  
    // No connection to DTC.  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  hr = g_pTransactionDispenser->BeginTransaction (  
                                0,                         // Must be null  
                                ISOLATIONLEVEL_ISOLATED,   // Isolation level  
                                ISOFLAG_RETAIN_DONTCARE,   // Isolation flags  
                                0,                         // Pointer to the transaction options object  
                                &pTransaction);           // Pointer to a pointer to the ITransaction   
                                                           // interface of the new transaction object  
  
  if (FAILED(hr))  
  {  
    MQCloseQueue(hQueue);  
    return hr;  
  }  
  
  // The default is to commit the transaction.  
  BOOL fCommit = TRUE;  
  
  // Call MQSendMessage to send the message to   
  // the receiver side within the transaction.  
  hr = MQSendMessage(  
                     hQueue,             // Handle to the destination queue  
                     &msgprops,          // Pointer to the MQMSGPROPS structure  
                     pTransaction        // Pointer to the ITransaction interface  
                     );                  // of the transaction object  
  
  if (FAILED(hr))  
  {  
    fCommit = FALSE;                     // Abort if MQSend failed  
  }  
  
  // Here the application can call other resource   
  // managers (such as SQL server) and enlist their   
  // actions in the transaction pTransaction.  If   
  // atomicity is required, set fCommit to FALSE.  
  
  // Commit the transaction or abort it.  
  if (fCommit)  
  {  
     hr = pTransaction->Commit(0, 0, 0);  
    if (FAILED(hr))  
    {  
      pTransaction->Release();  
      MQCloseQueue(hQueue);  
      return hr;  
    }  
  }  
  else  
  {  
    hr = pTransaction->Abort(0, 0, 0);  
    if (FAILED(hr))  
    {  
      pTransaction->Release();  
      MQCloseQueue(hQueue);  
      return hr;  
    }  
  }  
  
  // Release the transaction.  
  pTransaction->Release();  
  
  hr = MQCloseQueue(hQueue);  
  return hr;  
}