Если вы не можете изменить исходный код для устаревшего компонента COM, вам придется обойти его. Одной из возможностей является создание нового компонента VB6, который реализует IPasswordCallback
и который выполняет фактическую работу по получению пароля путем обратного вызова вашего кода .NET. Таким образом, код VB6 может работать с параметром ByRef
, а код .NET его не увидит.
Большая часть кода для его работы будет представлять собой код VB6, который вы можете вставить в новый проект ActiveX DLL и использовать в своем проекте .NET.
Вот схема различных необходимых классов и интерфейсов:
Класс ByValPasswordCallbackWrapper
(VB6): Этот класс является классом-оболочкой, который скрывает параметр ByRef
от кода .NET. Когда свойство Password
этого класса вызывается устаревшим компонентом COM, этот класс будет вызывать вспомогательный класс (написанный на .NET), который будет выполнять реальную функцию обратного вызова. Затем класс VB6 возьмет результаты из вспомогательного класса и вернет их устаревшему компоненту COM.
Класс PasswordCallbackArgs
(VB6): Этот класс используется для передачи параметров из вызова в IPasswordCall_Password
в вспомогательный класс .NET, который будет выполнять реальную работу.
Интерфейс IPasswordCallbackProvider
(VB6): это интерфейс, который будет реализован в вашем .NET-коде вместо непосредственной реализации IPasswordCallback
.
Код
Ниже приведен список кодов для каждого из компонентов VB6, упомянутых выше. Вы можете добавить этот код в новый проект ActiveX DLL и скомпилировать его для использования из своего кода .NET. Каждый файл указан отдельно. Кроме того, убедитесь, что для каждого из этих классов для свойства Instancing установлено значение "5-MultiUse".
Файл : PasswordCallbackArgs.cls
'Used to pass arguments around'
Public OwnerNeeded As Boolean
Public IsValidResult As Boolean
Файл : IPasswordCallbackProvider.cls
' This is the interface that your .NET '
' class should implement (instead of IPasswordCallback) '
Public Function GetPassword(ByVal args As PasswordCallbackArgs)
End Function
Файл : ByValPasswordCallbackWrapper.cls
Implements IPasswordCallback
Private m_callbackProvider As IPasswordCallbackProvider
Public Property Set CallbackProvider(ByVal callbackProvider As IPasswordCallbackProvider)
Set m_callbackProvider = callbackProvider
End Property
Private Property Get IPasswordCallback_Password( _
ByVal ownerNeeded As Boolean, _
ByRef isResultValid As Boolean) As String
IPasswordCallback_Password = DoGetPassword(ownerNeeded, isResultValid)
End Property
Private Function DoGetPassword(
ByVal ownerNeeded As Boolean, _
ByRef isResultValid As Boolean) As String
If m_callbackProvider Is Nothing Then
Err.Raise 5,,"No callback provider. DoGetPassword failed."
End If
'Wrap the arguments in a PasswordCallbackArgs object.'
'Do not need to fill args.IsResultValid here - the callback provider will do that'
Dim args As New PasswordCallbackArgs
args.OwnerNeeded = ownerNeeded
'Get the password and a value to put back into our ByRef isResultValid'
DoGetPassword = m_callbackProvider.GetPassword(args)
isResultValid = args.IsResultValid
End Sub
Как использовать этот код
Как только приведенный выше код был скомпилирован в DLL ActiveX, добавьте ссылку на него из вашего проекта .NET.
Затем поместите код .NET, который вы поместили бы в реализацию IPasswordCallback
, в новый класс, реализующий IPasswordCallbackProvider
. Помните, что параметры обратного вызова (ownerNeeded
и isResultValid
) передаются вашему классу провайдера в объекте PasswordCallbackArgs
, поэтому вам придется использовать args.ownerNeeded
и args.isResultValid
в вашем классе .NET для ссылки на них.
Вот класс заглушки для начинающих:
Файл : MyPasswordCallbackProvider.vb (VB.NET)
' A stub implementation of an IPasswordCallbackProvider '
Public Class MyPasswordCallbackProvider Implements IPasswordCallbackProvider
Public Function GetPassword(PasswordCallbackArgs args) As String _
Implements IPasswordCallbackProvider.Password
Dim password As String = ""
Dim resultWasValid As Boolean
If args.OwnerNeeded Then
'do stuff'
Else
'do other stuff'
End If
'do even more stuff'
'set whether the result was valid or not'
args.ResultValid = resultWasValid
Return password
End Property
End Class
Чтобы передать действительный IPasswordCallback
в ваш устаревший COM-объект, вам нужно будет создать ByValPasswordCallbackWrapepr
, установить его свойство CallbackProvider
, а затем передать объект-оболочку в устаревший COM-объект.
Используя приведенный выше пример и предполагая, что у вас есть экземпляр вашего устаревшего COM-компонента с именем LegacyComObject
, вы должны сделать что-то вроде следующего для настройки обратного вызова:
' Create the callback wrapper '
ByValPasswordCallbackWrapper wrapper = New ByValPasswordCallbackWrapper()
' Tell the wrapper to call our custom IPasswordCallbackProvider '
wrapper.CallbackProvider = New MyPasswordCallbackProvider()
''" Pass the call back wrapper to the legacy COM object ''"
''" (not sure how this is done in your scenario. ''"
''" I'm just pretending it's a property since I don't know... "''
LegacyComObject.PasswordCallback = wrapper
Почему это работает
Это работает, потому что ByValPasswordCallbackWrapper
реализует интерфейс IPasswordCallback
, который ожидает получить устаревший COM-объект. ByValPasswordCallbackWrapper
, в свою очередь, предоставляет возможность подключиться к процессу обратного вызова через интерфейс IPasswordCallbackProvider
. Интерфейс IPasswordCallbackProvider
COM совместим с .NET, поэтому вы можете написать реализующий класс, используя VB.NET. ByValPasswordCallbackWrapper
вызывает ваш IPasswordCallbackProvider
для получения пароля и гарантирует возврат значения в параметр ByRef
.