给定COM接口的名称为字符串,如何获取相应的IID以便调用QueryInterface()?
例如:
// Attempt to cast IDispatch referenced by pDisp to an ICommandBarButton
char *interface_name = "ICommandBarButton";
IID iid;
<<< code to get the iid for interface_name goes here >>>
hr = pDisp->QueryInterface(iid, &interface);
当然,这是假设接口名称在系统范围内是唯一的,如果不是,则需要更多的上下文。上下文是,我有一个用于自动化VS2010的脚本引擎,并且需要根据从脚本中读取的字符串形式的接口名称在类型之间进行强制转换。对于要强制转换的对象,我已经有了一个IDispatch*。
编辑:
用户Alf评论说,我不应该这样做,我很乐意不这样做。使用ITypeLib,我已经确定我的IDispatch(由CommandBarControls.Add(msoButton)创建)是CommandBarControl。我需要一个CommandBarButton的IDispatch,这样我就可以访问特定于按钮的属性,例如Style属性——CommandBarControl IDdispatch无法识别该属性。我的IDispatch,AFAIK上支持的接口是:
Interface:CommandBarControl GUID:43FD5911-7BAC-4BDC-AB6C-2DE65B5C0233
Interface:IDispatch GUID:00020400-0000-0000-C000-000000000046
Interface:IUnknown GUID:00000000-0000-0000-C000-000000000046
生成如下所示。CommandBarButton没有在这里列出,所以希望有人向我展示如何仅使用IDispatch的运行时机制进行此转换。
实验代码:
void
GetTypeInfo(ITypeInfo *pTypeInfo, int indent, char *&p)
{
BSTR olename;
TYPEATTR *typeattr;
HRESULT hr;
hr = pTypeInfo->GetDocumentation(MEMBERID_NIL, &olename, NULL, NULL, NULL);
if (hr == S_OK)
{
for (int i = 0; i < indent; i++)
*p++ = ' ';
p += sprintf(p, "Interface:");
int len = SysStringLen(olename);
for (int i = 0; i < len; i++)
*p++ = (char)olename[i];
*p++ = ' ';
SysFreeString(olename);
}
hr = pTypeInfo->GetTypeAttr(&typeattr);
if (hr == S_OK)
{
p += sprintf(p, " GUID:");
for (int i = 0; i < 4; i++)
p += sprintf(p, "%02X", ((unsigned char*)&typeattr->guid.Data1)[3-i]);
*p++ = '-';
for (int i = 0; i < 2; i++)
p += sprintf(p, "%02X", ((unsigned char*)&typeattr->guid.Data2)[1-i]);
*p++ = '-';
for (int i = 0; i < 2; i++)
p += sprintf(p, "%02X", ((unsigned char*)&typeattr->guid.Data3)[1-i]);
*p++ = '-';
for (int i = 0; i < 2; i++)
p += sprintf(p, "%02X", typeattr->guid.Data4[i]);
*p++ = '-';
for (int i = 2; i < 8; i++)
p += sprintf(p, "%02X", typeattr->guid.Data4[i]);
*p++ = 'n';
for (int i = 0; i < typeattr->cImplTypes; i++)
{
HREFTYPE reftype;
ITypeInfo *pTypeInfo2;
hr = pTypeInfo->GetRefTypeOfImplType(i, &reftype);
if (hr == S_OK)
{
hr = pTypeInfo->GetRefTypeInfo(reftype, &pTypeInfo2);
if (hr == S_OK)
{
GetTypeInfo(pTypeInfo2, indent + 2, p);
pTypeInfo2->Release();
}
}
}
pTypeInfo->ReleaseTypeAttr(typeattr);
}
}
void
GetDispatchInfo(IDispatch *pDisp)
{
char buffer[16384];
char *p = buffer;
UINT ticount;
HRESULT hr;
hr = pDisp->GetTypeInfoCount(&ticount);
if (hr == S_OK)
{
for (UINT ti = 0; ti < ticount; ti++)
{
ITypeInfo *pTypeInfo;
hr = pDisp->GetTypeInfo(ti, 0, &pTypeInfo);
if (hr == S_OK)
{
GetTypeInfo(pTypeInfo, 0, p);
pTypeInfo->Release();
}
}
}
*p = 0;
OutputDebugString(buffer);
}
好的,根据以上评论,我认为最初的问题(将接口名称全局转换为GUID)是不可能的。
换句话说,Visual Basic解释器,提供了如下命令:
control = commandBar.Controls.Add(MsoControlType.msoControlButton)
button = DirectCast(control, CommandBarButton)
它依赖于在某个系统范围的表中查找字符串"CommandBarButton"以外的其他内容。这个东西似乎在IDispatch及其相关类型库的运行时机制中。大概有一种方法可以复制VB在这里所做的事情,使用一些类型库voodoo。但这不是最初的问题所问的。。。。
编辑:
我找到了解决问题的方法,并发布了我相关问题的答案:
IDispatch为CommandBarButton返回DISP_E_UNKNOWNNAME。样式
简言之,在IDispatch中查询IUnknown,然后再次在IUnkknown中查询IDispatch,会返回一个不同的IDispatchs,它似乎是用于最派生的类(在本例中为CommandBarButton)。不需要voodoo类型库。希望这能帮助到别人。
如果接口已注册(通常用于编组),那么您可能会在注册表中的HKCR\interface下找到它。不幸的是,那里的接口是由IID注册的,所以如果你想从名称中找到IID,你必须进行线性搜索。
即便如此,它也不能保证工作(接口注册不是强制性的,注册时我甚至不确定接口名称是什么)。