在我的上一个开发环境中,我能够轻松地与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 translation,其相关部分是:
[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?
使用C#中的IDispatch快速搜索Stack Overflow会显示this post,说明我想要的东西在.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时)
答案 0 :(得分: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>");
在我的测试应用程序中,这正确地显示了我们都学会了恐惧和厌恶的令人难忘的消息。
答案 1 :(得分:3)
在.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);
}
最初说“在C#4.0之前不可能”的引用的SO问题被修改以显示在.NET 2.0中它是如何实现的。
答案 2 :(得分: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设置为off(仅适用于包装类),您可以使用VB内置的对后期绑定调用的支持,只需调用相应的IDispatch方法。在极少数情况下使用不常见的参数类型时,您可能需要对调用进行一些调整,但通常情况下,在VB中您可以执行此操作!即使动态添加到C#v4,VB仍然可能对后期绑定的COM调用有更好的支持。
如果由于某种原因您不能使用VB来包装自动化界面,那么您仍然可以使用反射从C#进行任何必要的调用。我不会详细说明,因为这个选项基本上不会被使用,但这里是small example involving Office automation。
答案 3 :(得分:0)
看到这篇文章: 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);