機器碼無法存取 Windows Forms 物件
從 .NET 5 開始,您無法再從機器碼中存取 Windows Forms 物件。
變更描述
在舊版 .NET 版本中,某些 Windows Forms 型別已被修飾為對 COM Interop 是可見的,因此機器碼可以進行存取。 從 .NET 5 開始,Windows Forms API 對 COM Interop 是不可見的,或無法從機器碼存取。 .NET 執行階段不再支援立即建立自訂型別程式庫。 而且,.NET 執行階段不能相依於 .NET Framework 的型別程式庫 (這會需要維持類別的圖形,就像它們在 .NET Framework 中一樣)。
變更原因
- 從用於型別程式庫 (TLB 檔案) 產生和查閱的列舉中移除
ComVisible(true)
:因為 .NET Core 沒未提供 WinForms TLB,所以保留此屬性沒有任何價值。 - 從
AccessibleObject
類別中移除ComVisible(true)
:類別不是 CoCreateable (它們沒有無參數建構函式),而且向 COM 公開已存在的執行個體不需要該屬性。 - 從
Control
和Component
中移除ComVisible(true)
:這是用來允許透過 OLE/ActiveX 裝載 WinForms 控制項,例如在 VB6 或 MFC 中。 但是,這需要一個適用於 WinForms 的 TLB (不再提供),以及登錄型的啟用 (這也無法立即運作)。 通常,不會維護以 COM 為基礎的 WinForms 控制項裝載,因此移除了支援,而不是讓它處於不受支援的狀態。 - 從控制項中移除
ClassInterface
屬性:如果不支援透過 OLE/ActiveX 進行裝載,則不再需要這些屬性。 它們會保留在物件仍公開給 COM 且屬性可能相關的其他位置中。 - 從
EventArgs
中移除ComVisible(true)
:它們最有可能與不再受支援的 OLE/ActiveX 裝載搭配使用。 它們也不是 CoCreateable,因此該屬性沒有任何用途。 而且,在不提供 TLB 的情況下公開現有的執行個體是沒有意義的。 - 從委派中移除
ComVisible(true)
:用途不明,但因為不再支援 WinForms 控制項的 ActiveX 裝載,所以不太可能有任何實用性。 - 從某個非公用程式代碼中移除
ComVisible(true)
:唯一可能的取用者是新的 Visual Studio 設計工具,但若未指定 GUID,就不太可能仍需要它。 - 從某些任意公用設計工具類別中移除
ComVisible(true)
:舊的 Visual Studio 設計工具可能已正在使用 COM Interop 來與這些類別交談。 但是,因為舊的設計工具不支援 .NET Core,所以很少有人會需要這些做為ComVisible
。 IWin32Window
定義了在 .NET Framework 中定義的相同 GUID,這會產生危險的後果。 如果您需要與 .NET Framework 相互運作,請使用ComImport
。- WinForms 受控的
IDataObject
變成了ComVisible
。 這不是必要的,IDataObject
COM interop 有一個個別的ComImport
介面宣告。 將受控的IDataObject
設為ComVisible
會適得其反,因為沒有提供 TLB,封送處理一律會失敗。 而且,GUID 未指定且不同於 .NET Framework,因此移除未記載的 IID 不太可能會對客戶產生負面的影響。 - 移除
ComVisible(false)
:這些會被放在看似任意的位置,而且在預設不向 COM Interop 公開類別時是多餘的。
導入的版本
.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 和更新版本上運作:
引入支援
IDispatch
的使用者宣告的ObjectForScripting
物件 (依預設會套用,除非在專案層級明確變更)。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 Forms API。