为什么C#中的null被翻译为VB6中的Empty,而不是Nothing?

15

我有一个引用VB6 dll的C#应用程序。当我将null从C#传递到VB6 dll函数时,null会被翻译为VB6中的Empty值(value),而不是Nothing对象。例如:

 // function in vb6 dll that referenced by c# app
 Public Sub TestFunc(ByVal oValue As Variant)
 {
   ...
   if oValue is Nothing then
     set oValue = someObject
   end if
   ...

 }

 // main c# code
 private void Form1_Load(object sender, EventArgs e)
 {
    object testObject = new object();
    testObject = null;
    TestFunc(testObject);
 }
当我传递一个对象(非空)时,它将作为对象传递到VB6中。 但是当null传递到vb6时,它会变成值类型Empty,而不是对象类型Nothing。 有人知道为什么吗?是否有任何方法可以在从C#应用程序传递null时强制将其作为Nothing传递给VB6?
非常感谢。
6个回答

5

这是更多信息,而不是答案。我刚刚运行了这个VB6程序来确认Nothing是否可以被传递ByVal。它可以。

Private Sub Form_Load()
  Call TestSub(Nothing)
End Sub
Private Sub TestSub(ByVal vnt As Variant)
  Debug.Print VarType(Nothing)
  Debug.Print VarType(vnt)
  If vnt Is Nothing Then Debug.Print "vnt Is Nothing"
  If IsEmpty(vnt) Then Debug.Print "vnt Is Empty"
End Sub

我得到了以下输出。请注意,9是vbObject,表示一个Variant持有一个对象引用。
 9 
 9 
vnt Is Nothing

我还没有测试将TestStub移动到另一个组件中,但我认为这仍然可以工作。因此,我认为.NET对COM的封送可能会更好。


是的,问题可能出在封送方面,但我不确定它是否可以修复。 - Sander Rijken
我不知道它是否可修复。我相信.NET的封送处理是非常可定制的,但我对此了解甚少。 - MarkJ

3
可能的原因是这是一个“按值传递”函数。null 可能会被编组为值类型 Variant,其尽可能“空”。

“ByVal”并不意味着它必须是值类型,它只是意味着如果传递了一个对象变量,则该例程不能将其指向另一个对象。VB6变量可以保存“Nothing”。 - MarkJ
关于这个问题,可以看一下我的回答。 - MarkJ

3

既然不允许修改VB dll中的函数,那么添加一个函数呢?

Public Sub TestFuncEx(ByVal oValue As Variant)
{
   If IsEmpty(oValue) Then
    TestFunc(Nothing)
   Else
    TestFunc(oValue)
   End If
}

假设这样做是可行的,因为1)它是一个子程序而不是函数,所以没有返回值;2)你通过值传递,所以它不会修改对象本身。


2

您尝试过以下方法吗:

Public Sub TestFunc(ByVal oValue As Variant)
 {
   ...
   If oValue Is Nothing Then
     Set oValue = someObject
   ElseIf IsEmpty(oValue) Then
     Set oValue = someObject
   End If
   ...

 }

编辑 - 我同意Sander Rijken的回答,说明为什么返回Empty而不是null


我尝试过这个方法并且它可以工作。但是我不应该更改VB6 dll中的任何代码,因为它可能被其他应用程序使用。我想知道是否有任何方法可以强制将空对象编组为VB6中的对象无。当我引用VB6 Dll时,Public Sub TestFunc(ByVal oValue As Variant)转换为C#中的Void TestFunc(object oValue)。它还会将一个初始化的对象正确地编组为VB6中的对象。它只会错误地将一个空对象编组为空值类型进入vb6。 - tiftif
+1. 实用的解决方案。将这个新测试放在代码块 If oValue Is Nothing Then ... End If 下面,它不应该改变其他应用程序的行为。假设其他应用程序从未将 Empty 作为此参数传递。 - MarkJ
@tiftif - 我同意MarkJ的观点,为什么不将这段代码放在当前VB6代码块检查“Nothing”的下面呢? - user65628
我也点赞这个解决方案——正如MarkJ和Heather已经提到的那样,它不应该影响调用该函数的其他代码(如果确实有影响,那么我认为其他应用程序中存在一个错误,因为该函数最初并没有特别处理“Empty”)。 - Mike Spross
我同意这将是我的临时解决方案,同时调查定制编组MarkJ和Robert建议的选项。非常感谢大家的帮助。 - tiftif

1

我遵循了另一个网站的建议,在 COM 方法调用中使用了 System.DBNull.Value。这对我起作用了,而传递 C# null 则不行。


编写一个C++测试COM DLL表明null = VT_EMPTY,System.DBNull.Value = VT_NULL。 - TheArtTrooper

0

由于VB6方法的参数是Variant,因此您应该测试Nothing、Missing和Empty,因为它们是可能传递的“非值或对象”,当没有真正的值或对象可用时。

回答您的问题,我认为这是因为在VB6中,Variant默认为空。如果您的参数是Object类型,则会传递Nothing,在您的情况下。

您可以尝试从VB.Net传递Nothing并查看发生了什么,如果Nothing持续到VB6 DLL,则知道可以将其传递,并通过查看IL代码找到答案。


1
我认为尝试从 VB.Net 传递“Nothing”值是值得一试的,但我始终都认为 Vb.Net 中的“Nothing”相当于 C# 中的“null”。我认为更有前途的方案是研究如何定制从 .Net 到 COM 的转换选项。 - MarkJ
在托管世界中,VB.Net的Nothing和C#的null是相同的,但生成Interop DLL的Interop代码生成器工作方式不同。我认为这是因为在VB.Net团队独立于C#团队工作时,在开始时有不同的设计目标。我认为VB.Net团队有一个动力去考虑VB.Net如何与VB6代码交互,因此使用了不同的技术来实现他们的目标。 - Robert
Interop 代码生成器在 VB.Net 和 C# 中不是一样的吗?当然,它只有一个独立版本:tlbimp。http://msdn.microsoft.com/en-us/library/tt0cf3sx.aspx - MarkJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接