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 étaient décorés comme étant visibles par COM Interop, et étaient donc accessibles au code natif. À compter de .NET 5, aucune API Windows Forms n’est visible pour COM Interop 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 (ce qui nécessiterait la maintenance de la forme des classes comme elles étaient dans .NET Framework).

Raison du changement

  • Suppression de 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 fichiers TLB WinForms fournis par .NET Core, conserver cet attribut n’a pas d’intérêt.
  • Suppression de ComVisible(true) de classes AccessibleObject : les classes ne sont pas cocréables (elles n’ont aucun constructeur sans paramètre) et l’exposition d’une instance déjà existante à COM ne nécessite pas cet attribut.
  • Suppression de ComVisible(true) des classes Control et Component : cette option était utilisée pour autoriser l’hébergement de contrôles WinForms via OLE/ActiveX, par exemple dans VB6 ou MFC. Toutefois, cela nécessite un fichier TLB pour WinForms, qui n’est plus fourni, ainsi que l’activation basée sur le Registre, qui ne fonctionnerait pas non plus telle quelle. 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. Ils sont conservés 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 cocréables non plus, de sorte que l’attribut n’a aucun but. En outre, l’exposition d’instances existantes sans fournir de fichier 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) d’un code non public : 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 concepteur public arbitraires : l’ancien concepteur Visual Studio a peut-être utilisé COM Interop pour communiquer 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 définissait le même GUID que celui qui était défini dans .NET Framework, ce 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 ComImport distincte pour IDataObject COM Interop. 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’était pas spécifié et différait 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) : ces éléments sont placés dans des endroits apparemment arbitraires et sont redondants 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 :

  • Introduisez un objet ObjectForScripting déclaré par l’utilisateur qui prend en charge IDispatch (appliqué par défaut, sauf si 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.