Машинный код не может получить доступ к объектам Windows Forms

Начиная с .NET 5 вы больше не можете получить доступ к объектам Windows Forms из машинного кода.

Описание изменения

В предыдущих версиях .NET некоторые типы Windows Forms были декорированы как видимые для COM-взаимодействия и поэтому были доступны для машинного кода. Начиная с .NET 5 API Windows Forms недоступны для COM-взаимодействия или машинного кода. Среда выполнения .NET больше не поддерживает создание пользовательских библиотек типов без дополнительной настройки. Кроме того, среда выполнения .NET не может зависеть от библиотеки типов для .NET Framework (что потребовало бы поддержания классов в том виде, в котором они предоставлялись в .NET Framework).

Причина изменения

  • ComVisible(true) Удаление из перечислений, которые использовались для создания и поиска библиотеки типов (TLB-файла): так как подсистема балансировки нагрузки WinForms, предоставляемая .NET Core, не имеет значения для сохранения этого атрибута.
  • Удаление из AccessibleObject классов: классы не являются CoCreateable (у них нет конструктора ComVisible(true) без параметров), и предоставление уже существующего экземпляра COM не требует этого атрибута.
  • ComVisible(true) Удаление из Control и Component классов: это было использовано для размещения элементов управления WinForms с помощью OLE/ActiveX, например в VB6 или MFC. Но для этого требуется TLB для WinForms (больше не предоставляется), а также активация на основе реестра (также не работает без дополнительной настройки). Как правило, обслуживание размещения элементов управления WinForms на основе COM не выполнялось, поэтому поддержка была удалена полностью, без перевода в неподдерживаемое состояние.
  • Удаление атрибутов 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 определяет тот же GUID, который был определен в .NET Framework, что имеет опасные последствия. Если вам нужно обеспечить взаимодействие с .NET Framework, используйте ComImport.
  • Управляемому объекту IDataObject WinForms присвоен атрибут ComVisible. Это необязательно, так как существует отдельное объявление интерфейса ComImport для COM-взаимодействия 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

Все API Windows Forms.