// -------------------------------------------------------------------- // // 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. // // -------------------------------------------------------------------- // // Includes // #include #include // // Unique include file for ActiveX MSMQ applications // #include // // 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; } //----------------------------------------------------- // // Check whether the local computer is enabled to // access the directory service (DS-enabled). // //----------------------------------------------------- short DetectDsConnection(void) { IMSMQApplication2 *pqapp = NULL; short fDsConnection; HRESULT hresult; hresult = CoCreateInstance( CLSID_MSMQApplication, NULL, // punkOuter CLSCTX_SERVER, IID_IMSMQApplication2, (LPVOID *)&pqapp); if (FAILED(hresult)) { printf("The attempt to create an MSMQApplication object failed (0x%X). Exiting...\n", hresult); RELEASE(pqapp); exit(1); } pqapp->get_IsDsEnabled(&fDsConnection); RELEASE(pqapp); return fDsConnection; } //----------------------------------------------------- // // Allow a DS-enabled client to connect to a // DS-disabled one. // //----------------------------------------------------- bool SetConnectionMode(void) { char cDirectMode; // // If 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() != 0) { printf("Do you want to connect to a DS-disabled computer (Y or N) ? "); int iRes = scanf_s("%c", &cDirectMode); if (iRes == 0 || iRes == EOF) { printf("\nInvalid input was entered.\n"); exit(1); } switch(tolower(cDirectMode)) { case 'y': return true; case 'n': return false; default: printf("Bye.\n"); exit(1); } } return true; // Local computer is DS-disabled. } //-------------------------------------------------------- // // Receiver Mode // ------------- // The receiver side does the following: // 1. Creates a local queue of type "guidMQTestType". // The queue is either public or private, depending // on the connection that we want to establish. // 2. Opens the queue. // 3. In a loop, // receives messages and // prints the message body and message label. // 4. Closes the handle to the queue. // 5. Deletes the queue from the directory service. // //-------------------------------------------------------- HRESULT Receiver(bool fDirectMode) { 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[MAX_COMPUTERNAME_LENGTH +100]; BOOL fQuit = FALSE; HRESULT hresult = NOERROR; printf("\nReceiver Mode on Machine: %s\n\n", mbsMachineName); // // Create an MSMQQueueInfo object. // hresult = CoCreateInstance( CLSID_MSMQQueueInfo, NULL, // punkOuter CLSCTX_SERVER, IID_IMSMQQueueInfo, (LPVOID *)&pqinfo); if (FAILED(hresult)) { PRINTERROR("The queue cannot be created", hresult); } // // Prepare properties for creating a queue on the local computer. // // Set the path name. if(fDirectMode) // Private queue path name { if(_snwprintf_s(wcsPathName, sizeof(wcsPathName)/sizeof(wcsPathName[0]), (sizeof(wcsPathName)/sizeof(wcsPathName[0]))-1, L"%S\\private$\\MSMQTest", mbsMachineName) < 0) { // overflow PRINTERROR("Path name creation failed", hresult); } else { wcsPathName[(sizeof(wcsPathName)/sizeof(wcsPathName[0]))-1] = L'\0'; } } else // Public queue path name { if(_snwprintf_s(wcsPathName, sizeof(wcsPathName)/sizeof(wcsPathName[0]), (sizeof(wcsPathName)/sizeof(wcsPathName[0]))-1, L"%S\\MSMQTest", mbsMachineName) < 0) { // Overflow occurred. PRINTERROR("Path name creation failed", hresult); } else { wcsPathName[(sizeof(wcsPathName)/sizeof(wcsPathName[0]))-1] = L'\0'; } } bstrPathName = SysAllocString(wcsPathName); if (bstrPathName == NULL) { PRINTERROR("OOM: pathname", E_OUTOFMEMORY); } pqinfo->put_PathName(bstrPathName); // // Set the type of the queue. // (This property 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); // // Add a descriptive label to the queue. // (This property is 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 whether the queue is 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)) { // // The API failed, but not because the queue exists. // if (hresult != MQ_ERROR_QUEUE_EXISTS) PRINTERROR("The queue cannot be created", hresult); } // // Open the queue with receive access. // hresult = pqinfo->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE, &pqReceive); // // 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. // // In this specific case, this can happen only if this program // is run on an MSMQ 1.0 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 this is just a sample. // while (hresult == MQ_ERROR_QUEUE_NOT_FOUND) { printf("."); // Wait a bit. Sleep(500); // Retry. hresult = pqinfo->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE, &pqReceive); } if (FAILED(hresult)) { PRINTERROR("The queue cannot be opened", 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 destination queue. varWantBody.vt = VT_BOOL; varWantBody.boolVal = TRUE; // Yes, we want the message body. varReceiveTimeout.vt = VT_I4; varReceiveTimeout.lVal = INFINITE; // Infinite time-out hresult = pqReceive->Receive( NULL, &varWantDestQueue, &varWantBody, &varReceiveTimeout, &pmessageReceive); if (FAILED(hresult)) { PRINTERROR("Receive failed", hresult); } // // Display the received message. // pmessageReceive->get_Label(&bstrMsgLabel); VariantInit(&varBody); VariantInit(&varBody2); hresult = pmessageReceive->get_Body(&varBody); if (FAILED(hresult)) { PRINTERROR("The message body cannot be retrieved", hresult); } hresult = VariantChangeType(&varBody2, &varBody, 0, VT_BSTR); if (FAILED(hresult)) { PRINTERROR("The message body cannot be converted to a string", hresult); } printf("%S : %s\n", bstrMsgLabel, (char *)V_BSTR(&varBody2)); // // Check for a request to end the application. // if (_stricmp((char *)V_BSTR(&varBody2), "quit") == 0) { fQuit = TRUE; } VariantClear(&varBody); VariantClear(&varBody2); // // Release the current message. // RELEASE(pmessageReceive); } /* while (!fQuit) */ // // Cleanup: Close the handle to the queue. // pqReceive->Close(); if (FAILED(hresult)) { PRINTERROR("The queue cannot be closed", hresult); } // // In the concluding stage, we delete the queue from the directory // service. (We don't need to do this. In case of a public queue, // leaving it in the DS enables sender applications to send messages // even if the receiver is not available.) // hresult = pqinfo->Delete(); if (FAILED(hresult)) { PRINTERROR("The queue cannot be deleted", 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: // // In domain (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 workgroup (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 domain (standard) mode // //----------------------------------------------------- HRESULT StandardSender() { IMSMQQuery *pquery = NULL; IMSMQQueueInfo *rgpqinfo[MAX_VAR]; IMSMQQueue *rgpqSend[MAX_VAR]; IMSMQQueueInfo *pqinfo = NULL; IMSMQQueueInfos *pqinfos = NULL; IMSMQMessage *pmessage = NULL; char szBuffer[MAX_BUFFER] = {0}; WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN] = {0}; BSTR bstrServiceType = NULL; BSTR bstrLabel = NULL; BSTR bstrBody = NULL; VARIANT varBody; DWORD i; DWORD cQueue = 0; HRESULT hresult = NOERROR; printf("\nSender mode on the computer %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("A query object cannot be created", hresult); } // // Prepare parameters to locate all queues that // have the test service type GUID. // 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 the service type GUID, so we // set the other variant parameters to VT_ERROR to simulate // "missing," i.e. optional, parameters. // 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 GUID", 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 object. // hresult = pqinfos->Reset(); if (FAILED(hresult)) { PRINTERROR("Reset failed", hresult); } // // Open each of the queues found. // cQueue = 0; hresult = pqinfos->Next(&rgpqinfo[cQueue]); if (FAILED(hresult)) { PRINTERROR("Next failed", hresult); } pqinfo = rgpqinfo[cQueue]; while (pqinfo) { // // Open the queue with send access. // hresult = pqinfo->Open( MQ_SEND_ACCESS, MQ_DENY_NONE, &rgpqSend[cQueue]); if (FAILED(hresult)) { PRINTERROR("Open failed", hresult); } cQueue++; hresult = pqinfos->Next(&rgpqinfo[cQueue]); if (FAILED(hresult)) { PRINTERROR("Next failed", hresult); } pqinfo = rgpqinfo[cQueue]; } if (cQueue == 0) { // // No queue could be found, so exit. // PRINTERROR("No queue is registered", hresult = E_INVALIDARG); } printf("\tQueue(s) found: %d\n", cQueue); printf("\nEnter \"quit\" to exit.\n"); // // Build the message label property. // if(_snwprintf_s(wcsMsgLabel, sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0]), (sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0]))-1, L"Message from %S", mbsMachineName) < 0) { // overflow PRINTERROR("Label creation failed", hresult); } else { wcsMsgLabel[(sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0]))-1] = L'\0'; } bstrLabel = SysAllocString(wcsMsgLabel); if (bstrLabel == NULL) PRINTERROR("OOM: label", E_OUTOFMEMORY); fflush(stdin); // // Main sender loop // while (1) { // // Get a string from the console. // printf("Enter a string: "); if (fgets(szBuffer, MAX_BUFFER - 1, stdin) == NULL) break; szBuffer[MAX_BUFFER - 1] = '\0'; // // Delete the new-line character from the string. // if(szBuffer[strlen(szBuffer) - 1] == '\n') { szBuffer[strlen(szBuffer) - 1] = '\0'; } // // 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++) { 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: message 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 a request to end the application. // 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); return hresult; } //----------------------------------------------------- // // Sender in direct (workgroup) mode // //----------------------------------------------------- HRESULT DirectSender(void) { IMSMQQueue *rgpqSend = NULL; IMSMQQueueInfo *pqinfo = NULL; IMSMQMessage *pmessage = NULL; char szBuffer[MAX_BUFFER]; WCHAR wcsMsgLabel[MQ_MAX_MSG_LABEL_LEN] = {0}; WCHAR wcsFormat[MAX_BUFFER + 100] = {0}; WCHAR wcsReceiverComputer[MAX_BUFFER] = {0}; BSTR bstrFormat = NULL; BSTR bstrServiceType = NULL; BSTR bstrLabel = NULL; BSTR bstrBody = NULL; VARIANT varBody; DWORD cQueue = 0; HRESULT hresult = NOERROR; // // Get the receiver computer name. // // // Flushing stdin to get rid of the new-line character entered earlier. // fflush(stdin); printf("\nEnter receiver computer name: "); if (fgetws(wcsReceiverComputer, MAX_BUFFER - 1, stdin) == NULL) return E_INVALIDARG; if(wcsReceiverComputer[wcslen(wcsReceiverComputer) - 1] == L'\n') wcsReceiverComputer[wcslen(wcsReceiverComputer) - 1] = L'\0'; if(wcsReceiverComputer[0] == 0) { printf("You have entered an incorrect parameter. Exiting...\n"); return E_INVALIDARG; } hresult = CoCreateInstance( CLSID_MSMQQueueInfo, NULL, // punkOuter CLSCTX_SERVER, IID_IMSMQQueueInfo, (LPVOID *)&pqinfo); if (FAILED(hresult)) PRINTERROR("The queue cannot be opened", hresult); if(_snwprintf_s(wcsFormat, sizeof(wcsFormat)/sizeof(wcsFormat[0]), (sizeof(wcsFormat)/sizeof(wcsFormat[0])) - 1, L"DIRECT=OS:%s\\private$\\MSMQTest", wcsReceiverComputer) < 0) { // Overflow occurred. PRINTERROR("The receiver format name is too long for the buffer specified", hresult); } else { wcsFormat[(sizeof(wcsFormat)/sizeof(wcsFormat[0])) - 1] = L'\0'; } bstrFormat = SysAllocString(wcsFormat); hresult = pqinfo->put_FormatName(bstrFormat); if (FAILED(hresult)) PRINTERROR("Open failed", hresult); // // Open the queue with send access. // hresult = pqinfo->Open( MQ_SEND_ACCESS, MQ_DENY_NONE, &rgpqSend); if (FAILED(hresult)) PRINTERROR("Open failed", hresult); printf("\nSender mode on the computer %s.\n\n", mbsMachineName); printf("\nEnter \"quit\" to exit.\n"); // // Build the message label property. // if(_snwprintf_s(wcsMsgLabel, sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0]), (sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0])) - 1, L"Message from %S", mbsMachineName) < 0) { // Overflow occurred. PRINTERROR("Label creation failed", hresult); } else { wcsMsgLabel[(sizeof(wcsMsgLabel)/sizeof(wcsMsgLabel[0])) - 1] = L'\0'; } bstrLabel = SysAllocString(wcsMsgLabel); if (bstrLabel == NULL) PRINTERROR("OOM: label", E_OUTOFMEMORY); fflush(stdin); // // Main sender loop. // while (1) { // // Get a string from the console. // printf("Enter a string: "); if (fgets(szBuffer,sizeof(szBuffer) - 1, stdin) == NULL) break; if(szBuffer[strlen(szBuffer) - 1] == '\n') { szBuffer[strlen(szBuffer) - 1] = '\0'; } // // Create a message object. // hresult = CoCreateInstance( CLSID_MSMQMessage, NULL, // punkOuter CLSCTX_SERVER, IID_IMSMQMessage, (LPVOID *)&pmessage); // // Send the message. // hresult = pmessage->put_Label(bstrLabel); 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, NULL); if (FAILED(hresult)) PRINTERROR("Send failed", hresult); VariantClear(&varBody); bstrBody = NULL; RELEASE(pmessage); // // Check for a request to end teh application. // if (_stricmp(szBuffer, "quit") == 0) break; } /* while (1) */ Cleanup: if(rgpqSend != NULL) { rgpqSend->Close(); rgpqSend->Release(); } RELEASE(pqinfo); RELEASE(pmessage); SysFreeString(bstrFormat); SysFreeString(bstrLabel); SysFreeString(bstrBody); SysFreeString(bstrServiceType); return hresult; } //------------------------------------------------------ // // Sender function // //------------------------------------------------------ HRESULT Sender(int fDirectMode) { if(fDirectMode != 0) return DirectSender(); else return StandardSender(); } //----------------------------------------------------- // // MAIN // //----------------------------------------------------- int main(int argc, char * * argv) { DWORD dwNumChars; HRESULT hresult = NOERROR; bool fDirectMode; if (argc != 2) return Syntax(); hresult = OleInitialize(NULL); if (FAILED(hresult)) PRINTERROR("OLE cannot be initialized", hresult); // // Retrieve the computer name. // dwNumChars = MAX_COMPUTERNAME_LENGTH; if(!GetComputerNameA(mbsMachineName, &dwNumChars)) { printf("The computer name cannot be obtained. Exiting...\n"); exit(1); } // // Detect DS connection and working mode. // fDirectMode = SetConnectionMode(); if (strcmp(argv[1], "-s") == 0) hresult = Sender(fDirectMode); else if (strcmp(argv[1], "-r") == 0) hresult = Receiver(fDirectMode); else hresult = Syntax(); printf("\nOK\n"); // Fall through... Cleanup: return (int)hresult; } void PrintError(char *s, HRESULT hr) { printf("%s (0x%X). Exiting...\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; }