Partager via


Le code natif ne peut pas accéder aux objets Windows Forms

À compter de .NET 5, vous ne pouvez plus accéder aux objets Windows Forms à partir du code natif.

Description de la modification

Dans les versions précédentes de .NET, certains types Windows Forms ont été décorés comme visibles par COM Interop, et ont donc été accessibles au code natif. À compter de .NET 5, aucune API Windows Forms n’est visible pour l’interopérabilité COM ou accessible au code natif. Le runtime .NET ne prend plus en charge la création de bibliothèques de types personnalisées prêtes à l’emploi. En outre, le runtime .NET ne peut pas dépendre de la bibliothèque de types pour .NET Framework (qui nécessiterait la maintenance de la forme des classes comme elles étaient dans .NET Framework).

Raison de la modification

  • Suppression de l’attribut ComVisible(true) des énumérations utilisées pour la génération et la recherche de bibliothèque de types (fichier TLB) : étant donné qu’il n’existe pas de bibliothèque de types WinForms fournie par .NET Core, la conservation de cet attribut n’a aucune valeur.
  • Suppression de ComVisible(true) des classes AccessibleObject : les classes ne peuvent pas être créées par CoCreate (elles n'ont pas de constructeur sans paramètre), et exposer une instance déjà existante à COM ne nécessite pas cet attribut.
  • Suppression de ComVisible(true) des classes Control et Component : cela a été utilisé pour autoriser l’hébergement de contrôles WinForms via OLE/ActiveX, par exemple dans VB6 ou MFC. Toutefois, cela nécessite un TLB pour WinForms, qui n'est plus fourni, ainsi qu'une activation basée sur le registre, qui ne fonctionnerait pas automatiquement. En règle générale, il n’y a pas eu de maintenance de l’hébergement COM des contrôles WinForms. La prise en charge a donc été supprimée au lieu de la laisser dans un état non pris en charge.
  • Suppression des attributs ClassInterface des contrôles : si l’hébergement via OLE/ActiveX n’est pas pris en charge, ces attributs ne sont plus nécessaires. Elles sont conservées dans d’autres endroits où les objets sont toujours exposés à COM et l’attribut peut être pertinent.
  • Suppression de ComVisible(true) de EventArgs : ils étaient probablement utilisés avec l’hébergement OLE/ActiveX, qui n’est plus pris en charge. Ils ne sont pas CoCreateable non plus, donc l’attribut n’a aucun but. En outre, l’exposition d’instances existantes sans fournir un TLB n’a aucun sens.
  • Suppression de ComVisible(true) des délégués : l’objectif est inconnu, mais étant donné que l’hébergement ActiveX des contrôles WinForms n’est plus pris en charge, il est peu probable qu’il ait une utilité.
  • Suppression de ComVisible(true) dans certains codes non publics : le seul consommateur potentiel serait le nouveau concepteur Visual Studio, mais sans GUID spécifié, il est peu probable qu’il soit toujours nécessaire.
  • Suppression de ComVisible(true) de certaines classes de concepteurs publics arbitraires : l’ancien concepteur de Visual Studio a peut-être utilisé l’interopérabilité COM pour interagir avec ces classes. Toutefois, l’ancien concepteur ne prend pas en charge .NET Core, donc peu de personnes en auraient besoin en tant que ComVisible.
  • IWin32Window a défini le même GUID qui a été défini dans .NET Framework, qui a des conséquences dangereuses. Si vous avez besoin d’interopérabilité avec .NET Framework, utilisez ComImport.
  • Le IDataObject WinForms managé a été rendu ComVisible. Cela n’est pas obligatoire, il existe une déclaration d’interface distincte ComImport pour IDataObject l’interopérabilité COM. Il est contre-productif que le IDataObject managé soit ComVisible, car aucun TLB n’est fourni et le marshalling échouera toujours. En outre, le GUID n’a pas été spécifié et diffère de .NET Framework. Par conséquent, il est peu probable que la suppression d’un IID non documenté affecte négativement les clients.
  • Suppression de ComVisible(false): celles-ci sont placées dans des emplacements apparemment arbitraires et sont redondantes lorsque la valeur par défaut consiste à ne pas exposer les classes à COM Interop.

Version introduite

.NET 5.0

L’exemple suivant fonctionne sur .NET Framework et .NET Core 3.1. Cet exemple s’appuie sur la bibliothèque de types .NET Framework, qui permet au JavaScript d’effectuer un rappel dans la sous-classe de formulaire via la réflexion.

[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");
    }
}

Il existe deux façons de faire en sorte que l’exemple fonctionne sur .NET 5 et les versions ultérieures :

  • Introduire un objet ObjectForScripting déclaré par l'utilisateur qui supporte IDispatch (appliqué par défaut, à moins qu'il soit modifié explicitement au niveau du projet).

    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);
    
            ...
        }
    }
    
  • Déclarez une interface avec les méthodes à exposer.

    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 affectées

Toutes les API Windows Forms.