2

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)?

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Gerd K
  • 1,134
  • 9
  • 23
  • It's a little hard to answer without the background. Could it be an empty HtmlFrameElement (with members = null)? Stating the obvious - the "is" doesn't lie. – LongChalk Feb 08 '22 at 14:27
  • The "and no mshtml interop" clause is vague, you really do have to deploy Interop.mshtml.dll to that machine. If you don't then you run the risk of getting the old PIA, [it is not sound](https://stackoverflow.com/questions/5170078/net-document-write-with-mshtml/5171569#5171569). Do test for the interface to avoid such trouble, not the synthetic class, use `activeElement is IHTMLFrameElement` – Hans Passant Feb 08 '22 at 14:27
  • The mshtml interop is deployed to the target machine of course in the build directory. But there is no preinstalled interop in the GAC. @LongChalk: It does lie here. Otherwise I wouldn't post that question. If the value of a member is null it's not a causing a member not found exception. – Gerd K Feb 08 '22 at 14:34
  • 1
    Can you post a reproducing project somewhere, including the dll/tlb files you use? – Simon Mourier Feb 16 '22 at 18:39
  • @SimonMourier - I just provided the sample project – Gerd K Feb 17 '22 at 09:22
  • HTMLFrameElement is a coclass (doesn't exist as such, just for cocreating it). This coclass declares it implements DispHTMLFrameElement which is a dispinterface (same, it's more metadata, it has no real existence). .NET just builds some fancy classes over these but they mean nothing in COM terms. At COM level, we only know IUnknown (or derived interfaces such as IDispatch). You should check with IHTMLFrameElement (which mshtml also exposes) and this will work all the time. – Simon Mourier Feb 17 '22 at 11:37
  • Thanks - I also tried to to cast it to `HTMLFrameElementClass`? Is this a proper way to work with COM objects, or should I prefer the interfaces? – Gerd K Feb 17 '22 at 12:21
  • COM is all about interfaces. Only check interfaces. – Simon Mourier Feb 17 '22 at 12:34
  • You are right. If you summarize all in an answer I'll set it as accepted. – Gerd K Feb 17 '22 at 13:25

2 Answers2

2

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;
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • I posted a [follow up question](https://stackoverflow.com/questions/71248417/how-ensure-only-to-use-correct-com-types-in-code) on this topic – Gerd K Feb 24 '22 at 07:23
1

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.

Serge P
  • 1,173
  • 3
  • 25
  • 39