暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

从VBA的vbNullString认识API参数传递

VBA 学习 2021-07-30
756



最近在写个简单的程序时,用到了API FindWindow,复制声明后,直接就把代码写了,可是发现一直找不到窗口,代码:

    Sub testFindWindow()
    Dim str As String

    str = "新建文本文档.txt - 记事本"

        Debug.Print str, FindWindow("", str)
    End Sub

    然后自己就以为是不是窗口名称写错了,检查好久才发现是第一个参数错了!


    其实这个参数要传递vbNullString一直是知道的,但没有仔细想过,知其然不知其所以然,正确代码应该是:

      Sub testFindWindow()
      Dim str As String

          str = "新建文本文档.txt - 记事本"

          Debug.Print str, FindWindow(vbNullString, str)
      End Sub


      这个错误是只使用VBA的人容易犯的吧,总以为空字符""vbNullString是一回事,那么,它们有什么区别呢?

      ""和vbNullString

      首先查看帮助文件:vbNullString 值为 0 的字符串,用来调用外部过程;与长度为零的字符串 ("") 不同 

      帮助文件已经告诉我们这2个是不一样的,可是我们使用VBA再写个简单的测试代码又会发现奇怪的地方:

        Sub TestNullString()
        Dim str As String

        Debug.Print str, str = "", str = vbNullString

        str = ""
        Debug.Print str, str = "", str = vbNullString

        Dim v1 As Variant, v2 As Variant
        v1 = VBA.Strings.StrComp(str, "", vbBinaryCompare)
        v2 = VBA.Strings.StrComp(str, vbNullString, vbBinaryCompare)

        Debug.Print str, v1, VBA.IsNull(v2)
        End Sub


        '输出:
        True True
        True True
        0 False


        从输出来看,用=StrComp进行对比,这2个东西是相同的!这又是为什么呢!


        这里只能进行猜测了,VBA在比较2个字符串的时候,可能是先读取长度,如果都为0,则判断为相同了,""vbNullString在使用LEN函数的时候,返回的都是0。


        那么,它们2个不同之处在哪里呢?

        这个可以使用Strptr来查看,""这个是分配了地址的,vbNullString是没有初始化的,这就是它们2个的最大不同之处:

          Sub TestSrtPtr()
          Debug.Print StrPtr(""), StrPtr(vbNullString)
          End Sub
          '输出 163726236 0


          那么在使用API传递String类型参数的时候,如果需要传1个空字符,非得要vbNullString吗?

          既然vbNullString是一个没有初始化的字符串,那么,其实这样传递也是一样:

            Sub testFindWindow()
            Dim str As String
            Dim tmp As String

            str = "新建文本文档.txt - 记事本"

            Debug.Print str, FindWindow(tmp, str)
            End Sub

            这样就可以得到正确结果了,声明了一个tmp变量,在没有初始化的时候,它就是一个vbNullString


            API String类型参数传递

            从帮助文件中知道,vbNullString 值为 0 的字符串,如果真的传递0过去,很明显也是不行的,数据类型就不对,所以这个只是一个标志,VBA编译器会具体去处理这种情况。


            对API的参数传递,VBA为我们做了太多了,以至于使用者不需要明白底层原理就可以简单的使用。


            如果了解一点C语言的知识,我们就能大概理解了。

            在C语言里,并没有String类型,只有Char类型(也就是VBA里的Byte),而API里的String类型其实就是Char数组的指针,VBA在API参数传递的时候,碰到String类型,它又帮我们做了什么?

            VBA会帮使用者将VBA的String类型首先从Unicode转换为ANSI编码,然后取出转换后的Char数组的第一个地址,再将这个地址传递给了API,API如果有返回值,VBA就会做一个相反的操作,测试代码:

              Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
              Private Declare Function FindWindowByPtr Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long


              Sub testFindWindowPtr()
              Dim str As String
              Dim tmp As String

              str = "新建文本文档.txt - 记事本"

              Debug.Print str, FindWindow(vbNullString, str)
              Debug.Print str, FindWindowByPtr(0, StrPtr(str))

              Dim strANSI As String
              strANSI = VBA.StrConv(str, vbFromUnicode)
              Debug.Print str, FindWindowByPtr(0, StrPtr(strANSI))
              End Sub
              '输出:
              新建文本文档.txt - 记事本 67244
              新建文本文档.txt - 记事本 0
              新建文本文档.txt - 记事本 67244


              这里声明了一个FindWindowByPtr函数,只是把FindWindow的参数由String类型修改为了Long类型,从代码的输出过程就可以看到,如果不进行String类型的编码转换,FindWindowByPtr得不到正确的结果。

              而在FindWindow里,VBA编译器自动为使用者做了处理,所以我们在使用API的时候,根本就不需要关注这个。这也容易造成VBA使用者不清楚原理,出现错误的时候很难找到具体的原因。






              文章转载自VBA 学习,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论