Re: Init COM interfaces
* Alex:
The only documentation I have is some example in VB:
Dim oI As Object
oI = CreateObject("SomeCOM.SomeInterface")
oI.Init()
[snip]
> But this VB example doesn't give me any idea about this COM class
name. So my question is - do I have to ask this COM distributors for
some additional information - Interface Classes/Namespaces, GUIDs...?
Or something can be done just knowing that this COM object was
registered and ProgID is "SomeCOM.SomeInterface" ?
Well you have already solved the problem and got good advice.
But I was inspired by your question to do a bit of coding, it's so long ago!
So herewith, the most (well, OK, at least a very) inefficient way to output the
text "Microsoft Word". Perhaps this can be useful to someone else, even if the
functionality and error handling etc. is rather incomplete. And perhaps I or
others can learn something from comments about how to do this in an easier way
(of course not using type lib, since that's the point) or errors, whatever.
Cheers,
- Alf
<output comment="After a little waiting period... :-)">
Microsoft Word
</output>
<code filename="main.cpp">
#include <cassert>
#include <iostream>
#include <ostream>
#include <stdexcept>
#include <string>
#include "OleAutomationObject.hpp"
bool throwX( char const s[] ) { throw std::runtime_error( s ); }
class WordAppObject: public alfs::OleAutomationObject
{
private:
void quit()
{
try
{
call( L"Quit", 3 ); // 3 defaulted arguments.
}
catch( std::exception const& )
{
assert( "Word application object failed to quit" && 0 );
std::abort();
}
}
public:
WordAppObject(): alfs::OleAutomationObject( L"Word.Application" ) {}
~WordAppObject() { quit(); }
std::wstring name() const { return getAsStdString( L"Name" ); }
};
struct ComUsage
{
ComUsage()
{
(SUCCEEDED( CoInitialize(0) )) || throwX( "CoInitialize failed" );
}
~ComUsage() { CoUninitialize(); }
};
void cppMain()
{
ComUsage com;
WordAppObject wordApp;
std::wcout << wordApp.name() << std::endl;
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
return EXIT_FAILURE;
}
} // main
</code>
<code filename="OleAutomationObject.hpp">
#ifndef OLE_AUTOMATION_OBJECT
#define OLE_AUTOMATION_OBJECT
#include "wrapper/windows_h.h"
#include <ole2.h> // IDispatch
#include <comdef.h> // _com_ptr_t, _variant_t, _bstr_t
#include <vector>
#include <stdexcept>
#include <string>
namespace alfs
{
namespace detail
{
// Exception helper, should really be in a file of its own:
bool throwX( char const s[] ) { throw std::runtime_error( s ); }
std::string narrow( wchar_t const wide[] )
{
std::string result( 2*wcslen(wide), '\0' );
size_t const n = wcstombs( &result[0], wide, result.size() );
(n != size_t(-1)) || throwX( "alfs::narrow: failed" );
result.resize( n );
return result;
}
bool throwHRX( char const s[], HRESULT hr )
{
throw std::runtime_error( s + narrow( _com_error( hr
).ErrorMessage() ) );
}
_COM_SMARTPTR_TYPEDEF( IDispatch, __uuidof(IDispatch) ); // IDispatchPtr
} // namespace detail
class DispatchParams
{
private:
std::vector<VARIANTARG> myArgValues;
std::vector<DISPID> myNamedArgIds;
DISPPARAMS myParams;
public:
static VARIANTARG defaultParam()
{
VARIANTARG v = {};
v.vt = VT_ERROR;
v.scode = DISP_E_PARAMNOTFOUND;
return v;
}
DispatchParams( int nParameters )
: myArgValues( nParameters == 0? 1 : nParameters, defaultParam() )
, myNamedArgIds( 1 )
{
myParams.rgvarg = &myArgValues[0];
myParams.rgdispidNamedArgs = &myNamedArgIds[0];
myParams.cArgs = nParameters;
myParams.cNamedArgs = 0;
}
DISPPARAMS* ptr() { return &myParams; }
};
class OleAutomationObject
{
private:
detail::IDispatchPtr myDispatch;
enum CallTypeEnum
{
methodCall = DISPATCH_METHOD,
propertyGet = DISPATCH_PROPERTYGET,
propertyPut = DISPATCH_PROPERTYPUT,
propertyPutRef = DISPATCH_PROPERTYPUTREF
};
_variant_t invokeAs(
CallTypeEnum callType,
wchar_t const name[],
DispatchParams& params
) const
{
using namespace detail;
EXCEPINFO exceptionInfo = {};
_variant_t returnValue;
unsigned erronousArgIndex = 0;
DISPID const dispId = dispIdOf( name );
HRESULT const hr = myDispatch->Invoke(
dispId,
IID_NULL, // reserved
0, // locale id
static_cast<WORD>( callType ),
params.ptr(),
&returnValue, // VARIANT FAR* pVarResult
&exceptionInfo, // EXCEPINFO FAR* pExcepInfo
&erronousArgIndex
);
(SUCCEEDED( hr )) || throwHRX( "OleAutomationObject: Invoke: ", hr );
return returnValue;
}
public:
OleAutomationObject( wchar_t const progId[], DWORD serverType =
CLSCTX_ALL )
: myDispatch( progId, 0, serverType )
{
using namespace detail;
(myDispatch != 0) || throwX( "OleAutomationObject::<init> failed" );
}
DISPID dispIdOf( wchar_t const name[] ) const
{
using namespace detail;
DISPID dispId = 0;
HRESULT const hr = myDispatch->GetIDsOfNames(
IID_NULL, // reserved
const_cast<wchar_t**>( &name ),
1, // count of names
0, // locale id
&dispId
);
(SUCCEEDED( hr )) ||
throwHRX( "OleAutomationObject::dispIdOf: GetIDsOfNames: ", hr );
return dispId;
}
_variant_t call( wchar_t const methodName[], DispatchParams& params )
{
return invokeAs( methodCall, methodName, params );
}
_variant_t call( wchar_t const methodName[], int nArgs )
{
DispatchParams params( nArgs );
return call( methodName, params );
}
_variant_t get( wchar_t const propertyName[] ) const
{
// E.g. WordApp.Name requires non-zero arg pointers. Silly.
DispatchParams params( 0 );
return invokeAs( propertyGet, propertyName, params );
}
_bstr_t getAsBString( wchar_t const propertyName[] ) const
{
return get( propertyName );
}
std::wstring getAsStdString( wchar_t const propertyName[] ) const
{
return getAsBString( propertyName ).operator wchar_t const*();
}
};
}
#endif
</code>