【问题标题】:Workaround for VB.Net - ByRef parameter of a propertyVB.Net 的解决方法 - 属性的 ByRef 参数
【发布时间】:2009-03-19 16:32:11
【问题描述】:

我有一个旧的 COM 组件,其接口声明了这样的属性(IDL 表示法):

interface IPasswordCallback : IUnknown {
    [propget] HRESULT Password( [in] VARIANT_BOOL ownerNeeded, [in, out]
        VARIANT_BOOL* isResultValid, [out, retval] BSTR* password );
}

这个接口是在一个用VB6编写的调用应用程序中实现的,如下所示:

Public Property Get IPasswordCallback_Password(ByVal ownerNeeded As Boolean,
    ByRef isResultValid As Boolean) As String
    'implementation cut
End Property

在我尝试在 VB.Net 中做同样的事情之前,一切都很好。 VB.Net 拒绝编译通过 ByRef 传递属性参数的代码。 MSDN saysVB.Net 不允许这样的参数。

有什么方法可以在 VB.Net 中实现这样的属性?

【问题讨论】:

  • 代码不是VB6,看起来像VB.net。你能纠正一下吗?

标签: .net vb.net vb6 properties


【解决方案1】:

围绕已正确实现属性的旧接口创建一个新接口。您不需要原始源代码,您可以从 VB6 中引用它并创建一个新的 ActiveX DLL/OCX 来完成这项工作。我意识到有点痛苦。

我自己在转换项目中遇到了这个问题,不得不回到旧的 VB6 代码并确保所有属性参数都声明为 ByVal。问题是我们必须等待 VB6 代码的主要版本更改(当我们破坏二进制兼容性时)。这样做会使新的 DLL 与旧的 DLL 不兼容。

【讨论】:

  • 您是否建议创建一个包装器来实现旧接口并通过隧道调用到/来自我的应用程序?如果是这样,除了 .Net 运行时之外,我还必须为我的应用程序的每个副本分发 VB6 运行时。
  • 您将您的问题标记为 vb6,所以我认为 COM 库是用 VB6 制作的。在这种情况下,如果您正在使用该库,则存在运行时。但即使不使用 VB6 来创建也比使用 C++ 更容易。
【解决方案2】:

所有答案都非常有用,但它们都建议我在 VB6 中创建这个回调重定向器,这不是很方便,因为我可能必须使用客户端应用程序重新分发 VB6 运行时。

这是解决方案的扩展版本。重定向器可以用 C# 编写,因为 C# 不会抱怨通过 ref。它可以作为类库实现并作为 .dll 程序集分发。这样,任何安装了 .Net Framework 的人都可以使用这样的重定向器。使用 VB.Net 的人肯定都安装了 .Net Framework。

【讨论】:

  • 确保没有副作用。但如果它有效,那么这是一个很好的提示
  • 我同意 RS Conley 的观点。如果它在 C# 中工作,那对于面临这个问题的其他人来说绝对是一个不错的提示。让我们知道结果如何。
  • 嗯,看起来一切正常。目前没有发现任何问题。
  • VB6 运行时是预装在任何可能运行 VB.Net 的 Windows 版本中的系统组件。唯一需要注意的可能是在较旧的运行时版本上执行测试,以确保没有任何问题通过以后的 Service Pack 得到纠正。
【解决方案3】:

如果您无法更改旧 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 的新类中。请记住,回调参数(ownerNeededisResultValid)在PasswordCallbackArgs 对象中传递给您的提供程序类,因此您必须在.NET 类中使用args.ownerNeededargs.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 参数中。

【讨论】:

  • 除了 .Net 运行时之外,我还必须为我的应用程序的每个副本重新分发 VB6 运行时,不是吗?
  • 好点。我已经习惯了这样做(我们的主要产品是一个 VB6 应用程序),以至于我忘记了这可能并不容易/不可取。
  • 但是,我想不出另一种方法来做到这一点(并不是说没有)。您可以使用 C++ 来实现我使用 VB6 的部分。问题是您必须以一种或另一种方式离开 .NET 来解决 ByRef 问题。
  • VB6 运行时随 XP 和 Vista 提供。
  • @RS Conley:我因为不知道这一点而感到愚蠢。我们的 VB6 程序的安装程序仍然分发 msvbvm60.dll,因为原始应用程序在 Win9x 机器上运行。我没有意识到它预装在 XP 和 Vista 上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-12
  • 2019-04-11
  • 1970-01-01
  • 2014-08-11
  • 1970-01-01
  • 2014-07-17
相关资源
最近更新 更多