从 .NET 5 开始,你无法再从本机代码访问 Windows 窗体对象。
更改描述
在旧版 .NET 中,某些 Windows 窗体类型被修饰为对 COM 互操作可见,因此可通过本机代码访问。 从 .NET 5 开始,Windows 窗体 API 对 COM 互操作不可见或不可通过本机代码访问。 .NET 运行时不再支持现装创建自定义类型库。 此外,.NET 运行时不能依赖于 .NET Framework 的类型库(因为这需要保持类在 .NET Framework 中的原有结构)。
更改原因
- 删除枚举中用于类型库(TLB 文件)生成和查找的
ComVisible(true):由于 .NET Core 不提供 WinForms TLB,因此保留此属性没有意义。 - 从
ComVisible(true)类中移除AccessibleObject:这些类不可共同创建(它们没有无参数构造函数),并且向 COM 公开已存在的实例不需要该属性。 - 从
ComVisible(true)和Control类中删除Component:这用于允许通过 OLE/ActiveX 托管 WinForms 控件,例如在 VB6 或 MFC 中。 但是,这需要用于 WinForms 的 TLB (不再提供),以及基于注册表的激活(这也不能开箱即用)。 通常,没有对基于 COM 的 WinForms 控件托管进行维护,因此删除了支持,而不是使其处于不受支持的状态。 -
ClassInterface从控件中删除属性:如果不支持通过 OLE/ActiveX 进行托管,则不再需要这些属性。 它们保留在对象仍向 COM 公开的其他位置,并且该属性可能相关。 - 从
ComVisible(true)中删除EventArgs:它们很可能曾用于 OLE/ActiveX 托管,而这种托管不再受支持。 它们也不能进行共同创建,因此该属性没有意义。 此外,在不提供 TLB 的情况下公开现有实例毫无意义。 - 从委托中删除
ComVisible(true):具体目的不详,但由于不再支持使用 ActiveX 托管 WinForms 控件,因此其作用可能不大。 - 从某些非公共代码中删除
ComVisible(true):唯一的潜在使用者是新的 Visual Studio 设计器,但如果没有指定 GUID,就不太可能仍然需要它。 -
ComVisible(true)从一些任意公共设计器类中删除:旧的 Visual Studio 设计器可能一直在使用 COM 互作来与这些类通信。 然而,旧版设计器不支持 .NET Core,所以很少有人会需要将这些设置为ComVisible。 -
IWin32Window定义了在 .NET Framework 中定义的相同 GUID,这会产生危险后果。 如果需要与 .NET Framework 互作,请使用ComImport。 - WinForms 管理的
IDataObject被设置为ComVisible。 这不是必需的,对于ComImportCOM 互操作,有一个单独的IDataObject接口声明。 将管理的IDataObject设置为ComVisible会适得其反,因为没有提供 TLB,封送处理总是会失败。 此外,GUID 未指定,与 .NET Framework 不同,因此删除未记录的 IID 不太可能对客户产生负面影响。 - 删除
ComVisible(false):当默认不向 COM 互操作公开类时,它们被放置在看似任意的位置上,并且是多余的。
已引入的版本
.NET 5.0
建议的措施
以下示例适用于 .NET Framework 和 .NET Core 3.1。 此示例依赖于 .NET Framework 类型库,该库允许 JavaScript 通过反射调用回表单子类。
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class Form1 : Form
{
private WebBrowser webBrowser1 = new WebBrowser();
protected override void OnLoad(EventArgs e)
{
webBrowser1.AllowWebBrowserDrop = false;
webBrowser1.IsWebBrowserContextMenuEnabled = false;
webBrowser1.WebBrowserShortcutsEnabled = false;
webBrowser1.ObjectForScripting = this;
webBrowser1.DocumentText =
"<html><body><button " +
"onclick=\"window.external.Test('called from script code')\">" +
"call client code from script code</button>" +
"</body></html>";
}
public void Test(String message)
{
MessageBox.Show(message, "client code");
}
}
可通过两种可能的方法使示例适用于 .NET 5 和更高版本:
引入一个用户声明的
ObjectForScripting对象,该对象支持IDispatch(默认情况下应用,除非在项目级别显式更改)。public class MyScriptObject { private Form1 _form; public MyScriptObject(Form1 form) { _form = form; } public void Test(string message) { MessageBox.Show(message, "client code"); } } public partial class Form1 : Form { protected override void OnLoad(EventArgs e) { ... // Works correctly. webBrowser1.ObjectForScripting = new MyScriptObject(this); ... } }声明一个接口,并定义要公开的方法。
public interface IForm1 { void Test(string message); } [ComDefaultInterface(typeof(IForm1))] public partial class Form1 : Form, IForm1 { protected override void OnLoad(EventArgs e) { ... // Works correctly. webBrowser1.ObjectForScripting = this; ... } }
受影响的 API
所有 Windows 窗体 API。