.NET2.0 C# 互操作:如何从 C# 调用 COM 代码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/469802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
.NET2.0 C# Interop: How to call COM code from C#?
提问by Ian Boyd
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):
在我上一个开发环境中,我能够轻松地与 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;
}
Now begins the tedious, painful, process of trying to interop with COM from managed code.
现在开始尝试从托管代码与 COM 互操作的乏味、痛苦的过程。
PInvoke.net already contains the IWebBrower2 translation, the relavent porition of which is:
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; }
}
I've created the COM class:
我创建了 COM 类:
[ComImport]
[Guid("0002DF01-0000-0000-C000-000000000046")]
public class InternetExplorer
{
}
So now it's time for my actual C# transaction:
所以现在是我实际的 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;
}
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.
细心的读者会注意到 IWebBrowser2.Document 是一个后期绑定的 IDispatch。我们在我们和我们客户的机器上使用 Visual Studio 2005 和 .NET 2.0。
So what's the .NET 2.0 method to invoke methods on an object that, on some level, only supports late-bound IDispatch?
那么 .NET 2.0 方法是什么来调用对象上的方法,在某种程度上,该对象仅支持后期绑定 IDispatch?
A quick search of Stack Overflow for using IDispatch from C# turns up this postsaying what I want is not possible in .NET.
快速搜索 Stack Overflow 以使用 C# 中的 IDispatch 会发现这篇文章说我想要的东西在 .NET 中是不可能的。
So is it possible to use COM from C# .NET 2.0?
那么是否可以使用 C# .NET 2.0 中的 COM?
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.
问题是我想在 C#/.NET 中使用一个公认的设计模式。它涉及在进程外启动 Internet Explorer,并为其提供 HTML 内容,同时不使用临时文件。
A rejected design idea is hosting Internet Explorer on a WinForm.
一个被拒绝的设计想法是在 WinForm 上托管 Internet Explorer。
An acceptable alternative is launching the system registered web browser, giving it HTML to display, without using a temporary file.
一种可接受的替代方法是启动系统注册的 Web 浏览器,使其显示 HTML,而不使用临时文件。
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)
在 .NET 世界中继续使用 COM 对象是绊脚石。具体问题涉及在不需要 C# 4.0 的情况下执行对 IDispatch 的后期绑定调用。(即使用 .NET 2.0 时)
采纳答案by Ian Boyd
Late bound IDispatch called is relativly easy in .NET, although piss-poor:
在 .NET 中调用后期绑定的 IDispatch 相对容易,尽管 piss-poor:
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);
}
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.
引用的 SO 问题最初说“在 C# 4.0 之前不可能”被修改以显示它在 .NET 2.0 中是如何可能的。
回答by el2iot2
Update:Based on question updates, I have removed the portions of my answer that are no longer relevant to the question. However, in case other readers are looking for a quick and dirty way to generate HTML in a winforms app and do not require an in-process IE, I will leave the following:
更新:根据问题更新,我删除了答案中不再与问题相关的部分。但是,如果其他读者正在寻找一种在 winforms 应用程序中生成 HTML 的快速而肮脏的方法,并且不需要进程内 IE,我将保留以下内容:
Possible Scenario 1: The ultimate goal is to simply display HTML to your end user and are using Windows Forms
可能的场景 1:最终目标是简单地向最终用户显示 HTML 并使用 Windows 窗体
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:
System.Windows.Forms.WebBrowser
是您尝试手动实现的接口的极其简单的 .NET 包装器。要获得它,请将该对象的一个实例从您的工具栏(在“所有 Windows 窗体”部分下列为“Web 浏览器”)拖放到您的窗体上。然后,在一些合适的事件处理程序上:
webBrowser1.Navigate("about:blank");
webBrowser1.Document.Write("<html><body>Hello World</body></html>");
On my test app, this correctly displayed the haunting message we all have learned to fear and loath.
在我的测试应用程序中,这正确地显示了我们都学会害怕和厌恶的令人难以忘怀的信息。
回答by Stephen Martin
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:
您链接到的帖子中的答案实际上是不正确的。在 .Net 中处理基于 IDispatch 的对象通常很容易。基本上你会经历三个步骤:
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.
大多数作为 IDispatch 接口公开的自动化对象(可能超过 90%)具有可由非脚本类型 COM 客户端使用的其他接口(IDispatch 接口实际上是从 IDispatch 派生的完整 COM 接口,或者对象支持一个或更多其他 IUnknown 派生接口)。在这种情况下,您只需导入适当的 COM 接口定义,然后将对象转换为适当的接口。演员表在幕后调用 QueryInterface 并返回对所需接口的包装引用。
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:
这是您将在上面介绍的场景中使用的技术。从 IE 自动化对象返回的 Document 对象支持 IHTMLDocument、IHTMLDocument2、IHTMLDocument3、IHTMLDocument4 和 IHTMLDocument5 接口(取决于您使用的 IE 版本)。您应该转换到适当的接口,然后调用适当的方法。例如:
IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();
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.
在自动化对象不支持替代接口的极少数情况下。那么你应该使用 VB.Net 来包装该接口。将 Option Strict 设置为关闭(仅适用于包装类),您可以使用 VB 对后期绑定调用的内置支持来简单地调用适当的 IDispatch 方法。在具有不寻常参数类型的极少数情况下,您可能需要对调用进行一些调整,但通常,在 VB 中您可以做到!即使对 C# v4 进行了动态添加,VB 仍可能对后期绑定 COM 调用提供更好的支持。
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.
如果由于某种原因您不能使用 VB 来包装自动化接口,那么您仍然可以使用反射从 C# 进行任何必要的调用。我不会讨论任何细节,因为这个选项基本上永远不会被使用,但这里有一个涉及办公自动化的小例子。
回答by Ian Boyd
See this article : http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
见这篇文章:http: //www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Internet Explorer Late Binding Automation By yincekara
yincekara 的 Internet Explorer 后期绑定自动化
Internet Explorer automation sample code using late binding, without Microsoft.mshtml and shdocvw dependency.
使用后期绑定的 Internet Explorer 自动化示例代码,不依赖 Microsoft.mshtml 和 shdocvw。
for htmlDoc.write(htmlString); modify
对于 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);