.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 翻译,其相关部分是:
[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 时)
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
可能的场景 1:最终目标是简单地向最终用户显示 HTML 并使用 Windows 窗体
System.Windows.Forms.WebBrowser
是您尝试手动实现的界面的极其简单的 .NET 包装器。 要获取它,请将该对象的实例从工具栏(在“所有 Windows 窗体”部分下列为“Web 浏览器”)拖放到表单上。 然后,在一些合适的事件处理程序上:在我的测试应用程序上,这正确地显示了我们都学会恐惧和厌恶的令人难以忘怀的消息。
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:On my test app, this correctly displayed the haunting message we all have learned to fear and loath.
在 .NET 中调用后期绑定 IDispatch 相对容易,尽管很糟糕:
最初说“在 C# 4.0 之前不可能”的引用 SO 问题已被修改,以显示它在 .NET 2.0 中是如何可能的。
C# .NET 支持 IDispatch 后期绑定吗?
Late bound IDispatch called is relativly easy in .NET, although piss-poor:
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?
您链接到的帖子中的答案实际上是不正确的。 在 .Net 中处理基于 IDispatch 的对象通常非常容易。 基本上,您会经历三个步骤:
大多数作为 IDispatch 接口公开的自动化对象(可能超过 90%)都具有可由非脚本类型 COM 客户端使用的其他接口(IDispatch 接口实际上是派生自的完整 COM 接口) IDispatch 或对象支持一个或多个其他 IUnknown 派生接口)。 在这种情况下,您只需导入适当的 COM 接口定义,然后将对象转换为适当的接口。 该转换在幕后调用 QueryInterface 并返回对所需接口的包装引用。
这是您在上面介绍的场景中将使用的技术。 从 IE 自动化对象返回的 Document 对象支持 IHTMLDocument、IHTMLDocument2、IHTMLDocument3、IHTMLDocument4 和 IHTMLDocument5 接口(取决于您使用的 IE 版本)。 您应该转换为适当的接口,然后调用适当的方法。 例如:
在极少数情况下,自动化对象不支持替代接口。 那么您应该使用 VB.Net 来包装该接口。 将 Option Strict 设置为关闭(仅适用于包装类)时,您可以使用 VB 对后期绑定调用的内置支持来简单地在幕后调用适当的 IDispatch 方法。 在极少数情况下,如果参数类型不寻常,您可能需要对调用进行一些修改,但一般来说,在 VB 中您可以这样做! 即使对 C# v4 进行了动态添加,VB 仍可能对后期绑定 COM 调用提供更好的支持。
如果由于某种原因您无法使用 VB 来包装自动化接口,那么您仍然可以使用反射从 C# 进行任何必要的调用。 我不会详细介绍任何细节,因为基本上永远不应该使用此选项,但这里有一个涉及 Office 的 小示例自动化。
The answers in the post that you link to are actually incorrect. It is generally very easy to deal with IDispatch based objects in .Net. Basically you go through three steps:
Most automation objects (probably well over 90%) that are exposed as IDispatch interfaces have other interfaces that can be used by non-scripting type COM clients (either the IDispatch interface is actually a full COM interface derived from IDispatch or the object supports one or more other IUnknown derived interfaces). In this case, you would simply import the appropriate COM interface definition and then cast the object to the appropriate interface. The cast calls QueryInterface under the covers and returns a wrapped reference to the interface you want.
This is the technique you would use in the scenario that you presented above. The Document object that is returned from the IE automation object supports the IHTMLDocument, IHTMLDocument2, IHTMLDocument3, IHTMLDocument4 and IHTMLDocument5 interfaces (depending on the version of IE you are using). You should cast to the appropriate interface and then call the appropriate method. For example:
In the rare case where the automation object does not support an alternative interface. Then you should use VB.Net to wrap that interface. With Option Strict set to off (for the wrapper class only) you can use VB's built in support for late bound calls to simply call the appropriate IDispatch methods under the covers. In rare cases with unusual argument types you may need to fiddle a bit with the call but, in general, in VB you can just do it! Even with the dynamic additions to C# v4 VB will still probably have significantly better support for late-bound COM calls.
If for some reason you can't use VB to wrap the automation interface then you can still make any necessary calls from C# using reflection. I won't go into any details since this option should basically never be used but here is a small example involving Office automation.
请参阅这篇文章:
http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Internet Explorer 后期绑定自动化
由 yincekara
Internet Explorer 自动化示例代码使用后期绑定,无需 Microsoft.mshtml 和 shdocvw 依赖。
对于 htmlDoc.write(htmlString);
调整
See this article :
http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Internet Explorer Late Binding Automation
By yincekara
Internet Explorer automation sample code using late binding, without Microsoft.mshtml and shdocvw dependency.
for htmlDoc.write(htmlString);
modify