.NET2.0 C#互操作:如何从C#调用COM代码?

.NET2.0 C#互操作:如何从C#调用COM代码?,c#,com,interop,.net-2.0,idispatch,C#,Com,Interop,.net 2.0,Idispatch,在我上一个开发环境中,我能够轻松地与COM交互,调用COM对象上的方法。以下是原始代码,翻译成C#风格的代码(用于屏蔽原始语言): 现在开始尝试从托管代码与COM进行互操作的繁琐、痛苦的过程 PInvoke.net已包含,其相关位置为: [ComImport, DefaultMember("Name"), Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"), InterfaceType(ComInterfaceType.Interfa

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

现在开始尝试从托管代码与COM进行互操作的繁琐、痛苦的过程

PInvoke.net已包含,其相关位置为:

[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

那么,在某种程度上只支持后期绑定IDispatch的对象上调用方法的.NET2.0方法是什么呢

使用C#中的IDispatch快速搜索堆栈溢出时发现,我想要的东西在.NET中是不可能的

那么,是否可以从C#NET2.0使用COM


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

一个被拒绝的设计理念是在WinForm上托管InternetExplorer

一个可接受的替代方案是启动系统注册的web浏览器,让它显示HTML,而不使用临时文件

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

更新:根据问题更新,我删除了答案中与问题不再相关的部分。但是,如果其他读者正在寻找一种在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>");
webBrowser1.导航(“关于:空白”);
webBrowser1.Document.Write(“Hello World”);

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

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

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

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

IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();
在自动化对象不支持替代接口的罕见情况下。然后您应该使用VB.Net来包装该接口。通过将Option Strict设置为off(仅适用于包装器类),您可以使用VB内置的对后期绑定调用的支持,只需在封面下调用适当的IDispatch方法。在极少数情况下,对于不寻常的参数类型,您可能需要稍微修改一下调用,但一般来说,在VB中您可以这样做!即使对C#v4进行了动态添加,VB对后期绑定COM调用的支持也可能会大大提高


如果出于某种原因,您不能使用VB包装自动化界面,那么您仍然可以使用反射从C#进行任何必要的调用。我不会详细介绍任何细节,因为基本上不应该使用此选项,但这里有一个。

在.NET中调用晚绑定IDispatch是相对容易的,尽管很差:

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);
}
引用的SO问题最初说“在C#4.0之前不可能”被修改,以显示它在.NET2.0中是如何可能的

请参阅本文:

Internet Explorer后期绑定自动化 尹切卡拉

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

对于htmlDoc.write(htmlString); 修改


出于好奇,总体目标是什么?您似乎希望启动IE,然后显示传递字符串的HTML。是否要求您专门启动IE,而不是System.Windows.Forms.WebBrowser控件?您能否在进程之外启动System.Windows.Forms.WebBrowser?我想这是可以做到的,但我自己还没有做到。如果我得到了一些有用的东西,我会发回。如果你成功做到了(不参考PIA),你会得到一个湿滑的吻。场景1:不确定你在问什么。场景2:目标是使用源代码启动web浏览器。场景3:Web浏览器是一个概念性的例子,我正试图解决这个问题。其他试图将COM代码移植到.NET的人可能会从本案例提供的答案中受益。场景1:为我的误解留下了空间。基本上,我知道一种使用.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);
}
   [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);