home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / software / pelne / optionp / msmqocm.cab / mqtrans.cpp < prev    next >
C/C++ Source or Header  |  1997-10-06  |  37KB  |  1,245 lines

  1.  
  2.             /////////////////////////////////////
  3.             //                                 //
  4.             // Transactions Sample Application //
  5.             //                                 //
  6.             /////////////////////////////////////
  7.  
  8.  
  9. #define UNICODE                     // For all MSMQ applications
  10.  
  11. #include <stdio.h>
  12.  
  13.  
  14. //------------------------------------------------------------------------------
  15. // Include MS DTC specific header files.
  16. //------------------------------------------------------------------------------
  17. #define INITGUID
  18. #include <transact.h>
  19.  
  20. // Because we are compiling in UNICODE, here is a problem with DTC...
  21. //#include <xolehlp.h>
  22. extern HRESULT DtcGetTransactionManager(
  23.                                     LPSTR  pszHost,
  24.                                     LPSTR   pszTmName,
  25.                                     REFIID rid,
  26.                                     DWORD   dwReserved1,
  27.                                     WORD    wcbReserved2,
  28.                                     void FAR * pvReserved2,
  29.                                     void** ppvObject )  ;
  30.  
  31. //------------------------------------------------------------------------------
  32. // Include ODBC specific header file.
  33. //------------------------------------------------------------------------------
  34. #ifndef DBNTWIN32
  35. #define DBNTWIN32
  36.  
  37. #include <SQLEXT.h>
  38.  
  39. // from  <odbcss.h>
  40. #define SQL_COPT_SS_BASE               1200
  41. #define SQL_COPT_SS_ENLIST_IN_DTC      (SQL_COPT_SS_BASE+7) // Enlist in a Viper transaction
  42.  
  43. // Defines for use with SQL_ENLIST_IN_DTC
  44. #define SQL_DTC_DONE 0L       // Delimits end of Viper transaction
  45.  
  46. #endif
  47.  
  48. //--------------------------------------------------------------------------
  49. //  Enable Ansi ODBC on VC5
  50. //--------------------------------------------------------------------------
  51.  
  52. #ifdef  SQLExecDirect
  53. #undef  SQLExecDirect
  54. #define SQLExecDirect SQLExecDirectA
  55. #endif
  56.  
  57. #ifdef  SQLSetConnectOption
  58. #undef  SQLSetConnectOption
  59. #define SQLSetConnectOption  SQLSetConnectOptionA
  60. #endif
  61.  
  62. #ifdef  SQLError
  63. #undef  SQLError
  64. #define SQLError  SQLErrorA
  65. #endif
  66.  
  67. #ifdef  SQLConnect
  68. #undef  SQLConnect
  69. #define SQLConnect  SQLConnectA
  70. #endif
  71.  
  72. //------------------------------------------------------------------------------
  73. // Include MSMQ specific header file.
  74. //------------------------------------------------------------------------------
  75. #include "mq.h"
  76.  
  77. //------------------------------------------------------------------------------
  78. // Define constants
  79. //------------------------------------------------------------------------------
  80. #define STR_LEN      40
  81. #define MAX_VAR      20
  82. #define MAX_FORMAT  100
  83.  
  84.  
  85. //------------------------------------------------------------------------------
  86. // Define datatypes
  87. //------------------------------------------------------------------------------
  88. typedef struct DBCONN
  89. {
  90.    char  pszSrv [STR_LEN];    // data source name, configured through control panel
  91.    char  pszUser [STR_LEN];   // Login user name
  92.    char  pszPasswd[STR_LEN];  // Login user password
  93.    HDBC  hdbc;                // handle to an ODBC database connection
  94.    HSTMT hstmt;               // an ODBC statement handle, for use with SQLExecDirect
  95.  
  96. }  DBCONN;
  97.  
  98.  
  99. //------------------------------------------------------------------------------
  100. // Define Globals
  101. //------------------------------------------------------------------------------
  102.  
  103. // global DB connection struct for the server
  104. static DBCONN  gSrv =
  105.    {  "MSMQDemo",
  106.       "sa",
  107.       "",
  108.       SQL_NULL_HDBC,
  109.       SQL_NULL_HSTMT
  110.    };
  111.  
  112.  
  113. // guid type for MQTransTest queues
  114. static CLSID guidMQTransTestType =
  115. { 0xb856ab1, 0x16b6, 0x11d0, { 0x80, 0x48, 0x0, 0xa0, 0x24, 0x53, 0xc1, 0x6f } };
  116.  
  117.  
  118. //handle to ODBC environment
  119. HENV  g_hEnv = SQL_NULL_HENV ;
  120.  
  121.  
  122. //buffer for machine name
  123. WCHAR g_wszMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ];
  124.  
  125.  
  126. //--------------------------------------------------------------------------
  127. // Forward declaration of routines used.
  128. //--------------------------------------------------------------------------
  129.  
  130. void LogonToDB(DBCONN *ptr);
  131. void ExecuteStatement(DBCONN *ptr, char* pszBuf, BOOL ProcessFlag);
  132. BOOL ProcessRetCode(char*   pszFuncName,
  133.                     DBCONN  *ptr,
  134.                     RETCODE retcode,
  135.                     BOOL    fExit = TRUE);
  136. void DoSQLError(DBCONN *ptr);
  137. void FreeODBCHandles(DBCONN *ptr);
  138. void Error(char *s, HRESULT hr);
  139. void Syntax();
  140. void LocateTargetQueue(CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]);
  141. void PrepareSendMessageProperties(MSGPROPID     amPropId[MAX_VAR],
  142.                           MQPROPVARIANT aPropVar[MAX_VAR],
  143.                           MQMSGPROPS    &msgprops,
  144.                           DWORD        &TransferSum);
  145. void CreateQueue(CLSID *pGuidType, WCHAR wsFormat[]);
  146. void GetMachineName();
  147. void DisplayDollars (DBCONN *ptr, char *psAccount);
  148.  
  149.  
  150. //------------------------------------------------------------------------------
  151. // SENDER MODE:
  152. //
  153. // The Sender side does the following:
  154. //    1. Creates database "SenderAccount".
  155. //    2. Locates a MSMQ queue of type MQTransTest and opens it.
  156. //       (NOTE: for simplicity, this sample assumes there's only one queue of this type)
  157. //   3. In a loop:
  158. //            Prompts the user to enter TransferSum.
  159. //            Creates a transaction using MS DTC.
  160. //            Within the transaction:
  161. //                 Updates "SenderAccount" database (subtracts TransferSum).
  162. //                 Sends a message to Receiver side.
  163. //            Commits the transaction.
  164. //
  165. //    4. Cleanup.
  166. //
  167. //
  168. //
  169. // The transaction in the Sender mode includes two operations:
  170. // (1) Update "SenderAccount" database (subtract TransferSum).
  171. // (2) Send message to Receiver side.
  172. //------------------------------------------------------------------------------
  173.  
  174. void Sender()
  175. {
  176.  
  177.    ITransactionDispenser   *pTransactionDispenser;
  178.    ITransaction            *pTransaction;
  179.    BOOL                    fTransactionCommitFlag;
  180.                             // used to decide wother to Commit or Abort
  181.  
  182.    HRESULT              hr;
  183.    RETCODE              retcode;
  184.    DWORD                dwTransferSum;              // set by user
  185.    char                 sUserString[ STR_LEN ];
  186.    char                 sSQLStatement[ STR_LEN*2 ];
  187.  
  188.    MQMSGPROPS           msgprops;
  189.    MQPROPVARIANT        aPropVar[MAX_VAR];
  190.    MSGPROPID            amPropId[MAX_VAR];
  191.    WCHAR                wsFormat[MAX_FORMAT];
  192.    QUEUEHANDLE          aqh;
  193.  
  194.  
  195.     printf("\nSender Side.\n\n");
  196.  
  197.    //---------------------------------------------------------------------
  198.    // Build "SenderAccount" database (with the sum $1000)
  199.    //---------------------------------------------------------------------
  200.  
  201.    printf ("Building SenderAccount with the sum $1000...   ");
  202.  
  203.    // Get ODBC environment handle
  204.    retcode = SQLAllocEnv(&g_hEnv);
  205.  
  206.    ProcessRetCode("SQLAllocEnv",0, retcode);
  207.  
  208.    // Establish connection to database
  209.    LogonToDB(&gSrv);
  210.  
  211.    // Clear database from previous run.
  212.    ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE);
  213.  
  214.    // Create new table in database
  215.    ExecuteStatement(&gSrv,
  216.     "CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE);
  217.  
  218.    // Insert new data in database
  219.    ExecuteStatement(&gSrv,"INSERT INTO SenderAccount   VALUES(1000)",TRUE);
  220.  
  221.    printf ("OK.\n\n");
  222.  
  223.    //-----------------------------------------------------------------------
  224.    // Locate target queue and Open it for send
  225.    //-----------------------------------------------------------------------
  226.  
  227.    printf ("Searching Receiver queue...   ");
  228.  
  229.    // Locate target queue
  230.    LocateTargetQueue (&guidMQTransTestType, wsFormat);
  231.  
  232.    // Open target queue
  233.    hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh);
  234.  
  235.    if (FAILED(hr))
  236.    {
  237.       Error ("Open Queue ",hr);
  238.    }
  239.  
  240.    //--------------------------------------------------------------------------
  241.    // Get Transaction Dispenser
  242.    //--------------------------------------------------------------------------
  243.  
  244.    // Obtain an interface pointer from MS DTC proxy
  245.    hr = DtcGetTransactionManager(
  246.                NULL,                        // pszHost
  247.                NULL,                        // pszTmName
  248.                IID_ITransactionDispenser,   // IID of  interface
  249.                0,                           // Reserved -- must be null
  250.                0,                           // Reserved -- must be null
  251.                0,                           // Reserved -- must be null
  252.                (void **)&pTransactionDispenser  // pointer to pointer to requested interface
  253.                                  );
  254.  
  255.    if (FAILED(hr))
  256.    {
  257.       Error ("DTCGetTransactionManager",hr);
  258.    }
  259.  
  260.    //--------------------------------------------------------------------
  261.    // Sender Main Loop
  262.    //--------------------------------------------------------------------
  263.    while (TRUE)
  264.    {
  265.  
  266.       // Prompt user to enter TransferSum
  267.       printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit  ==> ");
  268.  
  269.       // Read user input
  270.       fgets (sUserString, STR_LEN, stdin);
  271.  
  272.       // Convert user string to DWORD
  273.       dwTransferSum = atoi(sUserString);
  274.  
  275.       // Prepare properties of message to send
  276.       PrepareSendMessageProperties (amPropId,
  277.                                     aPropVar,
  278.                                     msgprops,
  279.                                     dwTransferSum);
  280.  
  281.       //---------------------------------------------------------------------
  282.       // Create transaction (Inside Sender's Main Loop)
  283.       //---------------------------------------------------------------------
  284.  
  285.       printf ("\nStarting transaction...\n\n");
  286.  
  287.       // Initiate an MS DTC transaction
  288.       hr = pTransactionDispenser->BeginTransaction (
  289.             0,                         // must be null
  290.             ISOLATIONLEVEL_ISOLATED,   // Isolation Level
  291.             ISOFLAG_RETAIN_DONTCARE,   // Isolation flags
  292.             0,                         // pointer to transaction options object
  293.             &pTransaction);            // pointer to pointer to transaction object
  294.  
  295.       if (FAILED(hr))
  296.       {
  297.          Error ("BeginTransaction",hr);
  298.       }
  299.  
  300.       // Default is to commit transaction
  301.       fTransactionCommitFlag = TRUE;
  302.  
  303.       //
  304.       // SQL is a resource manager in the transaction.
  305.       // It must be enlisted.
  306.       //
  307.  
  308.       // Enlist database in the transaction
  309.       retcode = SQLSetConnectOption (gSrv.hdbc,
  310.                                      SQL_COPT_SS_ENLIST_IN_DTC,
  311.                                      (UDWORD)pTransaction);
  312.  
  313.       if (retcode != SQL_SUCCESS)
  314.       {
  315.          ProcessRetCode("SQLSetConnection", &gSrv, retcode, FALSE);
  316.          fTransactionCommitFlag = FALSE;
  317.       }
  318.  
  319.  
  320.       // Prepare SQL statement to update SenderAccount
  321.       sprintf (sSQLStatement,
  322.                "UPDATE SenderAccount  SET Rate = Rate - %lu", dwTransferSum) ;
  323.  
  324.       // Allocate a statement handle for use with SQLExecDirect
  325.       retcode = SQLAllocStmt(gSrv.hdbc, &gSrv.hstmt);
  326.  
  327.       if (retcode != SQL_SUCCESS)
  328.       {
  329.          ProcessRetCode("SQLAllocStmt", &gSrv, retcode, FALSE);
  330.          fTransactionCommitFlag = FALSE;
  331.       }
  332.  
  333.       // Update database  (subtract TransferSum from SenderAccount)
  334.       retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);
  335.  
  336.       if (retcode != SQL_SUCCESS)
  337.       {
  338.          ProcessRetCode("SQLExecDirect", &gSrv, retcode, FALSE);
  339.          fTransactionCommitFlag = FALSE;
  340.       }
  341.  
  342.       // Free the statement handle
  343.       retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);
  344.  
  345.       gSrv.hstmt = SQL_NULL_HSTMT;
  346.  
  347.       //
  348.       // MSMQ is another resource manager in the transaction.
  349.       // Its enlistment is implicit.
  350.       //
  351.  
  352.       // Within the transaction: Send message to Receiver Side
  353.       hr = MQSendMessage(aqh,              // Handle to destination queue
  354.                          &msgprops,        // pointer to MQMSGPROPS structure
  355.                          pTransaction);    // pointer to Transaction Object
  356.  
  357.  
  358.       if (FAILED(hr))
  359.       {
  360.          printf("\nFailed in MQSendMessage(). hresult- %lxh\n", (DWORD) hr) ;
  361.          fTransactionCommitFlag = FALSE;
  362.       }
  363.  
  364.  
  365.       // Commit the transaction
  366.       if (fTransactionCommitFlag)
  367.       {
  368.          printf ("Committing the transaction...   ");
  369.  
  370.          hr = pTransaction->Commit(0, 0, 0);
  371.  
  372.          if (FAILED(hr))
  373.             printf ("Failed... Transaction aborted.\n\n");
  374.          else
  375.             printf ("Transaction committed successfully.\n\n");
  376.  
  377.       }
  378.       else
  379.       {
  380.          printf ("Aborting the transaction...   ");
  381.  
  382.          hr = pTransaction->Abort(0, 0, 0);
  383.  
  384.          if (FAILED(hr))
  385.             Error("Transaction Abort",hr);
  386.          else
  387.             printf ("Transaction aborted.\n\n");
  388.       }
  389.  
  390.       // Release the transaction
  391.       pTransaction->Release();
  392.  
  393.       // End enlistment of database
  394.       retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);
  395.  
  396.       ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);
  397.  
  398.       // Display sum of dollars in Sender Account
  399.       DisplayDollars (&gSrv,"SenderAccount");
  400.  
  401.       // quit loop when nothing was transferred.
  402.       if (dwTransferSum == 0)
  403.          break;
  404.    }
  405.  
  406.    //--------------------------------------------------------------------------
  407.    // Cleanup
  408.    //--------------------------------------------------------------------------
  409.  
  410.    // Release Transaction Dispenser
  411.    pTransactionDispenser->Release();
  412.  
  413.  
  414.    // Free database
  415.    ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE);
  416.  
  417.  
  418.    // Free ODBC handle
  419.    FreeODBCHandles(&gSrv);
  420.  
  421.  
  422.    // Free the ODBC environment handle
  423.    retcode = SQLFreeEnv(g_hEnv);
  424.  
  425.    if (retcode == SQL_ERROR)
  426.       Error ("SQL FreeEnv ",0);
  427.  
  428.  
  429.    // Free MSMQ queue handle
  430.    MQCloseQueue(aqh);
  431.  
  432.  
  433.    printf ("\n\nSender Side completed.\n\n");
  434.  
  435. }
  436.  
  437.  
  438.  
  439.  
  440. //------------------------------------------------------------------------------
  441. // RECEIVER MODE:
  442. //
  443. // The Receiver side does the following:
  444. //    1. Creates database "ReceiverAccount".
  445. //    2. Creates a MSMQ public queue (with the Transactional property)
  446. //       of type MQTransTest on its own machine and opens it.
  447. //    3. In a loop:
  448. //            Creates a transaction using MS DTC.
  449. //            Within the transaction:
  450. //                 Receives a message from the queue (with the TransferSum).
  451. //                 Updates "ReceiverAccount" database (adds TransferSum).
  452. //            Commits the transaction.
  453. //
  454. //    4. Cleanup.
  455. //
  456. //
  457. //
  458. // The transaction in the Receiver mode include two operations:
  459. // (1) Receive message from queue (sent by Sender Side).
  460. // (2) Update "ReceiverAccount" database  (add TransferSum).
  461. //------------------------------------------------------------------------------
  462.  
  463. void Receiver()
  464. {
  465.    MSGPROPID            amPropId[MAX_VAR];
  466.    MQMSGPROPS           msgprops;
  467.    MQPROPVARIANT        aPropVar[MAX_VAR];
  468.    DWORD             cProps;
  469.    HRESULT              hr;
  470.    WCHAR             wsFormat[MAX_FORMAT];
  471.    QUEUEHANDLE          aqh;
  472.  
  473.    ITransactionDispenser   *pTransactionDispenser;
  474.    ITransaction         *pTransaction;
  475.    BOOL              TransactionCommitFlag;  // used to decide Commit or Abort
  476.  
  477.    RETCODE              retcode;
  478.    DWORD             TransferSum;
  479.  
  480.    DWORD             MessageBuffer;       // message body is the TransferSum
  481.    char              sSQLStatement[STR_LEN*2];
  482.  
  483.  
  484.  
  485.  
  486.  
  487.    printf ("\nReceiver Side.\n\n");
  488.  
  489.    //-----------------------------------------------------------------------
  490.    // Build "ReceiverAccount" database (with the rate $500)
  491.    //-----------------------------------------------------------------------
  492.  
  493.    printf ("Building ReceiverAccount with the rate $500...   ");
  494.  
  495.    // Get ODBC environment handle
  496.    retcode = SQLAllocEnv(&g_hEnv);
  497.  
  498.    ProcessRetCode("SQLAllocEnv",0, retcode);
  499.  
  500.    // Establish connection to database.
  501.    LogonToDB(&gSrv);
  502.  
  503.    // Clear table from previous run.
  504.    ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE);
  505.  
  506.    // Create new table.
  507.    ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE);
  508.  
  509.    // Insert new data in the table.
  510.    ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount  VALUES(500)",TRUE);
  511.  
  512.    printf ("OK.\n\n");
  513.  
  514.    //-----------------------------------------------------------------------
  515.    // Create queue and Open it for receive
  516.    //-----------------------------------------------------------------------
  517.  
  518.    printf ("Creating Receiver queue...   ");
  519.  
  520.    // Create the queue
  521.    CreateQueue (&guidMQTransTestType, wsFormat);
  522.  
  523.    // Prepare message properties to read
  524.    cProps = 0;
  525.  
  526.    amPropId[cProps] =             PROPID_M_BODY;
  527.  
  528.    aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
  529.    aPropVar[cProps].caub.cElems =  sizeof(MessageBuffer);
  530.    aPropVar[cProps].caub.pElems =  (unsigned char *)&MessageBuffer;
  531.    cProps++;
  532.  
  533.    // Create a MSGPROPS structure
  534.    msgprops.cProp =    cProps;
  535.    msgprops.aPropID =  amPropId;
  536.    msgprops.aPropVar = aPropVar;
  537.    msgprops.aStatus =  0;
  538.  
  539.    // Open the queue
  540.    hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
  541.  
  542.    //
  543.    // Little bit tricky. MQCreateQueue succeeded but it does not mean
  544.    // that MQOpenQueue will, because of replication delay. The queue is
  545.    // registered in MQIS, but it might take a replication interval
  546.    // until the replica reach the server I am connected to.
  547.    // To overcome this, open the queue in a loop.
  548.    //
  549.    if (hr == MQ_ERROR_QUEUE_NOT_FOUND)
  550.    {
  551.        int iCount = 0 ;
  552.        while((hr == MQ_ERROR_QUEUE_NOT_FOUND) && (iCount < 120))
  553.        {
  554.           printf(".");
  555.  
  556.           // Wait a bit
  557.           iCount++ ;
  558.           Sleep(500);
  559.  
  560.           // And retry
  561.           hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
  562.        }
  563.    }
  564.  
  565.    if (FAILED(hr))
  566.    {
  567.       Error ("Can't OpenQueue", hr);
  568.    }
  569.  
  570.    printf("OK.");
  571.  
  572.  
  573.    //--------------------------------------------------------------------------
  574.    // Get Transaction Dispenser
  575.    //--------------------------------------------------------------------------
  576.  
  577.    // Obtain an interface pointer from MS DTC proxy
  578.    hr = DtcGetTransactionManager(
  579.          NULL, NULL, // pszHost, pszTmName
  580.          IID_ITransactionDispenser,         // IID of requested interface
  581.          0,0,0,                             // Reserved -- must be null
  582.          (void **)&pTransactionDispenser);  // pointer to pointer to requested interface
  583.  
  584.  
  585.    if (FAILED(hr))
  586.       Error ("DTCGetTransactionManager",hr);
  587.  
  588.  
  589.    //--------------------------------------------------------------------------
  590.    // Receiver Main Loop
  591.    //--------------------------------------------------------------------------
  592.    while (TRUE)
  593.    {
  594.  
  595.       printf ("\n\nWaiting for a message to come...   ");
  596.  
  597.       // Peek outside the transaction, to avoid database lock
  598.       // for long/infinite period.
  599.       //
  600.       //dwSize = sizeof(wsResponse);
  601.       hr = MQReceiveMessage(
  602.                     aqh,                     // Handle to queue
  603.                     INFINITE,                // Timeout
  604.                     MQ_ACTION_PEEK_CURRENT,  // Peek Action
  605.                     &msgprops,               // Message Properties
  606.                     NULL,                    // Overlap
  607.                     NULL,                    // Receive Callback
  608.                     NULL,                    // Cursor
  609.                     NULL                     // No transaction yet
  610.                            );
  611.  
  612.       if (FAILED(hr))
  613.          Error("MQReceiveMessage (PEEKING) ",hr);
  614.  
  615.  
  616.  
  617.       //--------------------------------------------------------------------------
  618.       // Create transaction
  619.       //--------------------------------------------------------------------------
  620.       printf ("\n\nStarting transaction...\n\n");
  621.  
  622.  
  623.       // Initiate an MS DTC transaction
  624.       hr = pTransactionDispenser->BeginTransaction (
  625.             0,                                 // must be null
  626.             ISOLATIONLEVEL_ISOLATED,         // Isolation Level
  627.             ISOFLAG_RETAIN_DONTCARE,         // Isolation flags
  628.             0,                                 // pointer to transaction options object
  629.             &pTransaction);                    // pointer to pointer to transaction object
  630.  
  631.       if (FAILED(hr))
  632.          Error ("BeginTransaction",hr);
  633.  
  634.  
  635.       // Default is to commit transaction
  636.       TransactionCommitFlag = TRUE;
  637.  
  638.       //
  639.       // SQL is a resource manager in the transaction.
  640.       // It must be enlisted.
  641.       //
  642.  
  643.       // Enlist database in the transaction
  644.       retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);
  645.  
  646.       if (retcode != SQL_SUCCESS)
  647.          TransactionCommitFlag = FALSE;
  648.  
  649.  
  650.  
  651.       // Receive the message from the queue
  652.       //dwSize = sizeof(wsResponse);
  653.       hr = MQReceiveMessage(
  654.             aqh,                      // Handle to queue
  655.             INFINITE,                         // Timeout
  656.             MQ_ACTION_RECEIVE,                // Receive Action
  657.             &msgprops,                   // Message Properties
  658.             NULL,NULL,NULL,                   // Overlap, Receive Callback, Cursor
  659.             pTransaction);                    // pointer to transaction object
  660.  
  661.       if (FAILED(hr))
  662.          TransactionCommitFlag = FALSE;
  663.  
  664.  
  665.       // Message buffer holds the TransferSum
  666.       TransferSum = (DWORD)MessageBuffer;
  667.  
  668.  
  669.       // Prepare SQL statement to update ReceiverAccount
  670.       sprintf (sSQLStatement, "UPDATE ReceiverAccount   SET Rate = Rate + %i",TransferSum);
  671.  
  672.  
  673.       // Allocate a statement handle for use with SQLExecDirect
  674.       retcode = SQLAllocStmt(gSrv.hdbc,&gSrv.hstmt);
  675.  
  676.       if (retcode != SQL_SUCCESS)
  677.          TransactionCommitFlag = FALSE;
  678.  
  679.  
  680.       // Update database  (add TransferSum to ReceiverAccount)
  681.       retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);
  682.  
  683.       if (retcode != SQL_SUCCESS)
  684.          TransactionCommitFlag = FALSE;
  685.  
  686.  
  687.       // Free the statement handle
  688.       retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);
  689.  
  690.       gSrv.hstmt = SQL_NULL_HSTMT;
  691.  
  692.  
  693.  
  694.       // Commit the transaction
  695.       if (TransactionCommitFlag)
  696.       {
  697.          printf ("Committing the transaction...   ");
  698.  
  699.          hr = pTransaction->Commit(0, 0, 0);
  700.  
  701.          if (FAILED(hr))
  702.             printf ("Failed... Transaction aborted.\n\n");
  703.          else
  704.             printf ("Transaction committed successfully.\n\n");
  705.  
  706.       }
  707.  
  708.  
  709.       // Abort the transaction
  710.       else
  711.       {
  712.          printf ("Aborting the transaction...   ");
  713.  
  714.          hr = pTransaction->Abort(0, 0, 0);
  715.  
  716.          if (FAILED(hr))
  717.             Error("Transaction Abort",hr);
  718.          else
  719.             printf ("Transaction aborted.\n\n");
  720.  
  721.       }
  722.  
  723.  
  724.  
  725.       // Release the transaction
  726.       pTransaction->Release();
  727.  
  728.  
  729.       // End enlistment of database
  730.       retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);
  731.  
  732.       ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);
  733.  
  734.  
  735.       // Display sum of dollars in Receiver Account
  736.       DisplayDollars (&gSrv, "ReceiverAccount");
  737.  
  738.  
  739.       // Decide if to continue loop
  740.       if (TransferSum == 0)
  741.          break;
  742.  
  743.  
  744.    }
  745.  
  746.  
  747.    //--------------------------------------------------------------------------
  748.    // Cleanup
  749.    //--------------------------------------------------------------------------
  750.  
  751.    // Release Transaction Dispenser
  752.    pTransactionDispenser->Release();
  753.  
  754.  
  755.    // Free database
  756.    ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE);
  757.  
  758.  
  759.    // Free ODBC handle
  760.    FreeODBCHandles(&gSrv);
  761.  
  762.  
  763.    // Free the ODBC environment handle
  764.    retcode = SQLFreeEnv(g_hEnv);
  765.  
  766.    if (retcode == SQL_ERROR)
  767.       Error ("SQL FreeEnv ",0);
  768.  
  769.  
  770.    // Free queue handle
  771.     MQCloseQueue(aqh);
  772.  
  773.  
  774.    // Delete queue from directory
  775.    MQDeleteQueue(wsFormat);
  776.  
  777.  
  778.    printf ("\n\nReceiver Side completed.\n\n");
  779. }
  780.  
  781.  
  782.  
  783. //------------------------------------------------------------------------------
  784. //  MAIN
  785. //------------------------------------------------------------------------------
  786. main(int argc, char * * argv)
  787. {
  788.    DWORD dwSize;
  789.  
  790.     if(argc != 2)
  791.         Syntax();
  792.  
  793.  
  794.     // Retrieve machine name
  795.     dwSize = sizeof(g_wszMachineName);
  796.     GetComputerName(g_wszMachineName, &dwSize);
  797.  
  798.  
  799.  
  800.     if(strcmp(argv[1], "-s") == 0)
  801.         Sender();
  802.  
  803.     else if(strcmp(argv[1], "-r") == 0)
  804.         Receiver();
  805.  
  806.     else
  807.         Syntax();
  808.  
  809.     return(1);
  810.  
  811. }
  812.  
  813.  
  814.  
  815. //------------------------------------------------------------------------------
  816. // Subroutines
  817. //------------------------------------------------------------------------------
  818.  
  819. void Error(char *s, HRESULT hr)
  820. {
  821.  
  822.     printf("\n\nError: %s (0x%X)  \n", s, hr);
  823.     exit(1);
  824. }
  825.  
  826. //------------------------------------------------------------------------------
  827.  
  828. void Syntax()
  829. {
  830.     printf("\n");
  831.     printf("Syntax: msmqtrans -s | -r\n");
  832.     printf("\t-s - Sender Side\n");
  833.     printf("\t-r - Receiver Side\n");
  834.     exit(1);
  835.  
  836. }
  837.  
  838. //------------------------------------------------------------------------------
  839.  
  840. void LocateTargetQueue (CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT])
  841. {
  842.  
  843.    DWORD      dwSize;
  844.    DWORD      i;
  845.  
  846.    DWORD      cQueue;
  847.    DWORD      cProps;
  848.    HRESULT       hr;
  849.    MQPROPERTYRESTRICTION   aPropRestriction[MAX_VAR];
  850.    MQRESTRICTION        Restriction;
  851.    MQCOLUMNSET          Column;
  852.    QUEUEPROPID          aqPropId[MAX_VAR];
  853.    HANDLE               hEnum;
  854.    MQPROPVARIANT        aPropVar[MAX_VAR];
  855.  
  856.    //--------------------------------------------------------------------------
  857.     // Prepare Parameters to locate a queue
  858.     //--------------------------------------------------------------------------
  859.  
  860.     // 1. Restriction = All queue with PROPID_TYPE
  861.     //            equal the type of MQTransTest queue.
  862.     cProps = 0;
  863.  
  864.     aPropRestriction[cProps].rel = PREQ;
  865.     aPropRestriction[cProps].prop = PROPID_Q_TYPE;
  866.     aPropRestriction[cProps].prval.vt = VT_CLSID;
  867.     aPropRestriction[cProps].prval.puuid = pGuidType;
  868.     cProps++;
  869.  
  870.     Restriction.cRes = cProps;
  871.     Restriction.paPropRes = aPropRestriction;
  872.  
  873.  
  874.     // 2. Columnset (In other words what property I want to retrieve).
  875.     //   Only the instance is important.
  876.     cProps = 0;
  877.     aqPropId[cProps] = PROPID_Q_INSTANCE;
  878.     cProps++;
  879.  
  880.     Column.cCol = cProps;
  881.     Column.aCol = aqPropId;
  882.  
  883.     //--------------------------------------------------------------------------
  884.     // Locate the queues. Issue the query
  885.     //--------------------------------------------------------------------------
  886.     hr = MQLocateBegin(NULL,&Restriction,&Column,NULL,&hEnum);
  887.  
  888.    if (FAILED(hr))
  889.       Error ("Locate Begin ",hr);
  890.  
  891.  
  892.     //--------------------------------------------------------------------------
  893.     // Get the results
  894.     //--------------------------------------------------------------------------
  895.     cQueue = MAX_VAR;
  896.     hr = MQLocateNext(hEnum, &cQueue, aPropVar);
  897.  
  898.    if (FAILED(hr))
  899.       Error ("MQLocateNext ",hr);
  900.  
  901.     hr = MQLocateEnd(hEnum);
  902.  
  903.     if(cQueue == 0)
  904.     {
  905.         // Could Not find any queue, so exit
  906.         printf("NOT FOUND...   exiting.\n\n");
  907.         exit(0);
  908.     }
  909.  
  910.  
  911.     printf("FOUND.", cQueue);
  912.  
  913.     dwSize = sizeof(WCHAR)*MAX_FORMAT;
  914.  
  915.    //Transform the Instance GUID to format name
  916.    hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize);
  917.  
  918.    if (FAILED(hr))
  919.       Error ("Guidto Format Name ",hr);
  920.  
  921.  
  922.    // Free the GUID memory that was allocated during the locate
  923.     for(i = 0; i < cQueue; i++)
  924.       MQFreeMemory(aPropVar[i].puuid);
  925.  
  926.  
  927. }
  928.  
  929.  
  930. //------------------------------------------------------------------------------
  931.  
  932. void PrepareSendMessageProperties (MSGPROPID     amPropId[MAX_VAR],
  933.                            MQPROPVARIANT aPropVar[MAX_VAR],
  934.                            MQMSGPROPS    &msgprops,
  935.                            DWORD     &TransferSum)
  936. {
  937.  
  938.    DWORD      cProps;
  939.  
  940.     cProps = 0;
  941.     amPropId[cProps] =             PROPID_M_BODY;
  942.     aPropVar[cProps].vt =          VT_UI1 | VT_VECTOR;
  943.     aPropVar[cProps].caub.cElems =  sizeof(TransferSum);
  944.     aPropVar[cProps].caub.pElems =  (unsigned char *)&TransferSum;
  945.     cProps++;
  946.  
  947.     // Create a MSGPROPS structure
  948.     msgprops.cProp =    cProps;
  949.     msgprops.aPropID =  amPropId;
  950.     msgprops.aPropVar = aPropVar;
  951.     msgprops.aStatus =  0;
  952.  
  953. }
  954.  
  955. //--------------------------------------------------------------------------
  956.  
  957. void  CreateQueue (CLSID *pGuidType, WCHAR wsFormat[])
  958. {
  959.    QUEUEPROPID       aqPropId[MAX_VAR];
  960.    WCHAR             wsPathName[1000];  //Big path name
  961.    MQPROPVARIANT     aPropVar[MAX_VAR];
  962.    DWORD             cProps;
  963.    MQQUEUEPROPS      qprops;
  964.    DWORD             dwSize;
  965.    HRESULT           hr;
  966.  
  967.    //---------------------------------------------------------------------
  968.    // Prepare properties to create a queue on local machine
  969.    //---------------------------------------------------------------------
  970.    cProps = 0;
  971.  
  972.    // Set the PathName
  973.    aqPropId[cProps] =          PROPID_Q_PATHNAME;
  974.  
  975.    wsprintf(wsPathName, TEXT("%s\\MSMQDemo"), g_wszMachineName);
  976.    aPropVar[cProps].vt =       VT_LPWSTR;
  977.    aPropVar[cProps].pwszVal =  wsPathName;
  978.    cProps++;
  979.  
  980.    // Set the queue to transactional
  981.    aqPropId[cProps] =        PROPID_Q_TRANSACTION;
  982.  
  983.    aPropVar[cProps].vt =     VT_UI1;
  984.    aPropVar[cProps].bVal =   MQ_TRANSACTIONAL;
  985.    cProps++;
  986.  
  987.    // Set the type of the queue (Will be used to locate queues of this type)
  988.    aqPropId[cProps] =        PROPID_Q_TYPE;
  989.  
  990.    aPropVar[cProps].vt =     VT_CLSID;
  991.    aPropVar[cProps].puuid =  pGuidType;
  992.    cProps++;
  993.  
  994.    // Create a QUEUEPROPS structure
  995.    qprops.cProp =    cProps;
  996.    qprops.aPropID =  aqPropId;
  997.    qprops.aPropVar = aPropVar;
  998.    qprops.aStatus =  0;
  999.  
  1000.    //-----------------------------------------------------------------------
  1001.    // Create the queue
  1002.    //-----------------------------------------------------------------------
  1003.    dwSize = sizeof(WCHAR)*MAX_FORMAT;
  1004.    hr = MQCreateQueue(NULL, &qprops, wsFormat, &dwSize);
  1005.  
  1006.    if(FAILED(hr))
  1007.    {
  1008.       // API Fails, not because the queue exists
  1009.       if(hr != MQ_ERROR_QUEUE_EXISTS)
  1010.             Error("Cannot create queue.", hr);
  1011.  
  1012.       // Queue exist, so get its format name
  1013.       // Note: Since queue already exists, this sample assumes
  1014.       // that it was created earlier by this program, so we
  1015.       // do not check if queue is transactional. If at this point the
  1016.       // queue is Not Transactional, the transactions will abort later...
  1017.       //
  1018.       hr = MQPathNameToFormatName(wsPathName, wsFormat, &dwSize);
  1019.  
  1020.       if (FAILED(hr))
  1021.          Error ("Cannot retrieve format name",hr);
  1022.    }
  1023. }
  1024.  
  1025. //-------------------------------------------------------------------------------
  1026.  
  1027. void LogonToDB(DBCONN *ptr)
  1028. {
  1029.    RETCODE retcode = 0;
  1030.  
  1031.    retcode = SQLAllocConnect(g_hEnv, &(ptr->hdbc) );
  1032.  
  1033.    if (ProcessRetCode("SQLAllocConnect",ptr,retcode))
  1034.    {
  1035.       retcode = SQLConnect(ptr->hdbc,
  1036.                   (UCHAR *)(ptr->pszSrv),
  1037.                   SQL_NTS,
  1038.                   (UCHAR *)(ptr->pszUser),
  1039.                   SQL_NTS,
  1040.                   (UCHAR *)(ptr->pszPasswd),
  1041.                   SQL_NTS
  1042.                   );
  1043.  
  1044.       ProcessRetCode("SQLConnect",ptr,retcode);
  1045.    }
  1046. }
  1047.  
  1048. //------------------------------------------------------------------------------
  1049.  
  1050. void ExecuteStatement(DBCONN *ptr, char* pszBuf,BOOL ProcessFlag)
  1051. {
  1052.    RETCODE retcode = 0;
  1053.  
  1054.    // Allocate a statement handle for use with SQLExecDirect
  1055.    retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));
  1056.  
  1057.    if (ProcessFlag)
  1058.       ProcessRetCode("SQLAllocStmt",ptr,retcode);
  1059.  
  1060.    // Execute the passed string as a SQL statement
  1061.     retcode = SQLExecDirect (ptr->hstmt,(UCHAR *) pszBuf,SQL_NTS);
  1062.  
  1063.    if (ProcessFlag)
  1064.       ProcessRetCode("SQLExecDirect",ptr,retcode);
  1065.  
  1066.    // Free the statement handle
  1067.    retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
  1068.    ptr->hstmt = SQL_NULL_HSTMT;
  1069.  
  1070.    if (ProcessFlag)
  1071.       ProcessRetCode("SQLFreeStmt",ptr,retcode);
  1072.  
  1073. }
  1074.  
  1075. // ---------------------------------------------------------------------------
  1076.  
  1077. void DisplayDollars (DBCONN *ptr, char *psAccount)
  1078. {
  1079.  
  1080.    DWORD             DollarsSum;               // in SQL database
  1081.    SDWORD               cbValue;                  // OUT argument for SQL query
  1082.    char              sSQLStatement[STR_LEN*2];
  1083.    RETCODE              retcode;
  1084.  
  1085.  
  1086.  
  1087.  
  1088.    // Allocate a statement handle for use with SQLExecDirect
  1089.    retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));
  1090.  
  1091.    ProcessRetCode("SQLAllocStmt",ptr,retcode);
  1092.  
  1093.  
  1094.    // Prepare SQL Statement to issue query
  1095.    sprintf (sSQLStatement, "SELECT * FROM %s", psAccount);
  1096.  
  1097.  
  1098.    // Issue SQL query
  1099.    retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS);
  1100.  
  1101.    ProcessRetCode ("SQLExecDirect",ptr,retcode);
  1102.  
  1103.  
  1104.    // Prepare data structure to retrieve query results
  1105.    retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,&cbValue);
  1106.  
  1107.    ProcessRetCode ("SQLBindCol",ptr,retcode);
  1108.  
  1109.  
  1110.    // Retrieve query results
  1111.    retcode = SQLFetch (ptr->hstmt);
  1112.  
  1113.    ProcessRetCode ("SQLFetch",ptr,retcode);
  1114.  
  1115.  
  1116.    // Display query results
  1117.    printf ("Sum of dollars in %s is %d .\n\n",psAccount,DollarsSum);
  1118.  
  1119.  
  1120.    // Free the statement handle
  1121.    retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
  1122.    ptr->hstmt = SQL_NULL_HSTMT;
  1123.  
  1124.    ProcessRetCode("SQLFreeStmt",ptr,retcode);
  1125.  
  1126. }
  1127.  
  1128. // ---------------------------------------------------------------------------
  1129.  
  1130. void FreeODBCHandles(DBCONN *ptr)
  1131. {
  1132.    SQLDisconnect(ptr->hdbc);
  1133.  
  1134.    SQLFreeConnect(ptr->hdbc);
  1135.  
  1136.    ptr->hdbc   = SQL_NULL_HDBC;
  1137.    ptr->hstmt  = SQL_NULL_HSTMT;
  1138. }
  1139.  
  1140.  
  1141. // ---------------------------------------------------------------------------
  1142.  
  1143. BOOL ProcessRetCode(char*   pszFuncName,
  1144.                     DBCONN  *ptr,
  1145.                     RETCODE retcode,
  1146.                     BOOL    fExit)
  1147. {
  1148.    BOOL state = TRUE ;
  1149.    BOOL fExitP = fExit ;
  1150.  
  1151.    switch (retcode)
  1152.    {
  1153.  
  1154.    case SQL_SUCCESS:
  1155.          fExitP = FALSE ;
  1156.          break;
  1157.  
  1158.    case SQL_SUCCESS_WITH_INFO:
  1159.          fExitP = FALSE ;
  1160.          break;
  1161.  
  1162.    case SQL_ERROR:
  1163.          printf("%s Failed - see more info\n",pszFuncName);
  1164.          DoSQLError(ptr);
  1165.          state = FALSE;
  1166.          break;
  1167.  
  1168.    case SQL_INVALID_HANDLE:
  1169.          printf("%s Failed - SQL_INVALID_HANDLE\n",pszFuncName);
  1170.          state = FALSE;
  1171.          break;
  1172.  
  1173.    case SQL_NO_DATA_FOUND:
  1174.          printf("%s Failed - SQL_NO_DATA_FOUND\n",pszFuncName);
  1175.          fExitP = FALSE ;
  1176.          state = FALSE;
  1177.          break;
  1178.  
  1179.    case SQL_STILL_EXECUTING:
  1180.          printf("%s Failed - SQL_STILL_EXECUTING\n",pszFuncName);
  1181.          fExitP = FALSE ;
  1182.          state = FALSE;
  1183.          break;
  1184.  
  1185.    case SQL_NEED_DATA:
  1186.          printf("%s Failed - SQL_NEED_DATA\n",pszFuncName);
  1187.          fExitP = FALSE ;
  1188.          state = FALSE;
  1189.          break;
  1190.  
  1191.    default:
  1192.          printf("%s Failed - unexpected error, retcode = %x\n",pszFuncName,retcode);
  1193.          DoSQLError(ptr);
  1194.          state = FALSE;
  1195.          break;
  1196.    }
  1197.  
  1198.    if (fExitP)
  1199.    {
  1200.       exit(-1) ;
  1201.    }
  1202.    return state ;
  1203. }
  1204.  
  1205. // ---------------------------------------------------------------------------
  1206.  
  1207. void DoSQLError(DBCONN *ptr)
  1208. {
  1209.  
  1210.    const INT            MSG_BUF_SIZE = 300;
  1211.    UCHAR                szSqlState[MSG_BUF_SIZE];
  1212.    UCHAR             szErrorMsg[MSG_BUF_SIZE];
  1213.  
  1214.    SQLINTEGER  fNativeError   = 0;
  1215.    SWORD    cbErrorMsg     = MSG_BUF_SIZE;
  1216.    RETCODE     retcode;
  1217.  
  1218.    retcode = SQLError(g_hEnv,
  1219.               ptr ? ptr->hdbc : 0,
  1220.               ptr ? ptr->hstmt :0,
  1221.               szSqlState,
  1222.               &fNativeError,
  1223.               szErrorMsg,
  1224.               MSG_BUF_SIZE,
  1225.               &cbErrorMsg
  1226.               );
  1227.  
  1228.    if (retcode != SQL_NO_DATA_FOUND && retcode != SQL_ERROR)
  1229.    {
  1230.       if (fNativeError != 0x1645)   // ignore change database to master context message
  1231.       {
  1232.          printf("SQLError info:\n");
  1233.          printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError);
  1234.          printf("Error Message: %s\n\n",szErrorMsg);
  1235.       }
  1236.    }
  1237.    else
  1238.    {
  1239.       printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",retcode);
  1240.    }
  1241.  
  1242. }
  1243. // ---------------------------------------------------------------------------
  1244.  
  1245.