Устранение неполадок взаимодействия (Visual Basic)
При взаимодействии между COM и управляемым кодом .NET Framework может возникнуть одна или несколько следующих распространенных проблем.
Маршаллинг интероперабельности
Иногда может потребоваться использовать типы данных, которые не являются частью .NET Framework. Сборки взаимодействия обрабатывают большую часть работы для COM-объектов, но может потребоваться управлять типами данных, которые используются при доступе управляемых объектов к COM. Например, структуры в библиотеках классов должны указывать неуправляемый тип BStr
для строк, отправляемых COM-объектам, созданным Visual Basic 6.0 и более ранними версиями. В таких случаях атрибут MarshalAsAttribute можно использовать, чтобы сделать управляемые типы неуправляемыми типами.
Экспорт строк Fixed-Length в неуправляемый код
В Visual Basic 6.0 и более ранних версиях строки экспортируются в COM-объекты в виде последовательностей байтов без символа завершения null. Для совместимости с другими языками Visual Basic .NET включает символ завершения при экспорте строк. Лучший способ решения этой несовместимости — экспортировать строки, которые не имеют символа завершения в виде массивов Byte
или Char
.
Экспорт иерархий наследования
Иерархии управляемых классов упрощаются при использовании их в виде COM-объектов. Например, если вы определяете базовый класс с членом, а затем наследуете базовый класс в производном классе, который представлен как COM-объект, клиенты, использующие производный класс в объекте COM, не смогут использовать унаследованные члены. К членам базового класса можно обращаться только из COM-объектов только в качестве экземпляров базового класса, а затем только в том случае, если базовый класс также создается в качестве COM-объекта.
Перегруженные методы
Несмотря на то что вы можете создавать перегруженные методы с помощью Visual Basic, они не поддерживаются COM. Если класс, содержащий перегруженные методы, предоставляется в виде COM-объекта, новые имена методов создаются для перегруженных методов.
Например, рассмотрим класс с двумя перегрузками метода Synch
. Если класс предоставляется в виде COM-объекта, новые созданные имена методов могут быть Synch
и Synch_2
.
Переименование может вызвать две проблемы для потребителей объекта COM.
Клиенты могут не ожидать сгенерированных имен методов.
Созданные имена методов в классе, предоставляемые в виде COM-объекта, могут изменяться при добавлении новых перегрузок в класс или его базовый класс. Это может привести к проблемам с версиями.
Чтобы решить обе проблемы, присвойте каждому методу уникальное имя, а не используйте перегрузку, при создании объектов, которые будут предоставляться как COM-объекты.
Использование COM-объектов через межоперационные сборки для взаимодействия
Вы используете сборки взаимодействия почти так же, как если бы они являлись управляемыми аналогами для представляемых ими объектов COM. Тем не менее, поскольку они являются оболочками и не фактическими COM-объектами, существуют некоторые различия между использованием сборок взаимодействия и стандартных сборок. Эти области различия включают воздействие классов и типов данных для параметров и возвращаемых значений.
Классы, представленные как интерфейсы и сами классы
В отличие от классов в стандартных сборках, классы COM в сборках взаимодействия представлены как интерфейс и как класс, представляющие класс COM. Имя интерфейса идентично имени класса COM. Имя класса взаимодействия совпадает с именем исходного COM-класса, но с добавленным словом "Класс". Например, предположим, что у вас есть проект со ссылкой на интероп-сборку для COM-объекта. Если класс COM называется MyComClass
, IntelliSense и обозреватель объектов отображают интерфейс с именем MyComClass
и класс с именем MyComClassClass
.
Создание экземпляров класса .NET Framework
Как правило, вы создаете экземпляр класса .NET Framework с помощью инструкции New
с именем класса. Наличие COM-класса, представленного сборкой межоперационного взаимодействия, является единственным случаем, когда можно использовать оператор New
с интерфейсом. Если вы не используете класс COM с оператором Inherits
, вы можете использовать интерфейс так же, как если бы вы работали с классом. В следующем коде показано, как создать объект Command
в проекте с ссылкой на com-объект библиотеки Microsoft ActiveX Data Objects 2.8:
Dim cmd As New ADODB.Command
Однако если класс COM используется в качестве базы для производного класса, необходимо использовать класс взаимодействия, представляющий класс COM, как показано в следующем коде:
Class DerivedCommand
Inherits ADODB.CommandClass
End Class
Заметка
Сборки межоперационного взаимодействия неявно реализуют интерфейсы, представляющие классы COM. Не следует пытаться использовать инструкцию Implements
для реализации этих интерфейсов, иначе это приведет к ошибке.
Типы данных для параметров и возвращаемых значений
В отличие от элементов стандартных сборок, элементы сборки взаимодействия могут иметь типы данных, которые отличаются от тех, которые используются в объявлении исходного объекта. Хотя сборки взаимодействия неявно преобразуют COM-типы в совместимые типы среды CLR, следует обратить внимание на типы данных, используемые обеими сторонами, чтобы предотвратить ошибки среды выполнения. Например, в COM-объектах, созданных в Visual Basic 6.0 и более ранних версиях, значения типа Integer
принимают эквивалентный тип .NET Framework Short
. Рекомендуется использовать обозреватель объектов для изучения характеристик импортированных элементов перед их использованием.
Методы COM уровня модуля
Большинство com-объектов используются путем создания экземпляра КЛАССА COM с помощью ключевого слова New
, а затем вызова методов объекта. Одним из исключений из этого правила являются COM-объекты, содержащие AppObj
или GlobalMultiUse
COM-классы. Такие классы похожи на методы уровня модуля в классах .NET Visual Basic. Visual Basic 6.0 и более ранних версий неявно создают экземпляры таких объектов при первом вызове одного из их методов. Например, в Visual Basic 6.0 можно добавить ссылку на библиотеку объектов Microsoft DAO 3.6 и вызвать метод DBEngine
без первого создания экземпляра:
Dim db As DAO.Database
' Open the database.
Set db = DBEngine.OpenDatabase("C:\nwind.mdb")
' Use the database object.
Для visual Basic .NET необходимо всегда создавать экземпляры ОБЪЕКТОВ COM, прежде чем использовать их методы. Чтобы использовать эти методы в Visual Basic, объявите переменную требуемого класса и используйте новое ключевое слово для назначения объекта переменной объекта. Ключевое слово Shared
можно использовать при создании только одного экземпляра класса.
' 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
Необработанные ошибки в обработчиках событий
Одна из распространенных проблем взаимодействия связана с ошибками в обработчиках событий, обрабатывающих события, вызванные COM-объектами. Такие ошибки игнорируются, если вы не проверяете наличие ошибок с помощью инструкций On Error
или Try...Catch...Finally
. Например, в следующем примере представлен проект .NET Visual Basic с ссылкой на COM-объект библиотеки 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
В этом примере возникает ошибка, как ожидалось. Однако если вы попытаетесь использовать тот же пример без блока Try...Catch...Finally
, ошибка игнорируется, как если бы вы использовали инструкцию OnError Resume Next
. Без обработки ошибок деление на ноль автоматически завершается ошибкой. Так как такие ошибки никогда не вызывают необработанных ошибок исключений, важно использовать некоторую форму обработки исключений в обработчиках событий, обрабатывающих события из COM-объектов.
Общие сведения об ошибках взаимодействия COM
Без обработки ошибок вызовы взаимодействия часто создают ошибки, предоставляющие мало информации. По возможности используйте структурированную обработку ошибок, чтобы получить дополнительные сведения о проблемах при их возникновении. Это может быть особенно полезно при отладке приложений. Например:
Try
' Place call to COM object here.
Catch ex As Exception
' Display information about the failed call.
End Try
Вы можете найти такие сведения, как описание ошибки, HRESULT и источник ошибок COM, проверив содержимое объекта исключения.
Проблемы с элементом управления ActiveX
Большинство элементов ActiveX, работающих с Visual Basic 6.0, работают с Visual Basic .NET без проблем. Основными исключениями являются элементы управления контейнерами или элементы управления, которые визуально содержат другие элементы управления. Ниже приведены некоторые примеры старых элементов управления, которые неправильно работают с Visual Studio:
Элемент управления рамкой Microsoft Forms 2.0
элемент управления Up-Down, также известный как контроллер вращения
Элемент управления вкладками Sheridan
Существует лишь несколько обходных решений для проблем с неподдерживаемыми элементами управления ActiveX. Вы можете перенести существующие элементы управления в Visual Studio, если вы владеете исходным исходным кодом. В противном случае вы можете обратиться к поставщикам программного обеспечения для получения обновленных версий элементов управления, совместимых с .NET, чтобы заменить неподдерживаемые элементы ActiveX.
Передача свойств ReadOnly элементов управления ByRef
Visual Basic иногда порождает ошибки COM, такие как "Ошибка 0x800A017F CTL_E_SETNOTSUPPORTED", когда вы передаете свойства ReadOnly
некоторых старых элементов ActiveX в качестве параметров ByRef
другим процедурам. Аналогичные вызовы процедуры из Visual Basic 6.0 не вызывают ошибку, а параметры обрабатываются так, как если бы вы передали их по значению. Сообщение об ошибке в Visual Basic .NET указывает, что вы пытаетесь изменить свойство, для которого отсутствует процедура Set
.
Если у вас есть доступ к вызываемой процедуре, эту ошибку можно предотвратить с помощью ключевого слова ByVal
для объявления параметров, которые принимают свойства ReadOnly
. Например:
Sub ProcessParams(ByVal c As Object)
'Use the arguments here.
End Sub
Если у вас нет доступа к исходному коду для вызываемой процедуры, можно принудительно передать свойство по значению, добавив дополнительный набор квадратных скобок вокруг вызываемой процедуры. Например, в проекте с ссылкой на COM-объект библиотеки Microsoft ActiveX Data Objects 2.8 можно использовать следующее:
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
Развертывание сборок, которые обеспечивают интероперабельность
Развертывание сборок, которые предоставляют COM-интерфейсы, представляет некоторые уникальные проблемы. Например, потенциальная проблема возникает, когда отдельные приложения ссылались на одну сборку COM. Эта ситуация распространена, когда установлена новая версия сборки, а другое приложение по-прежнему использует старую версию сборки. При удалении сборки, которая предоставляет общий доступ к библиотеке DLL, вы можете непреднамеренно сделать ее недоступной для других сборок.
Чтобы избежать этой проблемы, необходимо установить общие сборки в глобальный кэш сборок (GAC) и использовать MergeModule для компонента. Если приложение не удается установить в GAC, его следует установить в CommonFilesFolder в подкаталоге для конкретной версии.
Сборки, которые не являются общими, должны находиться параллельно в каталоге с вызывающим приложением.