I want to expose methods of CUIAutomation COM class objects to scripts I load and run through my Active / Windows Script application (I am not implementing a script engine, I am using one, specifically the "JScript" engine). The script host is normally able to expose any IDispatch-implementing object automatically, but CUIAutomation class does not implement IDispatch. Calls to QueryInterface for an IDispatch pointer on the object return E_NOINTERFACE.
My entire question, on which I elaborate below, basically boils down to this: is it possible to implement dispatching for an object that doesn't implement IDispatch? I bet having type information for the coclass of the object would be a necessary (and possibly sufficient) requirement, if it were possible. If it is possible, what is wrong with my attempt to do so as explained below? What are my alternatives?
As mentioned, my solution centers around my hypothesis that if I should have the type information (ITypeInfo) for CUIAutomation coclass, then I should theoretically be able to do runtime dispatching on objects of said coclass, even without it implementing IDispatch but just through methods of ITypeInfo like GetIDsOfNames and Invoke. Practically, I'd design a class of my own that does implement IDispatch, wraps a CUIAutomation object (or any IUnknown for that matter that I can pair with proper type information) and delegates member dispatch to the wrapped object.
I have been successful in loading type information for at least the CUIAutomation coclass -- it's all in the Windows Registry -- by locating the path to the module that implements it and using the LoadTypeLib procedure:
(Note: I have assertions that check if calls succeed (by comparing to S_OK or ERROR_SUCCESS etc -- depends on what's code for success), but I omit said error checking in the snippets, for brevity -- if a call isn't checked for return value there is invariably an assertion in place around it, as described)
/// Return zero if and only if successful
int LoadTypeInfo(LPOLESTR szCLSID, ITypeInfo * * ppTypeInfo) {
HKEY hRegKeyCLSIDs;
RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_READ, &hRegKeyCLSIDs); /// Only need to do this once through application lifetime, but here for context
HKEY hRegKeyCLSID;
RegOpenKeyEx(hRegKeyCLSIDs, szCLSID , 0, KEY_READ, &hRegKeyCLSID);
BYTE data[MAX_PATH];
DWORD cbData = sizeof(data);
RegGetValueW(hRegKeyCLSID, L"InprocServer32", NULL, RRF_RT_REG_SZ, NULL, data, &cbData);
ITypeLib * pTypeLib;
LoadTypeLib((LPOLESTR)data, &pTypeLib);
return (pTypeLib->GetTypeInfoOfGuid(CLSID, ppTypeInfo) == S_OK);
}
The delegating DispatchProxy class is designed as follows:
class DispatchProxy: public IDispatch {
private:
IUnknown * pUnknown;
ITypeInfo * pTypeInfo;
public:
DispatchProxy(IUnknown * pUnknown, ITypeInfo * pTypeInfo): pUnknown(pUnknown), pTypeInfo(pTypeInfo) {
/// `pUnknown` is the object that doesn't implement `IDispatch` and `pTypeInfo` is the type information for objects like what `pUnknown` points to.
}
/// Omitting `AddRef` and `Release` -- these are rather standard.
HRESULT STDMETHODCALLTYPE DispatchProxy::QueryInterface(REFIID riid, void * * ppvObject) {
if(ppvObject == nullptr) {
return E_POINTER;
}
else
if(riid == IID_IUnknown || riid == IID_IDispatch) {
*ppvObject = this;
((IUnknown *)*ppvObject)->AddRef();
return S_OK;
}
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
/// NOT returning any type information -- explanation below, if you're surprised
HRESULT STDMETHODCALLTYPE DispatchProxy::GetTypeInfoCount(UINT * pctinfo) {
*pctinfo = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE DispatchProxy::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo ** ppTInfo) {
if(iTInfo != 0) return DISP_E_BADINDEX;
_ASSERTE(*ppTInfo == NULL);
return E_NOTIMPL; /// Even though type information for the object being delegated to, is available, obviously, I am unsure whether it technically is valid for `DispatchProxy`, which may have a completely different, incompatible, layout. Granted, `E_NOTIMPL` isn't part of the contract for this method, but like I said -- I am unsure about this one.
}
HRESULT STDMETHODCALLTYPE DispatchProxy::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) {
return pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); /// Returns S_OK, all good. Also tried `DispGetIDsOfNames(pTypeInfo, rgszNames, cNames, rgDispId)` with same result
}
HRESULT STDMETHODCALLTYPE DispatchProxy::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) {
return pTypeInfo->Invoke(pUnknown, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); /// Fails with `E_NOTIMPL`. Also tried `DispInvoke(pUnknown, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr)` with same result
}
};
On a related note, I need a way for the script to obtain references to objects like that of the CUIAutomation class, before they (scripts) can call methods on these. I straight up allow scripts to create COM objects of specified CLSID by exposing a createObject method on a "global" IDispatch-implementing object, much like VBScript's CreateObject function or new ActiveXObject(progID) in Internet Explorer back in the day. It uses CoCreateInstance to create an object of the COM class identified by specified CLSID:
HRESULT Global::CreateObject(VARIANT * pvCLSID, VARIANT * pvResult) {
_ASSERTE(V_VT(pvCLSID) == VT_BSTR);
CLSID CLSID;
CLSIDFromString(V_BSTR(pvCLSID), &CLSID);
IUnknown * pUnknown;
CoCreateInstance(CLSID, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, &pUnknown);
IDispatch * pDispatch;
HRESULT hResult = pUnknown->QueryInterface(&pDispatch);
if(hResult != S_OK) {
_ASSERTE(hResult == E_NOINTERFACE);
ITypeInfo * pTypeInfo;
if(LoadTypeInfo(V_BST(pvCLSID), &pTypeInfo)) { /// No type information was available -- not much choice but to return the created object as `IUnknown`
V_VT(pvResult) = VT_UNKNOWN;
V_UNKNOWN(pvResult) = pUnknown;
return S_OK;
} else {
pDispatch = new DispatchProxy(pUnknown, pTypeInfo);
}
}
if(pvResult) {
V_VT(pvResult) = VT_DISPATCH;
V_DISPATCH(pvResult) = pDispatch;
}
return S_OK;
}
A script can create a CUIAutomation object and get a reference to the new DispatchProxy wrapping it like so:
uiautomation = createObject("{ff48dba4-60ef-4201-aa87-54103eef594e}");
It should then be able to call methods (here GetRootElement) on the object:
uiautomation.GetRootElement(/* parameters */);
Unfortunately, the pTypeInfo->Invoke call at the heart of all of it returns E_NOTIMPL. That's the immediate problem, as of now.
What is not implemented, and why? The member ID (dispIdMember) matches what pTypeInfo->GetIDsOfNames writes earlier, and the latter returns S_OK, so the member ID, according to it at least, is valid. I don't think the parameter format has anything to do with it either -- I would expect another error code from the pTypeInfo->Invoke call if it did.
Making GetTypeInfoCount write 1 as type information count and writing pTypeInfo as result of GetTypeInfo has no effect on the result of subsequent ITypeInfo::Invoke call -- it still fails.
I also tried using the actual IUIAutomation interface type information (pTypeInfoDefaultInterface in the snippet below) that I obtain on the original coclass ITypeInfo object, as opposed to that of the coclass itself, even though documentation sort of implies ITypeInfo::Invoke may recurse into referenced types automatically:
HREFTYPE hRefType;
pTypeInfo->GetRefTypeOfImplType(0, &hRefType);
ITypeInfo * pTypeInfoDefaultInterface;
pTypeInfo->GetRefTypeInfo(hRefType, &pTypeInfoDefaultInterface);
The effect is the same, regardless of whether the interface or the coclass type information is used -- ITypeInfo::Invoke returns E_NOTIMPL.
What am I doing wrong? Am I missing some crucial information about COM, or dispatching, or what type information can do for me? I don't write IDL files, and the DispatchProxy isn't part of some COM server, it's strictly internal class for my application. I looked at the virtual function tables that Visual C++ lets me peek at, and I also did some investigation with GetFuncDesc on the type information -- what it fills out seems to be solid -- there is everything -- names and parameter type and count for every expected method that I am attempting to invoke. The pointers are valid and available.
I admit that at least with GetRootElement which expects a pointer to a pointer to an object, dispatching such method from a script that may not even be able to pass parameters of such type, may be the culprit. But according to documentation, ITypeInfo::Invoke should probably return E_INVALIDARG or DISP_E_EXCEPTION, in such case.
I tried playing around with CreateStdDispatch, too, but two things irk at me -- why shouldn't the above work, for starters? And second, I don't understand exactly what dispatches from where with CreateStdDispatch and which pointers go as which arguments. I suppose unless it's the idiomatic alternative here, it's not my actual question, but if it will help my case I am all for getting an explanation on what exactly does it do and how to plug it in.
I have an interface defined in C# that implements IEnumerable. The implementation of the interface will be done in C++/WinRT as it needs direct access to native code. When I attempt to implement this interface using C++/WinRT, the generated header/implementation contains two 'First()' functions (one from IIterable, and one from IBindableIterable) with different return types. Obviously this isn't going to compile.
Is there some way to "rename" one (or both) of the conflicting functions in the IDL file? C++/CX had a work around that allowed you to use a different function name and then 'bind' it back to the interface name.
Simplified example code below:
Interface:
public interface IUInt32Array : IEnumerable<uint> {}
IDL:
[default_interface]
runtimeclass UInt32Array : IUInt32Array
{
UInt32Array(UInt32 size);
}
IDL Generated Header:
struct UInt32Array : UInt32ArrayT<UInt32Array>
{
UInt32Array(uint32_t size);
Windows::Foundation::Collections::IIterator<uint32_t> First(); // <-- Problem
Windows::UI::Xaml::Interop::IBindableIterator First(); // <-- Problem
}
A solution for this specific problem is to use a combination of 'auto' as the declared return type for the First() function implementation, and to return a type with conversion operators for the two different return types.
Here is an example showing how this was solved in the CppWinRT source code. The linked source code is for the base_collections_vector.h header, specifically see the convertible_observable_vector::First() function (replicated below).
auto First() {
struct result {
container_type* container;
operator wfc::IIterator<T>() {
return static_cast<base_type*>(container)->First();
}
operator wfc::IIterator<Windows::Foundation::IInspectable>() {
return make<iterator>(container);
}
};
return result{ this };
}
Notice here that the function itself is defined as returning auto, which allows us to return an intermediate type. This intermediate type then implements conversion operators for converting to the type expected by the caller. This works for this particular problem as the generated CppWinRT source code immediately assigns the result of the call to a value of the expected type, thus immediately causing the invocation of the conversion operators which in turn return the final correct iterator type.
Thanks to Kenny Kerr who pointed me at both the example and a write-up explaining the above.
Is this a proper restriction on overloading behavior. I can't figure out how to replicate a similar issue in pure C++ to compare behavior.
C++/CLI code:
class A {};
List<int>^ g(A &) {
return gcnew List<int>();
}
template<typename T>
List<T>^ g(T &) {
return gcnew List<T>();
}
void f() {
g(A()); // compiler error C3235
}
Generates:
error C3225: generic type argument for 'T' cannot be 'A', it must be a
value type or a handle to a reference type
The problem appears to be from the return values. It's requiring that the templated g() has a valid definition even though the non-templated g() overload should (shouldn't it?) be selected.
I am trying to write a small library which will use DirectShow. This library is to be utilised by a .NET application so I thought it would be best to write it in C++/CLI.
I am having trouble with this line however:
HRESULT hr = CoCreateInstance( CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)(&graphBuilder) ); //error C2440:
Where graphBuilder is declared:
public ref class VideoPlayer
{
public:
VideoPlayer();
void Load(String^ filename);
IGraphBuilder* graphBuilder;
};
If I am understanding this page correctly, I can use */& as usual to denote 'native' pointers to unmanaged memory in my C++/CLI library; ^ is used to denote a pointer to a managed object. However, this code produces:
error C2440: 'type cast' : cannot convert from 'cli::interior_ptr' to 'void **'
The error suggests that graphBuilder is considered to be a 'cli::interior_ptr<Type>'. That is a pointer/handle to managed memory, isn't it? But it is a pure native pointer. I am not trying to pass the pointer to a method expecting a handle or vice versa - I simply want to store it in my managed class) If so, how do I say graphBuilder is to be a 'traditional' pointer?
(This question is similar but the answer, to use a pin_ptr, I do not see helping me, as it cannot be a member of my class)
The error message is a bit cryptic, but the compiler is trying to remind you that you cannot pass a pointer to a member of a managed class to unmanaged code. That cannot work by design, disaster strikes when the garbage collector kicks in while the function is executing and moves the managed object. Invalidating the pointer to the member in the process and causing the native code to spray bytes into the gc heap at the wrong address.
The workaround is simple, just declare a local variable and pass a pointer to it instead. Variables on the stack can't be moved. Like this:
void init() {
IGraphBuilder* builder; // Local variable, okay to pass its address
HRESULT hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)(&builder) );
if (SUCCEEDED(hr)) {
graphBuilder = builder;
// etc...
}
}
I have a C++/CLI class definition where I'm trying to get Equality testing to be Value based rahter than Reference (similar to the behavior of String). The following definitions work:
namespace MyCode
{
public ref class MyClass
{
public:
MyClass();
bool operator==(MyClass^ obj) { return Equals(obj); }
bool operator!=(MyClass^ obj) { return !Equals(obj); }
virtual bool Equals(MyClass^ obj);
virtual bool Equals(System::Object^ obj) override;
virtual int GetHashCode() override;
};
}
However, my company is now requiring (and rightly so) that all code needs to conform to the Code Analysis rules. Code analysis consistently reports two warnings on the above class:
CA2226 : Microsoft.Usage : Since ''MyClass'' redefines operator '==', it should also redefine operator '!='.
CA2226 : Microsoft.Usage : Since ''MyClass'' redefines operator '!=', it should also redefine operator '=='.
The Microsoft documentation on warning CA2226 makes it clear that this is an important warning and should not be suppressed - but what else can I do?
I'm looking for a way (if possible) to 'fix' the code in order to remove this warning. Is that possible, or do I just need to suppress it?
For a ref class, you're supposed to implement operator==(MyClass^ left, MyClass^ right) as a static member function, this is the one other .NET languages will find.
Your current implementation defines operator==(MyClass%, MyClass^ right) instead, which is unusual.
Note that you can't rely on left != nullptr, you need to test ReferenceEquals(left, nullptr).
This is a .NET implementation detail. Having instance operator overloads is a C++ feature, the code analyzer chokes on it. The .NET way is to have operator overloads as static functions. Notably C# requires this. Solve your problem similar to this:
static bool operator==(MyClass^ lhs, MyClass^ rhs) { return lhs->Equals(rhs); }
static bool operator!=(MyClass^ lhs, MyClass^ rhs) { return !lhs->Equals(rhs); }