Compartilhar 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 do código nativo.

Descrição da alteração

Nas versões anteriores do .NET, alguns tipos de Windows Forms eram decorados como visíveis para 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 personalizadas prontas para uso. Além disso, o runtime do .NET não pode depender da biblioteca de tipos do .NET Framework (o que exigiria manter a forma das classes como estavam no .NET Framework).

Motivo da alteração

  • Remoção de ComVisible(true) das enumerações que foram usadas na geração e pesquisa da biblioteca de tipos (arquivo TLB): como não há nenhuma biblioteca TLB de WinForms fornecida pelo .NET Core, não há valor em manter esse atributo.
  • Remoção de ComVisible(true) das classes AccessibleObject: as classes não são CoCriáveis (não têm nenhum construtor sem parâmetros), e a exposição de uma instância já existente para o 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 WinForms por meio do OLE/ActiveX, por exemplo, no VB6 ou MFC. No entanto, isso requer um TLB para WinForms, que não é mais fornecido, bem como a ativação baseada em registro, que também não funcionaria de imediato. Em geral, não houve manutenção da hospedagem baseada em COM dos controles WinForms, portanto, o suporte foi removido em vez de deixá-lo em um estado sem suporte.
  • Remoção dos atributos de controles ClassInterface: caso não haja suporte para hospedagem via OLE/ActiveX, esses atributos não serão mais necessários. Eles são mantidos em outros locais em que os objetos ainda sã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 são CoCreateable, 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 alguns códigos não públicos: o único consumidor potencial seria o novo designer do Visual Studio, mas, sem um GUID especificado, é improvável que ainda sejam necessários.
  • 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 delas como ComVisible.
  • IWin32Window definiu o mesmo GUID que foi definido no .NET Framework, com 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, pois há uma declaração separada de interface ComImport para interoperabilidade IDataObject COM. É 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 do .NET Framework, portanto, é improvável que a remoção de uma IID não documentada afete negativamente os clientes.
  • Remoção de ComVisible(false): elas são colocadas 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 do .NET Framework, que permite que o JavaScript chame novamente para a subclasse de formulário por meio de 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:

  • Introduza um objeto declarado ObjectForScripting pelo usuário que dá suporte 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.