65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2001年3月27日

viewsIcon

83125

本文介绍如何使用 Microsoft 消息队列 (MSMQ) 作为 DCOM 传输层,通过互联网或您的局域网发送 Microsoft Word 文档。

引言

本文介绍如何使用 Microsoft 消息队列 (MSMQ) 作为 DCOM 传输层,通过互联网或您的局域网发送 Microsoft Word 文档(持久数据)。我第一次接触 MSMQ 时,觉得它非常有用。我了解到它可以用来传输持久数据——那么还有什么比 Word 文档更持久的呢?在花了很多时间研究 MSMQ,以及 Word 自动化之后,我能够通过 MSMQ 传输 Word 文档。

请注意,这篇文章不适合初学者 COM 程序员,因为 COM 对于初学者来说可能非常难以理解。此外,要写下每一个细节需要大量的篇幅,但如果您有任何问题,欢迎通过 cupid@programmer.nethelloIBM@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 专家可以帮助我和您。 

© . All rights reserved.