13. januar 2005 - 16:55
Der er
3 kommentarer og
1 løsning
Invoke af global funktion i typelib (appobject)
Koden nedenfor forklarer det meste... Bare opret en Win32 console app. med ATL understøttelse. Det er på engelsk, da jeg også har posted det i et engelsk forum... Af en eller anden grund så fejler den altid, når jeg kalder den funktion (+nogle andre).
-Broholm
// ATLGlobal.cpp : Defines the entry point for the console application.
//
/* Should do the same as the following .vbs file (which works)
Dim w
Set w = CreateObject("Word.Application")
MsgBox(w.CentimetersToPoints(0.5))
Set w = Nothing
*/
/* IDL for CentimetersToPoints is:
[id(0x00000173), helpcontext(0x09700173)]
HRESULT CentimetersToPoints(
[in] single Centimeters,
[out, retval] single* prop);
*/
//This is for demonstration purposes only - Pardon the hardcoding of method names and values, and forgetting to invoke Quit method.
#include "stdafx.h"
#define hrCheck(hr) if(FAILED(##hr)) { return -1;};
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
{ //<-- Scope to force CComPtr destructors before ::CoUninitialize()
CComBSTR ProgId("Word.Application");
HRESULT hr;
CComPtr<IUnknown> pUnk;
hr = pUnk.CoCreateInstance(ProgId,NULL,CLSCTX_SERVER);
hrCheck(hr);
CComQIPtr<IDispatch> pDisp;
hr = pUnk.QueryInterface(&pDisp);
hrCheck(hr);
DISPID dispIdClass;
CComBSTR MethodName("CentimetersToPoints");
hr = pDisp->GetIDsOfNames(IID_NULL,&MethodName,1,LOCALE_USER_DEFAULT,&dispIdClass);
hrCheck(hr);
CComVariant vArg(0.5);
hr = vArg.ChangeType(VT_R4); //lpfuncdesc->lprgelemdescParam[0].tdesc.vt is 4 => VT_R4
hrCheck(hr);
DISPID dispidNamedArgs = -1;
DISPPARAMS dpArgs;
dpArgs.cArgs = 1;
dpArgs.cNamedArgs = 0;
dpArgs.rgvarg = &vArg;
dpArgs.rgdispidNamedArgs = &dispidNamedArgs;
EXCEPINFO e;
CComVariant vResult;
//vResult.ChangeType(VT_R4); //<--- Even tried this
UINT puArgErr(0);
hr = pDisp->Invoke(dispIdClass,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&dpArgs,&vResult,&e,&puArgErr);
hrCheck(hr); //<--- Always fails!
//I've also tried getting a pointer to the hidden _Global interface and invoking the method there... But same error.
};
::CoUninitialize();
return 0;
}
14. januar 2005 - 13:48
#3
Her er lige et andet eksempel, hvir jeg går til flere yderligheder:
// ATLGlobal.cpp : Defines the entry point for the console application.
//
/* Should do the same as the following .vbs file (which works)
Dim w
Set w = CreateObject("Word.Application")
MsgBox(w.CentimetersToPoints(0.5))
Set w = Nothing
*/
/* IDL for CentimetersToPoints is:
[id(0x00000173), helpcontext(0x09700173)]
HRESULT CentimetersToPoints(
[in] single Centimeters,
[out, retval] single* prop);
*/
//This is for demonstration purposes only -
//Pardon the hardcoding of method names and values, and forgetting to invoke Quit method.
//I'm also aware that not all resources is released
#include "stdafx.h"
#define hrCheck(hr) if(FAILED(##hr)) { return -1;};
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
{ //<-- Scope to force CComPtr destructors before ::CoUninitialize()
CComBSTR ProgId("Word.Application");
HRESULT hr;
CComPtr<IUnknown> pUnk;
hr = pUnk.CoCreateInstance(ProgId,NULL,CLSCTX_SERVER);
hrCheck(hr);
CComQIPtr<IDispatch> pDisp;
hr = pUnk.QueryInterface(&pDisp);
hrCheck(hr);
DISPID dispIdClass;
CComBSTR MethodName("CentimetersToPoints");
hr = pDisp->GetIDsOfNames(IID_NULL,&MethodName,1,LOCALE_USER_DEFAULT,&dispIdClass);
hrCheck(hr);
CComVariant vArg(0.5);
hr = vArg.ChangeType(VT_R4); //lpfuncdesc->lprgelemdescParam[0].tdesc.vt is 4 => VT_R4
hrCheck(hr);
DISPID dispidNamedArgs = -1;
DISPPARAMS dpArgs;
dpArgs.cArgs = 1;
dpArgs.cNamedArgs = 0;
dpArgs.rgvarg = &vArg;
dpArgs.rgdispidNamedArgs = &dispidNamedArgs;
EXCEPINFO e;
CComVariant vResult;
//vResult.ChangeType(VT_R4); //<--- Even tried this :-(
UINT puArgErr(0);
hr = pDisp->Invoke(dispIdClass,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&dpArgs,&vResult,&e,&puArgErr); //<-- FAILS with hr = E_FAIL
if(FAILED(hr))
std::cout << "Invoke failed" << std::endl;
//Ok that one fails - now get the appobject coclass and invoke thru that interface
//Get TypeLib
CComPtr<ITypeInfo> pTI;
hr = pDisp->GetTypeInfo(0,LOCALE_USER_DEFAULT,&pTI);
CComQIPtr<ITypeLib> pTypeLib;
UINT TLindex(0);
hr = pTI->GetContainingTypeLib(&pTypeLib,&TLindex);
hrCheck(hr);
//Find CoClass'es in TypeLib - BTW Any comments on how to do this more efficient than a for-loop?
UINT iTypeInfoCount = pTypeLib->GetTypeInfoCount();
for(UINT i(0); i < iTypeInfoCount; ++i)
{
TYPEKIND typeKind;
hr = pTypeLib->GetTypeInfoType(i, &typeKind);
hrCheck(hr);
if(typeKind != TKIND_COCLASS)
continue;
//Found a CoClass - now check for TYPEFLAG_FAPPOBJECT
CComQIPtr<ITypeInfo> pAOTypeInfo;
hr = pTypeLib->GetTypeInfo(i, &pAOTypeInfo);
hrCheck(hr);
TYPEATTR* pAOTypeAttr;
hr = pAOTypeInfo->GetTypeAttr(&pAOTypeAttr);
hrCheck(hr);
if((pAOTypeAttr->wTypeFlags & TYPEFLAG_FAPPOBJECT) != TYPEFLAG_FAPPOBJECT)
{
pAOTypeInfo->ReleaseTypeAttr(pAOTypeAttr);
continue;
};
//Found AppObject
CComBSTR name;
hr = pTypeLib->GetDocumentation(i, &name, 0, 0, 0);
hrCheck(hr);
//Enumerate the coclass's interfaces (only one in this case)
for(int j(0); j < pAOTypeAttr->cImplTypes; ++j)
{
//Get GUID of implemented interface
CComQIPtr<ITypeInfo> pAOTypeInfoImpl;
HREFTYPE hRefType;
hr = pAOTypeInfo->GetRefTypeOfImplType(j,&hRefType);
hrCheck(hr);
hr = pAOTypeInfo->GetRefTypeInfo(hRefType,&pAOTypeInfoImpl);
hrCheck(hr);
TYPEATTR* pAOTypeAttrImpl;
hr = pAOTypeInfoImpl->GetTypeAttr(&pAOTypeAttrImpl);
hrCheck(hr);
//Create instance of coclass, requesting the implemented interface
CComPtr<IDispatch> pAODisp;
hr = pAOTypeInfo->CreateInstance(NULL,pAOTypeAttrImpl->guid,(void**)&pAODisp);
pAOTypeInfoImpl->ReleaseTypeAttr(pAOTypeAttrImpl);
hrCheck(hr);
//Get ID and Invoke
hr = pAODisp->GetIDsOfNames(IID_NULL,&MethodName,1,LOCALE_USER_DEFAULT,&dispIdClass);
hrCheck(hr);
hr = pAODisp->Invoke(dispIdClass,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,&dpArgs,&vResult,&e,&puArgErr);
hrCheck(hr); //<--- Still fails! :-(
};
pAOTypeInfo->ReleaseTypeAttr(pAOTypeAttr);
};
};
::CoUninitialize();
return 0;
}