windows-nt/Source/XPSP1/NT/inetsrv/msmq/sdk/samples/msmqtest/msmqtest.c
2020-09-26 16:20:57 +08:00

854 lines
20 KiB
C

/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright (C) 1999 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/
//
// Includes
//
#include <stdio.h>
#include <windows.h>
//
// Unique include file for MSMQ apps
//
#include <mq.h>
//
// Various defines
//
#define MAX_VAR 20
#define MAX_FORMAT 100
#define MAX_BUFFER 500
#define DIRECT 1
#define STANDARD 0
#define DS_ENABLED 1
#define DS_DISABLED 0
//
// GUID created with the tool "GUIDGEN"
//
static CLSID guidMQTestType =
{ 0xc30e0960, 0xa2c0, 0x11cf, { 0x97, 0x85, 0x0, 0x60, 0x8c, 0xb3, 0xe8, 0xc } };
//
// Prototypes
//
void Error(char *s, HRESULT hr);
void Syntax();
char mbsMachineName[MAX_COMPUTERNAME_LENGTH + 1];
//-----------------------------------------------------
//
// Check if local computer is DS enabled or DS disabled
//
//-----------------------------------------------------
int 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);
}
if(PrivateProps.aPropVar[0].boolVal == 0)
return DS_DISABLED;
return DS_ENABLED;
}
//-----------------------------------------------------
//
// Allow a DS enabled client connect with a
// DS disabled one.
//
//-----------------------------------------------------
int SetConnectionMode(void)
{
char cDirectMode;
//
// In case the local computer is in a domain and not in workgroup mode,
// we have two cases:
// 1. Other side is a computer in a domain.
// 2. Other side is a computer working in workgroup mode.
//
if(DetectDsConnection() == DS_ENABLED)
{
printf("Do you wish to connect with a DS disabled client (y or n) ? ");
scanf("%c", &cDirectMode);
switch(tolower(cDirectMode))
{
case 'y':
return DIRECT;
case 'n':
return STANDARD;
default:
printf("Bye.\n");
exit(1);
}
}
return DIRECT; // Local computer is DS disabled
}
//--------------------------------------------------------
//
// Receiver Mode
// -------------
// The receiver side does the following:
// 1. Creates a queue on its own computer'
// of type "guidMQTestType".
// The queue is either public or private, depending
// on the connection we wish to establish
// 2. Opens the queue
// 3. In a Loop
// Receives messages
// Prints message body and message label
// 4. Cleanup handles
// 5. Deletes the queue from the directory service
//
//--------------------------------------------------------
void Receiver(int dDirectMode)
{
MQQUEUEPROPS qprops;
MQMSGPROPS msgprops;
MQPROPVARIANT aPropVar[MAX_VAR];
QUEUEPROPID aqPropId[MAX_VAR];
MSGPROPID amPropId[MAX_VAR];
DWORD cProps;
WCHAR wcsFormat[MAX_FORMAT];
UCHAR Buffer[MAX_BUFFER];
WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
WCHAR wcsPathName[1000];
DWORD dwNumChars;
QUEUEHANDLE qh;
HRESULT hr;
printf("\nReceiver Mode on Machine: %s\n\n", mbsMachineName);
//
// Prepare properties to create a queue on local machine
//
cProps = 0;
// Set the PathName
if(dDirectMode == DIRECT) // Private queue pathname
{
swprintf(wcsPathName, L"%S\\private$\\MSMQTest", mbsMachineName);
}
else // Public queue pathname
{
swprintf(wcsPathName, L"%S\\MSMQTest", mbsMachineName);
}
aqPropId[cProps] = PROPID_Q_PATHNAME;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsPathName;
cProps++;
// Set the type of the queue
// (Will be used to locate all the queues of this type)
aqPropId[cProps] = PROPID_Q_TYPE;
aPropVar[cProps].vt = VT_CLSID;
aPropVar[cProps].puuid = &guidMQTestType;
cProps++;
// Put a description to the queue
// (Useful for administration through the MSMQ admin tools)
aqPropId[cProps] = PROPID_Q_LABEL;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = L"Sample application of MSMQ SDK";
cProps++;
// Create a QUEUEPROPS structure
qprops.cProp = cProps;
qprops.aPropID = aqPropId;
qprops.aPropVar = aPropVar;
qprops.aStatus = 0;
//
// Create the queue
//
dwNumChars = MAX_FORMAT;
hr = MQCreateQueue(
NULL, // IN: Default security
&qprops, // IN/OUT: Queue properties
wcsFormat, // OUT: Format name (OUT)
&dwNumChars); // IN/OUT: Size of format name
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
//
printf("Queue already exists. Open it anyway.\n");
if(dDirectMode == DIRECT)
// It's a private queue, so we know its format name
{
swprintf(wcsFormat, L"DIRECT=OS:%s", wcsPathName);
}
else
// It's a public queue, so we must get it from the DS
{
dwNumChars = MAX_FORMAT;
hr = MQPathNameToFormatName(
wcsPathName, // IN: Queue pathname
wcsFormat, // OUT: Format name
&dwNumChars); // IN/OUT: Size of format name
if (FAILED(hr))
Error("Cannot retrieve format name", hr);
}
}
//
// Open the queue for receive access
//
hr = MQOpenQueue(
wcsFormat, // IN: Queue format name
MQ_RECEIVE_ACCESS, // IN: Want to receive from queue
0, // IN: Allow sharing
&qh); // OUT: Handle of open queue
//
// Little bit tricky. MQCreateQueue succeeded but, in case
// it's a public queue, 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 reaches the server I am connected to.
// To overcome this, open the queue in a loop.
//
// (in this specific case, this can happen only if this
// program is run on a Backup Server Controller - BSC, or on
// a client connected to a BSC)
// To be totally on the safe side, we should have put some code
// to exit the loop after a few retries, but hey, this is just a sample.
//
while (hr == MQ_ERROR_QUEUE_NOT_FOUND)
{
printf(".");
// Wait a bit
Sleep(500);
// And retry
hr = MQOpenQueue(wcsFormat, MQ_RECEIVE_ACCESS, 0, &qh);
}
if (FAILED(hr))
Error("Cannot open queue", hr);
//
// Main receiver loop
//
if(dDirectMode == DIRECT)
{
printf("\n* Working in direct mode.");
}
printf("\n* Waiting for messages...\n");
for(;;)
{
//
// Prepare message properties to read
//
cProps = 0;
// Ask for the body of the message
amPropId[cProps] = PROPID_M_BODY;
aPropVar[cProps].vt = VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(Buffer);
aPropVar[cProps].caub.pElems = Buffer;
cProps++;
// Ask for the label of the message
amPropId[cProps] = PROPID_M_LABEL;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsMsgLabel;
cProps++;
// Ask for the length of the label of the message
amPropId[cProps] = PROPID_M_LABEL_LEN;
aPropVar[cProps].vt = VT_UI4;
aPropVar[cProps].ulVal = MQ_MAX_MSG_LABEL_LEN;
cProps++;
// Create a MSGPROPS structure
msgprops.cProp = cProps;
msgprops.aPropID = amPropId;
msgprops.aPropVar = aPropVar;
msgprops.aStatus = 0;
//
// Receive the message
//
hr = MQReceiveMessage(
qh, // IN: Queue handle
INFINITE, // IN: Timeout
MQ_ACTION_RECEIVE, // IN: Read operation
&msgprops, // IN/OUT: Message properties to receive
NULL, // IN/OUT: No overlap
NULL, // IN: No callback
NULL, // IN: No cursor
NULL); // IN: Not part of a transaction
if (FAILED(hr))
Error("Receive message", hr);
//
// Display the received message
//
printf("%S : %s\n", wcsMsgLabel, Buffer);
//
// Check for end of app
//
if (stricmp(Buffer, "quit") == 0)
break;
} /* while (1) */
//
// Cleanup - Close handle to the queue
//
MQCloseQueue(qh);
//
// Finish - Let's delete the queue from the directory service
// (We don't need to do it. In case of a public queue, leaving
// it in the DS enables sender applications to send messages
// even if the receiver is not available.)
//
hr = MQDeleteQueue(wcsFormat);
if (FAILED(hr))
Error("Cannot delete queue", hr);
}
//-----------------------------------------------------
//
// Sender Mode
// -----------
// The sender side does the following:
//
// If we work in STANDARD mode:
// 1. Locates all queues of type "guidMQTestType"
// 2. Opens handles to all the queues
// 3. In a loop
// Sends messages to all those queues
// 4. Cleans up handles
//
// If we work in DIRECT mode:
// 1. Opens a handle to a private queue labeled
// "MSMQTest" on the computer specified
// 2. Sends messages to that queue
// 3. Cleans up handles
//-----------------------------------------------------
//-----------------------------------------------------
//
// Sender in STANDARD mode
//
//-----------------------------------------------------
void StandardSender(void)
{
DWORD cProps;
MQMSGPROPS msgprops;
MQPROPVARIANT aPropVar[MAX_VAR];
QUEUEPROPID aqPropId[MAX_VAR];
MSGPROPID amPropId[MAX_VAR];
MQPROPERTYRESTRICTION aPropRestriction[MAX_VAR];
MQRESTRICTION Restriction;
MQCOLUMNSET Column;
HANDLE hEnum;
WCHAR wcsFormat[MAX_FORMAT];
UCHAR Buffer[MAX_BUFFER];
WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
DWORD i;
DWORD cQueue;
DWORD dwNumChars;
QUEUEHANDLE aqh[MAX_VAR];
HRESULT hr;
printf("\nSender Mode on Machine: %s\n\n", mbsMachineName);
//
// Prepare parameters to locate a queue
//
//
// 1. Restriction = Queues with PROPID_TYPE = MSMQTest queue type
//
cProps = 0;
aPropRestriction[cProps].rel = PREQ;
aPropRestriction[cProps].prop = PROPID_Q_TYPE;
aPropRestriction[cProps].prval.vt = VT_CLSID;
aPropRestriction[cProps].prval.puuid = &guidMQTestType;
cProps++;
Restriction.cRes = cProps;
Restriction.paPropRes = aPropRestriction;
//
// 2. Columnset (i.e. queue properties to retrieve) = queue instance
//
cProps = 0;
aqPropId[cProps] = PROPID_Q_INSTANCE;
cProps++;
Column.cCol = cProps;
Column.aCol = aqPropId;
//
// Locate the queues. Issue the query
//
hr = MQLocateBegin(
NULL, // IN: Context must be NULL
&Restriction, // IN: Restriction
&Column, // IN: Columns (properties) to return
NULL, // IN: No need to sort
&hEnum); // OUT: Enumeration handle
if (FAILED(hr))
Error("LocateBegin", hr);
//
// Get the results (up to MAX_VAR)
// (For more results, call MQLocateNext in a loop)
//
cQueue = MAX_VAR;
hr = MQLocateNext(
hEnum, // IN: Enumeration handle
&cQueue, // IN/OUT: Count of properties
aPropVar); // OUT: Properties of located queues
if (FAILED(hr))
Error("LocateNext", hr);
//
// And that's it for locate
//
hr = MQLocateEnd(hEnum);
if (cQueue == 0)
{
//
// Could Not find any queue, so exit
//
printf("No queue registered");
exit(0);
}
printf("\t%d queue(s) found.\n", cQueue);
//
// Open a handle for each of the queues found
//
for (i = 0; i < cQueue; i++)
{
// Convert the queue instance to a format name
dwNumChars = MAX_FORMAT;
hr = MQInstanceToFormatName(
aPropVar[i].puuid, // IN: Queue instance
wcsFormat, // OUT: Format name
&dwNumChars); // IN/OUT: Size of format name
if (FAILED(hr))
Error("GuidToFormatName", hr);
//
// Open the queue for send access
//
hr = MQOpenQueue(
wcsFormat, // IN: Queue format name
MQ_SEND_ACCESS, // IN: Want to send to queue
0, // IN: Must be 0 for send access
&aqh[i]); // OUT: Handle of open queue
if (FAILED(hr))
Error("OpenQueue", hr);
//
// Free the GUID memory that was allocated during the locate.
//
MQFreeMemory(aPropVar[i].puuid);
}
printf("\nEnter \"quit\" to exit\n");
//
// Build the message label property
//
swprintf(wcsMsgLabel, L"Message from %S", mbsMachineName);
//
// Main sender loop
//
for(;;)
{
//
// Get a string from the console
//
printf("Enter a string: ");
if (gets(Buffer) == NULL)
break;
//
// Prepare properties of message to send
//
cProps = 0;
// Set the body of the message
amPropId[cProps] = PROPID_M_BODY;
aPropVar[cProps].vt = VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(Buffer);
aPropVar[cProps].caub.pElems = Buffer;
cProps++;
// Set the label of the message
amPropId[cProps] = PROPID_M_LABEL;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsMsgLabel;
cProps++;
// Create a MSGPROPS structure
msgprops.cProp = cProps;
msgprops.aPropID = amPropId;
msgprops.aPropVar = aPropVar;
msgprops.aStatus = 0;
//
// Send the message to all the queue
//
for (i = 0; i < cQueue; i++)
{
hr = MQSendMessage(
aqh[i], // IN: Queue handle
&msgprops, // IN: Message properties to send
NULL); // IN: Not part of a transaction
if (FAILED(hr))
Error("Send message", hr);
}
//
// Check for end of app
//
if (stricmp(Buffer, "quit") == 0)
break;
} /* for */
//
// Close all the queue handles
//
for (i = 0; i < cQueue; i++)
MQCloseQueue(aqh[i]);
}
//-----------------------------------------------------
//
// Sender in DIRECT mode
//
//-----------------------------------------------------
void DirectSender(void)
{
MQMSGPROPS msgprops;
MQPROPVARIANT aPropVar[MAX_VAR];
MSGPROPID amPropId[MAX_VAR];
DWORD cProps;
WCHAR wcsFormat[MAX_FORMAT];
WCHAR wcsReceiverComputer[MAX_BUFFER];
UCHAR Buffer[MAX_BUFFER];
WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
QUEUEHANDLE qhSend;
HRESULT hr;
//
// Get the receiver computer name
//
printf("Enter receiver computer name: ");
wscanf(L"%s", wcsReceiverComputer);
if(wcsReceiverComputer[0] == 0)
{
printf("You have entered an incorrect parameter. Exiting...\n");
return;
}
//
// Open the queue for send access
//
swprintf(wcsFormat, L"DIRECT=OS:%s\\private$\\MSMQTest", wcsReceiverComputer);
hr = MQOpenQueue(
wcsFormat, // IN: Queue format name
MQ_SEND_ACCESS, // IN: Want to send to queue
0, // IN: Must be 0 for send access
&qhSend); // OUT: Handle of open queue
if (FAILED(hr))
Error("OpenQueue", hr);
printf("\nEnter \"quit\" to exit\n");
//
// Build the message label property
//
swprintf(wcsMsgLabel, L"Message from %S", mbsMachineName);
fflush(stdin);
//
// Main sender loop
//
for(;;)
{
//
// Get a string from the console
//
printf("Enter a string: ");
if (gets(Buffer) == NULL)
break;
//
// Prepare properties of message to send
//
cProps = 0;
// Set the body of the message
amPropId[cProps] = PROPID_M_BODY;
aPropVar[cProps].vt = VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(Buffer);
aPropVar[cProps].caub.pElems = Buffer;
cProps++;
// Set the label of the message
amPropId[cProps] = PROPID_M_LABEL;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsMsgLabel;
cProps++;
// Create a MSGPROPS structure
msgprops.cProp = cProps;
msgprops.aPropID = amPropId;
msgprops.aPropVar = aPropVar;
msgprops.aStatus = 0;
//
// Send the message
//
hr = MQSendMessage(
qhSend, // IN: Queue handle
&msgprops, // IN: Message properties to send
NULL); // IN: Not part of a transaction
if (FAILED(hr))
Error("Send message", hr);
//
// Check for end of app
//
if (stricmp(Buffer, "quit") == 0)
break;
} /* for */
//
// Close queue handle
//
MQCloseQueue(qhSend);
}
//------------------------------------------------------
//
// Sender function
//
//------------------------------------------------------
void Sender(int dDirectMode)
{
if(dDirectMode == DIRECT)
DirectSender();
else
StandardSender();
}
//-----------------------------------------------------
//
// MAIN
//
//-----------------------------------------------------
main(int argc, char * * argv)
{
DWORD dwNumChars;
int dDirectMode;
if (argc != 2)
{
Syntax();
}
//
// Retrieve machine name
//
dwNumChars = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerName(mbsMachineName, &dwNumChars);
//
// Detect DS connection and working mode
//
dDirectMode = SetConnectionMode();
if(strcmp(argv[1], "-s") == 0)
Sender(dDirectMode);
else if (strcmp(argv[1], "-r") == 0)
Receiver(dDirectMode);
else
Syntax();
printf("\nOK\n");
return(1);
}
void Error(char *s, HRESULT hr)
{
printf("Error: %s (0x%X)\n", s, hr);
exit(1);
}
void Syntax()
{
printf("\n");
printf("Syntax: msmqtest -s | -r\n");
printf("\t-s: Sender\n");
printf("\t-r: Receiver\n");
exit(1);
}