Поделиться через


Устранение неполадок взаимодействия (Visual Basic)

При взаимодействии между COM и управляемым кодом .NET Framework, можно столкнуться с типичными вопросами, рассматриваемыми в следующих разделах:

Маршалинг взаимодействия

Иногда возникает необходимость в использовании типов данных, не входящие в состав .NET Framework. Сборки взаимодействия выполняют большую часть работы с объектами COM, но может возникнуть необходимость управления типами данных, используемых, когда управляемые объекты предоставляются COM. Например, структуры в библиотеках класса должны задать неуправляемый тип BStr для строк, отправляемых в объекты COM, созданные Visual Basic 6.0 и предыдущими версиями. В таких случаях можно использовать атрибут MarshalAsAttribute, чтобы управляемые типы предоставлялись как неуправляемые.

Экспорт строк фиксированной длины в неуправляемый код

В Visual Basic 6.0 и более ранних версиях строки экспортировались в объекты COM как последовательности байтов без пустого завершающего знака. Для совместимости с другими языками Visual Basic 2005 при экспорте строк включает завершающий знак. Лучший способ решить проблему несовместимости — это экспортировать строки без завершающего знака как массивы типа Byte или Char.

Экспорт иерархий наследования

Иерархии управляемого класса выравниваются при представлении их как объектов COM. Например, если определить базовый класс с помощью элемента, а затем наследовать базовый класс в производном классе, который предоставляется как объект COM, клиенты, использующие производный класс в объекте COM, не смогут использовать унаследованные элементы. Элементы базового класса доступны из объектов COM только как экземпляры базового класса и только при условии, что базовый класс также создан как объект COM.

Перегруженные методы

Хотя можно создать перегруженные методы с помощью Visual Basic, они не поддерживаются COM. Когда класс, содержащий перегруженные методы, выставлен как объект COM, новые имена методов создаются для перегруженных методов.

Например, рассмотрим класс, имеющий две перегрузки метода Synch. Когда класс выставлен как объект COM, новые созданные имена методов могут быть Synch и Synch_2.

Переименование может вызвать две проблемы для потребителей COM-объекта.

  1. Клиенты могут не ожидать созданных имен методов.

  2. Созданные имена методов в классе, выставленном как COM-объект, можно изменить при добавлении новых перегрузок к классу или его базовому классу. Это может вызвать проблемы управления версиями.

Для решения обоих проблем при разработке объектов, которые будут предоставлены в виде объектов COM, присвойте каждому методу уникальное имя вместо использования перегрузки.

Использование объектов COM в сборках взаимодействия

Сборки взаимодействия используются почти так же, как если бы они были управляемым кодом, заменяющим объекты COM, которые они представляют. Тем не менее, поскольку они являются обертками, а не настоящими объектами COM, есть некоторые различия в использовании сборок взаимодействия и обычных сборок. Эти различия касаются предоставления классов, а также типов данных параметров и возвращаемых значений.

Классы, предоставляемые и как интерфейсы, и как классы

В отличие от классов в обычных сборках классы COM представлены в сборках взаимодействия и как интерфейсы, и как классы, представляющие класс COM. Имя интерфейса совпадает с именем класса COM. Имя класса взаимодействия то же, что и имя исходного класса COM, но с добавленным в конце словом "Class". Предположим, что имеется проект с ссылкой на сборку взаимодействия для объекта 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 в совместимые типы языковой среды выполнения, во избежание ошибок времени выполнения следует уделять внимание типам данных, используемым обеими сторонами. Например в объектах COM, созданных в Visual Basic 6.0 и в более ранних версиях, значения типа Integer принимают эквивалентный .NET Framework тип Short. Рекомендуется использовать Обозреватель Объектов для проверки характеристик импортированных элементов перед их использованием.

Методы COM уровня модуля

Большинство объектов COM используются путем создания экземпляра класса COM с помощью ключевого слова New с последующим вызыванием методов объекта. Одно исключение из этого правила включает COM-объекты, содержащие COM-классы AppObj или GlobalMultiUse. Такие классы напоминают методы уровня модуля в классах Visual Basic 2005. В Visual Basic 6.0 и более ранних версиях неявно создаются экземпляры таких объектов при первом вызове одного из их методов. Например, в Visual Basic 6.0 можно добавить ссылку на Microsoft DAO 3.6 Object Library и вызвать метод DBEngine без предварительного создания экземпляра:

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

Visual Basic 2005 требует, чтобы экземпляры объектов COM всегда создавались до использования их методов. Чтобы использовать эти методы в Visual Basic 2005, объявите переменную нужного класса и с помощью ключевого слова "new" присвойте объект для переменной объекта. Ключевое слово 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. Например, следующий пример взят из проекта Visual Basic 2005, в котором присутствует ссылка на объект 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 =
    "Provider=Microsoft.Jet.OLEDB.4.0;" &
    "Data Source=C:\NWIND.MDB"
    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 2005. Большинство исключений — это контейнерные элементы управления, то есть элементы управления, которые визуально содержат в себе другие элементы управления. Ниже приведены некоторые примеры старых элементов управления, которые не работают корректно с Visual Studio.

  • Элемент управления Frame из Microsoft Forms 2.0

  • Элемент управления Up-Down, называемый также элементом управления вращением

  • Элемент управления Sheridan Tab

Способов обойти проблемы с неподдерживаемыми элементами управления ActiveX мало. Если у вас есть исходный код, можно выполнить миграцию существующих элементов управления в Visual Studio. Другой вариант — запросить у поставщиков программы обновленные версии элементов управления, совместимые с .NET, для замены неподдерживаемых элементов управления ActiveX.

Передача ByRef свойств ReadOnly элементов управления

Visual Basic 2005 иногда вызывает ошибки COM, такие как "Ошибка 0x800A017F CTL_E_SETNOTSUPPORTED", когда Вы передаете свойства ReadOnly некоторых старых элементов управления ActiveX в качестве параметров ByRef в другие процедуры. Такая же процедура, вызванная в Visual Basic 6.0, не вызывает ошибки, и параметры обрабатываются так, как если бы они передавались по значению. Сообщение об ошибке в Visual Basic 2005 является COM-объектом отчетности от том, что вы пытаетесь изменить свойство, у которого нет процедуры свойства 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 для этого компонента. Если невозможно установить приложение в глобальный кэш сборок, тогда его следует установить в CommonFilesFolder в связанную с версией вложенную папку.

Сборки, не используемые совместно, должны быть размещены в одной папке с вызывающим приложением.

См. также

Задачи

Пошаговое руководство. Реализация наследования с использованием COM-объектов (Visual Basic)

Практическое руководство. Добавление модулей слияния в проект развертывания

Ссылки

MarshalAsAttribute

Tlbimp.exe (программа экспорта библиотек типов)

Tlbexp.exe (программа экспорта библиотек типов)

Инструкция Inherits

Основные понятия

глобальный кэш сборок

Другие ресурсы

COM-взаимодействие (Visual Basic)