MQTRANS.CPP
            ///////////////////////////////////// 
            //                                 // 
            // Transactions Sample Application // 
            //                                 // 
            ///////////////////////////////////// 
 
 
#define UNICODE                     // For all MSMQ applications 
 
#include <stdio.h> 
 
 
//------------------------------------------------------------------------------ 
// Include MS DTC specific header files. 
//------------------------------------------------------------------------------ 
#define INITGUID 
#include <transact.h> 
 
// Because we are compiling in UNICODE, here is a problem with DTC... 
//#include <xolehlp.h> 
extern HRESULT DtcGetTransactionManager( 
                                    LPSTR  pszHost, 
                                    LPSTR   pszTmName, 
                                    REFIID rid, 
                                    DWORD   dwReserved1, 
                                    WORD    wcbReserved2, 
                                    void FAR * pvReserved2, 
                                    void** ppvObject )  ; 
 
//------------------------------------------------------------------------------ 
// Include ODBC specific header file. 
//------------------------------------------------------------------------------ 
#ifndef DBNTWIN32 
#define DBNTWIN32 
 
#include <SQLEXT.h> 
 
// from  <odbcss.h> 
#define SQL_COPT_SS_BASE               1200 
#define SQL_COPT_SS_ENLIST_IN_DTC      (SQL_COPT_SS_BASE+7) // Enlist in a Viper transaction 
 
// Defines for use with SQL_ENLIST_IN_DTC 
#define SQL_DTC_DONE 0L       // Delimits end of Viper transaction 
 
#endif 
 
//-------------------------------------------------------------------------- 
//  Enable Ansi ODBC on VC5 
//-------------------------------------------------------------------------- 
 
#ifdef  SQLExecDirect 
#undef  SQLExecDirect 
#define SQLExecDirect SQLExecDirectA 
#endif 
 
#ifdef  SQLSetConnectOption 
#undef  SQLSetConnectOption 
#define SQLSetConnectOption  SQLSetConnectOptionA 
#endif 
 
#ifdef  SQLError 
#undef  SQLError 
#define SQLError  SQLErrorA 
#endif 
 
#ifdef  SQLConnect 
#undef  SQLConnect 
#define SQLConnect  SQLConnectA 
#endif 
 
//------------------------------------------------------------------------------ 
// Include MSMQ specific header file. 
//------------------------------------------------------------------------------ 
#include "mq.h" 
 
//------------------------------------------------------------------------------ 
// Define constants 
//------------------------------------------------------------------------------ 
#define STR_LEN      40 
#define MAX_VAR      20 
#define MAX_FORMAT  100 
 
 
//------------------------------------------------------------------------------ 
// Define datatypes 
//------------------------------------------------------------------------------ 
typedef struct DBCONN 
{ 
   char  pszSrv [STR_LEN];    // data source name, configured through control panel 
   char  pszUser [STR_LEN];   // Login user name 
   char  pszPasswd[STR_LEN];  // Login user password 
   HDBC  hdbc;                // handle to an ODBC database connection 
   HSTMT hstmt;               // an ODBC statement handle, for use with SQLExecDirect 
 
}  DBCONN; 
 
 
//------------------------------------------------------------------------------ 
// Define Globals 
//------------------------------------------------------------------------------ 
 
// global DB connection struct for the server 
static DBCONN  gSrv = 
   {  "MSMQDemo", 
      "sa", 
      "", 
      SQL_NULL_HDBC, 
      SQL_NULL_HSTMT 
   }; 
 
 
// guid type for MQTransTest queues 
static CLSID guidMQTransTestType = 
{ 0xb856ab1, 0x16b6, 0x11d0, { 0x80, 0x48, 0x0, 0xa0, 0x24, 0x53, 0xc1, 0x6f } }; 
 
 
//handle to ODBC environment 
HENV  g_hEnv = SQL_NULL_HENV ; 
 
 
//buffer for machine name 
WCHAR g_wszMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ]; 
 
 
//-------------------------------------------------------------------------- 
// Forward declaration of routines used. 
//-------------------------------------------------------------------------- 
 
void LogonToDB(DBCONN *ptr); 
void ExecuteStatement(DBCONN *ptr, char* pszBuf, BOOL ProcessFlag); 
BOOL ProcessRetCode(char*   pszFuncName, 
                    DBCONN  *ptr, 
                    RETCODE retcode, 
                    BOOL    fExit = TRUE); 
void DoSQLError(DBCONN *ptr); 
void FreeODBCHandles(DBCONN *ptr); 
void Error(char *s, HRESULT hr); 
void Syntax(); 
void LocateTargetQueue(CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]); 
void PrepareSendMessageProperties(MSGPROPID     amPropId[MAX_VAR], 
                          MQPROPVARIANT aPropVar[MAX_VAR], 
                          MQMSGPROPS    &msgprops, 
                          DWORD        &TransferSum); 
void CreateQueue(CLSID *pGuidType, WCHAR wsFormat[]); 
void GetMachineName(); 
void DisplayDollars (DBCONN *ptr, char *psAccount); 
 
 
//------------------------------------------------------------------------------ 
// SENDER MODE: 
// 
// The Sender side does the following: 
//    1. Creates database "SenderAccount". 
//    2. Locates a MSMQ queue of type MQTransTest and opens it. 
//       (NOTE: for simplicity, this sample assumes there's only one queue of this type) 
//   3. In a loop: 
//            Prompts the user to enter TransferSum. 
//            Creates a transaction using MS DTC. 
//            Within the transaction: 
//                 Updates "SenderAccount" database (subtracts TransferSum). 
//                 Sends a message to Receiver side. 
//            Commits the transaction. 
// 
//    4. Cleanup. 
// 
// 
// 
// The transaction in the Sender mode includes two operations: 
// (1) Update "SenderAccount" database (subtract TransferSum). 
// (2) Send message to Receiver side. 
//------------------------------------------------------------------------------ 
 
void Sender() 
{ 
 
   ITransactionDispenser   *pTransactionDispenser; 
   ITransaction            *pTransaction; 
   BOOL                    fTransactionCommitFlag; 
                            // used to decide wother to Commit or Abort 
 
   HRESULT              hr; 
   RETCODE              retcode; 
   DWORD                dwTransferSum;              // set by user 
   char                 sUserString[ STR_LEN ]; 
   char                 sSQLStatement[ STR_LEN*2 ]; 
 
   MQMSGPROPS           msgprops; 
   MQPROPVARIANT        aPropVar[MAX_VAR]; 
   MSGPROPID            amPropId[MAX_VAR]; 
   WCHAR                wsFormat[MAX_FORMAT]; 
   QUEUEHANDLE          aqh; 
 
 
    printf("\nSender Side.\n\n"); 
 
   //--------------------------------------------------------------------- 
   // Build "SenderAccount" database (with the sum $1000) 
   //--------------------------------------------------------------------- 
 
   printf ("Building SenderAccount with the sum $1000...   "); 
 
   // Get ODBC environment handle 
   retcode = SQLAllocEnv(&g_hEnv); 
 
   ProcessRetCode("SQLAllocEnv",0, retcode); 
 
   // Establish connection to database 
   LogonToDB(&gSrv); 
 
   // Clear database from previous run. 
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE); 
 
   // Create new table in database 
   ExecuteStatement(&gSrv, 
    "CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE); 
 
   // Insert new data in database 
   ExecuteStatement(&gSrv,"INSERT INTO SenderAccount   VALUES(1000)",TRUE); 
 
   printf ("OK.\n\n"); 
 
   //----------------------------------------------------------------------- 
   // Locate target queue and Open it for send 
   //----------------------------------------------------------------------- 
 
   printf ("Searching Receiver queue...   "); 
 
   // Locate target queue 
   LocateTargetQueue (&guidMQTransTestType, wsFormat); 
 
   // Open target queue 
   hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh); 
 
   if (FAILED(hr)) 
   { 
      Error ("Open Queue ",hr); 
   } 
 
   //-------------------------------------------------------------------------- 
   // Get Transaction Dispenser 
   //-------------------------------------------------------------------------- 
 
   // Obtain an interface pointer from MS DTC proxy 
   hr = DtcGetTransactionManager( 
               NULL,                        // pszHost 
               NULL,                        // pszTmName 
               IID_ITransactionDispenser,   // IID of  interface 
               0,                           // Reserved -- must be null 
               0,                           // Reserved -- must be null 
               0,                           // Reserved -- must be null 
               (void **)&pTransactionDispenser  // pointer to pointer to requested interface 
                                 ); 
 
   if (FAILED(hr)) 
   { 
      Error ("DTCGetTransactionManager",hr); 
   } 
 
   //-------------------------------------------------------------------- 
   // Sender Main Loop 
   //-------------------------------------------------------------------- 
   while (TRUE) 
   { 
 
      // Prompt user to enter TransferSum 
      printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit  ==> "); 
 
      // Read user input 
      fgets (sUserString, STR_LEN, stdin); 
 
      // Convert user string to DWORD 
      dwTransferSum = atoi(sUserString); 
 
      // Prepare properties of message to send 
      PrepareSendMessageProperties (amPropId, 
                                    aPropVar, 
                                    msgprops, 
                                    dwTransferSum); 
 
      //--------------------------------------------------------------------- 
      // Create transaction (Inside Sender's Main Loop) 
      //--------------------------------------------------------------------- 
 
      printf ("\nStarting transaction...\n\n"); 
 
      // Initiate an MS DTC transaction 
      hr = pTransactionDispenser->BeginTransaction ( 
            0,                         // must be null 
            ISOLATIONLEVEL_ISOLATED,   // Isolation Level 
            ISOFLAG_RETAIN_DONTCARE,   // Isolation flags 
            0,                         // pointer to transaction options object 
            &pTransaction);            // pointer to pointer to transaction object 
 
      if (FAILED(hr)) 
      { 
         Error ("BeginTransaction",hr); 
      } 
 
      // Default is to commit transaction 
      fTransactionCommitFlag = TRUE; 
 
      // 
      // SQL is a resource manager in the transaction. 
      // It must be enlisted. 
      // 
 
      // Enlist database in the transaction 
      retcode = SQLSetConnectOption (gSrv.hdbc, 
                                     SQL_COPT_SS_ENLIST_IN_DTC, 
                                     (UDWORD)pTransaction); 
 
      if (retcode != SQL_SUCCESS) 
      { 
         ProcessRetCode("SQLSetConnection", &gSrv, retcode, FALSE); 
         fTransactionCommitFlag = FALSE; 
      } 
 
 
      // Prepare SQL statement to update SenderAccount 
      sprintf (sSQLStatement, 
               "UPDATE SenderAccount  SET Rate = Rate - %lu", dwTransferSum) ; 
 
      // Allocate a statement handle for use with SQLExecDirect 
      retcode = SQLAllocStmt(gSrv.hdbc, &gSrv.hstmt); 
 
      if (retcode != SQL_SUCCESS) 
      { 
         ProcessRetCode("SQLAllocStmt", &gSrv, retcode, FALSE); 
         fTransactionCommitFlag = FALSE; 
      } 
 
      // Update database  (subtract TransferSum from SenderAccount) 
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS); 
 
      if (retcode != SQL_SUCCESS) 
      { 
         ProcessRetCode("SQLExecDirect", &gSrv, retcode, FALSE); 
         fTransactionCommitFlag = FALSE; 
      } 
 
      // Free the statement handle 
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP); 
 
      gSrv.hstmt = SQL_NULL_HSTMT; 
 
      // 
      // MSMQ is another resource manager in the transaction. 
      // Its enlistment is implicit. 
      // 
 
      // Within the transaction: Send message to Receiver Side 
      hr = MQSendMessage(aqh,              // Handle to destination queue 
                         &msgprops,        // pointer to MQMSGPROPS structure 
                         pTransaction);    // pointer to Transaction Object 
 
 
      if (FAILED(hr)) 
      { 
         printf("\nFailed in MQSendMessage(). hresult- %lxh\n", (DWORD) hr) ; 
         fTransactionCommitFlag = FALSE; 
      } 
 
 
      // Commit the transaction 
      if (fTransactionCommitFlag) 
      { 
         printf ("Committing the transaction...   "); 
 
         hr = pTransaction->Commit(0, 0, 0); 
 
         if (FAILED(hr)) 
            printf ("Failed... Transaction aborted.\n\n"); 
         else 
            printf ("Transaction committed successfully.\n\n"); 
 
      } 
      else 
      { 
         printf ("Aborting the transaction...   "); 
 
         hr = pTransaction->Abort(0, 0, 0); 
 
         if (FAILED(hr)) 
            Error("Transaction Abort",hr); 
         else 
            printf ("Transaction aborted.\n\n"); 
      } 
 
      // Release the transaction 
      pTransaction->Release(); 
 
      // End enlistment of database 
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE); 
 
      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode); 
 
      // Display sum of dollars in Sender Account 
      DisplayDollars (&gSrv,"SenderAccount"); 
 
      // quit loop when nothing was transferred. 
      if (dwTransferSum == 0) 
         break; 
   } 
 
   //-------------------------------------------------------------------------- 
   // Cleanup 
   //-------------------------------------------------------------------------- 
 
   // Release Transaction Dispenser 
   pTransactionDispenser->Release(); 
 
 
   // Free database 
   ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE); 
 
 
   // Free ODBC handle 
   FreeODBCHandles(&gSrv); 
 
 
   // Free the ODBC environment handle 
   retcode = SQLFreeEnv(g_hEnv); 
 
   if (retcode == SQL_ERROR) 
      Error ("SQL FreeEnv ",0); 
 
 
   // Free MSMQ queue handle 
   MQCloseQueue(aqh); 
 
 
   printf ("\n\nSender Side completed.\n\n"); 
 
} 
 
 
 
 
//------------------------------------------------------------------------------ 
// RECEIVER MODE: 
// 
// The Receiver side does the following: 
//    1. Creates database "ReceiverAccount". 
//    2. Creates a MSMQ public queue (with the Transactional property) 
//       of type MQTransTest on its own machine and opens it. 
//    3. In a loop: 
//            Creates a transaction using MS DTC. 
//            Within the transaction: 
//                 Receives a message from the queue (with the TransferSum). 
//                 Updates "ReceiverAccount" database (adds TransferSum). 
//            Commits the transaction. 
// 
//    4. Cleanup. 
// 
// 
// 
// The transaction in the Receiver mode include two operations: 
// (1) Receive message from queue (sent by Sender Side). 
// (2) Update "ReceiverAccount" database  (add TransferSum). 
//------------------------------------------------------------------------------ 
 
void Receiver() 
{ 
   MSGPROPID            amPropId[MAX_VAR]; 
   MQMSGPROPS           msgprops; 
   MQPROPVARIANT        aPropVar[MAX_VAR]; 
   DWORD             cProps; 
   HRESULT              hr; 
   WCHAR             wsFormat[MAX_FORMAT]; 
   QUEUEHANDLE          aqh; 
 
   ITransactionDispenser   *pTransactionDispenser; 
   ITransaction         *pTransaction; 
   BOOL              TransactionCommitFlag;  // used to decide Commit or Abort 
 
   RETCODE              retcode; 
   DWORD             TransferSum; 
 
   DWORD             MessageBuffer;       // message body is the TransferSum 
   char              sSQLStatement[STR_LEN*2]; 
 
 
 
 
 
   printf ("\nReceiver Side.\n\n"); 
 
   //----------------------------------------------------------------------- 
   // Build "ReceiverAccount" database (with the rate $500) 
   //----------------------------------------------------------------------- 
 
   printf ("Building ReceiverAccount with the rate $500...   "); 
 
   // Get ODBC environment handle 
   retcode = SQLAllocEnv(&g_hEnv); 
 
   ProcessRetCode("SQLAllocEnv",0, retcode); 
 
   // Establish connection to database. 
   LogonToDB(&gSrv); 
 
   // Clear table from previous run. 
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE); 
 
   // Create new table. 
   ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE); 
 
   // Insert new data in the table. 
   ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount  VALUES(500)",TRUE); 
 
   printf ("OK.\n\n"); 
 
   //----------------------------------------------------------------------- 
   // Create queue and Open it for receive 
   //----------------------------------------------------------------------- 
 
   printf ("Creating Receiver queue...   "); 
 
   // Create the queue 
   CreateQueue (&guidMQTransTestType, wsFormat); 
 
   // Prepare message properties to read 
   cProps = 0; 
 
   amPropId[cProps] =             PROPID_M_BODY; 
 
   aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR; 
   aPropVar[cProps].caub.cElems =  sizeof(MessageBuffer); 
   aPropVar[cProps].caub.pElems =  (unsigned char *)&MessageBuffer; 
   cProps++; 
 
   // Create a MSGPROPS structure 
   msgprops.cProp =    cProps; 
   msgprops.aPropID =  amPropId; 
   msgprops.aPropVar = aPropVar; 
   msgprops.aStatus =  0; 
 
   // Open the queue 
   hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh); 
 
   // 
   // Little bit tricky. MQCreateQueue succeeded but it does not mean 
   // that MQOpenQueue will, because of replication delay. The queue is 
   // registered in MQIS, but it might take a replication interval 
   // until the replica reach the server I am connected to. 
   // To overcome this, open the queue in a loop. 
   // 
   if (hr == MQ_ERROR_QUEUE_NOT_FOUND) 
   { 
       int iCount = 0 ; 
       while((hr == MQ_ERROR_QUEUE_NOT_FOUND) && (iCount < 120)) 
       { 
          printf("."); 
 
          // Wait a bit 
          iCount++ ; 
          Sleep(500); 
 
          // And retry 
          hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh); 
       } 
   } 
 
   if (FAILED(hr)) 
   { 
      Error ("Can't OpenQueue", hr); 
   } 
 
   printf("OK."); 
 
 
   //-------------------------------------------------------------------------- 
   // Get Transaction Dispenser 
   //-------------------------------------------------------------------------- 
 
   // Obtain an interface pointer from MS DTC proxy 
   hr = DtcGetTransactionManager( 
         NULL, NULL, // pszHost, pszTmName 
         IID_ITransactionDispenser,         // IID of requested interface 
         0,0,0,                             // Reserved -- must be null 
         (void **)&pTransactionDispenser);  // pointer to pointer to requested interface 
 
 
   if (FAILED(hr)) 
      Error ("DTCGetTransactionManager",hr); 
 
 
   //-------------------------------------------------------------------------- 
   // Receiver Main Loop 
   //-------------------------------------------------------------------------- 
   while (TRUE) 
   { 
 
      printf ("\n\nWaiting for a message to come...   "); 
 
      // Peek outside the transaction, to avoid database lock 
      // for long/infinite period. 
      // 
      //dwSize = sizeof(wsResponse); 
      hr = MQReceiveMessage( 
                    aqh,                     // Handle to queue 
                    INFINITE,                // Timeout 
                    MQ_ACTION_PEEK_CURRENT,  // Peek Action 
                    &msgprops,               // Message Properties 
                    NULL,                    // Overlap 
                    NULL,                    // Receive Callback 
                    NULL,                    // Cursor 
                    NULL                     // No transaction yet 
                           ); 
 
      if (FAILED(hr)) 
         Error("MQReceiveMessage (PEEKING) ",hr); 
 
 
 
      //-------------------------------------------------------------------------- 
      // Create transaction 
      //-------------------------------------------------------------------------- 
      printf ("\n\nStarting transaction...\n\n"); 
 
 
      // Initiate an MS DTC transaction 
      hr = pTransactionDispenser->BeginTransaction ( 
            0,                                 // must be null 
            ISOLATIONLEVEL_ISOLATED,         // Isolation Level 
            ISOFLAG_RETAIN_DONTCARE,         // Isolation flags 
            0,                                 // pointer to transaction options object 
            &pTransaction);                    // pointer to pointer to transaction object 
 
      if (FAILED(hr)) 
         Error ("BeginTransaction",hr); 
 
 
      // Default is to commit transaction 
      TransactionCommitFlag = TRUE; 
 
      // 
      // SQL is a resource manager in the transaction. 
      // It must be enlisted. 
      // 
 
      // Enlist database in the transaction 
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction); 
 
      if (retcode != SQL_SUCCESS) 
         TransactionCommitFlag = FALSE; 
 
 
 
      // Receive the message from the queue 
      //dwSize = sizeof(wsResponse); 
      hr = MQReceiveMessage( 
            aqh,                      // Handle to queue 
            INFINITE,                         // Timeout 
            MQ_ACTION_RECEIVE,                // Receive Action 
            &msgprops,                   // Message Properties 
            NULL,NULL,NULL,                   // Overlap, Receive Callback, Cursor 
            pTransaction);                    // pointer to transaction object 
 
      if (FAILED(hr)) 
         TransactionCommitFlag = FALSE; 
 
 
      // Message buffer holds the TransferSum 
      TransferSum = (DWORD)MessageBuffer; 
 
 
      // Prepare SQL statement to update ReceiverAccount 
      sprintf (sSQLStatement, "UPDATE ReceiverAccount   SET Rate = Rate + %i",TransferSum); 
 
 
      // Allocate a statement handle for use with SQLExecDirect 
      retcode = SQLAllocStmt(gSrv.hdbc,&gSrv.hstmt); 
 
      if (retcode != SQL_SUCCESS) 
         TransactionCommitFlag = FALSE; 
 
 
      // Update database  (add TransferSum to ReceiverAccount) 
      retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS); 
 
      if (retcode != SQL_SUCCESS) 
         TransactionCommitFlag = FALSE; 
 
 
      // Free the statement handle 
      retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP); 
 
      gSrv.hstmt = SQL_NULL_HSTMT; 
 
 
 
      // Commit the transaction 
      if (TransactionCommitFlag) 
      { 
         printf ("Committing the transaction...   "); 
 
         hr = pTransaction->Commit(0, 0, 0); 
 
         if (FAILED(hr)) 
            printf ("Failed... Transaction aborted.\n\n"); 
         else 
            printf ("Transaction committed successfully.\n\n"); 
 
      } 
 
 
      // Abort the transaction 
      else 
      { 
         printf ("Aborting the transaction...   "); 
 
         hr = pTransaction->Abort(0, 0, 0); 
 
         if (FAILED(hr)) 
            Error("Transaction Abort",hr); 
         else 
            printf ("Transaction aborted.\n\n"); 
 
      } 
 
 
 
      // Release the transaction 
      pTransaction->Release(); 
 
 
      // End enlistment of database 
      retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE); 
 
      ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode); 
 
 
      // Display sum of dollars in Receiver Account 
      DisplayDollars (&gSrv, "ReceiverAccount"); 
 
 
      // Decide if to continue loop 
      if (TransferSum == 0) 
         break; 
 
 
   } 
 
 
   //-------------------------------------------------------------------------- 
   // Cleanup 
   //-------------------------------------------------------------------------- 
 
   // Release Transaction Dispenser 
   pTransactionDispenser->Release(); 
 
 
   // Free database 
   ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE); 
 
 
   // Free ODBC handle 
   FreeODBCHandles(&gSrv); 
 
 
   // Free the ODBC environment handle 
   retcode = SQLFreeEnv(g_hEnv); 
 
   if (retcode == SQL_ERROR) 
      Error ("SQL FreeEnv ",0); 
 
 
   // Free queue handle 
    MQCloseQueue(aqh); 
 
 
   // Delete queue from directory 
   MQDeleteQueue(wsFormat); 
 
 
   printf ("\n\nReceiver Side completed.\n\n"); 
} 
 
 
 
//------------------------------------------------------------------------------ 
//  MAIN 
//------------------------------------------------------------------------------ 
main(int argc, char * * argv) 
{ 
   DWORD dwSize; 
 
    if(argc != 2) 
        Syntax(); 
 
 
    // Retrieve machine name 
    dwSize = sizeof(g_wszMachineName); 
    GetComputerName(g_wszMachineName, &dwSize); 
 
 
 
    if(strcmp(argv[1], "-s") == 0) 
        Sender(); 
 
    else if(strcmp(argv[1], "-r") == 0) 
        Receiver(); 
 
    else 
        Syntax(); 
 
    return(1); 
 
} 
 
 
 
//------------------------------------------------------------------------------ 
// Subroutines 
//------------------------------------------------------------------------------ 
 
void Error(char *s, HRESULT hr) 
{ 
 
    printf("\n\nError: %s (0x%X)  \n", s, hr); 
    exit(1); 
} 
 
//------------------------------------------------------------------------------ 
 
void Syntax() 
{ 
    printf("\n"); 
    printf("Syntax: msmqtrans -s | -r\n"); 
    printf("\t-s - Sender Side\n"); 
    printf("\t-r - Receiver Side\n"); 
    exit(1); 
 
} 
 
//------------------------------------------------------------------------------ 
 
void LocateTargetQueue (CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]) 
{ 
 
   DWORD      dwSize; 
   DWORD      i; 
 
   DWORD      cQueue; 
   DWORD      cProps; 
   HRESULT       hr; 
   MQPROPERTYRESTRICTION   aPropRestriction[MAX_VAR]; 
   MQRESTRICTION        Restriction; 
   MQCOLUMNSET          Column; 
   QUEUEPROPID          aqPropId[MAX_VAR]; 
   HANDLE               hEnum; 
   MQPROPVARIANT        aPropVar[MAX_VAR]; 
 
   //-------------------------------------------------------------------------- 
    // Prepare Parameters to locate a queue 
    //-------------------------------------------------------------------------- 
 
    // 1. Restriction = All queue with PROPID_TYPE 
    //            equal the type of MQTransTest queue. 
    cProps = 0; 
 
    aPropRestriction[cProps].rel = PREQ; 
    aPropRestriction[cProps].prop = PROPID_Q_TYPE; 
    aPropRestriction[cProps].prval.vt = VT_CLSID; 
    aPropRestriction[cProps].prval.puuid = pGuidType; 
    cProps++; 
 
    Restriction.cRes = cProps; 
    Restriction.paPropRes = aPropRestriction; 
 
 
    // 2. Columnset (In other words what property I want to retrieve). 
    //   Only the instance is important. 
    cProps = 0; 
    aqPropId[cProps] = PROPID_Q_INSTANCE; 
    cProps++; 
 
    Column.cCol = cProps; 
    Column.aCol = aqPropId; 
 
    //-------------------------------------------------------------------------- 
    // Locate the queues. Issue the query 
    //-------------------------------------------------------------------------- 
    hr = MQLocateBegin(NULL,&Restriction,&Column,NULL,&hEnum); 
 
   if (FAILED(hr)) 
      Error ("Locate Begin ",hr); 
 
 
    //-------------------------------------------------------------------------- 
    // Get the results 
    //-------------------------------------------------------------------------- 
    cQueue = MAX_VAR; 
    hr = MQLocateNext(hEnum, &cQueue, aPropVar); 
 
   if (FAILED(hr)) 
      Error ("MQLocateNext ",hr); 
 
    hr = MQLocateEnd(hEnum); 
 
    if(cQueue == 0) 
    { 
        // Could Not find any queue, so exit 
        printf("NOT FOUND...   exiting.\n\n"); 
        exit(0); 
    } 
 
 
    printf("FOUND.", cQueue); 
 
    dwSize = sizeof(WCHAR)*MAX_FORMAT; 
 
   //Transform the Instance GUID to format name 
   hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize); 
 
   if (FAILED(hr)) 
      Error ("Guidto Format Name ",hr); 
 
 
   // Free the GUID memory that was allocated during the locate 
    for(i = 0; i < cQueue; i++) 
      MQFreeMemory(aPropVar[i].puuid); 
 
 
} 
 
 
//------------------------------------------------------------------------------ 
 
void PrepareSendMessageProperties (MSGPROPID     amPropId[MAX_VAR], 
                           MQPROPVARIANT aPropVar[MAX_VAR], 
                           MQMSGPROPS    &msgprops, 
                           DWORD     &TransferSum) 
{ 
 
   DWORD      cProps; 
 
    cProps = 0; 
    amPropId[cProps] =             PROPID_M_BODY; 
    aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR; 
    aPropVar[cProps].caub.cElems =  sizeof(TransferSum); 
    aPropVar[cProps].caub.pElems =  (unsigned char *)&TransferSum; 
    cProps++; 
 
    // Create a MSGPROPS structure 
    msgprops.cProp =    cProps; 
msgprops.aPropID =  amPropId; 
    msgprops.aPropVar = aPropVar; 
    msgprops.aStatus =  0; 
 
} 
 
//-------------------------------------------------------------------------- 
 
void  CreateQueue (CLSID *pGuidType, WCHAR wsFormat[]) 
{ 
   QUEUEPROPID       aqPropId[MAX_VAR]; 
   WCHAR             wsPathName[1000];  //Big path name 
   MQPROPVARIANT     aPropVar[MAX_VAR]; 
   DWORD             cProps; 
   MQQUEUEPROPS      qprops; 
   DWORD             dwSize; 
   HRESULT           hr; 
 
   //--------------------------------------------------------------------- 
   // Prepare properties to create a queue on local machine 
   //--------------------------------------------------------------------- 
   cProps = 0; 
 
   // Set the PathName 
   aqPropId[cProps] =          PROPID_Q_PATHNAME; 
 
   wsprintf(wsPathName, TEXT("%s\\MSMQDemo"), g_wszMachineName); 
   aPropVar[cProps].vt =       VT_LPWSTR; 
   aPropVar[cProps].pwszVal =  wsPathName; 
   cProps++; 
 
   // Set the queue to transactional 
   aqPropId[cProps] =        PROPID_Q_TRANSACTION; 
 
   aPropVar[cProps].vt =     VT_UI1; 
   aPropVar[cProps].bVal =   MQ_TRANSACTIONAL; 
   cProps++; 
 
   // Set the type of the queue (Will be used to locate queues of this type) 
   aqPropId[cProps] =        PROPID_Q_TYPE; 
 
   aPropVar[cProps].vt =     VT_CLSID; 
   aPropVar[cProps].puuid =  pGuidType; 
   cProps++; 
 
   // Create a QUEUEPROPS structure 
   qprops.cProp =    cProps; 
   qprops.aPropID =  aqPropId; 
   qprops.aPropVar = aPropVar; 
   qprops.aStatus =  0; 
 
   //----------------------------------------------------------------------- 
   // Create the queue 
   //----------------------------------------------------------------------- 
   dwSize = sizeof(WCHAR)*MAX_FORMAT; 
   hr = MQCreateQueue(NULL, &qprops, wsFormat, &dwSize); 
 
   if(FAILED(hr)) 
   { 
      // API Fails, not because the queue exists 
      if(hr != MQ_ERROR_QUEUE_EXISTS) 
            Error("Cannot create queue.", hr); 
 
      // Queue exist, so get its format name 
      // Note: Since queue already exists, this sample assumes 
      // that it was created earlier by this program, so we 
      // do not check if queue is transactional. If at this point the 
      // queue is Not Transactional, the transactions will abort later... 
      // 
      hr = MQPathNameToFormatName(wsPathName, wsFormat, &dwSize); 
 
      if (FAILED(hr)) 
         Error ("Cannot retrieve format name",hr); 
   } 
} 
 
//------------------------------------------------------------------------------- 
 
void LogonToDB(DBCONN *ptr) 
{ 
   RETCODE retcode = 0; 
 
   retcode = SQLAllocConnect(g_hEnv, &(ptr->hdbc) ); 
 
   if (ProcessRetCode("SQLAllocConnect",ptr,retcode)) 
   { 
      retcode = SQLConnect(ptr->hdbc, 
                  (UCHAR *)(ptr->pszSrv), 
                  SQL_NTS, 
                  (UCHAR *)(ptr->pszUser), 
                  SQL_NTS, 
                  (UCHAR *)(ptr->pszPasswd), 
                  SQL_NTS 
                  ); 
 
      ProcessRetCode("SQLConnect",ptr,retcode); 
   } 
} 
 
//------------------------------------------------------------------------------ 
 
void ExecuteStatement(DBCONN *ptr, char* pszBuf,BOOL ProcessFlag) 
{ 
   RETCODE retcode = 0; 
 
   // Allocate a statement handle for use with SQLExecDirect 
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt)); 
 
   if (ProcessFlag) 
      ProcessRetCode("SQLAllocStmt",ptr,retcode); 
 
   // Execute the passed string as a SQL statement 
    retcode = SQLExecDirect (ptr->hstmt,(UCHAR *) pszBuf,SQL_NTS); 
 
   if (ProcessFlag) 
      ProcessRetCode("SQLExecDirect",ptr,retcode); 
 
   // Free the statement handle 
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP); 
   ptr->hstmt = SQL_NULL_HSTMT; 
 
   if (ProcessFlag) 
      ProcessRetCode("SQLFreeStmt",ptr,retcode); 
 
} 
 
// --------------------------------------------------------------------------- 
 
void DisplayDollars (DBCONN *ptr, char *psAccount) 
{ 
 
   DWORD             DollarsSum;               // in SQL database 
   SDWORD               cbValue;                  // OUT argument for SQL query 
   char              sSQLStatement[STR_LEN*2]; 
   RETCODE              retcode; 
 
 
 
 
   // Allocate a statement handle for use with SQLExecDirect 
   retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt)); 
 
   ProcessRetCode("SQLAllocStmt",ptr,retcode); 
 
 
   // Prepare SQL Statement to issue query 
   sprintf (sSQLStatement, "SELECT * FROM %s", psAccount); 
 
 
   // Issue SQL query 
   retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS); 
 
   ProcessRetCode ("SQLExecDirect",ptr,retcode); 
 
 
   // Prepare data structure to retrieve query results 
   retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,&cbValue); 
 
   ProcessRetCode ("SQLBindCol",ptr,retcode); 
 
 
   // Retrieve query results 
   retcode = SQLFetch (ptr->hstmt); 
 
   ProcessRetCode ("SQLFetch",ptr,retcode); 
 
 
   // Display query results 
   printf ("Sum of dollars in %s is %d .\n\n",psAccount,DollarsSum); 
 
 
   // Free the statement handle 
   retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP); 
   ptr->hstmt = SQL_NULL_HSTMT; 
 
   ProcessRetCode("SQLFreeStmt",ptr,retcode); 
 
} 
 
// --------------------------------------------------------------------------- 
 
void FreeODBCHandles(DBCONN *ptr) 
{ 
   SQLDisconnect(ptr->hdbc); 
 
   SQLFreeConnect(ptr->hdbc); 
 
   ptr->hdbc   = SQL_NULL_HDBC; 
   ptr->hstmt  = SQL_NULL_HSTMT; 
} 
 
 
// --------------------------------------------------------------------------- 
 
BOOL ProcessRetCode(char*   pszFuncName, 
                    DBCONN  *ptr, 
                    RETCODE retcode, 
                    BOOL    fExit) 
{ 
   BOOL state = TRUE ; 
   BOOL fExitP = fExit ; 
 
   switch (retcode) 
   { 
 
   case SQL_SUCCESS: 
         fExitP = FALSE ; 
         break; 
 
   case SQL_SUCCESS_WITH_INFO: 
         fExitP = FALSE ; 
         break; 
 
   case SQL_ERROR: 
         printf("%s Failed - see more info\n",pszFuncName); 
         DoSQLError(ptr); 
         state = FALSE; 
         break; 
 
   case SQL_INVALID_HANDLE: 
         printf("%s Failed - SQL_INVALID_HANDLE\n",pszFuncName); 
         state = FALSE; 
         break; 
 
   case SQL_NO_DATA_FOUND: 
         printf("%s Failed - SQL_NO_DATA_FOUND\n",pszFuncName); 
         fExitP = FALSE ; 
         state = FALSE; 
         break; 
 
   case SQL_STILL_EXECUTING: 
         printf("%s Failed - SQL_STILL_EXECUTING\n",pszFuncName); 
         fExitP = FALSE ; 
         state = FALSE; 
         break; 
 
   case SQL_NEED_DATA: 
         printf("%s Failed - SQL_NEED_DATA\n",pszFuncName); 
         fExitP = FALSE ; 
         state = FALSE; 
         break; 
 
   default: 
         printf("%s Failed - unexpected error, retcode = %x\n",pszFuncName,retcode); 
         DoSQLError(ptr); 
         state = FALSE; 
         break; 
   } 
 
   if (fExitP) 
   { 
      exit(-1) ; 
   } 
   return state ; 
} 
 
// --------------------------------------------------------------------------- 
 
void DoSQLError(DBCONN *ptr) 
{ 
 
   const INT            MSG_BUF_SIZE = 300; 
   UCHAR                szSqlState[MSG_BUF_SIZE]; 
   UCHAR             szErrorMsg[MSG_BUF_SIZE]; 
 
   SQLINTEGER  fNativeError   = 0; 
   SWORD    cbErrorMsg     = MSG_BUF_SIZE; 
   RETCODE     retcode; 
 
   retcode = SQLError(g_hEnv, 
              ptr ? ptr->hdbc : 0, 
              ptr ? ptr->hstmt :0, 
              szSqlState, 
              &fNativeError, 
              szErrorMsg, 
              MSG_BUF_SIZE, 
              &cbErrorMsg 
              ); 
 
   if (retcode != SQL_NO_DATA_FOUND && retcode != SQL_ERROR) 
   { 
      if (fNativeError != 0x1645)   // ignore change database to master context message 
      { 
         printf("SQLError info:\n"); 
         printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError); 
         printf("Error Message: %s\n\n",szErrorMsg); 
      } 
   } 
   else 
   { 
      printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",retcode); 
   } 
 
} 
// ---------------------------------------------------------------------------