Resolução de Problemas de Interoperabilidade (Visual Basic)
Quando interopera entre COM e o código gerido do .NET Framework, poderá encontrar um ou mais dos seguintes problemas comuns.
Interop Marshalling
Por vezes, poderá ter de utilizar tipos de dados que não fazem parte do .NET Framework. As assemblagens interop processam a maior parte do trabalho para objetos COM, mas poderá ter de controlar os tipos de dados que são utilizados quando os objetos geridos são expostos ao COM. Por exemplo, as estruturas nas bibliotecas de classes têm de especificar o BStr
tipo não gerido em cadeias enviadas para objetos COM criados pelo Visual Basic 6.0 e versões anteriores. Nesses casos, pode utilizar o MarshalAsAttribute atributo para fazer com que os tipos geridos sejam expostos como tipos não geridos.
Exportar cadeias de Fixed-Length para código não gerido
No Visual Basic 6.0 e versões anteriores, as cadeias são exportadas para objetos COM como sequências de bytes sem um caráter de terminação nulo. Para compatibilidade com outros idiomas, o Visual Basic .NET inclui um caráter de terminação ao exportar cadeias. A melhor forma de resolver esta incompatibilidade é exportar cadeias que não têm o caráter de terminação como matrizes de Byte
ou Char
.
Exportar Hierarquias de Herança
As hierarquias de classe gerida aplanam-se quando expostas como objetos COM. Por exemplo, se definir uma classe base com um membro e, em seguida, herdar a classe base numa classe derivada que é exposta como um objeto COM, os clientes que utilizam a classe derivada no objeto COM não poderão utilizar os membros herdados. Os membros da classe base só podem ser acedidos a partir de objetos COM como instâncias de uma classe base e, em seguida, apenas se a classe base também for criada como um objeto COM.
Métodos Sobrecarregados
Embora possa criar métodos sobrecarregados com o Visual Basic, estes não são suportados pelo COM. Quando uma classe que contém métodos sobrecarregados é exposta como um objeto COM, são gerados novos nomes de métodos para os métodos sobrecarregados.
Por exemplo, considere uma classe que tenha duas sobrecargas do Synch
método. Quando a classe é exposta como um objeto COM, os novos nomes de métodos gerados podem ser Synch
e Synch_2
.
O nome pode causar dois problemas aos consumidores do objeto COM.
Os clientes podem não esperar os nomes dos métodos gerados.
Os nomes dos métodos gerados na classe exposta como um objeto COM podem ser alterados quando são adicionadas novas sobrecargas à classe ou à respetiva classe base. Isto pode causar problemas de controlo de versões.
Para resolver ambos os problemas, atribua a cada método um nome exclusivo, em vez de utilizar a sobrecarga, quando desenvolver objetos que serão expostos como objetos COM.
Utilização de Objetos COM através de Assemblagens Interop
Utiliza assemblagens interop quase como se fossem substituições de código geridas para os objetos COM que representam. No entanto, como são wrappers e não objetos COM reais, existem algumas diferenças entre a utilização de assemblagens interop e assemblagens padrão. Estas áreas de diferença incluem a exposição de classes e tipos de dados para parâmetros e valores devolvidos.
Classes Expostas como Interfaces e Classes
Ao contrário das classes em assemblagens padrão, as classes COM são expostas em assemblagens interop 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 interop é o mesmo da classe COM original, mas com a palavra "Classe" anexada. Por exemplo, suponha que tem um projeto com uma referência a uma assemblagem interop para um objeto COM. Se a classe COM tiver o nome MyComClass
, o IntelliSense e o Browser de Objetos mostrarão uma interface com o nome MyComClass
e uma classe com o nome MyComClassClass
.
Criar Instâncias de uma Classe .NET Framework
Geralmente, cria uma instância de uma classe .NET Framework com a New
instrução com um nome de classe. Ter uma classe COM representada por uma assemblagem interop é o único caso em que pode utilizar a New
instrução com uma interface. A menos que esteja a utilizar a classe COM com uma instrução Inherits
, pode utilizar a interface tal como faria com uma classe. O código seguinte demonstra como criar um Command
objeto num projeto que tenha uma referência ao objeto COM da Biblioteca microsoft ActiveX Data Objects 2.8:
Dim cmd As New ADODB.Command
No entanto, se estiver a utilizar a classe COM como base para uma classe derivada, tem de utilizar a classe interop que representa a classe COM, tal como no seguinte código:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Nota
As assemblagens interop implementam implicitamente interfaces que representam classes COM. Não deve tentar utilizar a Implements
instrução para implementar estas interfaces ou será apresentado um erro.
Tipos de Dados para Parâmetros e Valores Devolvidos
Ao contrário dos membros das assemblagens padrão, os membros da assemblagem interop podem ter tipos de dados diferentes dos utilizados na declaração de objeto original. Embora as assemblagens interop convertam implicitamente tipos COM em tipos de runtime de linguagem comuns compatíveis, deve prestar atenção aos tipos de dados que são utilizados 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 que o .NET Framework tipo equivalente, Short
. Recomenda-se que utilize o Object Browser para examinar as características dos membros importados antes de os utilizar.
Métodos COM ao nível do módulo
A maioria dos objetos COM é utilizada ao criar uma instância de uma classe COM com a New
palavra-chave e, em seguida, chamar métodos do objeto. Uma exceção a esta regra envolve objetos COM que contêm AppObj
ou GlobalMultiUse
classes COM. Estas classes assemelham-se a métodos ao nível do módulo nas classes .NET do Visual Basic. O Visual Basic 6.0 e versões anteriores criam implicitamente instâncias desses objetos da primeira vez que chamar um dos respetivos métodos. Por exemplo, no Visual Basic 6.0, pode adicionar uma referência à Biblioteca de Objetos do Microsoft DAO 3.6 e chamar o DBEngine
método sem criar primeiro uma instância:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
O Visual Basic .NET requer que crie sempre instâncias de objetos COM antes de poder utilizar os respetivos métodos. Para utilizar estes métodos no Visual Basic, declare uma variável da classe pretendida e utilize a nova palavra-chave para atribuir o objeto à variável de objeto. A Shared
palavra-chave pode ser utilizada quando pretender certificar-se de que apenas é criada uma instância da classe.
' 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 Processados nos Processadores de Eventos
Um problema comum entre opções envolve erros em processadores de eventos que lidam com eventos gerados por objetos COM. Estes erros são ignorados, a menos que verifique especificamente se existem erros ao utilizar On Error
ou Try...Catch...Finally
instruções. Por exemplo, o exemplo seguinte é 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 esperado. No entanto, se tentar o mesmo exemplo sem o Try...Catch...Finally
bloco, o erro será ignorado como se tivesse utilizado a OnError Resume Next
instrução. Sem processamento de erros, a divisão por zero falha silenciosamente. Uma vez que estes erros nunca geram erros de exceção não processados, é importante que utilize alguma forma de processamento de exceções em processadores de eventos que processam eventos de objetos COM.
Compreender os erros de interop com
Sem processamento de erros, as chamadas interop geram frequentemente erros que fornecem pouca informação. Sempre que possível, utilize o processamento de erros estruturados para fornecer mais informações sobre problemas quando ocorrem. Isto pode ser especialmente útil quando depura aplicações. Por exemplo:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Pode encontrar informações como a descrição do erro, HRESULT e a origem de erros COM ao examinar o conteúdo do objeto de exceção.
Problemas de Controlo ActiveX
A maioria dos controlos ActiveX que funcionam com o Visual Basic 6.0 funcionam com o Visual Basic .NET sem problemas. As principais exceções são controlos de contentor ou controlos que contêm visualmente outros controlos. Alguns exemplos de controlos mais antigos que não funcionam corretamente com o Visual Studio são os seguintes:
Microsoft Forms controlo fotogramas 2.0
Up-Down controlo, também conhecido como controlo de rotação
Controlo de Tabulação Sheridan
Existem apenas algumas soluções para problemas de controlo ActiveX não suportados. Pode migrar controlos existentes para o Visual Studio se for o proprietário do código fonte original. Caso contrário, pode consultar os fornecedores de software para obter atualizações. Versões de controlos compatíveis com NET para substituir controlos ActiveX não suportados.
Passagem de Propriedades ReadOnly de Controlos ByRef
Por vezes, o .NET do Visual Basic gera erros COM, tais como "Erro 0x800A017F CTL_E_SETNOTSUPPORTED", quando transmite ReadOnly
propriedades de alguns controlos ActiveX mais antigos como ByRef
parâmetros para outros procedimentos. As chamadas de procedimento semelhantes do Visual Basic 6.0 não geram um erro e os parâmetros são tratados como se os tivesse transmitido por valor. A mensagem de erro .NET do Visual Basic indica que está a tentar alterar uma propriedade que não tem um procedimento de propriedade Set
.
Se tiver acesso ao procedimento que está a ser chamado, pode impedir este erro ao utilizar a ByVal
palavra-chave para declarar parâmetros que aceitam ReadOnly
propriedades. Por exemplo:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Se não tiver acesso ao código fonte para o procedimento que está a ser chamado, pode forçar a passagem da propriedade por valor ao adicionar um conjunto adicional de parênteses em torno do procedimento de chamada. Por exemplo, num projeto que tenha uma referência ao objeto MICROSOFT ActiveX Data Objects 2.8 Library COM, pode utilizar:
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
Implementar assemblagens que expõem o Interop
A implementação de assemblagens que expõem interfaces COM apresenta alguns desafios exclusivos. Por exemplo, um potencial problema ocorre quando aplicações separadas referenciam a mesma assemblagem COM. Esta situação é comum quando uma nova versão de uma assemblagem é instalada e outra aplicação ainda está a utilizar a versão antiga da assemblagem. Se desinstalar uma assemblagem que partilha uma DLL, pode torná-la indisponível para as outras assemblagens.
Para evitar este problema, deve instalar assemblagens partilhadas na Cache de Assemblagem Global (GAC) e utilizar um MergeModule para o componente. Se não conseguir instalar a aplicação no GAC, esta deverá ser instalada no CommonFilesFolder num subdiretório específico da versão.
As assemblagens que não são partilhadas devem estar localizadas lado a lado no diretório com a aplicação de chamadas.