|
|
/////////////////////////////////////
// //
// Transactions Sample Application //
// //
/////////////////////////////////////
#define UNICODE // For all MSMQ applications
#include <stdio.h>
#include <assert.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 whether 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"); }
/*
//-----------------------------------------------------
// Check if local computer is DS enabled or DS disabled
//-----------------------------------------------------
bool DetectDsConnection(void) {
MQPRIVATEPROPS PrivateProps; QMPROPID aPropId[MAX_VAR]; MQPROPVARIANT aPropVar[MAX_VAR]; HRESULT aStatus[MAX_VAR]; DWORD cProp; HRESULT hr;
// Prepare ds-enabled property
cProp = 0;
aPropId[cProp] = PROPID_PC_DS_ENABLED; aPropVar[cProp].vt = VT_NULL; ++cProp;
// Create a PRIVATEPROPS structure
PrivateProps.cProp = cProp; PrivateProps.aPropID = aPropId; PrivateProps.aPropVar = aPropVar; PrivateProps.aStatus = aStatus;
//
// Retrieve the information
//
hr = MQGetPrivateComputerInformation( NULL, &PrivateProps); if(FAILED(hr)) Error("Unable to detect DS connection", hr); return PrivateProps.aPropVar[0].boolVal; } */ BOOL IsDsEnabledLocaly() /*++
Routine Description: The rutine checked if the local computer is in DS-enabled Mode or in a DS-disabled mode
Arguments: None
Return Value: TRUE - DS-enabled mode. FALSE - DS-disabled mode.
--*/
{ MQPRIVATEPROPS PrivateProps; QMPROPID aPropId[MAX_VAR]; MQPROPVARIANT aPropVar[MAX_VAR]; DWORD cProp; HRESULT hr; //
// Prepare DS-enabled property.
//
cProp = 0;
aPropId[cProp] = PROPID_PC_DS_ENABLED; aPropVar[cProp].vt = VT_NULL; ++cProp; //
// Create a PRIVATEPROPS structure.
//
PrivateProps.cProp = cProp; PrivateProps.aPropID = aPropId; PrivateProps.aPropVar = aPropVar; PrivateProps.aStatus = NULL;
//
// Retrieve the information.
//
//
// This code is used to detect DS connection.
// This code is designed to allow compilation both on
// NT4 and Windows 2000.
//
HINSTANCE hMqrtLibrary = GetModuleHandle(TEXT("mqrt.dll")); assert(hMqrtLibrary != NULL);
typedef HRESULT (APIENTRY *MQGetPrivateComputerInformation_ROUTINE)(LPCWSTR , MQPRIVATEPROPS*); MQGetPrivateComputerInformation_ROUTINE pfMQGetPrivateComputerInformation = (MQGetPrivateComputerInformation_ROUTINE)GetProcAddress(hMqrtLibrary, "MQGetPrivateComputerInformation"); if(pfMQGetPrivateComputerInformation == NULL) { //
// There is no entry point in the dll matching to this routine
// it must be an old version of mqrt.dll -> product one.
// It will be OK to handle this case as a case of DS-enabled mode.
//
return TRUE; }
hr = pfMQGetPrivateComputerInformation( NULL, &PrivateProps); if(FAILED(hr)) { //
// We were not able to determine if DS is enabled or disabled
// notify the user and assume the worst case - (i.e. we are DS-disasbled).
//
Error("Unable to detect DS connection", hr); return FALSE; } if(PrivateProps.aPropVar[0].boolVal == 0) { //
// DS-disabled.
//
return FALSE; }
return TRUE;
}
//------------------------------------------------------------------------------
// MAIN
//------------------------------------------------------------------------------
main(int argc, char * * argv) { DWORD dwSize;
if(argc != 2) Syntax();
// Fail if local computer is DS disabled
if(!IsDsEnabledLocaly()) { printf("Unable to work on a DS disabled computer.\nExiting..."); exit(1); }
// 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,(SQLLEN *)&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); }
} // ---------------------------------------------------------------------------
|