.NET2.0 C#互操作:如何从C#调用COM代码?
题
在我的最后一个开发环境,我能轻松地与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翻译,的relavent porition其是:
[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?
使用的IDispatch从C#快速搜索堆栈溢出的变成了此篇说我想要什么是不可能的.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)
解决方案 2
称为后期绑定的IDispatch是在.NET relativly容易,虽然小便-差:
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”进行了修改,以显示它是如何可能在.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>");
在我的测试应用程序,这正常显示困扰的消息,我们都已经学会了恐惧和厌恶。
在您链接到后期的答案实际上是不正确。它通常是很容易对付的基于.NET的IDispatch的对象。基本上,你要经过三个步骤:
这被公开为IDispatch接口大部分自动化对象(可能超过90%)具有能够被非脚本型COM客户端使用(或者IDispatch接口实际上是从IDispatch接口或对象衍生的全COM接口以外的接口支持一个或多个其它的IUnknown衍生接口)。在这种情况下,您只需输入相应的COM接口定义,然后把对象到相应的接口。演员在幕后调用QueryInterface并返回一个包裹引用到你想要的界面。
这是你在你上文呈现的场景中使用的技术。即从IE自动化对象返回的文档对象支持IHTMLDocument,的IHTMLDocument2,IHTMLDocument3,IHTMLDocument4和IHTMLDocument5接口(取决于使用的是IE的版本)。您应该转换为相应的接口,然后调用相应的方法。例如:
IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();
在罕见情况下自动化对象不支持的替代接口。那么你应该使用VB.Net来包装接口。有了严格的选项设置为关闭(仅用于包装类),可以使用VB的内置支持后期绑定调用只需调用覆盖下的适当IDispatch方法。在极少数情况下不寻常的参数类型,你可能需要摆弄了一下与呼叫,但在一般情况下,在VB中你可以做到这一点!即使在动态添加到C#V4 VB仍将可能有后期绑定COM调用显著更好的支持。
如果由于某种原因,你不能用VB来包装自动化接口,那么你仍然可以从C#中使用反射任何必要的调用。我不会去到任何细节,因为这个选项应该基本上都不使用,但这里是一个小例子,涉及办公自动化。
请参阅这篇文章: http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Internet Explorer中后期绑定自动化 通过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);