Click here to Skip to main content
Click here to Skip to main content

Transporting persistent data over the internet using MSMQ

, 26 Mar 2001
Rate this:
Please Sign up or sign in to vote.
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

About the Author

No Biography provided

Comments and Discussions

 
GeneralSend to .\private$\queuename [modified] Pinmemberdhanu_kapp15-Dec-06 2:21 
GeneralMSMQ and win 98 PinmemberAbuDhaim19-Nov-02 8:18 
GeneralXML PinmemberChagit7-Jul-02 8:21 
GeneralRe: XML PinmemberSahil Wadhwa25-Dec-08 23:21 
GeneralText file PinmemberChagit3-Jul-02 3:56 
QuestionDo We need SQL server and NT server ? PinmemberBenj7-Jan-02 6:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140718.1 | Last Updated 27 Mar 2001
Article Copyright 2001 by Bashir Irfan Malik
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid