Accessing a COM DLL in C++

In Microsoft Visual C++ 6.0, I want to access a COM DLL (written in VFoxpro 6.0).  In VB and VFP the syntax was CREATEOBJECT("test.class").  How do I go about doing this?  

Thanks.
LVL 1
mgonyeaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

jkrCommented:
Well, in C/C++ things are more compilcated. The instantiation is done by 'CoCreateInstance()' which takes a CLSID as the parameter, and additionally an interface ID. But, as you're referring to VB, these objects usually use IDispatch interfaces, which makes life easier. When you aren't about to use any class library or framework (like MFC), the code would be

#include <tchar.h>

    HRESULT    sc;
      IDispatch* pObj;
      CLSID      clsid;

      CLSIDFromProgID ( TEXT( "test.class"), &clsid);

    sc    =     CoCreateInstance  (   clsid,
                                      NULL,
                                      CLSCTX_SERVER,
                                      IID_IDispatch,
                                      ( void**) &pObj
                                  );


Feel free to ask if you need more information!
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Brain2000Commented:
I just ran into this solution about 2 days ago.  I asked a similar question about a month ago, but it wasn't answered quite how I wanted, so I continued to research it myself.  It has taken me over a year to not only use, but also understand what I'm going to post here.  It seems this is sorely undocumented, and what little is documented, is buried deep within pages of specs and nothingness.

So here's how to do Automation in C++.  This example would be equivalent to VB:

wordApplication=CreateObject("Word.Application")
wordDocuments=wordApplication.Documents
currentDocument=wordDocuments.Open('file',0,0,0,'','',0,'','',wdOpenFormatAuto)


Now, in C++ (this is a roller coaster ride...):

//got this definition from the .idl file
int wdOpenFormatAuto=0;

void main(void)
{
  CLSID wordCLSID;
  IDispatch *wordApplication,*wordDocuments,*currentDocument;
  DISPID dispid;
  HRESULT hr;
  DISPPARAMS dp;
  VARIANT res;
  VARIANTARG va[11];
  OLECHAR *oc;
  int x;

  OleInitialize(NULL);
  //get the CLSID of Word.Application
  CLSIDFromProgID(L"Word.Application",&wordCLSID);

  hr = CoCreateInstance(wordCLSID,NULL,CLSCTX_LOCAL_SERVER,IID_IDispatch,(void **)&wordApplication);

  //find the dispatch id for "Documents"
  oc=L"Documents";
  hr=wordApplication->GetIDsOfNames(IID_NULL,&oc,1,LOCALE_SYSTEM_DEFAULT,&dispid);
  printf("Documents DispID=0x%x\n",dispid);

  //setup for 0 arguments
  dp.rgvarg=NULL;
  dp.cArgs=0;
  dp.rgdispidNamedArgs=NULL;
  dp.cNamedArgs=0;

  //now get the Documents interface.  Note, this is NOT a COM function, it is a property.
  hr=wordApplication->Invoke(dispid,IID_NULL,LOCALE_SYSTEM_DEFAULT,DISPATCH_PROPERTYGET,&dp,&res,NULL,NULL);

  //now save the IDispatch interface pointer for Documents into our wordDocuments variable
  wordDocuments=res.pdispVal;


  //now let's use this new interface to Open() a .doc file

  //get the Open() dispatch ID
  oc=L"Open";
  hr=wordDocuments->GetIDsOfNames(IID_NULL,&oc,1,LOCALE_SYSTEM_DEFAULT,&dispid);

  //now set up the parameters needed for the open function.
  //note, there are 10 variants that need to be set up!
  dp.rgvarg=va;
  dp.cArgs=10;
  dp.cNamedArgs=0;
  dp.rgdispidNamedArgs=NULL;

  VariantInit(&dp.rgvarg[0]);
  dp.rgvarg[0].vt=VT_I4;
  dp.rgvarg[0].lVal=wdOpenFormatAuto;

  VariantInit(&dp.rgvarg[1]);
  dp.rgvarg[1].vt=VT_BSTR;
  oc=L"";
  dp.rgvarg[1].bstrVal=SysAllocString(oc);

  VariantInit(&dp.rgvarg[2]);
  dp.rgvarg[2].vt=VT_BSTR;
  oc=L"";
  dp.rgvarg[2].bstrVal=SysAllocString(oc);

  VariantInit(&dp.rgvarg[3]);
  dp.rgvarg[3].vt=VT_BOOL;
  dp.rgvarg[3].boolVal=0x0000;

  VariantInit(&dp.rgvarg[4]);
  dp.rgvarg[4].vt=VT_BSTR;
  oc=L"";
  dp.rgvarg[4].bstrVal=SysAllocString(oc);

  VariantInit(&dp.rgvarg[5]);
  dp.rgvarg[5].vt=VT_BSTR;
  oc=L"";
  dp.rgvarg[5].bstrVal=SysAllocString(oc);

  VariantInit(&dp.rgvarg[6]);
  dp.rgvarg[6].vt=VT_BOOL;
  dp.rgvarg[6].boolVal=0x0000;

  VariantInit(&dp.rgvarg[7]);
  dp.rgvarg[7].vt=VT_BOOL;
  dp.rgvarg[7].boolVal=0x0000;

  VariantInit(&dp.rgvarg[8]);
  dp.rgvarg[8].vt=VT_BOOL;
  dp.rgvarg[8].boolVal=0x0000;

  VariantInit(&dp.rgvarg[9]);
  dp.rgvarg[9].vt=VT_BSTR;
  oc=L"test.doc"
  dp.rgvarg[9].bstrVal=SysAllocString(oc);

  //now call the Open() function.  Note that this IS a method, not a property like above
  hr=wordDocuments->Invoke(dispid,IID_NULL,LOCALE_SYSTEM_DEFAULT,DISPATCH_METHOD,&dp,&res,NULL,NULL);

  //now free up variables if needed
  for(x=0;x<10;x++) VariantClear(&dp.rgvarg[x]);

  //now save the IDispatch Interface to the newly opened document
  currentDocument=res.pdispVal;


  currentApplication->Release();
  wordDocuments->Release();
  wordApplication->Release();

  OleUninitialize();
}
0
Brain2000Commented:
I forgot to mention one VERY important thing.  If you're compiling this yourself with a makefile, you will pull out your hair wondering why IID_IDispatch and IDispatch is not declared anywhere.  To get it to compile, also include uuid.lib

i.e.

cl test.cpp ole32.lib oleaut32.lib uuid.lib
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

mgonyeaAuthor Commented:
In MS Visual C++ 6, the first parameter expected in CLSISFromProgID is an unsigned short *, and won't accept a char[x].
0
jkrCommented:
>>the first parameter expected in CLSISFromProgID is an
>>unsigned short *

Thus the 'TEXT()' macro, which will expand to a 'L' string prefix that instructs the compiler to handle the following string as UNICODE, which in fact is 'unsigned short*'

The macro is sth. like that:

#ifdef UNICODE
#define TEXT(x) L##x
#else
#define TEXT(x) x

You could of course hard-code it:

CLSIDFromProgID ( L"test.class", &clsid);
0
Brain2000Commented:
Yes, what he said.
0
mgonyeaAuthor Commented:
One last thing, and I'll bump the points up to 200.  The "VARIANT res;" now contains the results of my OLE functions (Yay!).  How do I convert it BACK from a BSTR, into a char *?

Thanks everyone.
0
jkrCommented:
As BSTR* is the same as OLECHAR* (which is the same as LPOLESTR ;-)

(see wtypes.h, i just left out the RPC stuff:
typedef OLECHAR *BSTR;
)

this function does what you need:
char* ANSIStringFromUNICODE( LPOLESTR pszOLE)
{
HRESULT hResult;

static char acBuffer[ BUFFER_MAX];


    hResult =   ::WideCharToMultiByte   (   CP_ACP,
                                            0,
                                            pszOLE,
                                            -1,
                                            acBuffer,
                                            sizeof  (   acBuffer),
                                            NULL,
                                            NULL
                                        );


    if  (   FAILED  (   hResult))
            return  (   NULL)

    return ( acBuffer);
}

So, just call it like

char* pc = ANSIStringFromUNICODE ( res.bstrVal);
0
Brain2000Commented:
Also, don't forget to call VariantClear() when you're done so you don't get memory leaks.
0
Brain2000Commented:
So who gets the points on this one?

jkr proposed the original answer and locked the question, but I answered your question much more thorough.  But then he answered your second part.  I think it should split 100/100, but that's a pain.
0
mgonyeaAuthor Commented:
Unfortunately, I'll grade the answer.  Works great!  I'm posting another question Brain, so you can answer that one and I'll give you the same amount of points.  The question is "Declaring absolute globals in MSVC++6"

Thanks to you to, jkr!
0
jkrCommented:
Fine that I could be of some help!
0
jkrCommented:
mgonyea, you should grade this one so that it moves to the PAQ section...
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Editors IDEs

From novice to tech pro — start learning today.