Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Transporting persistent data over the internet using MSMQ

0.00/5 (No votes)
26 Mar 2001 1  
This article is about using Microsoft Message Queue (MSMQ) as a DCOM transport layer to send a Microsoft Word document across the internet or your local LAN.

Introduction

This article is about using Microsoft Message Queue (MSMQ) as a DCOM transport layer to send a Microsoft Word document (persistent data) across the internet or your local LAN. When I first met MSMQ I found it really helpful. I came to know that it can be used to transfer persistent data - so what can be more persistent then a Word document? After spending a lot of time studying  MSMQ, and naturally Word automation, I was able to transport a Word document over MSMQ.

Mind you this is not an article for novice COM programmers, since COM can be extremely difficult thing for a novice to understand. Besides, it would take a truck of ink to write each and every detail but if you have any questions you are welcome to email  me on cupid@programmer.net or helloIBM@yahoo.com .

Less theory, more practice.

Lets start. Create a simple win32 console application and fill it with the following directives.

#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")

The first import was for MSMQ and the rest are there to setup the necessary initialization for Word automation. We will be using the following send and receive functions along with a helper error detector function. 

void ErrHandler(HRESULT , EXCEPINFO);
 
void Send();
 
void Receive();

Here is how the main looks.

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;
}

You should always have some error diagnostics in place.

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 );
    }
}

In the send function we will be first creating an instance of Word which will be used to get a pointer towards our document's object,  and this document's object will actually give us the document pointer which we will be needing to transport our document by using its IUnknown pointer (You can understand better if you study the .tlh and .tli�s yourself).

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 ;
    }
}

I warned you before that this code can be a nightmare for novice COM programmers but I am sorry it was the best I could do on my behalf . But as I mentioned earlier you can mail me for any problem you face and certainly there are COM Guru�s out there to help me and you out.�

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here