MQTRANS.CPP


/////////////////////////////////////
// //
// Transactions Sample Application //
// //
/////////////////////////////////////


#define UNICODE // For all MSMQ applications

#include <stdio.h>


//------------------------------------------------------------------------------
// Include MS DTC specific header files.
//------------------------------------------------------------------------------
#define INITGUID
#include <transact.h>

// Because we are compiling in UNICODE, here is a problem with DTC...
//#include <xolehlp.h>
extern HRESULT DtcGetTransactionManager(
LPSTR pszHost,
LPSTR pszTmName,
REFIID rid,
DWORD dwReserved1,
WORD wcbReserved2,
void FAR * pvReserved2,
void** ppvObject ) ;

//------------------------------------------------------------------------------
// Include ODBC specific header file.
//------------------------------------------------------------------------------
#ifndef DBNTWIN32
#define DBNTWIN32

#include <SQLEXT.h>

// from <odbcss.h>
#define SQL_COPT_SS_BASE 1200
#define SQL_COPT_SS_ENLIST_IN_DTC (SQL_COPT_SS_BASE+7) // Enlist in a Viper transaction

// Defines for use with SQL_ENLIST_IN_DTC
#define SQL_DTC_DONE 0L // Delimits end of Viper transaction

#endif

//--------------------------------------------------------------------------
// Enable Ansi ODBC on VC5
//--------------------------------------------------------------------------

#ifdef SQLExecDirect
#undef SQLExecDirect
#define SQLExecDirect SQLExecDirectA
#endif

#ifdef SQLSetConnectOption
#undef SQLSetConnectOption
#define SQLSetConnectOption SQLSetConnectOptionA
#endif

#ifdef SQLError
#undef SQLError
#define SQLError SQLErrorA
#endif

#ifdef SQLConnect
#undef SQLConnect
#define SQLConnect SQLConnectA
#endif

//------------------------------------------------------------------------------
// Include MSMQ specific header file.
//------------------------------------------------------------------------------
#include "mq.h"

//------------------------------------------------------------------------------
// Define constants
//------------------------------------------------------------------------------
#define STR_LEN 40
#define MAX_VAR 20
#define MAX_FORMAT 100


//------------------------------------------------------------------------------
// Define datatypes
//------------------------------------------------------------------------------
typedef struct DBCONN
{
char pszSrv [STR_LEN]; // data source name, configured through control panel
char pszUser [STR_LEN]; // Login user name
char pszPasswd[STR_LEN]; // Login user password
HDBC hdbc; // handle to an ODBC database connection
HSTMT hstmt; // an ODBC statement handle, for use with SQLExecDirect

} DBCONN;


//------------------------------------------------------------------------------
// Define Globals
//------------------------------------------------------------------------------

// global DB connection struct for the server
static DBCONN gSrv =
{ "MSMQDemo",
"sa",
"",
SQL_NULL_HDBC,
SQL_NULL_HSTMT
};


// guid type for MQTransTest queues
static CLSID guidMQTransTestType =
{ 0xb856ab1, 0x16b6, 0x11d0, { 0x80, 0x48, 0x0, 0xa0, 0x24, 0x53, 0xc1, 0x6f } };


//handle to ODBC environment
HENV g_hEnv = SQL_NULL_HENV ;


//buffer for machine name
WCHAR g_wszMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ];


//--------------------------------------------------------------------------
// Forward declaration of routines used.
//--------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr);
void ExecuteStatement(DBCONN *ptr, char* pszBuf, BOOL ProcessFlag);
BOOL ProcessRetCode(char* pszFuncName,
DBCONN *ptr,
RETCODE retcode,
BOOL fExit = TRUE);
void DoSQLError(DBCONN *ptr);
void FreeODBCHandles(DBCONN *ptr);
void Error(char *s, HRESULT hr);
void Syntax();
void LocateTargetQueue(CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT]);
void PrepareSendMessageProperties(MSGPROPID amPropId[MAX_VAR],
MQPROPVARIANT aPropVar[MAX_VAR],
MQMSGPROPS &msgprops,
DWORD &TransferSum);
void CreateQueue(CLSID *pGuidType, WCHAR wsFormat[]);
void GetMachineName();
void DisplayDollars (DBCONN *ptr, char *psAccount);


//------------------------------------------------------------------------------
// SENDER MODE:
//
// The Sender side does the following:
// 1. Creates database "SenderAccount".
// 2. Locates a MSMQ queue of type MQTransTest and opens it.
// (NOTE: for simplicity, this sample assumes there's only one queue of this type)
// 3. In a loop:
// Prompts the user to enter TransferSum.
// Creates a transaction using MS DTC.
// Within the transaction:
// Updates "SenderAccount" database (subtracts TransferSum).
// Sends a message to Receiver side.
// Commits the transaction.
//
// 4. Cleanup.
//
//
//
// The transaction in the Sender mode includes two operations:
// (1) Update "SenderAccount" database (subtract TransferSum).
// (2) Send message to Receiver side.
//------------------------------------------------------------------------------

void Sender()
{

ITransactionDispenser *pTransactionDispenser;
ITransaction *pTransaction;
BOOL fTransactionCommitFlag;
// used to decide wother to Commit or Abort

HRESULT hr;
RETCODE retcode;
DWORD dwTransferSum; // set by user
char sUserString[ STR_LEN ];
char sSQLStatement[ STR_LEN*2 ];

MQMSGPROPS msgprops;
MQPROPVARIANT aPropVar[MAX_VAR];
MSGPROPID amPropId[MAX_VAR];
WCHAR wsFormat[MAX_FORMAT];
QUEUEHANDLE aqh;


printf("\nSender Side.\n\n");

//---------------------------------------------------------------------
// Build "SenderAccount" database (with the sum $1000)
//---------------------------------------------------------------------

printf ("Building SenderAccount with the sum $1000... ");

// Get ODBC environment handle
retcode = SQLAllocEnv(&g_hEnv);

ProcessRetCode("SQLAllocEnv",0, retcode);

// Establish connection to database
LogonToDB(&gSrv);

// Clear database from previous run.
ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE);

// Create new table in database
ExecuteStatement(&gSrv,
"CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE);

// Insert new data in database
ExecuteStatement(&gSrv,"INSERT INTO SenderAccount VALUES(1000)",TRUE);

printf ("OK.\n\n");

//-----------------------------------------------------------------------
// Locate target queue and Open it for send
//-----------------------------------------------------------------------

printf ("Searching Receiver queue... ");

// Locate target queue
LocateTargetQueue (&guidMQTransTestType, wsFormat);

// Open target queue
hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh);

if (FAILED(hr))
{
Error ("Open Queue ",hr);
}

//--------------------------------------------------------------------------
// Get Transaction Dispenser
//--------------------------------------------------------------------------

// Obtain an interface pointer from MS DTC proxy
hr = DtcGetTransactionManager(
NULL, // pszHost
NULL, // pszTmName
IID_ITransactionDispenser, // IID of interface
0, // Reserved -- must be null
0, // Reserved -- must be null
0, // Reserved -- must be null
(void **)&pTransactionDispenser // pointer to pointer to requested interface
);

if (FAILED(hr))
{
Error ("DTCGetTransactionManager",hr);
}

//--------------------------------------------------------------------
// Sender Main Loop
//--------------------------------------------------------------------
while (TRUE)
{

// Prompt user to enter TransferSum
printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit ==> ");

// Read user input
fgets (sUserString, STR_LEN, stdin);

// Convert user string to DWORD
dwTransferSum = atoi(sUserString);

// Prepare properties of message to send
PrepareSendMessageProperties (amPropId,
aPropVar,
msgprops,
dwTransferSum);

//---------------------------------------------------------------------
// Create transaction (Inside Sender's Main Loop)
//---------------------------------------------------------------------

printf ("\nStarting transaction...\n\n");

// Initiate an MS DTC transaction
hr = pTransactionDispenser->BeginTransaction (
0, // must be null
ISOLATIONLEVEL_ISOLATED, // Isolation Level
ISOFLAG_RETAIN_DONTCARE, // Isolation flags
0, // pointer to transaction options object
&pTransaction); // pointer to pointer to transaction object

if (FAILED(hr))
{
Error ("BeginTransaction",hr);
}

// Default is to commit transaction
fTransactionCommitFlag = TRUE;

//
// SQL is a resource manager in the transaction.
// It must be enlisted.
//

// Enlist database in the transaction
retcode = SQLSetConnectOption (gSrv.hdbc,
SQL_COPT_SS_ENLIST_IN_DTC,
(UDWORD)pTransaction);

if (retcode != SQL_SUCCESS)
{
ProcessRetCode("SQLSetConnection", &gSrv, retcode, FALSE);
fTransactionCommitFlag = FALSE;
}


// Prepare SQL statement to update SenderAccount
sprintf (sSQLStatement,
"UPDATE SenderAccount SET Rate = Rate - %lu", dwTransferSum) ;

// Allocate a statement handle for use with SQLExecDirect
retcode = SQLAllocStmt(gSrv.hdbc, &gSrv.hstmt);

if (retcode != SQL_SUCCESS)
{
ProcessRetCode("SQLAllocStmt", &gSrv, retcode, FALSE);
fTransactionCommitFlag = FALSE;
}

// Update database (subtract TransferSum from SenderAccount)
retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

if (retcode != SQL_SUCCESS)
{
ProcessRetCode("SQLExecDirect", &gSrv, retcode, FALSE);
fTransactionCommitFlag = FALSE;
}

// Free the statement handle
retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

gSrv.hstmt = SQL_NULL_HSTMT;

//
// MSMQ is another resource manager in the transaction.
// Its enlistment is implicit.
//

// Within the transaction: Send message to Receiver Side
hr = MQSendMessage(aqh, // Handle to destination queue
&msgprops, // pointer to MQMSGPROPS structure
pTransaction); // pointer to Transaction Object


if (FAILED(hr))
{
printf("\nFailed in MQSendMessage(). hresult- %lxh\n", (DWORD) hr) ;
fTransactionCommitFlag = FALSE;
}


// Commit the transaction
if (fTransactionCommitFlag)
{
printf ("Committing the transaction... ");

hr = pTransaction->Commit(0, 0, 0);

if (FAILED(hr))
printf ("Failed... Transaction aborted.\n\n");
else
printf ("Transaction committed successfully.\n\n");

}
else
{
printf ("Aborting the transaction... ");

hr = pTransaction->Abort(0, 0, 0);

if (FAILED(hr))
Error("Transaction Abort",hr);
else
printf ("Transaction aborted.\n\n");
}

// Release the transaction
pTransaction->Release();

// End enlistment of database
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);

// Display sum of dollars in Sender Account
DisplayDollars (&gSrv,"SenderAccount");

// quit loop when nothing was transferred.
if (dwTransferSum == 0)
break;
}

//--------------------------------------------------------------------------
// Cleanup
//--------------------------------------------------------------------------

// Release Transaction Dispenser
pTransactionDispenser->Release();


// Free database
ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE);


// Free ODBC handle
FreeODBCHandles(&gSrv);


// Free the ODBC environment handle
retcode = SQLFreeEnv(g_hEnv);

if (retcode == SQL_ERROR)
Error ("SQL FreeEnv ",0);


// Free MSMQ queue handle
MQCloseQueue(aqh);


printf ("\n\nSender Side completed.\n\n");

}




//------------------------------------------------------------------------------
// RECEIVER MODE:
//
// The Receiver side does the following:
// 1. Creates database "ReceiverAccount".
// 2. Creates a MSMQ public queue (with the Transactional property)
// of type MQTransTest on its own machine and opens it.
// 3. In a loop:
// Creates a transaction using MS DTC.
// Within the transaction:
// Receives a message from the queue (with the TransferSum).
// Updates "ReceiverAccount" database (adds TransferSum).
// Commits the transaction.
//
// 4. Cleanup.
//
//
//
// The transaction in the Receiver mode include two operations:
// (1) Receive message from queue (sent by Sender Side).
// (2) Update "ReceiverAccount" database (add TransferSum).
//------------------------------------------------------------------------------

void Receiver()
{
MSGPROPID amPropId[MAX_VAR];
MQMSGPROPS msgprops;
MQPROPVARIANT aPropVar[MAX_VAR];
DWORD cProps;
HRESULT hr;
WCHAR wsFormat[MAX_FORMAT];
QUEUEHANDLE aqh;

ITransactionDispenser *pTransactionDispenser;
ITransaction *pTransaction;
BOOL TransactionCommitFlag; // used to decide Commit or Abort

RETCODE retcode;
DWORD TransferSum;

DWORD MessageBuffer; // message body is the TransferSum
char sSQLStatement[STR_LEN*2];





printf ("\nReceiver Side.\n\n");

//-----------------------------------------------------------------------
// Build "ReceiverAccount" database (with the rate $500)
//-----------------------------------------------------------------------

printf ("Building ReceiverAccount with the rate $500... ");

// Get ODBC environment handle
retcode = SQLAllocEnv(&g_hEnv);

ProcessRetCode("SQLAllocEnv",0, retcode);

// Establish connection to database.
LogonToDB(&gSrv);

// Clear table from previous run.
ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE);

// Create new table.
ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE);

// Insert new data in the table.
ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount VALUES(500)",TRUE);

printf ("OK.\n\n");

//-----------------------------------------------------------------------
// Create queue and Open it for receive
//-----------------------------------------------------------------------

printf ("Creating Receiver queue... ");

// Create the queue
CreateQueue (&guidMQTransTestType, wsFormat);

// Prepare message properties to read
cProps = 0;

amPropId[cProps] = PROPID_M_BODY;

aPropVar[cProps].vt = VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(MessageBuffer);
aPropVar[cProps].caub.pElems = (unsigned char *)&MessageBuffer;
cProps++;

// Create a MSGPROPS structure
msgprops.cProp = cProps;
msgprops.aPropID = amPropId;
msgprops.aPropVar = aPropVar;
msgprops.aStatus = 0;

// Open the queue
hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);

//
// Little bit tricky. MQCreateQueue succeeded but it does not mean
// that MQOpenQueue will, because of replication delay. The queue is
// registered in MQIS, but it might take a replication interval
// until the replica reach the server I am connected to.
// To overcome this, open the queue in a loop.
//
if (hr == MQ_ERROR_QUEUE_NOT_FOUND)
{
int iCount = 0 ;
while((hr == MQ_ERROR_QUEUE_NOT_FOUND) && (iCount < 120))
{
printf(".");

// Wait a bit
iCount++ ;
Sleep(500);

// And retry
hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
}
}

if (FAILED(hr))
{
Error ("Can't OpenQueue", hr);
}

printf("OK.");


//--------------------------------------------------------------------------
// Get Transaction Dispenser
//--------------------------------------------------------------------------

// Obtain an interface pointer from MS DTC proxy
hr = DtcGetTransactionManager(
NULL, NULL, // pszHost, pszTmName
IID_ITransactionDispenser, // IID of requested interface
0,0,0, // Reserved -- must be null
(void **)&pTransactionDispenser); // pointer to pointer to requested interface


if (FAILED(hr))
Error ("DTCGetTransactionManager",hr);


//--------------------------------------------------------------------------
// Receiver Main Loop
//--------------------------------------------------------------------------
while (TRUE)
{

printf ("\n\nWaiting for a message to come... ");

// Peek outside the transaction, to avoid database lock
// for long/infinite period.
//
//dwSize = sizeof(wsResponse);
hr = MQReceiveMessage(
aqh, // Handle to queue
INFINITE, // Timeout
MQ_ACTION_PEEK_CURRENT, // Peek Action
&msgprops, // Message Properties
NULL, // Overlap
NULL, // Receive Callback
NULL, // Cursor
NULL // No transaction yet
);

if (FAILED(hr))
Error("MQReceiveMessage (PEEKING) ",hr);



//--------------------------------------------------------------------------
// Create transaction
//--------------------------------------------------------------------------
printf ("\n\nStarting transaction...\n\n");


// Initiate an MS DTC transaction
hr = pTransactionDispenser->BeginTransaction (
0, // must be null
ISOLATIONLEVEL_ISOLATED, // Isolation Level
ISOFLAG_RETAIN_DONTCARE, // Isolation flags
0, // pointer to transaction options object
&pTransaction); // pointer to pointer to transaction object

if (FAILED(hr))
Error ("BeginTransaction",hr);


// Default is to commit transaction
TransactionCommitFlag = TRUE;

//
// SQL is a resource manager in the transaction.
// It must be enlisted.
//

// Enlist database in the transaction
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);

if (retcode != SQL_SUCCESS)
TransactionCommitFlag = FALSE;



// Receive the message from the queue
//dwSize = sizeof(wsResponse);
hr = MQReceiveMessage(
aqh, // Handle to queue
INFINITE, // Timeout
MQ_ACTION_RECEIVE, // Receive Action
&msgprops, // Message Properties
NULL,NULL,NULL, // Overlap, Receive Callback, Cursor
pTransaction); // pointer to transaction object

if (FAILED(hr))
TransactionCommitFlag = FALSE;


// Message buffer holds the TransferSum
TransferSum = (DWORD)MessageBuffer;


// Prepare SQL statement to update ReceiverAccount
sprintf (sSQLStatement, "UPDATE ReceiverAccount SET Rate = Rate + %i",TransferSum);


// Allocate a statement handle for use with SQLExecDirect
retcode = SQLAllocStmt(gSrv.hdbc,&gSrv.hstmt);

if (retcode != SQL_SUCCESS)
TransactionCommitFlag = FALSE;


// Update database (add TransferSum to ReceiverAccount)
retcode = SQLExecDirect (gSrv.hstmt,(UCHAR *) sSQLStatement, SQL_NTS);

if (retcode != SQL_SUCCESS)
TransactionCommitFlag = FALSE;


// Free the statement handle
retcode = SQLFreeStmt(gSrv.hstmt, SQL_DROP);

gSrv.hstmt = SQL_NULL_HSTMT;



// Commit the transaction
if (TransactionCommitFlag)
{
printf ("Committing the transaction... ");

hr = pTransaction->Commit(0, 0, 0);

if (FAILED(hr))
printf ("Failed... Transaction aborted.\n\n");
else
printf ("Transaction committed successfully.\n\n");

}


// Abort the transaction
else
{
printf ("Aborting the transaction... ");

hr = pTransaction->Abort(0, 0, 0);

if (FAILED(hr))
Error("Transaction Abort",hr);
else
printf ("Transaction aborted.\n\n");

}



// Release the transaction
pTransaction->Release();


// End enlistment of database
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);

ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);


// Display sum of dollars in Receiver Account
DisplayDollars (&gSrv, "ReceiverAccount");


// Decide if to continue loop
if (TransferSum == 0)
break;


}


//--------------------------------------------------------------------------
// Cleanup
//--------------------------------------------------------------------------

// Release Transaction Dispenser
pTransactionDispenser->Release();


// Free database
ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE);


// Free ODBC handle
FreeODBCHandles(&gSrv);


// Free the ODBC environment handle
retcode = SQLFreeEnv(g_hEnv);

if (retcode == SQL_ERROR)
Error ("SQL FreeEnv ",0);


// Free queue handle
MQCloseQueue(aqh);


// Delete queue from directory
MQDeleteQueue(wsFormat);


printf ("\n\nReceiver Side completed.\n\n");
}



//------------------------------------------------------------------------------
// MAIN
//------------------------------------------------------------------------------
main(int argc, char * * argv)
{
DWORD dwSize;

if(argc != 2)
Syntax();


// Retrieve machine name
dwSize = sizeof(g_wszMachineName);
GetComputerName(g_wszMachineName, &dwSize);



if(strcmp(argv[1], "-s") == 0)
Sender();

else if(strcmp(argv[1], "-r") == 0)
Receiver();

else
Syntax();

return(1);

}



//------------------------------------------------------------------------------
// Subroutines
//------------------------------------------------------------------------------

void Error(char *s, HRESULT hr)
{

printf("\n\nError: %s (0x%X) \n", s, hr);
exit(1);
}

//------------------------------------------------------------------------------

void Syntax()
{
printf("\n");
printf("Syntax: msmqtrans -s | -r\n");
printf("\t-s - Sender Side\n");
printf("\t-r - Receiver Side\n");
exit(1);

}

//------------------------------------------------------------------------------

void LocateTargetQueue (CLSID *pGuidType, WCHAR wsFormat[MAX_FORMAT])
{

DWORD dwSize;
DWORD i;

DWORD cQueue;
DWORD cProps;
HRESULT hr;
MQPROPERTYRESTRICTION aPropRestriction[MAX_VAR];
MQRESTRICTION Restriction;
MQCOLUMNSET Column;
QUEUEPROPID aqPropId[MAX_VAR];
HANDLE hEnum;
MQPROPVARIANT aPropVar[MAX_VAR];

//--------------------------------------------------------------------------
// Prepare Parameters to locate a queue
//--------------------------------------------------------------------------

// 1. Restriction = All queue with PROPID_TYPE
// equal the type of MQTransTest queue.
cProps = 0;

aPropRestriction[cProps].rel = PREQ;
aPropRestriction[cProps].prop = PROPID_Q_TYPE;
aPropRestriction[cProps].prval.vt = VT_CLSID;
aPropRestriction[cProps].prval.puuid = pGuidType;
cProps++;

Restriction.cRes = cProps;
Restriction.paPropRes = aPropRestriction;


// 2. Columnset (In other words what property I want to retrieve).
// Only the instance is important.
cProps = 0;
aqPropId[cProps] = PROPID_Q_INSTANCE;
cProps++;

Column.cCol = cProps;
Column.aCol = aqPropId;

//--------------------------------------------------------------------------
// Locate the queues. Issue the query
//--------------------------------------------------------------------------
hr = MQLocateBegin(NULL,&Restriction,&Column,NULL,&hEnum);

if (FAILED(hr))
Error ("Locate Begin ",hr);


//--------------------------------------------------------------------------
// Get the results
//--------------------------------------------------------------------------
cQueue = MAX_VAR;
hr = MQLocateNext(hEnum, &cQueue, aPropVar);

if (FAILED(hr))
Error ("MQLocateNext ",hr);

hr = MQLocateEnd(hEnum);

if(cQueue == 0)
{
// Could Not find any queue, so exit
printf("NOT FOUND... exiting.\n\n");
exit(0);
}


printf("FOUND.", cQueue);

dwSize = sizeof(WCHAR)*MAX_FORMAT;

//Transform the Instance GUID to format name
hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize);

if (FAILED(hr))
Error ("Guidto Format Name ",hr);


// Free the GUID memory that was allocated during the locate
for(i = 0; i < cQueue; i++)
MQFreeMemory(aPropVar[i].puuid);


}


//------------------------------------------------------------------------------

void PrepareSendMessageProperties (MSGPROPID amPropId[MAX_VAR],
MQPROPVARIANT aPropVar[MAX_VAR],
MQMSGPROPS &msgprops,
DWORD &TransferSum)
{

DWORD cProps;

cProps = 0;
amPropId[cProps] = PROPID_M_BODY;
aPropVar[cProps].vt = VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(TransferSum);
aPropVar[cProps].caub.pElems = (unsigned char *)&TransferSum;
cProps++;

// Create a MSGPROPS structure
msgprops.cProp = cProps;

msgprops.aPropID =  amPropId; 
msgprops.aPropVar = aPropVar;
msgprops.aStatus = 0;

}

//--------------------------------------------------------------------------

void CreateQueue (CLSID *pGuidType, WCHAR wsFormat[])
{
QUEUEPROPID aqPropId[MAX_VAR];
WCHAR wsPathName[1000]; //Big path name
MQPROPVARIANT aPropVar[MAX_VAR];
DWORD cProps;
MQQUEUEPROPS qprops;
DWORD dwSize;
HRESULT hr;

//---------------------------------------------------------------------
// Prepare properties to create a queue on local machine
//---------------------------------------------------------------------
cProps = 0;

// Set the PathName
aqPropId[cProps] = PROPID_Q_PATHNAME;

wsprintf(wsPathName, TEXT("%s\\MSMQDemo"), g_wszMachineName);
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wsPathName;
cProps++;

// Set the queue to transactional
aqPropId[cProps] = PROPID_Q_TRANSACTION;

aPropVar[cProps].vt = VT_UI1;
aPropVar[cProps].bVal = MQ_TRANSACTIONAL;
cProps++;

// Set the type of the queue (Will be used to locate queues of this type)
aqPropId[cProps] = PROPID_Q_TYPE;

aPropVar[cProps].vt = VT_CLSID;
aPropVar[cProps].puuid = pGuidType;
cProps++;

// Create a QUEUEPROPS structure
qprops.cProp = cProps;
qprops.aPropID = aqPropId;
qprops.aPropVar = aPropVar;
qprops.aStatus = 0;

//-----------------------------------------------------------------------
// Create the queue
//-----------------------------------------------------------------------
dwSize = sizeof(WCHAR)*MAX_FORMAT;
hr = MQCreateQueue(NULL, &qprops, wsFormat, &dwSize);

if(FAILED(hr))
{
// API Fails, not because the queue exists
if(hr != MQ_ERROR_QUEUE_EXISTS)
Error("Cannot create queue.", hr);

// Queue exist, so get its format name
// Note: Since queue already exists, this sample assumes
// that it was created earlier by this program, so we
// do not check if queue is transactional. If at this point the
// queue is Not Transactional, the transactions will abort later...
//
hr = MQPathNameToFormatName(wsPathName, wsFormat, &dwSize);

if (FAILED(hr))
Error ("Cannot retrieve format name",hr);
}
}

//-------------------------------------------------------------------------------

void LogonToDB(DBCONN *ptr)
{
RETCODE retcode = 0;

retcode = SQLAllocConnect(g_hEnv, &(ptr->hdbc) );

if (ProcessRetCode("SQLAllocConnect",ptr,retcode))
{
retcode = SQLConnect(ptr->hdbc,
(UCHAR *)(ptr->pszSrv),
SQL_NTS,
(UCHAR *)(ptr->pszUser),
SQL_NTS,
(UCHAR *)(ptr->pszPasswd),
SQL_NTS
);

ProcessRetCode("SQLConnect",ptr,retcode);
}
}

//------------------------------------------------------------------------------

void ExecuteStatement(DBCONN *ptr, char* pszBuf,BOOL ProcessFlag)
{
RETCODE retcode = 0;

// Allocate a statement handle for use with SQLExecDirect
retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

if (ProcessFlag)
ProcessRetCode("SQLAllocStmt",ptr,retcode);

// Execute the passed string as a SQL statement
retcode = SQLExecDirect (ptr->hstmt,(UCHAR *) pszBuf,SQL_NTS);

if (ProcessFlag)
ProcessRetCode("SQLExecDirect",ptr,retcode);

// Free the statement handle
retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
ptr->hstmt = SQL_NULL_HSTMT;

if (ProcessFlag)
ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void DisplayDollars (DBCONN *ptr, char *psAccount)
{

DWORD DollarsSum; // in SQL database
SDWORD cbValue; // OUT argument for SQL query
char sSQLStatement[STR_LEN*2];
RETCODE retcode;




// Allocate a statement handle for use with SQLExecDirect
retcode = SQLAllocStmt(ptr->hdbc,&(ptr->hstmt));

ProcessRetCode("SQLAllocStmt",ptr,retcode);


// Prepare SQL Statement to issue query
sprintf (sSQLStatement, "SELECT * FROM %s", psAccount);


// Issue SQL query
retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS);

ProcessRetCode ("SQLExecDirect",ptr,retcode);


// Prepare data structure to retrieve query results
retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,&cbValue);

ProcessRetCode ("SQLBindCol",ptr,retcode);


// Retrieve query results
retcode = SQLFetch (ptr->hstmt);

ProcessRetCode ("SQLFetch",ptr,retcode);


// Display query results
printf ("Sum of dollars in %s is %d .\n\n",psAccount,DollarsSum);


// Free the statement handle
retcode = SQLFreeStmt(ptr->hstmt, SQL_DROP);
ptr->hstmt = SQL_NULL_HSTMT;

ProcessRetCode("SQLFreeStmt",ptr,retcode);

}

// ---------------------------------------------------------------------------

void FreeODBCHandles(DBCONN *ptr)
{
SQLDisconnect(ptr->hdbc);

SQLFreeConnect(ptr->hdbc);

ptr->hdbc = SQL_NULL_HDBC;
ptr->hstmt = SQL_NULL_HSTMT;
}


// ---------------------------------------------------------------------------

BOOL ProcessRetCode(char* pszFuncName,
DBCONN *ptr,
RETCODE retcode,
BOOL fExit)
{
BOOL state = TRUE ;
BOOL fExitP = fExit ;

switch (retcode)
{

case SQL_SUCCESS:
fExitP = FALSE ;
break;

case SQL_SUCCESS_WITH_INFO:
fExitP = FALSE ;
break;

case SQL_ERROR:
printf("%s Failed - see more info\n",pszFuncName);
DoSQLError(ptr);
state = FALSE;
break;

case SQL_INVALID_HANDLE:
printf("%s Failed - SQL_INVALID_HANDLE\n",pszFuncName);
state = FALSE;
break;

case SQL_NO_DATA_FOUND:
printf("%s Failed - SQL_NO_DATA_FOUND\n",pszFuncName);
fExitP = FALSE ;
state = FALSE;
break;

case SQL_STILL_EXECUTING:
printf("%s Failed - SQL_STILL_EXECUTING\n",pszFuncName);
fExitP = FALSE ;
state = FALSE;
break;

case SQL_NEED_DATA:
printf("%s Failed - SQL_NEED_DATA\n",pszFuncName);
fExitP = FALSE ;
state = FALSE;
break;

default:
printf("%s Failed - unexpected error, retcode = %x\n",pszFuncName,retcode);
DoSQLError(ptr);
state = FALSE;
break;
}

if (fExitP)
{
exit(-1) ;
}
return state ;
}

// ---------------------------------------------------------------------------

void DoSQLError(DBCONN *ptr)
{

const INT MSG_BUF_SIZE = 300;
UCHAR szSqlState[MSG_BUF_SIZE];
UCHAR szErrorMsg[MSG_BUF_SIZE];

SQLINTEGER fNativeError = 0;
SWORD cbErrorMsg = MSG_BUF_SIZE;
RETCODE retcode;

retcode = SQLError(g_hEnv,
ptr ? ptr->hdbc : 0,
ptr ? ptr->hstmt :0,
szSqlState,
&fNativeError,
szErrorMsg,
MSG_BUF_SIZE,
&cbErrorMsg
);

if (retcode != SQL_NO_DATA_FOUND && retcode != SQL_ERROR)
{
if (fNativeError != 0x1645) // ignore change database to master context message
{
printf("SQLError info:\n");
printf("SqlState: %s, fNativeError: %x\n",szSqlState,fNativeError);
printf("Error Message: %s\n\n",szErrorMsg);
}
}
else
{
printf("SQLError() failed: %x, NO_DATA_FOUND OR SQL_ERROR\n",retcode);
}

}
// ---------------------------------------------------------------------------