Introduction
If you have used the VC++ ClassWizard to import a class from a type library,
you will be familiar with the
COleDispatchDriver
class. This class is typically used as the base class of machine
generated wrapper classes for COM objects. Actually, I have never
seen it being used for anything else except as the base of wrapper classes
(until now, of course). Why do I bother to write an article about
COleDispatchDriver
?
The reason is that I think this class has some unreleased power that will
make a C++ developer's life a lot easier.
Have you noticed that VB developers can create almost any COM object
and call its methods at run-time, and VC++ developers have to use compile-time
generated wrapper classes? Anyone understands COM will say that we
can do the same thing in C++, but we don't want to bother with writing
so much low-level com code to accomplish this. My goal is to derive
a utility class from COleDispatchDriver
so
that we can write code like the following in VC++ without generating any
wrapper class at compile-time:
MyUtilityClass utility;
utility.CreateObject("MyComObj1");
CString strData = utility.CallMethod("GetData");
utility.CreateObject("MyComOb2");
long nUserID = 3;
long nCount = utitilty.CallMethod("GetMsgCount", nUserID);
As you can see later, I came pretty close to achieving this goal.
If someone flames me for damaging a developer's brain with VB-style code,
then my job is done :-). Please note that this is not meant to replace other existing methods of creating and using a com object.
I would like to thank Chris Losinger and Aaron Schaefer
for answering my "anonymous" programming questions.
XYDispDriver
XYDispDriver
is the name of my utility class.
The source code project will build a library XYDispDriver.dll
which exports the
XYDispDriver
class.
As usual, you may use and modify the source code anyway you wish, I would
appreciate some acknowledgment for my contribution (if possible).
Here is the header file of my class.
#include <afxdisp.h>
class XYDispDriver: public COleDispatchDriver
{
CPtrArray m_arrayDispInfo;
int FindDispInfo(const CString strName, const WORD wFlag = DISPATCH_METHOD);
void Clear();
BOOL CreateDispatch(REFCLSID clsid, COleException* pError = NULL) { return FALSE; }
void AttachDispatch(LPDISPATCH lpDispatch, BOOL bAutoRelease = TRUE ) {}
LPDISPATCH DetachDispatch( ) { return NULL; }
public:
XYDispDriver();
~XYDispDriver();
BOOL CreateDispatch(LPCTSTR lpszProgID);
VARTYPE GetPropertyType(const CString strPropertyName);
void* GetProperty(const CString strPropertyName);
void SetProperty(const CString strPropertyName, ...);
VARTYPE GetReturnType(const CString strMethodName);
int GetParamCount(const CString strMethodName);
VARTYPE GetParamType(const CString strMethodName, const int nParamIndex);
void* InvokeMethod(const CString strMethodName, ...);
};
To use the XYDispDriver
class, you first
need to declare an instance of this class and call the CreateDispatch
member function passing the prog id of a com object. This method
not only creates an IDispatch pointer, it also retrieves and stores
all the necessary type information using the ITypeInfo interface.
Then you can call other member functions to either invoke a method in the
com object or get/set a property value.
Please note that GetProperty
and InvokeMethod
both return a void pointer. To access
the return data, you need to cast the pointer to the appropriate data type
and then dereference it. Also, the return data will be over-ridden
by the next call to the same method. I know this is cumbersome and
un-OO like, but who said the world is perfect? :-)
Please also note that methods of XYDispDriver
may throw exceptions of type COleException
,
an MFC class.
Sample code
Here is a sample program that creates an
XYMailClient
object described in my
other
article and sends/receives e-mail messages. The code can be written and executed without using any type library at compile-time.
#include "XYDispDriver.h"
void main()
{
XYDispDriver disp;
try
{
if(disp.CreateDispatch("XYMailClient.1"))
{
if(*(BOOL*)disp.InvokeMethod("InitSession","MyProfile","")==FALSE)
{
printf("Failed to call InitSession\n");
return;
}
if(!*(BOOL*)disp.InvokeMethod("SendMsg",""mailto:XiangYangL@aol.com">XiangYangL@aol.com",
"","","Hello","This is a test",""))
{
printf("Failed to call SendMsg\n");
}
long nCount = *(long*)disp.InvokeMethod("FetchMsg");
printf("Total messages = %d\n",nCount);
for(int i=0;i<nCount;i++)
{
printf("Message %d: %s\n",i,*(CString*)disp.InvokeMethod("GetMsg",i));
}
disp.InvokeMethod("DeleteMsg",0);
disp.InvokeMethod("EndSession");
}
else
{
printf("Failed to create com obj\n");
}
}
catch(COleException* e)
{
printf("Ole exception\n");
TCHAR buf[251];
e->GetErrorMessage(buf,250);
printf("%s\n",buf);
e->Delete();
}
printf("Done\n");
}
/**************** End of Test.cpp ****************/
Using XYDispDriver in your own program
XYDispDriver
is implemented with VC++ 5.0
and MFC.
To use XYDispDriver
, you need only to add
the source files to your project or use it as a dll. You should always
use the CString
class for string data, although
you can pass literal strings as input parameters. I have not done
extensive testing with various data types except for strings and numbers.
Please let me know if you found or fixed a bug.
Thank you for reading this article. Please visit my home
page for my other articles and programs.