【问题标题】:IDispatch returns DISP_E_UNKNOWNNAME for CommandBarButton.StyleIDispatch 为 CommandBarButton.Style 返回 DISP_E_UNKNOWNNAME
【发布时间】:2014-11-15 22:15:48
【问题描述】:

我有一个 Visual Studio 插件,其中包含一个用 C++ 实现的脚本引擎。 加载项只能使用 IDispatch 接口与 Visual Studio 进行通信。 我正在将其从 VS 2005 升级到 VS 2010。

外接程序进行一系列IDispatch::Invoke() 调用,与此 Visual Basic 等效:

control = commandBar.Controls.Add(MsoControlType.msoControlButton)
control.Caption = "My button"
control.FaceId = 59

在 VS 2005 中,这曾经可以工作。但在 VS 2010 中却不行。 GetIDsOfNames() 为“FaceId”返回 DISP_E_UNKNOWNNAME

请注意,“Caption”(成功)是 CommandBarControl 的属性,而“FaceId”(失败)是 CommandBarButton 子类的属性。按钮IDispatch* 的类名是CommandBarControl。所以我想我需要以某种方式将CommandBarControl IDispatch* 降级为CommandBarButton IDispatch*

在 Visual Basic 中我可以编写:

button = DirectCast(control, CommandBarButton)
button.FaceId = 59

但我不知道DirectCast() 在内部做什么。如果我这样做了,我可能会接近解决这个问题。

谢谢

【问题讨论】:

  • 多个调度接口的乐趣
  • 感谢 Matt... 多个 IDispatch 接口,真的吗?这不是意味着 MS 违反了他们自己的规则,即一个对象只能有一个 IDispatch 接口吗?我的理解是:“只允许一个 IDispatch(使用 QueryInterface 和 IDispatch 应该总是返回相同的接口)”(codeguru.com/cpp/com-tech/atl/atl/article.php/c47/…
  • 允许多个调度接口,但某些语言无法访问它们。这就是为什么有时建议不要使用多个双接口的原因。例如,在 C++ 中,您在接口请求中指定 IID,因此您可以拥有 IDispatch *,但您可以请求 IID_IFoo。如果对象将IFoo 作为双接口,则此方法有效。
  • (不知道和你的问题有没有关系)
  • 我希望我可以 QI ICommandBarButton 的 IDispatch 并提供编译的钩子让脚本直接调用它,但是我需要为脚本可能想要使用的每个其他类执行此操作. IDispatch 不是旨在确保脚本引擎不需要这些编译知识吗?

标签: c++ visual-studio-2010 com ole


【解决方案1】:

只是在这里回答我自己的问题......哎呀。

首先我发现,如果我在IDispatch 中查询ICommandBarButton,然后在IDispatch 中查询,我会得到一个不同的IDispatch(在不同的地址),它可以识别ICommandBarButton 属性。这让我知道如何找到脚本给我的任意接口名称的 IID(在本例中为字符串“CommandBarButton”)。

但是经过更多的实验,我可以通过简单地查询IUnknown,然后再次查询IDispatch 来将IDispatch 向下转换为最派生的类。不需要其他 IID 或诡计。

在我在问题中描述的情况下,返回的第二个IDispatch* 与第一个地址不同,其类型为_CommandBarButton(注意下划线),而原来的类型为ICommandBarControl_CommandBarButton 似乎拥有CommandBarButton 的所有功能。 (见http://technet.microsoft.com/en-us/microsoft.visualstudio.commandbars.commandbarbutton%28v=vs.90%29

我现在将这段代码包含在我的例程中,它会向脚本引擎返回一个 IDispatch 对象:

/*
 * Query for IUnknown, then for IDispatch again.  This APPEARS to return
 * an IDispatch for the most derived class, thus exposing all methods and
 * properties, and in the process returns a different IDispatch to the
 * original (multiple dispatch interfaces on the same object).
 *
 * For example, calling ICommandBarControls.Add(msoControlButton) returns
 * an IDispatch for the CommandBarControl base class, not the
 * CommandBarButton class.  After casting IDispatch to IUnknown and back,
 * we get an IDispatch for _CommandBarButton, which appears to be almost,
 * but not quite, a CommandBarButton.  That is, CommandBarButton inherits
 * just about every one of its properties and methods from
 * _CommandBarButton -- certainly all the ones we're interested in anyway.
 */
HRESULT hr;
IUnknown *unknown;
hr = dispatch->QueryInterface(IID_IUnknown, (void**)&unknown);
if (hr == S_OK)
{
    IDispatch *dispatch2 = NULL;
    unknown->QueryInterface(IID_IDispatch, (void**)&dispatch2);
    if (hr == S_OK)
    {
        dispatch->Release();
        dispatch = dispatch2;
    }
    unknown->Release();
}

【讨论】:

  • PS:我不清楚这是否表明 VS 2010 模型违反了 QueyInterface 规则。规则 (msdn.microsoft.com/en-us/library/aa910647.aspx) 规定对象上的接口集“它必须是自反的——如果持有指向一个接口的指针的客户端成功地查询另一个接口,则通过获得的第一个接口的指针的查询必须成功。”我想这并不是说指向第一个接口的指针必须与初始指针相同——只是调用成功,即返回一个有效的接口。
猜你喜欢
  • 2014-08-21
  • 2011-11-12
  • 2021-03-23
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 2012-07-06
  • 1970-01-01
相关资源
最近更新 更多