Gerd K
Gerd K

Reputation: 1147

Why can I cast a COM object to a wrong interface?

I have a .NET program, that interacts with a mshtml object from another process. I wrote a small sample project from scratch to illustrate the problem. In this example I directly use a COM reference for the mshtml interop.

HTMLDocument document = Document;
IHTMLElement activeElement = document.activeElement;
Log.Verbose(activeElement.tagName);
bool isHtmlFrameElement = activeElement is HTMLFrameElement;
Log.Verbose("active Element is " + (isHtmlFrameElement ? "" : "NOT ") + "a frame element");

I reference a custom mshtml, generated with the following call:

tlbimp c:\Windows\System32\mshtml.tlb /out:c:\tmp\Interop.mshtml.dll

On my dev machine (where Office is installed) I get this log which is the expected behavior:

INPUT 
active Element is NOT a frame element

But on a naked machine, where no office (and no mshtml interop) is installed I get the following log:

INPUT 
active Element is a frame element

Of course it is not an HTMLFrameElement and any access to one of it's members causes a member not found exception.

Why does COM allow this invalid cast in the second scenario? Can I work with my interop (in the build dir) or do I have to install it to GAC (like MS Office does)?

Upvotes: 2

Views: 237

Answers (2)

Simon Mourier
Simon Mourier

Reputation: 139187

mshtml's HTMLFrameElement is a COM coclass as generated by .NET. In reality and to make it short, this doesn't exist beyond tooling (like tlbimp, etc.). Usually we only use it's GUID (the CLSID) to be able to "cocreate" it.

This coclass declares it implements DispHTMLFrameElement which is a dispinterface. Again, this is more metadata for tooling, it has no real use in your case. You can see it declared in Windows SDK's mshtml.h:

EXTERN_C const IID DIID_DispHTMLFrameElement;

#if defined(__cplusplus) && !defined(CINTERFACE)

MIDL_INTERFACE("3050f513-98b5-11cf-bb82-00aa00bdce0b")
DispHTMLFrameElement : public IDispatch
{
};

#else /* C style interface */

So .NET builds some fancy classes over these but they should not be used to detect COM interfaces support in your case. Why the behavior is different depending on context just means implementation of these classes varies.

In your case, you should check with IHTMLFrameElement which is also defined in MsHTML.h:

EXTERN_C const IID IID_IHTMLFrameElement;

#if defined(__cplusplus) && !defined(CINTERFACE)

MIDL_INTERFACE("3050f313-98b5-11cf-bb82-00aa00bdce0b")
IHTMLFrameElement : public IDispatch
{
public:
    virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_borderColor( 
        /* [in] */ VARIANT v) = 0;
    
    virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_borderColor( 
        /* [out][retval] */ __RPC__out VARIANT *p) = 0;
    
};

#else /* C style interface */

Note the IID is similar but in fact different and testing with this one is really what you want to do:

bool isHtmlFrameElement = activeElement is IHTMLFrameElement;

Upvotes: 2

Serge P
Serge P

Reputation: 1161

Casting is not failed because returned object is NULL. Details are explained here

Shortly, it says there is no exceptions while casting improperly. Working with COM objects is not easiest to be honest. First of all, dll should be registered correctly, second of all objects should be described correctly.

Upvotes: 1

Related Questions