如果您无法更改旧 COM 组件的源代码,则必须解决它。一种可能性是创建一个新的 VB6 组件,该组件实现 IPasswordCallback,并通过回调您的 .NET 代码来完成获取密码的实际工作。这样VB6代码就可以处理ByRef参数,.NET代码就看不到了。
使其工作的大部分代码将是 VB6 代码,您可以将其放入新的 ActiveX DLL 项目并从您的 .NET 项目中引用。
以下是所需的不同类和接口的概述:
ByValPasswordCallbackWrapper 类 (VB6):此类是一个包装类,它对 .NET 代码隐藏了 ByRef 参数。当遗留 COM 组件调用此类的 Password 属性时,此类将调用一个帮助程序类(用 .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
如何使用此代码
将上述代码编译成 ActiveX DLL 后,从您的 .NET 项目中添加对它的引用。
接下来,将要放入 IPasswordCallback 实现的 .NET 代码放入实现 IPasswordCallbackProvider 的新类中。请记住,回调参数(ownerNeeded 和isResultValid)在PasswordCallbackArgs 对象中传递给您的提供程序类,因此您必须在.NET 类中使用args.ownerNeeded 和args.isResultValid 来引用它们。
这里有一个存根提供程序类可以帮助您入门:
文件: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 对象。
使用上面的示例,并假设您有一个名为 LegacyComObject 的旧 COM 组件实例,您可以执行以下操作来设置回调:
' 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 实现了旧版COM 对象期望接收的IPasswordCallback 接口。 ByValPasswordCallbackWrapper 反过来提供了一种通过IPasswordCallbackProvider 接口挂钩到回调过程的方法。 IPasswordCallbackProvider COM 接口与.NET 兼容,因此您可以使用VB.NET 编写实现类。 ByValPasswordCallbackWrapper 调用您的IPasswordCallbackProvider 来完成获取密码的工作,并确保将值放回ByRef 参数中。