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);
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,
errDesc);
::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;
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 ;
}
CComVariant var(OLESTR("E:\\transport.doc"));
Word::_DocumentPtr pdoc = NULL;
VARIANT vOpt;
vOpt.vt = VT_ERROR;
vOpt.scode = DISP_E_PARAMNOTFOUND;
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 ;
}
IMSMQQueueInfoPtr spQInfo("MSMQ.MSMQQueueInfo");
spQInfo->PutPathName(SysAllocString(OLESTR(".\\Queue123")))
IMSMQQueuePtr spQSend = spQInfo->Open(MQ_SEND_ACCESS, MQ_DENY_NONE);
IMSMQMessagePtr spQMsg("MSMQ.MSMQMessage");
spQMsg->Label = "This is a Word Document2";
spQMsg->Body = _variant_t(static_cast<IUnknown*>(pdoc));
spQMsg->Delivery = MQMSG_DELIVERY_RECOVERABLE;
hr = spQMsg->Send(spQSend);
if( FAILED(hr) )
{
MessageBox(NULL , "Message Sending Exception" , "Queue" , MB_OK);
EXCEPINFO excep1;
ErrHandler(hr , excep1);
return ;
}
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();
}
void Receive()
{
HRESULT hr;
IMSMQQueueInfoPtr spQInfo1("MSMQ.MSMQQueueInfo");
spQInfo1->PutPathName(SysAllocString(OLESTR(".\\Queue123")));
IMSMQQueuePtr spQRec = spQInfo1->Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE);
_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.�