2025-11-28 00:35:46 +09:00

1429 lines
40 KiB
C++

// --------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// --------------------------------------------------------------------
////////////////////////////////////
// //
// Transaction 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, there 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 the 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
// Define constant 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 the 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]; // Logon user name
char pszPasswd[STR_LEN]; // Logon user password
HDBC hdbc; // Handle to an ODBC database connection
HSTMT hstmt; // ODBC statement handle, for use with SQLExecDirect
} DBCONN;
//------------------------------------------------------------------------------
// Define globals
//------------------------------------------------------------------------------
// Global DB connection structure for the server
static DBCONN gSrv =
{ "MSMQDemo",
"sa",
"",
SQL_NULL_HDBC,
SQL_NULL_HSTMT
};
// Service type 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 computer 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 the "SenderAccount" database.
// 2. Locates an MSMQ queue of type MQTransTest and opens it.
// (NOTE: For simplicity, this sample assumes that there is 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 the "SenderAccount" database (subtracts TransferSum).
// Sends a message to the receiver side.
// Commits the transaction.
//
// 4. Cleans up.
//
//
//
// The transaction in Sender mode includes two operations:
// (1) Updating the "SenderAccount" database (subtracting TransferSum).
// (2) Sending a message to the 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 the 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 the "SenderAccount" database (with the sum of $1000)
//---------------------------------------------------------------------
printf ("Building SenderAccount with the sum of $1000...\n");
// Get the ODBC environment handle.
retcode = SQLAllocEnv(&g_hEnv);
ProcessRetCode("SQLAllocEnv",0, retcode);
// Establish a connection to the database.
LogonToDB(&gSrv);
// Clear the database from the previous run.
ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",FALSE);
// Create a new table in the database.
ExecuteStatement(&gSrv,
"CREATE TABLE SenderAccount (Rate INTEGER CONSTRAINT c1 CHECK (Rate>=0))",TRUE);
// Insert new data into the database.
ExecuteStatement(&gSrv,"INSERT INTO SenderAccount VALUES(1000)",TRUE);
printf ("OK.\n\n");
//-----------------------------------------------------------------------
// Locate the destination queue and open it for sending messages.
//-----------------------------------------------------------------------
printf ("Searching for the receiver queue...\n");
// Locate the destination queue.
LocateTargetQueue (&guidMQTransTestType, wsFormat);
// Open the destination queue.
hr = MQOpenQueue(wsFormat, MQ_SEND_ACCESS, 0, &aqh);
if (FAILED(hr))
{
Error ("MQOpenQueue failed", hr);
}
//--------------------------------------------------------------------------
// Get the transaction dispenser.
//--------------------------------------------------------------------------
// Obtain an interface pointer from the 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 failed", hr);
}
//--------------------------------------------------------------------
// Sender's main loop
//--------------------------------------------------------------------
while (TRUE)
{
// Prompt user to enter a value for TransferSum.
printf ("\n\nPlease enter the sum of dollars to transfer, or '0' to quit ==> ");
// Read the user input.
if (fgets (sUserString, STR_LEN - 1, stdin) == NULL)
{
printf("\nInvalid input was entered. Exiting...\n");
exit(1);
}
sUserString[STR_LEN - 1] = '\0';
//
// Remove the final new-line character introduced by fgets.
//
if(sUserString[strlen(sUserString) - 1] == '\n')
{
sUserString[strlen(sUserString) - 1] = '\0';
}
// Convert the user string to a DWORD
dwTransferSum = atoi(sUserString);
// Prepare message properties to sending a message.
PrepareSendMessageProperties (amPropId,
aPropVar,
msgprops,
dwTransferSum);
//---------------------------------------------------------------------
// Create a transaction (within the sender's main loop).
//---------------------------------------------------------------------
printf ("\nStarting a 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 failed",hr);
}
// The default is to commit the transaction.
fTransactionCommitFlag = TRUE;
//
// SQL is a resource manager in the transaction.
// It must be enlisted.
//
// Enlist the 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 an SQL statement for updating SenderAccount.
sprintf_s (sSQLStatement, sizeof(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 the 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 a message to the receiver side.
hr = MQSendMessage(aqh, // Handle to destination queue
&msgprops, // pointer to MQMSGPROPS structure
pTransaction); // pointer to Transaction Object
if (FAILED(hr))
{
printf("\nMQSendMessage() failed. Error code: %lxh\n", (DWORD) hr);
fTransactionCommitFlag = FALSE;
}
// Commit the transaction.
if (fTransactionCommitFlag)
{
printf ("Committing the transaction...\n");
hr = pTransaction->Commit(0, 0, 0);
if (FAILED(hr))
printf ("The transaction was aborted.\n\n");
else
printf ("The transaction was committed successfully.\n\n");
}
else
{
printf ("Aborting the transaction...\n");
hr = pTransaction->Abort(0, 0, 0);
if (FAILED(hr))
Error("The transaction was aborted", hr);
else
printf ("The transaction was aborted.\n\n");
}
// Release the transaction.
pTransaction->Release();
// End enlistment of the database.
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);
ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);
// Display the sum of dollars in SenderAccount.
DisplayDollars (&gSrv, "SenderAccount");
// Quit the loop when nothing was transferred.
if (dwTransferSum == 0)
break;
}
//--------------------------------------------------------------------------
// Cleanup
//--------------------------------------------------------------------------
// Release the transaction dispenser.
pTransactionDispenser->Release();
// Free the database.
ExecuteStatement(&gSrv,"DROP TABLE SenderAccount",TRUE);
// Free the ODBC handle.
FreeODBCHandles(&gSrv);
// Free the ODBC environment handle.
retcode = SQLFreeEnv(g_hEnv);
if (retcode == SQL_ERROR)
Error ("SQL FreeEnv ",0);
// Free the MSMQ queue handle.
MQCloseQueue(aqh);
printf ("\n\nThe sender side completed.\n\n");
}
//------------------------------------------------------------------------------
// Receiver Mode:
//
// The receiver side does the following:
// 1. Creates the "ReceiverAccount" database.
// 2. Creates an MSMQ transactional public queue of type
// MQTransTest locally and opens it.
// 3. In a loop:
// Creates a transaction using MS DTC.
// Within the transaction:
// Receives a message from the queue (containing the value of TransferSum).
// Updates the "ReceiverAccount" database (adds TransferSum).
// Commits the transaction.
// 4. Cleans up.
//
//
//
// The transaction in the receiver mode includes two operations:
// (1) Receiving a message (sent by the sender side) from the queue.
// (2) Updating the "ReceiverAccount" database (adding 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 whether to commit or abort
RETCODE retcode;
DWORD TransferSum;
DWORD MessageBuffer; // Message body containing TransferSum
char sSQLStatement[STR_LEN*2];
printf ("\nReceiver Side.\n\n");
//-----------------------------------------------------------------------
// Build a "ReceiverAccount" database (with the sum of $500).
//-----------------------------------------------------------------------
printf ("Building a ReceiverAccount with a sum of $500...\n");
// Get ODBC environment handle.
retcode = SQLAllocEnv(&g_hEnv);
ProcessRetCode("SQLAllocEnv",0, retcode);
// Establish a connection to the database.
LogonToDB(&gSrv);
// Clear the table from the previous run.
ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",FALSE);
// Create a new table.
ExecuteStatement(&gSrv,"CREATE TABLE ReceiverAccount (Rate INTEGER CONSTRAINT c2 CHECK (Rate>0))",TRUE);
// Insert new data into the table.
ExecuteStatement(&gSrv,"INSERT INTO ReceiverAccount VALUES(500)",TRUE);
printf ("OK.\n\n");
//-----------------------------------------------------------------------
// Create queue and open it for receiving messages.
//-----------------------------------------------------------------------
printf ("Creating a receiver queue...\n");
// Create the queue.
CreateQueue (&guidMQTransTestType, wsFormat);
// Prepare message properties for reading messages.
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);
//
// Things are a little bit tricky. MQCreateQueue succeeded, but in the
// case of a public queue, this does not mean that MQOpenQueue
// will succeed, because replication delays are possible. The queue is
// registered in the DS, but it might take a replication interval
// until the replica reaches the server that 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);
// Retry.
hr = MQOpenQueue(wsFormat, MQ_RECEIVE_ACCESS, 0, &aqh);
}
}
if (FAILED(hr))
{
Error ("MQOpenQueue failed", hr);
}
printf("OK.");
//--------------------------------------------------------------------------
// Get the transaction dispenser.
//--------------------------------------------------------------------------
// Obtain an interface pointer from the MS DTC proxy.
hr = DtcGetTransactionManager(
NULL, NULL, // pszHost, pszTmName
IID_ITransactionDispenser, // IID of the requested interface
0,0,0, // Reserved -- must be NULL
(void **)&pTransactionDispenser); // Pointer to pointer to the requested interface
if (FAILED(hr))
Error ("DTCGetTransactionManager failed", hr);
//--------------------------------------------------------------------------
// Receiver Main Loop
//--------------------------------------------------------------------------
while (TRUE)
{
printf ("\n\nWaiting for a message to come...\n");
// Peek outside the transaction to avoid database lock
// for a long/infinite period.
//
//dwSize = sizeof(wsResponse);
hr = MQReceiveMessage(
aqh, // Handle to queue
INFINITE, // Time-out
MQ_ACTION_PEEK_CURRENT, // Peek action
&msgprops, // Message properties
NULL, // No OVERLAPPED structure
NULL, // No callback function
NULL, // No cursor
NULL // No transaction yet
);
if (FAILED(hr))
Error("MQReceiveMessage (PEEKING) failed", hr);
//--------------------------------------------------------------------------
// Create a transaction.
//--------------------------------------------------------------------------
printf ("\n\nStarting a 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 failed", hr);
// The default is to commit the transaction.
TransactionCommitFlag = TRUE;
//
// SQL is a resource manager in the transaction.
// It must be enlisted.
//
// Enlist the database in the transaction.
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, (UDWORD)pTransaction);
if (retcode != SQL_SUCCESS)
TransactionCommitFlag = FALSE;
// Receive a message from the queue
//dwSize = sizeof(wsResponse);
hr = MQReceiveMessage(
aqh, // Handle to queue
INFINITE, // Time-out interval
MQ_ACTION_RECEIVE, // Receive action
&msgprops, // Message properties
NULL,NULL,NULL, // No OVERLAPPED structure, callback function, or cursor
pTransaction); // Pointer to transaction object
if (FAILED(hr))
TransactionCommitFlag = FALSE;
// The message buffer holds TransferSum.
TransferSum = (DWORD)MessageBuffer;
// Prepare an SQL statement to update ReceiverAccount.
sprintf_s(sSQLStatement, sizeof(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 the 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... The transaction was aborted.\n\n");
else
printf ("The transaction was committed successfully.\n\n");
}
// Abort the transaction.
else
{
printf ("Aborting the transaction...\n");
hr = pTransaction->Abort(0, 0, 0);
if (FAILED(hr))
Error("The transaction was aborted", hr);
else
printf ("The transaction was aborted.\n\n");
}
// Release the transaction.
pTransaction->Release();
// End the enlistment of the database.
retcode = SQLSetConnectOption (gSrv.hdbc, SQL_COPT_SS_ENLIST_IN_DTC, SQL_DTC_DONE);
ProcessRetCode ("SQLSetConnectOption", &gSrv, retcode);
// Display the number of dollars in ReceiverAccount.
DisplayDollars (&gSrv, "ReceiverAccount");
// Decide whether to continue the loop.
if (TransferSum == 0)
break;
}
//--------------------------------------------------------------------------
// Cleanup
//--------------------------------------------------------------------------
// Release the transaction dispenser.
pTransactionDispenser->Release();
// Free the database.
ExecuteStatement(&gSrv,"DROP TABLE ReceiverAccount",TRUE);
// Free the ODBC handle.
FreeODBCHandles(&gSrv);
// Free the ODBC environment handle.
retcode = SQLFreeEnv(g_hEnv);
if (retcode == SQL_ERROR)
Error ("SQL FreeEnv ",0);
// Free the queue handle.
MQCloseQueue(aqh);
// Delete the queue from the directory service.
MQDeleteQueue(wsFormat);
printf ("\n\nReceiver side completed.\n\n");
}
/*
//-----------------------------------------------------
// Check if teh local computer is enabled to access the
// directory service (DS-enabled).
//-----------------------------------------------------
bool DetectDsConnection(void)
{
MQPRIVATEPROPS PrivateProps;
QMPROPID aPropId[MAX_VAR];
MQPROPVARIANT aPropVar[MAX_VAR];
HRESULT aStatus[MAX_VAR];
DWORD cProp;
HRESULT hr;
//
// Specify the PROPID_PC_DS_ENABLED property, which
// indicates whether the local computer can access the DS.
//
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("A DS connection cannot be detected", hr);
return PrivateProps.aPropVar[0].boolVal;
}
*/
BOOL IsDsEnabledLocaly()
/*++
Routine Description:
The routine checks whether the local computer operates in
domain (DS-enabled) mode or in workgroup (DS-disabled) mode.
Arguments:
None
Return Value:
TRUE - domain (DS-enabled) mode.
FALSE - workgroup (DS-disabled) mode.
--*/
{
MQPRIVATEPROPS PrivateProps;
QMPROPID aPropId[MAX_VAR];
MQPROPVARIANT aPropVar[MAX_VAR];
DWORD cProp;
HRESULT hr;
//
// Specify the PROPID_PC_DS_ENABLED property, which
// indicates whether the local computer can access the DS.
//
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 a DS connection.
// This code is designed to allow compilation both on
// Windows NT 4.0 and on Windows 2000 and later.
//
HINSTANCE hMqrtLibrary = GetModuleHandle(TEXT("mqrt.dll"));
if(hMqrtLibrary == NULL)
{
printf("An incomplete installation of MSMQ was detected. Exiting...\n");
exit(1);
}
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 that matches this routine.
// It must be an old version of mqrt.dll (MSMQ 1.0).
// It will be OK to handle this case as the case of DS-enabled computer.
//
return TRUE;
}
hr = pfMQGetPrivateComputerInformation(
NULL,
&PrivateProps);
if(FAILED(hr))
{
//
// We were not able to determine whether the computer is DS-enabled or DS-disabled.
// Notify the user and assume the worst case (i.e., the computer is DS-disasbled).
//
Error("No DS connection could be detected", hr);
return FALSE;
}
if(PrivateProps.aPropVar[0].boolVal == 0)
{
//
// DS-disabled.
//
return FALSE;
}
return TRUE;
}
//------------------------------------------------------------------------------
// MAIN
//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
DWORD dwSize;
if(argc != 2)
Syntax();
// Fail if the local computer is DS-disabled.
if(!IsDsEnabledLocaly())
{
printf("We cannot work on a DS-disabled computer.\nExiting...");
exit(1);
}
// Retrieve computer name.
dwSize = MAX_COMPUTERNAME_LENGTH;
if(!GetComputerName(g_wszMachineName, &dwSize))
{
printf("The computer name cannot be retrieved.\nExiting...");
exit(1);
}
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\n%s (0x%X). Exiting...\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 for locating queues.
//
//
// 1. Restriction: Queues with PROPID_Q_TYPE = MSMQTransTest service type GUIID
//
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 (the queue properties to retrieve) = queue GUID
//
cProps = 0;
aqPropId[cProps] = PROPID_Q_INSTANCE;
cProps++;
Column.cCol = cProps;
Column.aCol = aqPropId;
//
// Issue the query to locate the queues.
//
hr = MQLocateBegin(NULL, &Restriction, &Column, NULL, &hEnum);
if (FAILED(hr))
Error ("MQLocateBegin failed", hr);
//
// Get the results.
//
cQueue = MAX_VAR;
hr = MQLocateNext(hEnum, &cQueue, aPropVar);
if (FAILED(hr))
Error ("MQLocateNext failed", hr);
hr = MQLocateEnd(hEnum);
if(cQueue == 0)
{
// No queue could be found, so exit
printf("No queue was found. Exiting...\n\n");
exit(0);
}
printf("FOUND %d.\n", cQueue);
dwSize = sizeof(WCHAR)*MAX_FORMAT;
// Transform the queue GUID into a format name.
hr = MQInstanceToFormatName(aPropVar[0].puuid, wsFormat, &dwSize);
if (FAILED(hr))
Error ("MQInstancetoFormatName failed", hr);
// Free the memory that was allocated for the queue GUID during the query.
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]; // Long path name
MQPROPVARIANT aPropVar[MAX_VAR];
DWORD cProps;
MQQUEUEPROPS qprops;
DWORD dwSize;
HRESULT hr;
//---------------------------------------------------------------------
// Prepare properties to create a queue on the local computer.
//---------------------------------------------------------------------
cProps = 0;
// Set the path name property.
aqPropId[cProps] = PROPID_Q_PATHNAME;
swprintf_s(wsPathName, sizeof(wsPathName)/sizeof(wsPathName[0]), TEXT("%s\\MSMQDemo"), g_wszMachineName);
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wsPathName;
cProps++;
// Set the transactional level of the queue to transactional.
aqPropId[cProps] = PROPID_Q_TRANSACTION;
aPropVar[cProps].vt = VT_UI1;
aPropVar[cProps].bVal = MQ_TRANSACTIONAL;
cProps++;
// Set the service type GUID of the queue (used to locate queues in a query).
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))
{
// The API failed, but not because the queue exists.
if(hr != MQ_ERROR_QUEUE_EXISTS)
Error("MQCreateQueue failed", hr);
// The queue exists, so get its format name.
// Note: Because the queue already exists, this sample assumes
// that it was created earlier by this program, so we do not
// check whether the 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 ("The format name cannot be retrieved", 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 string passed 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 for issuing a query.
int n = _snprintf_s(sSQLStatement, sizeof(sSQLStatement), STR_LEN*2 -1, "SELECT * FROM %s", psAccount);
if(n < 0)
{
printf("The string is too long for the buffer specified. Exiting...\n");
exit(1);
}
sSQLStatement[n] = L'\0';
// Issue the SQL query.
retcode = SQLExecDirect (ptr->hstmt,(UCHAR *)sSQLStatement,SQL_NTS);
ProcessRetCode ("SQLExecDirect",ptr,retcode);
// Prepare a data structure for retrieving the query results.
retcode = SQLBindCol(ptr->hstmt,1,SQL_C_ULONG,&DollarsSum,0,(SQLLEN *)&cbValue);
ProcessRetCode ("SQLBindCol",ptr,retcode);
// Retrieve the query results.
retcode = SQLFetch (ptr->hstmt);
ProcessRetCode ("SQLFetch",ptr,retcode);
// Display the 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)
{
UCHAR szSqlState[6];
UCHAR szErrorMsg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER fNativeError = 0;
SWORD cbErrorMsg = 0;
RETCODE retcode;
retcode = SQLError(g_hEnv,
ptr ? ptr->hdbc : 0,
ptr ? ptr->hstmt :0,
szSqlState,
&fNativeError,
szErrorMsg,
sizeof(szErrorMsg),
&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);
}
}
// ---------------------------------------------------------------------------