Compartilhar via


Solucionando problemas de interoperabilidade (Visual Basic)

Ao interoperar entre COM e o código gerenciado do .NET Framework, você poderá encontrar um ou mais dos seguintes problemas comuns.

Marshalling de interoperação

Às vezes, talvez seja necessário usar tipos de dados que não fazem parte do .NET Framework. Os assemblies de interoperabilidade lidam com a maior parte do trabalho para objetos COM, mas talvez seja necessário controlar os tipos de dados usados quando objetos gerenciados são expostos ao COM. Por exemplo, estruturas em bibliotecas de classes devem especificar o tipo BStr não gerenciado em cadeias de caracteres enviadas a objetos COM criados pelo Visual Basic 6.0 e versões anteriores. Nesses casos, você pode usar o atributo MarshalAsAttribute para fazer com que os tipos gerenciados sejam expostos como tipos não gerenciados.

Exportando cadeias de caracteres de comprimento fixo para código não gerenciado

No Visual Basic 6.0 e versões anteriores, as cadeias de caracteres são exportadas para objetos COM como sequências de bytes sem um caractere de encerramento nulo. Para compatibilidade com outras linguagens, o .NET do Visual Basic inclui um caractere de encerramento ao exportar cadeias de caracteres. A melhor maneira de resolver essa incompatibilidade é exportar cadeias de caracteres que não têm o caractere de encerramento como matrizes de Byte ou Char.

Exportar hierarquias de herança

Hierarquias de classe gerenciada se nivelam quando expostas como objetos COM. Por exemplo, se você definir uma classe base com um membro e herdar a classe base em uma classe derivada exposta como um objeto COM, os clientes que usam a classe derivada no objeto COM não poderão usar os membros herdados. Os membros da classe base podem ser acessados de objetos COM apenas como instâncias de uma classe base e, em seguida, somente se a classe base também for criada como um objeto COM.

Métodos sobrecarregados

Embora você possa criar métodos sobrecarregados com o Visual Basic, eles não têm suporte do COM. Quando uma classe que contém métodos sobrecarregados é exposta como um objeto COM, novos nomes de método são gerados para os métodos sobrecarregados.

Por exemplo, considere uma classe que tenha duas sobrecargas do método Synch. Quando a classe é exposta como um objeto COM, os novos nomes de método gerados podem ser Synch e Synch_2.

A renomeação pode causar dois problemas para os consumidores do objeto COM.

  1. Talvez os clientes não esperem os nomes de método gerados.

  2. Os nomes de método gerados na classe exposta como um objeto COM podem ser alterados quando novas sobrecargas são adicionadas à classe ou à classe base. Isso pode causar problemas de versão.

Para resolver ambos os problemas, dê a cada método um nome exclusivo, em vez de usar sobrecarga, quando você desenvolver objetos que serão expostos como objetos COM.

Uso de objetos COM por meio de assemblies de interoperabilidade

Você usa assemblies de interoperabilidade quase como se fossem substituições de código gerenciado para os objetos COM que eles representam. No entanto, como eles são wrappers e não objetos COM reais, há algumas diferenças entre o uso de assemblies de interoperabilidade e assemblies padrão. Essas áreas de diferença incluem a exposição de classes e tipos de dados para parâmetros e valores retornados.

Classes expostas como interfaces e classes

Ao contrário das classes em assemblies padrão, as classes COM são expostas em assemblies de interoperabilidade como uma interface e uma classe que representa a classe COM. O nome da interface é idêntico ao da classe COM. O nome da classe de interoperabilidade é o mesmo da classe COM original, mas com a palavra "Classe" acrescentada. Por exemplo, suponha que você tenha um projeto com uma referência a um assembly de interoperabilidade para um objeto COM. Se a classe COM for nomeada MyComClass, o IntelliSense e o Pesquisador de Objetos mostrarão uma interface nomeada MyComClass e uma classe chamada MyComClassClass.

Criando instâncias de uma classe .NET Framework

Geralmente, você cria uma instância de uma classe .NET Framework usando a instrução New com um nome de classe. Ter uma classe COM representada por um assembly de interoperabilidade é o único caso em que você pode usar a instrução New com uma interface. A menos que você esteja usando a classe COM com uma instrução Inherits, você pode usar a interface exatamente como faria com uma classe. O código a seguir demonstra como criar um objeto Command em um projeto que tenha uma referência ao objeto MICROSOFT ActiveX Data Objects 2.8 Library COM:

Dim cmd As New ADODB.Command

No entanto, se você estiver usando a classe COM como base para uma classe derivada, deverá usar a classe de interoperabilidade que representa a classe COM, como no seguinte código:

Class DerivedCommand
    Inherits ADODB.CommandClass
End Class

Observação

Assemblies de interoperabilidade implementam de modo implícito as interfaces que representam classes COM. Você não deve tentar usar a instrução Implements para implementar essas interfaces ou um erro resultará.

Tipos de dados para parâmetros e valores retornados

Ao contrário dos membros de assemblies padrão, os membros do assembly de interoperabilidade podem ter tipos de dados diferentes daqueles usados na declaração de objeto original. Embora os assemblies de interoperabilidade convertam implicitamente tipos COM em tipos de runtime de linguagem comuns compatíveis, você deve prestar atenção aos tipos de dados usados por ambos os lados para evitar erros de runtime. Por exemplo, em objetos COM criados no Visual Basic 6.0 e versões anteriores, os valores do tipo Integer assumem o tipo equivalente de .NET Framework, Short. É recomendável que você use o Pesquisador de Objetos para examinar as características dos membros importados antes de usá-los.

Métodos COM de nível de módulo

A maioria dos objetos COM é usada criando uma instância de uma classe COM usando a palavra-chave New e chamando métodos do objeto. Uma exceção a essa regra envolve objetos COM que contêm classes COM AppObj ou GlobalMultiUse. Essas classes se assemelham a métodos de nível de módulo em classes .NET do Visual Basic. O Visual Basic 6.0 e versões anteriores criam implicitamente instâncias desses objetos para você na primeira vez que você chama um de seus métodos. Por exemplo, no Visual Basic 6.0, você pode adicionar uma referência à Biblioteca de Objetos do Microsoft DAO 3.6 e chamar o método DBEngine sem primeiro criar uma instância:

Dim db As DAO.Database  
' Open the database.  
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")  
' Use the database object.  

O .NET do Visual Basic requer que você sempre crie instâncias de objetos COM antes de poder usar seus métodos. Para usar esses métodos no Visual Basic, declare uma variável da classe desejada e use a nova palavra-chave para atribuir o objeto à variável de objeto. A palavra-chave Shared pode ser usada quando você quiser garantir que apenas uma instância da classe seja criada.

' Class level variable.
Shared DBEngine As New DAO.DBEngine

Sub DAOOpenRecordset()
    Dim db As DAO.Database
    Dim rst As DAO.Recordset
    Dim fld As DAO.Field
    ' Open the database.
    db = DBEngine.OpenDatabase("C:\nwind.mdb")

    ' Open the Recordset.
    rst = db.OpenRecordset(
        "SELECT * FROM Customers WHERE Region = 'WA'",
        DAO.RecordsetTypeEnum.dbOpenForwardOnly,
        DAO.RecordsetOptionEnum.dbReadOnly)
    ' Print the values for the fields in the debug window.
    For Each fld In rst.Fields
        Debug.WriteLine(fld.Value.ToString & ";")
    Next
    Debug.WriteLine("")
    ' Close the Recordset.
    rst.Close()
End Sub

Erros sem tratamento em manipuladores de eventos

Um problema de interoperabilidade comum envolve erros em manipuladores de eventos que lidam com eventos gerados por objetos COM. Esses erros são ignorados, a menos que você verifique especificamente se há erros usando instruções On Error ou Try...Catch...Finally. Por exemplo, o exemplo a seguir é de um projeto .NET do Visual Basic que tem uma referência ao objeto COM da Biblioteca Microsoft ActiveX Data Objects 2.8.

' To use this example, add a reference to the
'     Microsoft ActiveX Data Objects 2.8 Library
' from the COM tab of the project references page.
Dim WithEvents cn As New ADODB.Connection
Sub ADODBConnect()
    cn.ConnectionString = "..."
    cn.Open()
    MsgBox(cn.ConnectionString)
End Sub

Private Sub Form1_Load(ByVal sender As System.Object,
    ByVal e As System.EventArgs) Handles MyBase.Load

    ADODBConnect()
End Sub

Private Sub cn_ConnectComplete(
    ByVal pError As ADODB.Error,
    ByRef adStatus As ADODB.EventStatusEnum,
    ByVal pConnection As ADODB.Connection) Handles cn.ConnectComplete

    '  This is the event handler for the cn_ConnectComplete event raised
    '  by the ADODB.Connection object when a database is opened.
    Dim x As Integer = 6
    Dim y As Integer = 0
    Try
        x = CInt(x / y) ' Attempt to divide by zero.
        ' This procedure would fail silently without exception handling.
    Catch ex As Exception
        MsgBox("There was an error: " & ex.Message)
    End Try
End Sub

Este exemplo gera um erro conforme o esperado. No entanto, se você tentar o mesmo exemplo sem o bloco Try...Catch...Finally, o erro será ignorado como se você tivesse usado a instrução OnError Resume Next. Sem tratamento de erros, a divisão por zero falha silenciosamente. Como esses erros nunca geram erros de exceção sem tratamento, é importante que você use alguma forma de tratamento de exceção em manipuladores de eventos que lidam com eventos de objetos COM.

Noções básicas sobre erros de interoperabilidade COM

Sem tratamento de erros, as chamadas de interoperabilidade geralmente geram erros que fornecem poucas informações. Sempre que possível, use o tratamento de erros estruturados para fornecer mais informações sobre problemas quando ocorrerem. Isso pode ser especialmente útil quando você depura aplicativos. Por exemplo:

Try
    ' Place call to COM object here.
Catch ex As Exception
    ' Display information about the failed call.
End Try

Você pode encontrar informações como a descrição do erro, HRESULT e a origem de erros COM examinando o conteúdo do objeto de exceção.

Problemas de controle ActiveX

A maioria dos controles ActiveX que funcionam com o Visual Basic 6.0 funcionam com o .NET do Visual Basic sem problemas. As principais exceções são controles de contêiner ou controles que contêm visualmente outros controles. Alguns exemplos de controles mais antigos que não funcionam corretamente com o Visual Studio são os seguintes:

  • Controle de quadro 2.0 do Microsoft Forms 2.0

  • Controle de baixo para cima, também conhecido como controle de giro

  • Controle da guia Sheridan

Há apenas algumas soluções alternativas para problemas de controle ActiveX sem suporte. Você pode migrar controles existentes para o Visual Studio se tiver o código-fonte original. Caso contrário, você pode verificar com fornecedores de software se há atualizações. Versões compatíveis com NET de controles para substituir controles ActiveX sem suporte.

Passando propriedades ReadOnly de Controles ByRef

O .NET do Visual Basic às vezes gera erros COM, como "Erro 0x800A017F CTL_E_SETNOTSUPPORTED", quando você passa propriedades ReadOnly de alguns controles ActiveX mais antigos como parâmetros ByRef para outros procedimentos. Chamadas de procedimento semelhantes do Visual Basic 6.0 não geram um erro e os parâmetros são tratados como se você as tivesse passado por valor. A mensagem de erro do .NET do Visual Basic indica que você está tentando alterar uma propriedade que não tem um procedimento de propriedade Set.

Se você tiver acesso ao procedimento que está sendo chamado, poderá evitar esse erro usando a palavra-chave ByVal para declarar parâmetros que aceitam propriedades ReadOnly. Por exemplo:

Sub ProcessParams(ByVal c As Object)
    'Use the arguments here.
End Sub

Se você não tiver acesso ao código-fonte do procedimento que está sendo chamado, poderá forçar a propriedade a ser passada pelo valor adicionando um conjunto extra de colchetes ao redor do procedimento de chamada. Por exemplo, em um projeto que tem uma referência ao objeto COM da Biblioteca de Microsoft ActiveX Data Objects 2.8, você pode usar:

Sub PassByVal(ByVal pError As ADODB.Error)
    ' The extra set of parentheses around the arguments
    ' forces them to be passed by value.
    ProcessParams((pError.Description))
End Sub

Implantando assemblies que expõem interoperabilidade

A implantação de assemblies que expõem interfaces COM apresenta alguns desafios exclusivos. Por exemplo, um problema potencial ocorre quando aplicativos separados fazem referência ao mesmo assembly COM. Essa situação é comum quando uma nova versão de um assembly é instalada e outro aplicativo ainda está usando a versão antiga do assembly. Se você desinstalar um assembly que compartilha uma DLL, poderá torná-lo indisponível sem querer para os outros assemblies.

Para evitar esse problema, você deve instalar assemblies compartilhados no GAC (Cache de Assembly Global) e usar um MergeModule para o componente. Se você não puder instalar o aplicativo no GAC, ele deverá ser instalado no CommonFilesFolder em um subdiretório específico da versão.

Os assemblies que não são compartilhados devem estar localizados lado a lado no diretório com o aplicativo de chamada.

Confira também