问题
I have a Visual Studio add-in, which contains a scripting engine implemented in C++.
The add-in can only communicate with Visual Studio using IDispatch
interfaces.
I am in the process of upgrading it from VS 2005 to VS 2010.
The add-in makes a series of IDispatch::Invoke()
calls equivalent to this Visual Basic:
control = commandBar.Controls.Add(MsoControlType.msoControlButton)
control.Caption = "My button"
control.FaceId = 59
In VS 2005, this used to work. But in VS 2010 it doesn't. GetIDsOfNames()
returns DISP_E_UNKNOWNNAME
for "FaceId".
Note that "Caption" (which succeeds) is a property of CommandBarControl
, and "FaceId" (which fails) is a property of the CommandBarButton
subclass. The classname for the button's IDispatch*
is CommandBarControl
. So I think I need to downcast the CommandBarControl IDispatch*
to a CommandBarButton IDispatch*
somehow.
In Visual Basic I could write:
button = DirectCast(control, CommandBarButton)
button.FaceId = 59
But I don't know what DirectCast()
does internally. If I did I'd probably be close to solving this.
Thanks
回答1:
Just answering my own question here... bleah.
First I discovered that if I query the IDispatch
for ICommandBarButton
, then query that for IDispatch
, I get a different IDispatch
(at a different address) which recognises the ICommandBarButton
properties. That left me with how to find the IID of some arbitrary interface name given to me by the script (in this case, the string "CommandBarButton").
But after more experimenting, it looks like I can downcast an IDispatch
to the most derived class by simply querying for IUnknown
, then querying that for IDispatch
again. No other IIDs or trickery needed.
In the situation I described in the question, The second IDispatch*
returned is a different address to the first, and is of type _CommandBarButton
(note the underscore) where the original was of type ICommandBarControl
. _CommandBarButton
seems to have all the functionality of CommandBarButton
. (See http://technet.microsoft.com/en-us/microsoft.visualstudio.commandbars.commandbarbutton%28v=vs.90%29)
I now include this code in my routine which returns an IDispatch object to the script engine:
/*
* 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();
}
来源:https://stackoverflow.com/questions/25965753/idispatch-returns-disp-e-unknownname-for-commandbarbutton-style