仍是从已知的指针获取不定长的空结尾字符串问题

shier2817 2009-07-22 05:01:03
前段时间我发过这样一个提问,帖子在:
http://topic.csdn.net/u/20090704/02/9bfc2a38-d97f-44bf-8c46-f0c684566994.html
当时按照我6楼的自己答案以为是没啥问题了,但实际使用时发现有些情况(也不知到底啥原因)就是获取不到。

后来我又找了些资料,写了个更简单的:

Private Declare Function lstrcpyA Lib "kernel32" (ByVal RetVal As String, ByVal Ptr As Long) As Long
Private Declare Function lstrlenA Lib "kernel32" (ByVal Ptr As Any) As Long
'获取指定字符串指针包含的字符串文本(以 0 结尾字符串)
Private Function GetStrFromPtrA(ByVal lpszA As Long) As String
GetStrFromPtrA = String$(lstrlenA(ByVal lpszA), 0)
Call lstrcpyA(ByVal GetStrFromPtrA, ByVal lpszA)
End Function

以为这个也没问题了。。在多数时候测试都能获取的到,可随着写程序发现这个也是同样的毛病,有的时候可以,有的时候得到的却是空,因此我干脆把 GetStrFromPtrA 的空间弄到最大,就写了这个:

'获取指定字符串指针包含的字符串文本(以 0 结尾字符串)
Private Function GetStrFromPtrA(ByVal lpszA As Long) As String
GetStrFromPtrA = String$(MAX_PATH, vbNullChar) 'MAX_PATH = 260
Call lstrcpyA(ByVal GetStrFromPtrA, ByVal lpszA)
GetStrFromPtrA = Left$(GetStrFromPtrA, InStr(1, GetStrFromPtrA, vbNullChar) - 1)
End Function

这次就是直接把字符串先假设足够长,再拷贝最后给截断。。。原本以为可以了。。。也确实是比上几个方法好用的多,有些以前的方法不能获取到的,现在都可以获取了。。。
但是我突然发现,也不是全部能获取。。。用来测试的话,可以写这么个函数:

Private Declare Function PathFindFileName Lib "shlwapi" Alias "PathFindFileNameA" (ByVal pPath As String) As Long

'获取指定路径中的文件名或最后的子目录名
Public Function FindFileName(ByVal lpPath As String) As String
Dim lRet As Long
lpPath = lpPath & vbNullChar
lRet = PathFindFileName(lpPath) '这个取得的就是要获取的字符串的内存指针了
FindFileName = GetStrFromPtrA(lRet) '尝试用上面的子程序获取
End Function

这个函数来获取文件名的话:输入 FindFileName("c:\123\abc.txt") 可以得到 abc.txt,而输入 FindFileName("c:\abc.txt") 那获取的竟然是空的。。。(基于最后一种 GetStrFromPtrA 而言)。但是如果调试指针(lRet)是有实际值的,并且如果调试第一种 GetStrFromPtrA 中的 lstrlenA(ByVal lpszA) 语句,同样=7,也就是说应该有7个字节(即abc.txt)被拷贝的,可为什么是空呢?
现在我想,原因可能出在我的API 声明上,就是 lstrcpyA 声明的参数格式,可我没改明白。。。哪位前辈懂。。请帮忙指点一下,感激不尽!!!

另:1、我不想用以前老帖子里那种一个一个字符判断直到空字符退出的办法,除非我现在这个函数确实存在BUG!
2、这里给的 FindFileName 获取文件名仅是为了测试(因为只为这个函数的话那么可以用判断路径最后的“\”字符来截取的笨办法!)程序中除了这个地方使用 GetStrFromPtrA 之外,还有其他的位置也需要获取内存指针处的字符串的(实际是回调函数中的参数表示的是字符串内存指针)。
...全文
116 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
yachong 2009-07-23
  • 打赏
  • 举报
回复
学习!
Tiger_Zhao 2009-07-23
  • 打赏
  • 举报
回复
问题出在 PathFindFileName 上,它的返回指针指向参数 lpPath 的字符串。
而你将参数 lpPath 声明为 String,VB 在调用时会自动进行 Unicode-Ansi 转换,用这个转换的临时字符串传递给 PathFindFileName,这样返回的指针指向的是临时字符串的内容。
然后你复制字符串时,如果该临时变量释放后的内存被重新使用或清零,就不能取得正确值。

GetStrFromPtrA() 没问题,正确用法如下:
Private Declare Function PathFindFileName Lib "shlwapi" Alias "PathFindFileNameA" (ByVal pPath As Long) As Long

Public Function FindFileName(ByVal lpPath As String) As String
Dim lRet As Long
Dim a() As Byte

a = StrConv(lpPath & vbNullChar, vbFromUnicode)
lRet = PathFindFileName(VarPtr(a(0)))
FindFileName = GetStrFromPtrA(lRet)
End Function
「已注销」 2009-07-23
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 tiger_zhao 的回复:]
问题出在 PathFindFileName 上,它的返回指针指向参数 lpPath 的字符串。
而你将参数 lpPath 声明为 String,VB 在调用时会自动进行 Unicode-Ansi 转换,用这个转换的临时字符串传递给 PathFindFileName,这样返回的指针指向的是临时字符串的内容。
然后你复制字符串时,如果该临时变量释放后的内存被重新使用或清零,就不能取得正确值。

GetStrFromPtrA() 没问题,正确用法如下:
VB codePrivate DeclareFunction PathFindFileName Lib"shlwapi" Alias"PathFindFileNameA" (ByVal pPathAsLong)AsLongPublicFunction FindFileName(ByVal lpPathAsString)AsStringDim lRetAsLongDim a()AsByte

a= StrConv(lpPath& vbNullChar, vbFromUnicode)
lRet= PathFindFileName(VarPtr(a(0)))
FindFileName= GetStrFromPtrA(lRet)End Function
[/Quote]

我靠。。。还有这说法。。。这真是学到了东西,以前都没有想过这方面的问题。。。高,高手啊,感谢感谢!!!
「已注销」 2009-07-22
  • 打赏
  • 举报
回复
。。。。我的问题咋没人回捏???




「已注销」 2009-07-22
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 cqq_chen 的回复:]
字符串操作用得了api吗?
[/Quote]

某些API,如上面提到的PathFindFileName,还有PathFindExtension等等,MSDN的声明中其返回值是字符串的缓冲区指针。指针自然在VB里声明为 Long 了,而要是强行声明其返回值为 String 的话是会提示错误的。
另外更常见的会有一些API函数带有回调函数,而回调函数中的某个参数其实是某个字符串值的缓冲区指针,对于个别的还有另一个参数指明了该缓冲的长度,那就好办了——直接 CopyMemory 即可。但是如果没有额外的标记指明这个缓冲的长度,那么就跟前面的情况类似了。。

这样就出现一个问题:VB中已经知道某个字符串数据的内存地址(缓冲区指针),但并不知道其具体长度,仅知道它是以 空结尾(vbNullChar 或者写为 chr(0)),那么如何获取到这个字符串的实际内容?

最为简单的思路,但应该不是最为效率的方法,就是从这个指针开始读一个字节,判断不是 空 则指针++,然后再读一个字节,直到为 空 则退出,将读到的字节转换为字符串就可以了。。。

但正如上面所说,这样并不效率,而 lstrcpy 函数按字面意思就可以看出是 字符串拷贝,实际作用是将第2个参数的字符串拷贝到第一个参数指定的缓冲区之中,返回值是这个缓冲区的指针(如果成功。)
其要求是第1个参数必须足够大可以容纳的下第2个字符串参数,第2个参数必须是以空结尾的字符串。
cqq_chen 2009-07-22
  • 打赏
  • 举报
回复
字符串操作用得了api吗?
贝隆 2009-07-22
  • 打赏
  • 举报
回复
学习

7,763

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧