/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright (C) 1996 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 ActiveX MSMQ apps
//
#include "mqoai.h"
//
// Various defines
//
#define MAX_VAR 20
#define MAX_BUFFER 500
//
// GUID created with the tool "GUIDGEN"
//
static WCHAR strGuidMQTestType[] =
L"{c30e0960-a2c0-11cf-9785-00608cb3e80c}";
//
// Prototypes
//
void PrintError(char *s, HRESULT hr);
HRESULT Syntax();
char mbsMachineName[MAX_COMPUTERNAME_LENGTH + 1];
// Some useful macros
#define RELEASE(punk) if (punk) { (punk)->Release(); (punk) = NULL; }
#define ADDREF(punk) ((punk) ? (punk)->AddRef() : 0)
#define PRINTERROR(s, hr) { PrintError(s, hr); goto Cleanup; }
//-----------------------------------------------------
//
// Receiver Mode
// -------------
// The receiver side does the following:
// 1. Creates a public queue on its own machine
// of type "strGuidMQTestType"
// 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
//
//-----------------------------------------------------
HRESULT Receiver()
{
IMSMQMessage *pmessageReceive = NULL;
IMSMQQueue *pqReceive = NULL;
IMSMQQueueInfo *pqinfo = NULL;
BSTR bstrPathName = NULL;
BSTR bstrServiceType = NULL;
BSTR bstrLabel = NULL;
BSTR bstrMsgLabel = NULL;
VARIANT varIsTransactional, varIsWorldReadable, varBody, varBody2, varWantDestQueue, varWantBody, varReceiveTimeout;
WCHAR wcsPathName[1000];
BOOL fQuit = FALSE;
HRESULT hresult = NOERROR;
printf("\nReceiver Mode on Machine: %s\n\n", mbsMachineName);
//
// Create MSMQQueueInfo object
//
hresult = CoCreateInstance(
CLSID_MSMQQueueInfo,
NULL, // punkOuter
CLSCTX_SERVER,
IID_IMSMQQueueInfo,
(LPVOID *)&pqinfo);
if (FAILED(hresult)) {
PRINTERROR("Cannot create queue", hresult);
}
//
// Prepare properties to create a queue on local machine
//
// Set the PathName
swprintf(wcsPathName, L"%S\\MSMQTest", mbsMachineName);
bstrPathName = SysAllocString(wcsPathName);
if (bstrPathName == NULL) {
PRINTERROR("OOM: pathname", E_OUTOFMEMORY);
}
pqinfo->put_PathName(bstrPathName);
//
// Set the type of the queue
// (Will be used to locate all the queues of this type)
//
bstrServiceType = SysAllocString(strGuidMQTestType);
if (bstrServiceType == NULL) {
PRINTERROR("OOM: ServiceType", E_OUTOFMEMORY);
}
pqinfo->put_ServiceTypeGuid(bstrServiceType);
//
// Put a description to the queue
// (Useful for administration through the MSMQ admin tools)
//
bstrLabel =
SysAllocString(L"Sample ActiveX application of MSMQ SDK");
if (bstrLabel == NULL) {
PRINTERROR("OOM: label ", E_OUTOFMEMORY);
}
pqinfo->put_Label(bstrLabel);
//
// specify if transactional
//
VariantInit(&varIsTransactional);
varIsTransactional.vt = VT_BOOL;
varIsTransactional.boolVal = MQ_TRANSACTIONAL_NONE;
VariantInit(&varIsWorldReadable);
varIsWorldReadable.vt = VT_BOOL;
varIsWorldReadable.boolVal = FALSE;
//
// create the queue
//
hresult = pqinfo->Create(&varIsTransactional, &varIsWorldReadable);
if (FAILED(hresult)) {
//
// API Fails, not because the queue exists
//
if (hresult != MQ_ERROR_QUEUE_EXISTS) {
PRINTERROR("Cannot create queue", hresult);
}
}
//
// Open the queue for receive access
//
hresult = pqinfo->Open(MQ_RECEIVE_ACCESS,
MQ_DENY_NONE,
&pqReceive);
//
// 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.
//
// (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 (hresult == MQ_ERROR_QUEUE_NOT_FOUND) {
printf(".");
// Wait a bit
Sleep(500);
// And retry
hresult = pqinfo->Open(MQ_RECEIVE_ACCESS,
MQ_DENY_NONE,
&pqReceive);
}
if (FAILED(hresult)) {
PRINTERROR("Cannot open queue", hresult);
}
//
// Main receiver loop
//
printf("\nWaiting for messages...\n");
while (!fQuit) {
//
// Receive the message
//
VariantInit(&varWantDestQueue);
VariantInit(&varWantBody);
VariantInit(&varReceiveTimeout);
varWantDestQueue.vt = VT_BOOL;
varWantDestQueue.boolVal = TRUE; // yes we want the dest queue
varWantBody.vt = VT_BOOL;
varWantBody.boolVal = TRUE; // yes we want the msg body
varReceiveTimeout.vt = VT_I4;
varReceiveTimeout.lVal = INFINITE; // infinite timeout
hresult = pqReceive->Receive(
NULL,
&varWantDestQueue,
&varWantBody,
&varReceiveTimeout,
&pmessageReceive);
if (FAILED(hresult)) {
PRINTERROR("Receive message", hresult);
}
//
// Display the received message
//
pmessageReceive->get_Label(&bstrMsgLabel);
VariantInit(&varBody);
VariantInit(&varBody2);
hresult = pmessageReceive->get_Body(&varBody);
if (FAILED(hresult)) {
PRINTERROR("can't get body", hresult);
}
hresult = VariantChangeType(&varBody2,
&varBody,
0,
VT_BSTR);
if (FAILED(hresult)) {
PRINTERROR("can't convert message to string.", hresult);
}
printf("%S : %s\n", bstrMsgLabel, V_BSTR(&varBody2));
//
// Check for end of app
//
if (stricmp((char *)V_BSTR(&varBody2), "quit") == 0) {
fQuit = TRUE;
}
VariantClear(&varBody);
VariantClear(&varBody2);
//
// release the current message
//
RELEASE(pmessageReceive);
} /* while (1) */
//
// Cleanup - Close handle to the queue
//
pqReceive->Close();
if (FAILED(hresult)) {
PRINTERROR("Cannot close queue", hresult);
}
//
// Finish - Let's delete the queue from the directory service
// (We don't need to do it. Leaving the queue in the DS, enables
// sender applications to send messages even if the receiver is not
// available.)
//
hresult = pqinfo->Delete();
if (FAILED(hresult)) {
PRINTERROR("Cannot delete queue", hresult);
}
// fall through...
Cleanup:
SysFreeString(bstrPathName);
SysFreeString(bstrMsgLabel);
SysFreeString(bstrServiceType);
SysFreeString(bstrLabel);
RELEASE(pmessageReceive);
RELEASE(pqReceive);
RELEASE(pqinfo);
return hresult;
}
//-----------------------------------------------------
//
// Sender Mode
// -----------
// The sender side does the following:
// 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. Cleanup handles
//
//-----------------------------------------------------
HRESULT Sender()
{
IMSMQQuery *pquery = NULL;
IMSMQQueueInfo *rgpqinfo[MAX_VAR];
IMSMQQueue *rgpqSend[MAX_VAR];
IMSMQQueueInfo *pqinfo = NULL;
IMSMQQueueInfos *pqinfos = NULL;
IMSMQMessage *pmessage = NULL;
char *szBuffer = new char[MAX_BUFFER];
WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
BSTR bstrServiceType = NULL;
BSTR bstrLabel = NULL;
BSTR bstrBody = NULL;
VARIANT varBody;
DWORD i;
DWORD cQueue = 0;
HRESULT hresult = NOERROR;
printf("\nSender Mode on Machine: %s\n\n", mbsMachineName);
//
// create query object for lookup
//
hresult = CoCreateInstance(
CLSID_MSMQQuery,
NULL, // punkOuter
CLSCTX_SERVER,
IID_IMSMQQuery,
(LPVOID *)&pquery);
if (FAILED(hresult)) {
PRINTERROR("Cannot create query", hresult);
}
//
// Prepare parameters to locate a queue: all queues that
// match test guid type
//
VARIANT varGuidQueue;
VARIANT varStrLabel;
VARIANT varGuidServiceType;
VARIANT varRelServiceType;
VARIANT varRelLabel;
VARIANT varCreateTime;
VARIANT varModifyTime;
VARIANT varRelCreateTime;
VARIANT varRelModifyTime;
VariantInit(&varGuidQueue);
VariantInit(&varStrLabel);
VariantInit(&varGuidServiceType);
VariantInit(&varRelServiceType);
VariantInit(&varRelLabel);
VariantInit(&varCreateTime);
VariantInit(&varModifyTime);
VariantInit(&varRelCreateTime);
VariantInit(&varRelModifyTime);
//
// We only want to specify service type so we set
// the other variant params to VT_ERROR to simulate
// "missing", i.e. optional, params.
//
V_VT(&varGuidQueue) = VT_ERROR;
V_VT(&varStrLabel) = VT_ERROR;
V_VT(&varRelServiceType) = VT_ERROR;
V_VT(&varRelLabel) = VT_ERROR;
V_VT(&varCreateTime) = VT_ERROR;
V_VT(&varModifyTime) = VT_ERROR;
V_VT(&varRelCreateTime) = VT_ERROR;
V_VT(&varRelModifyTime) = VT_ERROR;
bstrServiceType = SysAllocString(strGuidMQTestType);
if (bstrServiceType == NULL) {
PRINTERROR("OOM: Service Type", E_OUTOFMEMORY);
}
V_VT(&varGuidServiceType) = VT_BSTR;
V_BSTR(&varGuidServiceType) = bstrServiceType;
hresult = pquery->LookupQueue(&varGuidQueue,
&varGuidServiceType,
&varStrLabel,
&varCreateTime,
&varModifyTime,
&varRelServiceType,
&varRelLabel,
&varRelCreateTime,
&varRelModifyTime,
&pqinfos);
if (FAILED(hresult)) {
PRINTERROR("LookupQueue failed", hresult);
}
//
// reset the queue collection
//
hresult = pqinfos->Reset();
if (FAILED(hresult)) {
PRINTERROR("Reset failed", hresult);
}
//
// Open each of the queues found
//
i = 0;
hresult = pqinfos->Next(&rgpqinfo[i]);
if (FAILED(hresult)) {
PRINTERROR("Next failed", hresult);
}
pqinfo = rgpqinfo[i];
while (pqinfo) {
//
// Open the queue for send access
//
hresult = pqinfo->Open(
MQ_SEND_ACCESS,
MQ_DENY_NONE,
&rgpqSend[i]);
if (FAILED(hresult)) {
PRINTERROR("Open failed", hresult);
}
i++;
hresult = pqinfos->Next(&rgpqinfo[i]);
if (FAILED(hresult)) {
PRINTERROR("Next failed", hresult);
}
pqinfo = rgpqinfo[i];
}
cQueue = i;
if (cQueue == 0) {
//
// Could Not find any queue, so exit
//
PRINTERROR("No queue registered", hresult = E_INVALIDARG);
}
printf("\t%d queue(s) found.\n", cQueue);
printf("\nEnter \"quit\" to exit\n");
//
// Build the message label property
//
swprintf(wcsMsgLabel, L"Message from %S", mbsMachineName);
//
// Main sender loop
//
while (1) {
//
// Get a string from the console
//
printf("Enter a string: ");
if (gets(szBuffer) == NULL)
break;
//
// create a message object
//
hresult = CoCreateInstance(
CLSID_MSMQMessage,
NULL, // punkOuter
CLSCTX_SERVER,
IID_IMSMQMessage,
(LPVOID *)&pmessage);
//
// Send the message to all the queues
//
for (i = 0; i < cQueue; i++) {
bstrLabel = SysAllocString(wcsMsgLabel);
if (bstrLabel == NULL) {
PRINTERROR("OOM: label", E_OUTOFMEMORY);
}
hresult = pmessage->put_Label(bstrLabel);
//
// This isn't a "true" unicode string of course...
//
bstrBody = SysAllocStringByteLen(szBuffer, strlen(szBuffer)+1);
if (bstrBody == NULL) {
PRINTERROR("OOM: body", E_OUTOFMEMORY);
}
VariantInit(&varBody);
V_VT(&varBody) = VT_BSTR;
V_BSTR(&varBody) = bstrBody;
hresult = pmessage->put_Body(varBody);
if (FAILED(hresult)) {
PRINTERROR("put_body failed", hresult);
}
hresult = pmessage->Send(rgpqSend[i], NULL);
if (FAILED(hresult)) {
PRINTERROR("Send failed", hresult);
}
VariantClear(&varBody);
bstrBody = NULL;
}
RELEASE(pmessage);
//
// Check for end of app
//
if (stricmp(szBuffer, "quit") == 0)
break;
} /* while (1) */
Cleanup:
//
// Close and release all the queues
//
for (i = 0; i < cQueue; i++) {
rgpqSend[i]->Close();
rgpqSend[i]->Release();
rgpqinfo[i]->Release();
}
RELEASE(pqinfos);
RELEASE(pquery);
RELEASE(pmessage);
SysFreeString(bstrLabel);
SysFreeString(bstrBody);
SysFreeString(bstrServiceType);
delete [] szBuffer;
return hresult;
}
//-----------------------------------------------------
//
// MAIN
//
//-----------------------------------------------------
int main(int argc, char * * argv)
{
DWORD dwNumChars;
HRESULT hresult = NOERROR;
if (argc != 2)
return Syntax();
hresult = OleInitialize(NULL);
if (FAILED(hresult)) {
PRINTERROR("Cannot init OLE", hresult);
}
//
// Retrieve machine name
//
dwNumChars = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerNameA(mbsMachineName, &dwNumChars);
if (strcmp(argv[1], "-s") == 0)
hresult = Sender();
else if (strcmp(argv[1], "-r") == 0)
hresult = Receiver();
else
hresult = Syntax();
printf("\nOK\n");
// fall through...
Cleanup:
return (int)hresult;
}
void PrintError(char *s, HRESULT hr)
{
printf("Cleanup: %s (0x%X)\n", s, hr);
}
HRESULT Syntax()
{
printf("\n");
printf("Syntax: mqtestoa -s | -r\n");
printf("\t-s: Sender\n");
printf("\t-r: Receiver\n");
return E_INVALIDARG;
}