2

The class mshtml.HTMLDocumentClass in Microsoft.mshtml.dll assembly has a method:

public virtual void write(params object[] psarray);

Avoiding the real question for a moment, what code would you use to call write()? Would you use:

String html = "<html><body>Hello, world!</body></html>";
mshtml.HTMLDocumentClass doc;
...
doc.write(html);

or would you use:

String html = "<html><body>Hello, world!</body></html>";
mshtml.HTMLDocumentClass doc;
...
object[] params = new Object[1];
params[0] = html;
doc.write(params);

Because both of those throw an exception. (Type mismatch. 0x80020005)

The HTMLDocumentClass.write method actually comes from IHTMLDocument2 interface, which is documented as:

IHTMLDocument2::write Method

Writes one or more HTML expressions to a document in the specified window.

Syntax

HRESULT write(
   SAFEARRAY *psarray
);

Parameters

psarray

   [in] A **BSTR** that specifies the text and HTML tags to write.

So in reality the write method needs a pointer to a SAFEARRAY, even though Microsoft's Microsoft.mshtml interop assembly define the write method as taking a regular array:

public virtual void write(params object[] psarray);

Ignoring the mshtml interop declaration, i have to construct a SAFEARRAY object (verses an object array), fill it with a BSTR string (verses a String), and stuff it into a parameter that must be an object array.


Note: i'm unsure of the meaning of the params keyword. It is used to indicate a variable number of parameters.

Does that mean that it can take multiple array parameters?

object[] array1 = new Object[1];
array1 [0] = alpha;
object[] array2 = new Object[1];
array2 [0] = bravo;
object[] array3 = new Object[1];
array3 [0] = charlie;
object[] array4 = new Object[1];
array4 [0] = delta;

doc.write(array1, array2, array3, array4);

Or is object[] the method in which multiple parameters are passed, and you must literally create an array?

object[] params = new Object[4];
params[0] = alpha;
params[1] = bravo;
params[2] = charlie;
params[3] = delta;
doc.write(params);

Or is the array[] just a decoy, and really you pass:

doc.write(alpha, bravo, charlie, delta);

When i originally used this code, from a native Win32 app, the BSTR was placed inside a SAFEARRAY. In IDispatch based automation, everything is inside an array. In this case the late binding code:

doc.write(html);

was converted by the compiler into a SAFEARRAY, where the zero-th element contains a BSTR string (which is a length prefixed unicode string).

My problem becomes one of trying to construct a SAFEARRAY, converting a String into a BSTR, placing the BSTR into the zero-th element of the SAFEARRAY, and passing a variable that contains a SAFEARRAY to one that only accepts an object array (object[]).

This is the real question: how to create a BSTR SAFEARRAY?


Microsoft.mshtml

C:\Program Files\Microsoft.NET\Primary Interop Assemblies\Microsoft.mshtml.dll

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219

3 Answers3

1

It works like a charm this way :

[Guid("332C4425-26CB-11D0-B483-00C04FD90119")]
[ComImport]
[TypeLibType((short)4160)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
internal interface IHTMLDocument2
{
    [DispId(1054)]
    void write([MarshalAs(UnmanagedType.BStr)] string psArray); //modified 
    //void write([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] psarray); //instead of
  • Is it just a cut/paste snippet, or were you actually able to get away with declaring the interface with only one method? – Ian Boyd Jun 18 '09 at 17:06
1

The declaration for the write method on the IHTMLDocument2 interface created by TLBIMP/VS.NET is incorrect. It should be:

void Write([In, MarshalAs(UnmanagedType.SafeArray)] object[] psarray);

You will have to define this interface in code and then use that.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • Does that mean that i cannot use Microsoft's Microsoft.mshtml.dll primary interop assembly? – Ian Boyd Jan 28 '09 at 20:50
  • Perhaps you can, but I've never used this method, and looking at the IDL for the unmanaged interface, it would seem that the interop definition is incorrect. – casperOne Jan 28 '09 at 20:54
  • How are you importing it, when i import from the COM tab "Microsoft HTML Object Library", which is "c:\Windows\System32\MSHTML.TLB", it's still wrong. – Ian Boyd Jan 28 '09 at 21:15
  • I'm not importing it, I recommended declaring the interface in code manually and using that. – casperOne Jan 29 '09 at 04:50
  • It's a pretty large interface, is the solution you're describing to import all of it, and then change the one signature? – Ian Boyd Feb 11 '09 at 19:03
  • You could use reflector to get the interface from the assembly produced by TLBIMP, and then paste that into a code file into your project, and then use THAT definition (do not use the one in MSHTML). – casperOne Feb 11 '09 at 20:18
1

The params keyword indicates that you can supply multiple parameters in this place, and it will group automatically. For example, if I had a function thus:

public int SumNumbers(params int[] value)
{
       //Logic.
}

then I could call it like this:

int myValue = SumNumbers(1,2,3,4,5,6,7,8,9,10);

The array is constructed automagically. So hypothetically, you could call

mshtml.HTMLDocumentClass doc;
...
doc.write('H','I',' ','M','O','M');

And it would work. Not really practical though. I suppose you've tried calling

doc.write(myString.ToCharArray());

? I don't know anything about SAFEARRAYS, but its possible you might not have to know, either, depending on how the compiler helps/hinders here.

GWLlosa
  • 23,995
  • 17
  • 79
  • 116
  • Did you mean to write "params int value[]" rather than "params int[] value"? Your example seems to indicate that it accepts a varialbe number of integers, and the target method receives them as an array. But "int[] value" would be more logical if the final guy receives as array of values. – Ian Boyd Jan 29 '09 at 15:17