3

在我上一个开发环境中,我能够轻松地与 COM 交互,调用 COM 对象上的方法。这是原始代码,翻译成 C# 样式代码(以掩盖原始语言):

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;
}

现在开始尝试从托管代码与 COM 互操作的乏味、痛苦的过程。

PInvoke.net 已经包含IWebBrower2 翻译,其相关部分是:

[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; }
}

我创建了 COM 类:

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

所以现在是我实际的 C# 事务的时候了:

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;
}

细心的读者注意到 IWebBrowser2.Document 是一个后期绑定的 IDispatch。我们在我们和我们客户的机器上使用 Visual Studio 2005 和 .NET 2.0。

那么 .NET 2.0 调用对象上的方法的方法是什么,在某种程度上,它只支持后期绑定的 IDispatch?

快速搜索 Stack Overflow 以使用 C# 中的 IDispatch 出现这篇文章,说我想要的在 .NET 中是不可能的。

那么是否可以从 C# .NET 2.0 使用 COM?


问题是我想在 C#/.NET 中使用一种公认的设计模式。它涉及在进程外启动 Internet Explorer,并为其提供 HTML 内容,同时不使用临时文件。

一个被拒绝的设计想法是在 WinForm 上托管 Internet Explorer。

一个可接受的替代方法是启动系统注册的 Web 浏览器,使其显示 HTML,而不使用临时文件。

绊脚石是继续在 .NET 世界中使用 COM 对象。具体问题涉及在不需要 C# 4.0 的情况下执行对 IDispatch 的后期绑定调用。(即在使用 .NET 2.0 时)

4

4 回答 4

4

更新:根据问题更新,我删除了与问题不再相关的部分答案。但是,如果其他读者正在寻找一种在 winforms 应用程序中生成 HTML 并且不需要进程内 IE 的快速而肮脏的方法,我将留下以下内容:

可能的场景 1:最终目标是简单地向最终用户显示 HTML 并使用 Windows 窗体

System.Windows.Forms.WebBrowser是您尝试手动实现的接口的非常简单的 .NET 包装器。要获得它,将该对象的一个​​实例从您的工具栏(在“所有 Windows 窗体”部分下列为“Web 浏览器”)拖放到您的表单上。然后,在一些合适的事件处理程序上:

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

在我的测试应用程序上,这正确地显示了我们都学会恐惧和厌恶的令人难以忘怀的信息。

于 2009-01-23T04:04:47.813 回答
3

后期绑定的 IDispatch 调用在 .NET 中相对容易,虽然很糟糕:

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);
}

最初说“在 C# 4.0 之前不可能”的引用 SO 问题已被修改,以显示在 .NET 2.0 中它是如何实现的。

C# .NET 是否支持 IDispatch 后期绑定?

于 2009-01-29T19:28:49.257 回答
0

您链接到的帖子中的答案实际上是不正确的。在 .Net 中处理基于 IDispatch 的对象通常非常容易。基本上你会经历三个步骤:

大多数作为 IDispatch 接口公开的自动化对象(可能超过 90%)都有其他接口可供非脚本类型 COM 客户端使用(IDispatch 接口实际上是从 IDispatch 派生的完整 COM 接口,或者对象支持一个或更多其他 IUnknown 派生接口)。在这种情况下,您只需导入适当的 COM 接口定义,然后将对象转换为适当的接口。演员在幕后调用 QueryInterface 并返回对所需接口的包装引用。

这是您将在上面介绍的场景中使用的技术。从 IE 自动化对象返回的 Document 对象支持 IHTMLDocument、IHTMLDocument2、IHTMLDocument3、IHTMLDocument4 和 IHTMLDocument5 接口(取决于您使用的 IE 版本)。您应该转换到适当的接口,然后调用适当的方法。例如:

IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();

在自动化对象不支持替代接口的极少数情况下。然后你应该使用 VB.Net 来包装那个接口。将 Option Strict 设置为关闭(仅适用于包装类),您可以使用 VB 对后期绑定调用的内置支持来简单地调用适当的 IDispatch 方法。在极少数情况下,具有不寻常的参数类型,您可能需要对调用进行一些调整,但通常在 VB 中您可以做到!即使对 C# v4 VB 进行了动态添加,也可能对后期绑定的 COM 调用有更好的支持。

如果由于某种原因您不能使用 VB 来包装自动化接口,那么您仍然可以使用反射从 C# 进行任何必要的调用。我不会详细介绍,因为这个选项基本上不应该被使用,但这里是一个涉及 Office 自动化的小例子

于 2009-01-29T18:49:06.200 回答
0

见这篇文章: http: //www.codeproject.com/KB/cs/IELateBindingAutowod.aspx

Internet Explorer 后期绑定自动化 By yincekara

使用后期绑定的 Internet Explorer 自动化示例代码,没有 Microsoft.mshtml 和 shdocvw 依赖项。

对于 htmlDoc.write(htmlString); 调整

   [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);
        //void write([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] psarray);
于 2009-06-18T11:53:04.987 回答