C++ 使用ATL枚举COM对象(IDispatch)方法?

C++ 使用ATL枚举COM对象(IDispatch)方法?,c++,com,atl,C++,Com,Atl,使用ATL(VS2008)如何枚举给定IDispatch接口(IDispatch*)上可用的方法?我需要搜索一个具有特定名称的方法,一旦我有了DISPID,调用该方法(我知道该方法采用的参数)。理想情况下,我希望使用智能COM指针(CComPtr)来完成这项工作 这可能吗 除非对象实现IDispatchEx,否则无法枚举所有可用的方法 但是,如果知道要调用的方法的名称,可以使用GetIDsOfNames将名称映射到适当的DISPID HRESULT hr; CComPtr<IDispatc

使用ATL(VS2008)如何枚举给定IDispatch接口(
IDispatch*
)上可用的方法?我需要搜索一个具有特定名称的方法,一旦我有了
DISPID
,调用该方法(我知道该方法采用的参数)。理想情况下,我希望使用智能COM指针(
CComPtr
)来完成这项工作


这可能吗

除非对象实现IDispatchEx,否则无法枚举所有可用的方法

但是,如果知道要调用的方法的名称,可以使用GetIDsOfNames将名称映射到适当的DISPID

HRESULT hr;
CComPtr<IDispatch> dispatch;
DISPID dispid;
WCHAR *member = "YOUR-FUNCTION-NAME-HERE";
DISPPARAMS* dispparams;

// Get your pointer to the IDispatch interface on the object here.  Also setup your params in dispparams.

hr = dispatch->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (SUCCEEDED(hr)) {
  hr = dispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, dispparams, &varResult, NULL, NULL);
}
HRESULT-hr;
买方派遣;
DISPID DISPID;
WCHAR*member=“YOUR-FUNCTION-NAME-HERE”;
DISPPARAMS*DISPPARAMS;
//在此处获取指向对象上IDispatch接口的指针。还可以在dispparams中设置参数。
hr=dispatch->GetIDsOfNames(IID\u NULL,&member,1,LOCALE\u SYSTEM\u DEFAULT,&dispid);
如果(成功(hr)){
hr=dispatch->Invoke(1,IID\u NULL,LOCALE\u USER\u默认值,dispatch\u方法,dispparams,&varResult,NULL,NULL);
}

编辑:为了完整性,我怀疑有一种方法可以查询从IDispatch::GetTypeInfo获取的方法列表的ITypeInfo2接口(假设对象有一个类型库),但我没有这样做。查看另一个答案。

您可以枚举
IDispatch通过类型信息公开的方法。有两种方法可以获取类型信息:

  • 通过dispinterface的类型库(如果有)
  • 通过电话
不幸的是,
IDispatch
实现没有义务提供有关其实现的方法和属性的类型信息

但是,如果是这样,则基本枚举包括调用以获取接口的,并查看实现的方法(
cFuncs
)和变量(
cVars
)的数量,然后循环这些方法并调用or。当然,我可以在这里列出更多的细节,但这应该是你探索的一个很好的起点


这里有一个很好的VB.Net中的with代码。

这里有一些执行枚举的代码(它在映射中插入[Dispatch ID]-[Method Name]对,但这很容易更改)

///
///\brief为传入的IDispatch对象返回[DispId,Method Name]的映射
///
HRESULT COMTools::GetIDispatchMethods(u In_uidispatch*pDisp,
_输出标准::映射和方法映射)
{
HRESULT hr=S_正常;
CComPtr spDisp(pDisp);
如果(!spDisp)
返回E_INVALIDARG;
CComPtr spTypeInfo;
hr=spDisp->GetTypeInfo(0、0和spTypeInfo);
if(成功(hr)和spTypeInfo)
{
TYPEATTR*pTatt=nullptr;
hr=spTypeInfo->GetTypeAttr(&pTatt);
if(成功(hr)和pTatt)
{
FUNCDESC*fd=nullptr;
对于(int i=0;icFuncs;++i)
{
hr=spTypeInfo->GetFuncDesc(i和fd);
如果(成功(hr)和fd)
{
CComBSTR-funcName;
spTypeInfo->GetDocumentation(fd->memid,&funcName,nullptr,nullptr,nullptr);
如果(funcName.Length()>0)
{
methodsMap[fd->memid]=funcName;
}
spTypeInfo->releasefundesc(fd);
}
}
spTypeInfo->ReleaseTypeAttr(pTatt);
}
}
返回人力资源;
}

太棒了!正是我需要的。非常感谢。我相信我已经表达了你刚才在我的回答中评论的每一点。请仔细再读一遍。此外,海报只是希望能够调用一个他已经知道名称的方法。因此,我的回答为他真正想做的事情提供了解决方案,而不一定是他问的问题。我怀疑这就是为什么它被标记为正确答案的原因。最后,请冷静下来。只有1和0。好东西。感谢您添加此内容。@Franci,当属性是数组时,返回的VARDESC有一个varkind=IDispatch。如何判断属性是否是数组,如果是数组-如何访问其成员?调用Invoke获取数组时,结果是IDispatch。此IDispatch不支持“Item”或“Length”或任何类似属性。@Uri-请注意,属性不是字段,应通过
GetFuncDesc()
进行检查,该属性提供了一个
FUNCDESC
,您必须从中转到
elemdescFunc
(用于返回)或
lprgelemdescParam
(用于参数)。数组通常作为out参数返回,因此您应该检查后者。在任何情况下,这两个函数都给出了
ELEMDESC
,您应该在这里检查
tdesk
,它返回给您一个
TYPEDESC
,它基于
VARTYPE vt
可能实际上是一个
ARRAYDESC
。如果是这种情况,您有一个
SAFEARRAY
@Uri-如果它不是
SAFEARRAY
,则该属性很可能返回一个
IDispatch
指针,指向一个对象,该对象作为集合实现类似数组的功能。您应该向
IDispatch
询问其类型信息,并检查该类型信息,以找出访问集合成员的正确方法。@Uri-如果您的数组来自JScript,则它不是SAFEARRAY,而是JScript数组对象的IDispatch。这里描述了VBScript数组(即SAFEARRAYS)和JScript数组之间的区别。请参阅此工具(源代码):和此:我查找了其他示例,还发现了
///
/// \brief Returns a map of [DispId, Method Name] for the passed-in IDispatch object
///
HRESULT COMTools::GetIDispatchMethods(_In_ IDispatch * pDisp,
                                      _Out_ std::map<long, std::wstring> & methodsMap)
{
    HRESULT hr = S_OK;

    CComPtr<IDispatch> spDisp(pDisp);
    if(!spDisp)
        return E_INVALIDARG;

    CComPtr<ITypeInfo> spTypeInfo;
    hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
    if(SUCCEEDED(hr) && spTypeInfo)
    {
        TYPEATTR *pTatt = nullptr;
        hr = spTypeInfo->GetTypeAttr(&pTatt);
        if(SUCCEEDED(hr) && pTatt)
        {
            FUNCDESC * fd = nullptr;
            for(int i = 0; i < pTatt->cFuncs; ++i)
            {
                hr = spTypeInfo->GetFuncDesc(i, &fd);
                if(SUCCEEDED(hr) && fd)
                {
                    CComBSTR funcName;
                    spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr);
                    if(funcName.Length()>0)
                    {
                        methodsMap[fd->memid] = funcName;
                    }

                    spTypeInfo->ReleaseFuncDesc(fd);
                }
            }

            spTypeInfo->ReleaseTypeAttr(pTatt);
        }
    }

    return hr;

}