If you can't change the source code for the legacy COM component, you'll have to work around it. One possibility is to create a new VB6 component that implements IPasswordCallback
and which does the actual work of getting the password by calling back into your .NET code. This way, the VB6 code can deal with the ByRef
parameter, and the .NET code won't see it.
Most of the code to make it work will be VB6 code, which you can put into a new ActiveX DLL project and reference from your .NET project.
Here is an outline of the different classes and interfaces needed:
Class ByValPasswordCallbackWrapper
(VB6): This class is a wrapper class that hides the ByRef
parameter from the .NET code. When this class's Password
property is called by the legacy COM component, this class will call a helper class (written in .NET) that will do the actual callback work. The VB6 class will then take the results from the helper class and return them to the legacy COM component.
Class PasswordCallbackArgs
(VB6): This class is used to pass the parameters from a call to IPasswordCall_Password
to the .NET helper class that will doing the real work.
Interface IPasswordCallbackProvider
(VB6): This is the interface that your .NET code will implement instead of implementing IPasswordCallback
directly.
The Code
Below is the code listing for each of the VB6 components mentioned above. You can add this code to a new ActiveX DLL project and compile it for use from your .NET code. Each file is listed separately. As an aside, make sure the Instancing property for each of these classes is set to "5-MultiUse".
File: PasswordCallbackArgs.cls
'Used to pass arguments around'
Public OwnerNeeded As Boolean
Public IsValidResult As Boolean
File: IPasswordCallbackProvider.cls
' This is the interface that your .NET '
' class should implement (instead of IPasswordCallback) '
Public Function GetPassword(ByVal args As PasswordCallbackArgs)
End Function
File: ByValPasswordCallbackWrapper.cls
Implements IPasswordCallback
Private m_callbackProvider As IPasswordCallbackProvider
Pubic 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
How to Use This Code
Once the above code has been compiled into an ActiveX DLL, add a reference to it from your .NET project.
Next, put the .NET code that you would have put into your IPasswordCallback
implementation into a new class that implements IPasswordCallbackProvider
. Remember that the callback parameters (ownerNeeded
and isResultValid
) are passsed to your provider class in a PasswordCallbackArgs
object, so you will have to use args.ownerNeeded
and args.isResultValid
in your .NET class to refer to them.
Here is a stub provider class to get you started:
File: 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
In order to pass a valid IPasswordCallback
to your legacy COM object, you will need to create a ByValPasswordCallbackWrapepr
, set its CallbackProvider
property, and then pass the wrapper object to the legacy COM object.
Using the above example, and assuming you have a instance of your legacy COM component called LegacyComObject
, you would do something like the following to set up the callback:
' 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
Why This Works
This works because ByValPasswordCallbackWrapper
implements the IPasswordCallback
interface that the legacy COM object is expecting to receive. The ByValPasswordCallbackWrapper
in turn provides a way to hook into the callback process via the IPasswordCallbackProvider
interface. The IPasswordCallbackProvider
COM interface is compatible with .NET, so you can write an implementing class using VB.NET. The ByValPasswordCallbackWrapper
calls your IPasswordCallbackProvider
to do the work of getting the password, and makes sure to put a value back into the ByRef
parameter.