Partilhar via


Solução de problemas de interoperabilidade (Visual Basic)

Quando você interopera entre COM e o código gerenciado do .NET Framework, você pode encontrar um ou mais dos seguintes problemas comuns.

Interoperabilidade de Marshalling

À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 a COM. Por exemplo, estruturas em bibliotecas de classe devem especificar o tipo BStr não gerenciado em cadeias de caracteres enviadas para 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 Fixed-Length para código não gerenciado

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

Exportando hierarquias de herança

As hierarquias de classes geridas são aplanadas quando expostas como objetos COM. Por exemplo, se você definir uma classe base com um membro e, em seguida, herdar a classe base em uma classe derivada que é 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 a partir de objetos COM somente 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 são suportados pelo 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. Os clientes podem não esperar os nomes de método gerados.

  2. Os nomes de método gerados na classe exposta como um objeto COM podem mudar quando novas sobrecargas são adicionadas à classe ou sua classe base. Isso pode causar problemas de controlo 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 através de assemblies de interoperabilidade

Você usa assemblies de interoperabilidade quase como se fossem substituições de código gerenciado para os objetos COM que 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 de retorno.

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 "Class" anexada. Por exemplo, suponha que tenha um projeto com referência a uma biblioteca de interoperabilidade para um objeto COM. Se a classe COM for denominada MyComClass, o IntelliSense e o Pesquisador de Objetos mostrarão uma interface chamada MyComClass e uma classe chamada MyComClassClass.

Criando instâncias de uma classe do .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 da mesma forma que faria com uma classe. O código a seguir demonstra como criar um objeto Command em um projeto que tem uma referência ao objeto COM da biblioteca Microsoft ActiveX Data Objects 2.8:

Dim cmd As New ADODB.Command

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

Class DerivedCommand
    Inherits ADODB.CommandClass
End Class

Observação

Os assemblies de interoperabilidade implementam implicitamente interfaces que representam classes COM. Você não deve tentar usar a instrução Implements para implementar essas interfaces ou ocorrerá um erro.

Tipos de dados para parâmetros e valores de retorno

Ao contrário dos membros de um assembly padrão, os membros de um assembly de interoperabilidade podem ter tipos de dados diferentes daqueles usados nas declarações dos objetos originais. Embora os assemblies de interoperabilidade convertam implicitamente tipos COM em tipos compatíveis com o ambiente de execução comum, deve prestar atenção aos tipos de dados utilizados por ambos os lados para evitar erros de execução. Por exemplo, em objetos COM criados no Visual Basic 6.0 e versões anteriores, os valores do tipo Integer assumem o tipo equivalente do .NET Framework, Short. É recomendável usar 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 são usados criando uma instância de uma classe COM usando a palavra-chave New e, em seguida, chamando métodos do objeto. Uma exceção a essa regra envolve objetos COM que contêm AppObj ou GlobalMultiUse classes COM. Essas classes se assemelham a métodos de nível de módulo em classes do Visual Basic .NET. Visual Basic 6.0 e versões anteriores implicitamente criam instâncias de tais objetos para si na primeira vez que chamar um dos 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.

Visual Basic .NET requer que você sempre criar 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ê deseja 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 não tratados em manipuladores de eventos

Um problema comum de interoperabilidade envolve erros em manipuladores de eventos que manipulam 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 Visual Basic .NET 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 não tratados, é importante que você use alguma forma de tratamento de exceção em manipuladores de eventos que manipulam eventos de objetos COM.

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

Sem gestão de erros, as chamadas de interoperabilidade geralmente geram erros que oferecem pouca informação. Sempre que possível, use o tratamento de erros estruturados para fornecer mais informações sobre problemas quando eles ocorrerem. Isso pode ser especialmente útil quando se despista aplicações. 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 dos 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 Visual Basic .NET 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 do Microsoft Forms 2.0

  • Up-Down controle, também conhecido como controle de rotação

  • Controle de guia Sheridan

Existem apenas algumas soluções alternativas para problemas de controlo ActiveX não suportados. Você pode migrar controles existentes para o Visual Studio se você possuir o código-fonte original. Caso contrário, pode verificar junto aos fornecedores de software por versões de controlos compatíveis com .NET para substituir controlos ActiveX não suportados.

Passando propriedades ReadOnly de controles ByRef

Visual Basic às vezes gera erros COM, como "Erro 0x800A017F CTL_E_SETNOTSUPPORTED", quando você passa ReadOnly propriedades 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ê os passasse por valor. A mensagem de erro do Visual Basic .NET indica que está a tentar 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 Microsoft ActiveX Data Objects 2.8 Library COM, 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 a 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, você pode involuntariamente torná-lo indisponível para os outros assemblies.

Para evitar esse problema, você deve instalar assemblies compartilhados no GAC (Global Assembly Cache) e usar um MergeModule para o componente. Se você não pode instalar o aplicativo no GAC, ele deve ser instalado em 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.

Ver também