简而言之; 使用StrPtr
没有真正的风险,但也没有实际的好处。
虽然从InputBox
调用中看起来您会得到一个空指针,但实际上不是这样的。将StrPtr
的结果与VarPtr
进行比较:
Sub Test()
Dim result As String
result = InputBox("Enter something.") 'Hit cancel
Debug.Print StrPtr(result) '0
Debug.Print VarPtr(result) 'Not 0.
End Sub
这是因为InputBox
返回的是一个带有VT_BSTR
子类型的Variant
。以下代码演示了这一点(请注意,我已将result
声明为Variant
,以防止它被隐式转换 - 关于此更多信息请参见下面):
Sub OtherTest()
Dim result As Variant
result = InputBox("Enter something.") 'Hit cancel
Debug.Print StrPtr(result) '0
Debug.Print VarPtr(result) 'Not 0.
Debug.Print VarType(result) '8 (VT_BSTR)
Debug.Print TypeName(result) 'String
End Sub
StrPtr
返回0的原因是因为
InputBox
的返回值实际上存在格式错误(我认为这是实现中的一个错误)。
BSTR是一种自动化类型,它在实际字符数组前面加上字符串长度。这避免了C风格的以空字符终止的字符串自动化中存在的一个问题 - 您要么必须将字符串长度作为单独参数传递,要么调用者不知道将要接收多大的缓冲区。
InputBox
返回值的问题在于,它包装的
Variant
在数据区域中包含一个空指针。通常,这将包含字符串指针 - 调用者将解引用数据区域中的指针,获取大小,创建缓冲区,并读取跟随长度头的N个字节。通过在
数据区域中传递空指针,
InputBox
依赖于调用代码来检查数据类型(
VT_BSTR
)实际上是否与数据区域(
VT_EMPTY
或
VT_NULL
)相匹配。
将结果作为StrPtr
检查实际上是依赖于该函数的怪癖。当它在Variant
上被调用时,它返回指向数据区域中存储的底层字符串的指针,并通过长度前缀进行偏移,使其与需要C字符串的库函数兼容。这意味着StrPtr
必须在数据区域上执行空指针检查,因为它不返回指向实际数据开头的指针。此外,像任何其他在数据区域中存储指针的VARTYPE一样,它必须进行两次解引用。 VarPtr
实际上会给您一个内存地址,因为它会给出您传递的任何变量的原始指针(数组除外,但这不在此范围之内)。
因此...它与使用Len
没有什么不同。 Len
只返回BSTR
头中的值(根本不计算字符数),并且由于类似StrPtr
的原因,它也需要进行空测试。 它得出的逻辑结论是空指针长度为零 - 这是因为vbNullstring
是空指针:
Debug.Print StrPtr(vbNullString) '<-- 0
话虽如此,你现在依赖的是InputBox
中存在问题的行为。如果微软修复了实现(他们不会这么做),那么你的代码就会出现问题(这就是他们不会这么做的原因)。但一般来说,不要依赖这种不可靠的行为是一个更好的主意。除非你想要区分用户单击“取消”和用户未输入任何内容并单击“Enter”,否则使用StrPtr(result) = 0
没有太多意义,而用Len(result) = 0
或result = vbNullString
则更加清晰明了。我认为如果你需要做出这种区分,应该自己制作一个UserForm
并在自己的对话框中显式地处理取消和数据验证。
Application.InputBox
并获取一个真正的 Variant,该 Variant 始终具有StrPtr。 - ThunderFrameStrPtr
”。 - Mathieu GuindonInputBox
是VBA.InputBox
,它返回一个String
,而不是Variant/String
。在线文档中错误地省略了As String
,但在对象浏览器中查看时则正确包含了As String
。检查其返回值的StrPtr
是检测取消操作的唯一方法。相反,Application.InputBox
返回一个 Variant,你应该检查是否等于False
。 - GSergVarPtr
的涉及是另一个令人困惑/无关的点,因为当然你声明的变量位于内存中的某个位置,所以它的VarPtr
总是等于某个值,无论你是否将InputBox
的结果存储在其中。而且,即使你这样做了,那个VarPtr
也不会改变。 - GSerg