使用 MSMQ 通过 Internet 传输持久数据





5.00/5 (1投票)
2001年3月27日

83125
本文介绍如何使用 Microsoft 消息队列 (MSMQ) 作为 DCOM 传输层,通过互联网或您的局域网发送 Microsoft Word 文档。
引言
本文介绍如何使用 Microsoft 消息队列 (MSMQ) 作为 DCOM 传输层,通过互联网或您的局域网发送 Microsoft Word 文档(持久数据)。我第一次接触 MSMQ 时,觉得它非常有用。我了解到它可以用来传输持久数据——那么还有什么比 Word 文档更持久的呢?在花了很多时间研究 MSMQ,以及 Word 自动化之后,我能够通过 MSMQ 传输 Word 文档。
请注意,这篇文章不适合初学者 COM 程序员,因为 COM 对于初学者来说可能非常难以理解。此外,要写下每一个细节需要大量的篇幅,但如果您有任何问题,欢迎通过 cupid@programmer.net 或 helloIBM@yahoo.com 给我的邮件。
少说理论,多做实践。
让我们开始吧。创建一个简单的 Win32 控制台应用程序,并用以下指令填充它。
#include "stdafx.h" #include <stdio.h> #include <atlbase.h> #import <mqoa.dll> no_namespace #import "D:\Program Files\Office\mso9.dll" raw_interfaces_only #import "C:\Program Files\Common files\Microsoft Shared\VBA\VBA6\vbe6ext.olb" raw_interfaces_only #import "D:\Program Files\Office\msword9.olb" raw_interfaces_only rename("ExitWindows","WordExitWindows")
第一个导入是针对 MSMQ 的,其余的都是为了设置 Word 自动化的必要初始化。我们将使用以下发送和接收函数,以及一个辅助错误检测函数。
void ErrHandler(HRESULT , EXCEPINFO); void Send(); void Receive();
以下是主函数的样子。
int main(int argc, char* argv[]) { CoInitialize(NULL); // Use whatever apartment you like , if you know what you are doing Send(); Sleep(3000); Receive(); CoUninitialize(); return 0; }
您应该始终设置一些错误诊断。
void ErrHandler(HRESULT hr, EXCEPINFO excep) { if(hr==DISP_E_EXCEPTION) { char errDesc[512]; char errMsg[512]; wcstombs(errDesc, excep.bstrDescription, 512); sprintf(errMsg, "Run-time error %d:\n\n %s", excep.scode & 0x0000FFFF, //Lower 16-bits of SCODE errDesc); //Text error description ::MessageBox(NULL, errMsg, "Server Error", MB_SETFOREGROUND | MB_OK); } else { LPVOID lpMsgBuf; ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf, 0, NULL); ::MessageBox(NULL, (LPCTSTR)lpMsgBuf, "COM Error", MB_OK | MB_SETFOREGROUND); ::LocalFree( lpMsgBuf ); } }
在发送函数中,我们将首先创建一个 Word 实例,该实例将用于获取指向我们文档对象的指针,而该文档对象将实际为我们提供文档指针,我们将需要它来使用其 IUnknown
指针传输我们的文档(如果您自己研究 .tlh 和 .tli 文件,可以更好地理解)。
void Send() { HRESULT hr; Word::_ApplicationPtr word = NULL; CLSID clsid; // To be safe we are using version independent program ID to get the //appropriate CLSID of the main Application object. CLSIDFromProgID(L"Word.Application", &clsid); hr = word.CreateInstance( clsid , NULL ); if( FAILED(hr) ) { EXCEPINFO excep; ErrHandler(hr , excep); return ; } Word::DocumentsPtr pdocs = NULL; hr = word->get_Documents(&pdocs); if( FAILED(hr) ) { MessageBox(NULL , "Documents Exception" , "Oala" , MB_OK); EXCEPINFO excep1; ErrHandler(hr , excep1); return ; } // Set the path to your required document to transport CComVariant var(OLESTR("E:\\transport.doc")); Word::_DocumentPtr pdoc = NULL; // This is how you declare an optional parameter the Idispatch way. VARIANT vOpt; vOpt.vt = VT_ERROR; vOpt.scode = DISP_E_PARAMNOTFOUND; // Now obtain the document pointer in the last parameter as the rest are only optional. hr = pdocs->Open(&var , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &pdoc); if( FAILED(hr) ) { MessageBox(NULL , "Open Exception" , "Oala" , MB_OK); EXCEPINFO excep1; ErrHandler(hr , excep1); return ; } /*********** Code to send document over queue ***********/ // Using smart pointers approach here IMSMQQueueInfoPtr spQInfo("MSMQ.MSMQQueueInfo"); //You can use DIRECT Format names for TCP addresses . I am using local //queue here but it will definitely work for remote queues also. spQInfo->PutPathName(SysAllocString(OLESTR(".\\Queue123"))) // Open the queue for send operation IMSMQQueuePtr spQSend = spQInfo->Open(MQ_SEND_ACCESS, MQ_DENY_NONE); // Step 4: Set message-information properties IMSMQMessagePtr spQMsg("MSMQ.MSMQMessage"); spQMsg->Label = "This is a Word Document2"; spQMsg->Body = _variant_t(static_cast<IUnknown*>(pdoc)); spQMsg->Delivery = MQMSG_DELIVERY_RECOVERABLE; // We are not dealing with any kind of response or acknowledgment // Step 5: Send the message on the queue hr = spQMsg->Send(spQSend); if( FAILED(hr) ) { MessageBox(NULL , "Message Sending Exception" , "Queue" , MB_OK); EXCEPINFO excep1; ErrHandler(hr , excep1); return ; } // Step 6: Close the queue hr = spQSend->Close(); if( FAILED(hr) ) { MessageBox(NULL , "Queue Closing Exception" , "Queue" , MB_OK); EXCEPINFO excep1; ErrHandler(hr , excep1); return ; } ::MessageBox(NULL, _T("Message Sent"), _T("Test Send Message"), MB_OK); word->Quit(); } // All the same for the receive operation but in the opposite way void Receive() { HRESULT hr; IMSMQQueueInfoPtr spQInfo1("MSMQ.MSMQQueueInfo"); spQInfo1->PutPathName(SysAllocString(OLESTR(".\\Queue123"))); // Step 2: Open the queue for receive operation IMSMQQueuePtr spQRec = spQInfo1->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE); // Step 3: Attempt to receive a message for three seconds _variant_t vtReceiveTimeout = 3000L; IMSMQMessagePtr spRMsg = spQRec->Receive(&vtMissing, &vtMissing, &vtMissing, &vtReceiveTimeout); if (NULL == spRMsg) { ::MessageBox(NULL, _T("No messages found"), _T("Test Receive Message"), MB_OK); return ; } ::MessageBox(NULL, _T("Message Received"), _T("Test Receive Message"), MB_OK); Word::_DocumentPtr doc1 = NULL; doc1 = spRMsg->Body; CComVariant var1(OLESTR("E:\\receiveed.doc")); VARIANT vOpt; vOpt.vt = VT_ERROR; vOpt.scode = DISP_E_PARAMNOTFOUND; hr = doc1->SaveAs(&var1 , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt , &vOpt); if( FAILED(hr) ) { MessageBox(NULL , "File Saving Exception" , "Save" , MB_OK); EXCEPINFO excep1; ErrHandler(hr , excep1); return ; } }
我之前警告过您,这段代码对于初学者 COM 程序员来说可能是一场噩梦,但我很抱歉,这是我所能做的最好的。但是,正如我之前提到的,您可以就您遇到的任何问题给我发邮件,当然,那里也有 COM 专家可以帮助我和您。