Difference between VBA.CBlah and CBlah

怎甘沉沦 提交于 2021-02-07 14:09:47

问题


Weird observation:

Normally when I want to save an address to a function in a variable, I do something like this:

Function getAddress(ByVal func As LongPtr) As LongPtr
    getAddress = func
End Function

Sub printAddress()
    Dim functionPointer As LongPtr
    functionPointer = getAddress(AddressOf myFunc)
    Debug.Print functionPointer
End Sub

However I just discovered I can use a 1-liner

functionPointer = VBA.CLngPtr(AddressOf myFunc)

while

functionPointer = CLngPtr(AddressOf myFunc)

... does not work and raises

Compile error:

Expected: expression

What's going on? The only difference as far as I'm aware is that CLngPtr is declared in the globals (class?) whereas VBA.CLngPtr is explicitly qualified, but I have no idea why that would lead to the observed behaviour (they both point to the same function don't they?)


回答1:


This would be not very obvious if you used the default IDE settings which the keywords and identifiers aren't really set that differently. Here's how it looks when you use different colors:

You can see that CLngPtr lights up like a christmas tree and looks just like any other keywords. Compare this with Abs, which is also a function but stays light blue, as if it was just an identifier.

This is a hint that CLngPtr is optimized by VBA compiler so it's actually inlining the method1, which is why you get error if you try to use CLngPtr as an expression. However, a VBA.CLngPtr is a proper function and thus can be used as a part of an expression but with very slight performance penalty due to non-optimizing route.

You would see the same thing with say, CLng or any conversion functions, and even Mid the statement (not function). There are several functions within VBA that may get inlined by the compiler and usually can be differed by whether they turn into keywords or not. Note also that the parenthesis are colored differently.

Heck, even Debug.Print gets the special treatment, too and if you are familiar with it, you may know that it's not exactly a class nor a module, yet you can't Print without Debug.


  1. When we refer to "inlinling" here, we are talking about what the VBA compiler is doing at lower level, below than what we see at the source code level. From the source code, C***() and VBA.C***() are basically the same thing. However, the VBA compiler can and will try to optimize those bits, by internally rearranging the machine instructions for converting (or whatever the inlined function is doing). The effect of rearranging the instruction is that it might no longer be compatible in all contexts. In this case, I can imagine (but do not know for a fact!) that CLngPtr()'s inlined instructions returns a value, rather than a reference, which is incompatible for a parameter declaration, which is why we get a syntax error when we try to use it as a parameter. Note this does not happen with AddressOf -- any other function on the LHS will have the same syntax error, so it has nothing to do with the AddressOf and everything with the method being inlined.



回答2:


Not an answer, but more weirdness to ponder...

With the noted compile error in the comment, this works (ref):

Sub TestCasting()
    Dim value As Variant
    Debug.Print "value cast with     CBool  : " & CBool(value)
    Debug.Print "value cast with VBA.CBool  : " & VBA.CBool(value)

    Debug.Print "value cast with     CByte  : " & CByte(value)
    Debug.Print "value cast with VBA.CByte  : " & VBA.CByte(value)

    Debug.Print "value cast with     CCur   : " & CCur(value)
    Debug.Print "value cast with VBA.CCur   : " & VBA.CCur(value)

    Debug.Print "value cast with     CDate  : " & CDate(value)
    Debug.Print "value cast with VBA.CDate  : " & VBA.CDate(value)

    Debug.Print "value cast with     CDbl   : " & CDbl(value)
    Debug.Print "value cast with VBA.CDbl   : " & VBA.CDbl(value)

    Debug.Print "value cast with     CDec   : " & CDec(value)
    Debug.Print "value cast with VBA.CDec   : " & VBA.CDec(value)

    Debug.Print "value cast with     CInt   : " & CInt(value)
    Debug.Print "value cast with VBA.CInt   : " & VBA.CInt(value)

    Debug.Print "value cast with     CLng   : " & CLng(value)
    Debug.Print "value cast with VBA.CLng   : " & VBA.CLng(value)

    '--- Compile Error: Sub or Function not found
    '    (error displayed at run time)
    'Debug.Print "value cast with     CLngLng: " & CLngLng(value)
    'Debug.Print "value cast with VBA.CLngLng: " & VBA.CLngLng(value)

    Debug.Print "value cast with     CLngPtr: " & CLngPtr(value)
    Debug.Print "value cast with VBA.CLngPtr: " & VBA.CLngPtr(value)

    Debug.Print "value cast with     CSng   : " & CSng(value)
    Debug.Print "value cast with VBA.CSng   : " & VBA.CSng(value)

    Debug.Print "value cast with     CStr   : " & CStr(value)
    Debug.Print "value cast with VBA.CStr   : " & VBA.CStr(value)

    Debug.Print "value cast with     CVar   : " & CVar(value)
    Debug.Print "value cast with VBA.CVar   : " & VBA.CVar(value)

End Sub

And adding to your code example above:

Function myFunc() As String
    myFunc = "help!"
End Function

Function getAddress(ByVal func As LongPtr) As LongPtr
    getAddress = func
End Function

Sub printAddress()
    Dim functionPointer As LongPtr
    functionPointer = getAddress(AddressOf myFunc)
    Debug.Print functionPointer
    functionPointer = VBA.CLngPtr(AddressOf myFunc)

    '--- Compile Error: Syntax Error
    '    (error displayed in red in VBA Editor)
    'debug.Print CLngPtr(AddressOf myFunc)
    'functionPointer = CLngPtr(AddressOf myFunc)
    Debug.Print functionPointer
End Sub


来源:https://stackoverflow.com/questions/57395851/difference-between-vba-cblah-and-cblah

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!