Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1388 lines
38 KiB

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