Share via


O código nativo não pode acessar objetos do Windows Forms

A partir do .NET 5, você não pode mais acessar objetos do Windows Forms a partir do código nativo.

Descrição das alterações

Em versões anteriores do .NET, alguns tipos do Windows Forms eram decorados como visíveis para a interoperabilidade COM e, portanto, eram acessíveis ao código nativo. A partir do .NET 5, nenhuma API do Windows Forms é visível para a interoperabilidade COM ou acessível ao código nativo. O runtime do .NET não dá mais suporte à criação de bibliotecas de tipos personalizados prontas para uso. Além disso, o runtime do .NET não pode depender da biblioteca de tipos para o .NET Framework (isso exigiria manter a forma das classes como estavam no .NET Framework).

Motivo da alteração

  • Remoção de ComVisible(true) das enumerações usadas para a geração e pesquisa de biblioteca de tipos (arquivo TLB): como não há nenhum TLB do WinForms fornecido pelo .NET Core, não há valor em manter esse atributo.
  • Remoção de ComVisible(true) de classes AccessibleObject: as classes não podem ser cocriadas (não têm construtor sem parâmetros), e expor uma instância já existente ao COM não requer esse atributo.
  • Remoção de ComVisible(true) das classes Control e Component: isso foi usado para permitir a hospedagem de controles do WinForms por meio do OLE/ActiveX, por exemplo, no VB6 ou MFC. No entanto, isso requer um TLB para o WinForms, que não é mais fornecido, bem como ativação baseada em registro, que também não funcionaria prontamente. Em geral, não houve manutenção da hospedagem baseada em COM de controles do WinForms; portanto, o suporte foi removido em vez de deixá-lo em um estado sem suporte.
  • Remoção de atributos ClassInterface de controles: se a hospedagem via OLE/ActiveX não tiver suporte, esses atributos não serão mais necessários. Eles são mantidos em outros locais onde os objetos ainda estão expostos ao COM e o atributo pode ser relevante.
  • Remoção de ComVisible(true) de EventArgs: eles provavelmente foram usados com a hospedagem OLE/ActiveX, que não tem mais suporte. Eles também não podem ser cocriados; portanto, o atributo não tem nenhuma finalidade. Além disso, expor instâncias existentes sem fornecer um TLB não faz sentido.
  • Remoção de delegados ComVisible(true): a finalidade é desconhecida, mas como a hospedagem ActiveX de controles do WinForms não tem mais suporte, é improvável que tenha qualquer utilidade.
  • Remoção de ComVisible(true) de algum código não público: o único consumidor em potencial seria o novo designer do Visual Studio, mas sem um GUID especificado é improvável que ele ainda seja necessário.
  • Remoção de ComVisible(true) de algumas classes arbitrárias de designer público: o antigo designer do Visual Studio pode ter usado a interoperabilidade COM para conversar com essas classes. No entanto, o designer antigo não dá suporte ao .NET Core, portanto, poucas pessoas precisariam deles como ComVisible.
  • IWin32Window definiu o mesmo GUID que foi definido em .NET Framework, o que traz consequências perigosas. Se você precisar de interoperabilidade com o .NET Framework, use ComImport.
  • O WinForms gerenciado IDataObject foi tornado ComVisible. Isso não é necessário; há uma declaração de interface separada ComImport para a interoperabilidade COM IDataObject. É contraproducente ter o IDataObject gerenciado como ComVisible, já que nenhum TLB é fornecido e realizar marshaling sempre falhará. Além disso, o GUID não foi especificado e difere de .NET Framework; portanto, é improvável que a remoção de uma IID não documentada afete negativamente os clientes.
  • Remoção de ComVisible(false): eles são colocados em locais aparentemente arbitrários e são redundantes quando o padrão é não expor classes à interoperabilidade COM.

Versão introduzida

.NET 5.0

O exemplo a seguir funciona no .NET Framework e no .NET Core 3.1. Este exemplo se baseia na biblioteca de tipos .NET Framework, que permite que o JavaScript retorne à subclasse do formulário por meio da reflexão.

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

Há duas maneiras possíveis de fazer o exemplo funcionar no .NET 5 e versões posteriores:

  • Introduzir um objeto declarado pelo usuário ObjectForScripting com suporte a IDispatch (que é aplicado por padrão, a menos que seja alterado explicitamente no nível do projeto).

    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);
    
            ...
        }
    }
    
  • Declarar uma interface com os métodos a serem expostos.

    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;
    
            ...
        }
    }
    

APIs afetadas

Todas as APIs do Windows Forms.