.NET2.0 C# Interop: How to call COM code from C#?

13,073

Solution 1

Late bound IDispatch called is relativly easy in .NET, although piss-poor:

public static void SpawnIEWithSource(String szHtml)
{
    // Get the class type and instantiate Internet Explorer.
    Type ieType = Type.GetTypeFromProgID("InternetExplorer.Application");
    object ie = Activator.CreateInstance(ieType);

    //Navigate to the blank page in order to make sure the Document exists
    //ie.Navigate2("about:blank");
    Object[] parameters = new Object[1];
    parameters[0] = @"about:blank";
    ie.GetType().InvokeMember("Navigate2", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, ie, parameters);

    //Get the Document object now that it exists
    //Object document = ie.Document;
    object document = ie.GetType().InvokeMember("Document", BindingFlags.GetProperty | BindingFlags.IgnoreCase, null, ie, null);

    //document.Write(szSourceHTML);
    parameters = new Object[1];
    parameters[0] = szHtml;
    document.GetType().InvokeMember("Write", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, parameters);

    //document.Close()
    document.GetType().InvokeMember("Close", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, null);

    //ie.Visible = true;
    parameters = new Object[1];
    parameters[0] = true;
    ie.GetType().InvokeMember("Visible", BindingFlags.SetProperty | BindingFlags.IgnoreCase, null, ie, parameters);
}

The referenced SO question that originally said "not possible until C# 4.0" was amended to show how it is possible in .NET 2.0.

Does C# .NET support IDispatch late binding?

Solution 2

Update: Based on question updates, I have removed the portions of my answer that are no longer relevant to the question. However, in case other readers are looking for a quick and dirty way to generate HTML in a winforms app and do not require an in-process IE, I will leave the following:

Possible Scenario 1: The ultimate goal is to simply display HTML to your end user and are using Windows Forms

System.Windows.Forms.WebBrowser is the painstakingly easy .NET wrapper for the interface you are trying to manually implement. To get it, Drag and drop an instance of that object from your toolbar (listed as "Web Browser" under the "All Windows Forms" section) onto your form. Then, on some suitable event handler:

webBrowser1.Navigate("about:blank");
webBrowser1.Document.Write("<html><body>Hello World</body></html>");

On my test app, this correctly displayed the haunting message we all have learned to fear and loath.

Share:
13,073
mistertodd
Author by

mistertodd

Any code is public domain. No attribution required. జ్ఞా &lt;sup&gt;🕗&lt;/sup&gt;🕗 Yes, i do write i with a lowercase i. The Meta Stackexchange answer that I am most proud of

Updated on June 04, 2022

Comments

  • mistertodd
    mistertodd almost 2 years

    In my last development environment, I was able to easily interact with COM, calling methods on COM objects. Here is the original code, translated into C# style code (to mask the original language):

    public static void SpawnIEWithSource(String szSourceHTML)
    {
        OleVariant ie; //IWebBrowser2
        OleVariant ie = new InternetExplorer();
        ie.Navigate2("about:blank");
    
        OleVariant webDocument = ie.Document;
        webDocument.Write(szSourceHTML);
        webDocument.close;
    
        ie.Visible = True;
    }
    

    Now begins the tedious, painful, process of trying to interop with COM from managed code.

    PInvoke.net already contains the IWebBrower2 translation, the relavent porition of which is:

    [ComImport, 
       DefaultMember("Name"), 
       Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"), 
       InterfaceType(ComInterfaceType.InterfaceIsIDispatch), 
       SuppressUnmanagedCodeSecurity]
    public interface IWebBrowser2
    {
        [DispId(500)]
        void Navigate2([In] ref object URL, [In] ref object Flags, [In] ref object TargetFrameName, [In] ref object PostData, [In] ref object Headers);
    
        object Document { [return: MarshalAs(UnmanagedType.IDispatch)] [DispId(0xcb)] get; }
    }
    

    I've created the COM class:

    [ComImport]
    [Guid("0002DF01-0000-0000-C000-000000000046")]
    public class InternetExplorer
    {
    }
    

    So now it's time for my actual C# transaction:

    public static void SpawnIEWithSource(String szHtml)
    {
        PInvoke.ShellDocView.IWebBrowser2 ie;
        ie = (PInvoke.ShellDocView.IWebBrowser2)new PInvoke.ShellDocView.InternetExplorer();
    
        //Navigate to about:blank to initialize the browser
        object o = System.Reflection.Missing.Value;
        String url = @"about:blank";
        ie.Navigate2(ref url, ref o, ref o, ref o, ref o);
    
        //stuff contents into the document
        object webDocument = ie.Document;
        //webDocument.Write(szHtml);
        //webDocument.Close();
    
        ie.Visible = true;
    }
    

    The careful readers notice that IWebBrowser2.Document is a late-bound IDispatch. We're using Visual Studio 2005, with .NET 2.0 on our, and our customer's, machines.

    So what's the .NET 2.0 method to invoke methods on an object that, on some level, only supports late-bound IDispatch?

    A quick search of Stack Overflow for using IDispatch from C# turns up this post saying what I want is not possible in .NET.

    So is it possible to use COM from C# .NET 2.0?


    The question is that there is an accepted design pattern that I want to use in C#/.NET. It involves launching Internet Explorer out of process, and giving it HTML content, all the while not using temporary files.

    A rejected design idea is hosting Internet Explorer on a WinForm.

    An acceptable alternative is launching the system registered web browser, giving it HTML to display, without using a temporary file.

    The stumbling block is continuing to use COM objects in the .NET world. The specific problem involves performing late-binding calls to IDispatch without needing C# 4.0. (i.e. while using .NET 2.0)

  • mistertodd
    mistertodd over 15 years
    Scenario 1: Not sure what you're asking. Scenario 2: Goal is to launch the web-browser with source. Scenario 3: Web-browser is conceptual example, that i happen to be trying to solve. Others who are trying to port COM code to .NET might benefit from the answer that this case provides.
  • mistertodd
    mistertodd over 15 years
    Not every COM object supports interfaces besides IDispatch. Besides, the code you posted won't work (you'll find the imported declaration of the write() method is incorrect). i actually have 7 questions on stack overflow trying to solve the problem by attacking it from different avenues.
  • mistertodd
    mistertodd over 15 years
    i discovered the relativly easy way to call late-bound IDispatch based interfaces in C#.
  • el2iot2
    el2iot2 over 15 years
    Scenario 1: was leaving room for my misinterpretation. Basically, I had knowledge of one of the ".NET ways" of doing it. I couldn't tell from the original question whether you A. just wanted to simply solve html display, or if B. that was just a sample of the larger problem. It seems it was B.
  • el2iot2
    el2iot2 over 15 years
    Understood. It actually seems like something that happens quite a lot at SO: Ambiguity between a higher-level conceptual/approach question that use a specific scenario as an example, and questions about very specific questions. I think its a good distinction to know. Thanks for your feedback.