如何取一个字符串的地址? Dim strTest As StringstrTest = "ok"Debug.Print VarPtr(strTest)Debug.Print StrPtr(strTest) 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 你可以看看 学习再学习 地帖子!地址:http://www.csdn.net/expert/topic/533/533904.xml?temp=.5899774 回复人: AdamBear(学习再学习) ( ) 信誉:97 2002-2-20 18:55:25 得分:0 我的第一篇文章《VB到底为我们做了什么》已经发给了CSDN文档,已经两天了,还在审核中,不知是不是他们认为我的结论有问题。 文章有8000字,所以不好在这里贴。 只有继续等,想先看看的人,留下你们的Mail,给你们发。 第二篇《API和COM,文档里不清楚的问题》正在写,东西在我脑子里,不过,我想用实例和实验来说明问题,所以还要准备一些代码。 因为写东西,所以比较忙,只能断断续续来看看CSDN的贴子了。 下面这个贴子刚更新过,内存共享和进程通讯的新版本,前天刚加上的。我曾说这是这方面最快的方法,但在那个VC的DLL代码中有个位置我说错了,我在VC中用了ASNI和Unicode的转换,我说要避免这种转换只有什么Variant或Byte Array, 实际上就用BSTR也可以不转换,我是在写API调用的问题时突然意识到这个问题的,不过技术本身上应该还是最快的。你们可以看看下面的贴子中我的更新部分: http://www.csdn.net/Expert/Topicview.asp?id=516072 我也知道acptvb不好回答我这些问题,不过我们应该强烈要求他回答。我可以先告诉大家,由第1个问题而来的惊人猜想,我会在第一篇和第二篇文章里用实验来证明我的猜想,这个猜想就是凡是能够被重新定义的VBA函数在效率上就还有提高的必要。甚至就在VB中自己写也能写出比VBA函数更快的函数(如果你不怕可能产生的问题的话),当然VB已经对绝大部分计算相关的函数进行了优化,但仍有不少漏掉了,比如Rnd。Bruce是我佩服的一位大师,不是因为他的技术有多么历害,而是因为他的研究和创新精神,现在我把他的《VB核心编程》中关于CopyMemory的部分拿出给大家看看,让我们不能不佩服Bruce:同时也to Chice_wxg(我怕谁?我是谁!我是流氓我最贼。):【CopyMemory:一个奇怪而可怕的传奇】 关于这个用于复制内存内容的WIN32函数为什么叫CopyMemory,有一个很长的奇怪故事,尽管Visual Basic和Windows API中没有这种函数。 最开始是我想要查找WIN16 hmemcpy函数的WIN32等价函数,以用于VisualBasic 4.0版中。没有这个函数,哪怕是一个说明这个函数已经废弃的注释也没有。 最后我可提出的一个最接近的函数,CopyMemory函数,它与hmemcpy有着相同的参数,并且其文档也相同。不幸的是,不管你在WIN32文档中读过什么,其中没有像CopyMemory这样的东西。你可以使用DumpBin实用程序来搜索所有32位DLL,但是你将不会找到有任何DLL包含有CopyMemory。 如果你仔细地搜索WIN32的C包含文件,你将会在WINBASE.H中发现如下内容:#define CopyMemory RtlCopyMemory#define MoveMemory RtlMoveMemory#define ZeroMemory RtlZeroMemory 它指示CopyMemory是名为RtlCopyMemory的函数的另一个名称。不要问为什么;只需要在KERNEL32.DLL中查找RtlCopyMemory。同样,没有。WIN32包含文件中的其他跟踪显示了其原因:在WINNT.H中包含有类似于如下的内容:#define RtlCopyMemory(dst, src, len) memcpy(dst, src, len) 也就是说,RtlCopyMemory是C语言memcpy函数的一个别名,但是你不能在Basic中使用memcpy或其他任何C语言库函数。文档中声称CopyMemory是一个Windows函数而不是一个C语言函数,显然它是在说谎。如果它不是从一个DLL中输出,则你不能调用它。KERNEL32.DLL确实包含有一个用于RtlCopyMemory的项。如果你查看WIN32文档,你将看到MoveMemory与CopyMemory做相同的事情,除了它们处理重叠内存的方式不同之外。我不能想像这样一种情况,一个Basic程序将会去复制重叠的内存。没有理由不使用MoveMemory来代替它。名称CopyMemory看起来比hmemcpy或MoveMemory更智能一些,因此我对于16位和32位版本都使用这个别名:#If Win32 Then Declare Sub CopyMemory Lib "KERNEL32" Alias "tlMoveMemory"(_lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)#else Declare Sub CopyMemroy Lib "KERNEL" Alias "memcpy" ( _lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)#End If Windows API类型库具有一个CopyMemory函数的等价(或者几乎是等价)。上面解释了我为什么使用CopyMemory,但是为什么其他每一个人都使用CopyMemory呢?因为我把一份拷贝发送到了Microsoft的一个内部别名,并且某个读到它的人决定它应该成为一篇很好的Knowledge Base文章。我同意了,但条件是他们在提到这一段时说明这是摘录我的书。我认为这是一个好广告。自那以后,我读到了一些文章,在VBITS会议上听到一些人谈论CopyMemory,仿佛它真正存在一样。但是他们没有提到本书才是它的源来。因此不要被错误的广告所蒙蔽。如果他们谈论RtlMoveMemory,则他们是自己想出来的。如果他们谈论CopyMemory,则他们是从我这里得到的(或许他们自己还不知道)。 嘿嘿,怎么样,Bruce Mckinney酷! VarPtrStrPtr这两个函数是没有公布的具体使用情况请参阅Microsoft网站 我现在上网条件不好,所以更新较慢,很多东西只有线下写,下面的东西就是我今晚在家刚写好的: 好,我今天来解达我这四个问题。第1个问题,诚如我如说,是猜想,如果我在什么地方用了"推论"这个词,那是一时激动说错了。我在《VB到底为我们做了什么》中进行了实验,不过那都是表面上,你要验证只有自己去跟踪VB的程序,我这么做了,所以我了解一些不为人知的东西,不是我要卖关子,我是想让你们自己去试试,我在文章里有几处错误(找错是一种能力,Bruce大师的错误我就发现了几处),你如果自己跟踪过程序就能明白错在什么地方(可以给个提醒,我说rtc开头的引入函数被直接调用?我说__vbaAbsI4比rtcAbsVar快4倍?不要完全相信我的结论,自己去实验。那几个错误正是给那些懒汉们一点教训)。(to to hx(乱云山风)你觉得有错真不容易,我们就是要有这种不相信任何未经自己验证的技术之精神,如果你能发现我还不知道的错误,我另开贴子给你分!)当然要我自己给我的猜想的可信度打80分,但是这里面有意外,有些没有以__vba开头的函数也一样没有什么COM调用,一样经过优化,我下面要说的VarPtr就是这样一个不能再优化的函数。还有,虽然rtc开头的函数大部分由于COM调用而变慢,我们也应该相信VB开发小组这样做有他们的道理,因为他们要考虑到比我们更多的情况,更可况VB根本就建COM之上,有些东西想不用COM都不行。千万不要因为多用了一点额外的CPU时间,就否定一切COM调用,VB的成功有一半功劳要给COM开发小组,我们用的CInt,CLong之类的函数不过是VB开发小组对COM自动化数据类型转换函数一层薄薄的包装。VB和COM的高度合作是任何语言无法比拟的,这是他的优点。所以,能够被改写的以rtc开头的函数,你的确可以改写而获得性能上的些许提高,但你可能失去更多,比如VB文档里经常说的VB的稳定性,尤其在多线程环境里。我提出这个问题,只是想引起大家的思考,和对VB原理的探索,另外也是想哗众取宠而已。第2个问题,我本想在第二篇文章里讲,不过写文章不象写贴子,你可以顺着写,不改。我和第一篇文章就花了我两个整天,而现在我白天还要上班(不喜欢的工作也要干,因为我父母看不惯我整天在家里玩电脑),所以文章可能会更慢点,不过我不会食言。VarPtr, StrPtr, ObjPtr我现在告诉大家它们根本就是一个函数,在C里的样子是这样的:long VarPtr(void* pv){ return (long)pv;}仅此而已,不信你用我在第一篇文章里提供的察看类型库函数对应MSVBVM60.DLL中输出函数序号的小程序看看。它们的序号都是644, 对应的在DLL中的函数就是一个VarPtr,注意它没有用__vba开头,但我们来看看的汇编代码就知道它已经够优化了:j = VarPtr(i)004020FC lea eax,[i] 004020FF push eax '参数中的地址值压栈。00402100 call ___vba@00194188 (004011c2) '调用输入函数00402105 mov dword ptr [j],eax___vba@00194188: '跳转到输入函数,就是VarPtr的地址。004011C2 jmp dword ptr [__imp_@__vba@00194188 (0040108c)]'下面就是真正的VarPtr660E2C86 mov eax,dword ptr [esp+4]660E2C8A ret 4 '弹出压栈的参数的地址并返回。最后是一句: lea dword ptr[j], eax 相比其它VB函数,这是有限几个我用汇编看得懂的非常清晰的函数。如果你用j = StrPtr(strTmp)来看,仅仅是前两句压栈的方式不同,变成了push dword ptr[strTmp],所以同一个函数因为VB对其参数处理不同而有不同的功能,懂汇编的从上面就能看出,ValPtr返回的东西,是一个地址值,这个地址值所对应的指针所指之处和用StrPtr返回的指针所指是相同的。再看看下面的类型库,就知道为什么VB会这么做,根本就不是VB对VarPtr做了什么处理,不过是类型库定义上的小技巧而已,对第3个问题的解答同样需要API定义上的小技巧。这几个XxxPtr在类型库中是这样的: [entry(0x60000006), hidden] long _stdcall VarPtr([in] void* Ptr); [entry(0x60000007), hidden] long _stdcall StrPtr([in] BSTR Ptr); [entry(0x60000008), hidden] long _stdcall ObjPtr([in] IUnknown* Ptr);看看StrPtr,其中BSTR就是C里面的char*或unsigned short, 但由于压的是指针所指地址,因为返回的strTmp[0]的地址。 怎么样,我不再多说了,你可以想一想。我们传的是地址,API需要的是地址,为什么我们不能给Unicode API传地址,比如我们用SetWindowTextW为什么就不行,连Bruce大师都说不可以,其实Bruce大师想复杂了或者说Bruce大师产生了盲点,它认为为,API里定义的字符串是String, 而VB会对String进行Unicode到Ansi的转换,因此不用库型库你不可能调用Unicode的API,但是为什么我们非要给API传BSTR的String?我试过用ByRef的String,但该死的VB还是进行了转换,我在VC的DLL里收到还是一个指向临时ANSI字串的指针,我也一时产生盲点,连传指向String的指针的指针都要转换,看来是没办法了。盲点就是我们为什么非要在传String时传String, 我们对自定义类型不是一直传一个代表地址值的long型吗?有时候问题简单得你一时想不到。同时,在问题1中我们知道StrPtr我们可以改写。结合问题1和上面的提示你们能不能想出问题3的解决方案呢?算我布置的一个作业。 这个方案非常有实用价值,因为当程序使用有字串的API,面又在Win2000下运行时,VB会从U转到A,但Win2000又要从A转到U,两次不必要的转换开销很大,这个方案的价值就是:一我们可以象VC里一样通过不同编译参数编译不同平台的版本,二是再动动脑,我们还可以在运行时决定用什么的版本,虽然这样程序会大点,但能在两个平台上都高效运行。 另这个方法不知是不是我第一个想出来,我也不敢把这个方法打上我的商标,但希望你们能自己想出来,我思考着我快乐着。 我又花了这么多时间,呵呵,文章可能又要出慢点了。 VarPtr:变量的地址StrPtr:字符串的地址ObjPtr:对象的地址 散分 如何修改SysDateTimePick32 日期控件的日期? VB中如何比较时间在某个时间范围内? 请问用new生成的对象,怎么释放 VB中的commondialog中的SAVE对话框中按"确定"按纽的时候怎么实现判断文件名非空? 关于记录数 如何打包VB6编译的程序?(引用了ExcelXP对象) 谁有好思路,请进! 高手请进!应者有分 VB中如何使用SetBitmapDimensionEx? 关于在VB中控制WORD和EXCEL的问题! 送分,送免费短信帐号,求一问题。谢谢大家帮忙
我的第一篇文章《VB到底为我们做了什么》已经发给了CSDN文档,已经
两天了,还在审核中,不知是不是他们认为我的结论有问题。
文章有8000字,所以不好在这里贴。
只有继续等,想先看看的人,留下你们的Mail,给你们发。
第二篇《API和COM,文档里不清楚的问题》正在写,东西在我脑子里,
不过,我想用实例和实验来说明问题,所以还要准备一些代码。
因为写东西,所以比较忙,只能断断续续来看看CSDN的贴子了。
下面这个贴子刚更新过,内存共享和进程通讯的新版本,前天刚加上的。
我曾说这是这方面最快的方法,但在那个VC的DLL代码中有个位置我说错了,
我在VC中用了ASNI和Unicode的转换,我说要避免这种转换只有什么Variant或
Byte Array, 实际上就用BSTR也可以不转换,我是在写API调用的问题时突然
意识到这个问题的,不过技术本身上应该还是最快的。
你们可以看看下面的贴子中我的更新部分:
http://www.csdn.net/Expert/Topicview.asp?id=516072 我也知道acptvb不好回答我这些问题,不过我们应该强烈要求他回答。我可以先告诉大家,由第1个问题而来的惊人猜想,我会在第一篇和第二篇文章
里用实验来证明我的猜想,这个猜想就是凡是能够被重新定义的VBA函数在效率
上就还有提高的必要。甚至就在VB中自己写也能写出比VBA函数更快的函数(如
果你不怕可能产生的问题的话),当然VB已经对绝大部分计算相关的函数进行了
优化,但仍有不少漏掉了,比如Rnd。Bruce是我佩服的一位大师,不是因为他的技术有多么历害,而是因为他的研究和
创新精神,现在我把他的《VB核心编程》中关于CopyMemory的部分拿出给大家看看,
让我们不能不佩服Bruce:
同时也to Chice_wxg(我怕谁?我是谁!我是流氓我最贼。):【CopyMemory:一个奇怪而可怕的传奇】
关于这个用于复制内存内容的WIN32函数为什么叫CopyMemory,有一个很长的奇怪故事,尽管Visual Basic和Windows API中没有这种函数。
最开始是我想要查找WIN16 hmemcpy函数的WIN32等价函数,以用于VisualBasic 4.0版中。没有这个函数,哪怕是一个说明这个函数已经废弃的注释也没有。
最后我可提出的一个最接近的函数,CopyMemory函数,它与hmemcpy有着相同的参数,并且其文档也相同。不幸的是,不管你在WIN32文档中读过什么,其中没有像CopyMemory这样的东西。你可以使用DumpBin实用程序来搜索所有32位DLL,但是你将不会找到有任何DLL包含有CopyMemory。
如果你仔细地搜索WIN32的C包含文件,你将会在WINBASE.H中发现如下内容:
#define CopyMemory RtlCopyMemory
#define MoveMemory RtlMoveMemory
#define ZeroMemory RtlZeroMemory
它指示CopyMemory是名为RtlCopyMemory的函数的另一个名称。不要问为什么;只需要在KERNEL32.DLL中查找RtlCopyMemory。同样,没有。WIN32包含文件中的其他跟踪显示了其原因:在WINNT.H中包含有类似于如下的内容:
#define RtlCopyMemory(dst, src, len) memcpy(dst, src, len)
也就是说,RtlCopyMemory是C语言memcpy函数的一个别名,但是你不能在Basic中使用memcpy或其他任何C语言库函数。文档中声称CopyMemory是一个Windows函数而不是一个C语言函数,显然它是在说谎。如果它不是从一个DLL中输出,则你不能调用它。KERNEL32.DLL确实包含有一个用于RtlCopyMemory的项。如果你查看WIN32文档,你将看到MoveMemory与CopyMemory做相同的事情,除了它们处理重叠内存的方式不同之外。我不能想像这样一种情况,一个Basic程序将会去复制重叠的内存。没有理由不使用MoveMemory来代替它。名称CopyMemory看起来比hmemcpy或MoveMemory更智能一些,因此我对于16位和32位版本都使用这个别名:
#If Win32 Then
Declare Sub CopyMemory Lib "KERNEL32" Alias "tlMoveMemory"(_lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#else
Declare Sub CopyMemroy Lib "KERNEL" Alias "memcpy" ( _lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
#End If
Windows API类型库具有一个CopyMemory函数的等价(或者几乎是等价)。上面解释了我为什么使用CopyMemory,但是为什么其他每一个人都使用CopyMemory呢?因为我把一份拷贝发送到了Microsoft的一个内部别名,并且某个读到它的人决定它应该成为一篇很好的Knowledge Base文章。我同意了,但条件是他们在提到这一段时说明这是摘录我的书。我认为这是一个好广告。自那以后,我读到了一些文章,在VBITS会议上听到一些人谈论CopyMemory,仿佛它真正存在一样。但是他们没有提到本书才是它的源来。因此不要被错误的广告所蒙蔽。如果他们谈论RtlMoveMemory,则他们是自己想出来的。如果他们谈论CopyMemory,则他们是从我这里得到的(或许他们自己还不知道)。
嘿嘿,怎么样,Bruce Mckinney酷!
StrPtr这两个函数是没有公布的
具体使用情况请参阅Microsoft网站
就是我今晚在家刚写好的: 好,我今天来解达我这四个问题。
第1个问题,诚如我如说,是猜想,如果我在什么地方用了"推论"这个词,那是
一时激动说错了。我在《VB到底为我们做了什么》中进行了实验,不过那都是
表面上,你要验证只有自己去跟踪VB的程序,我这么做了,所以我了解一些不为
人知的东西,不是我要卖关子,我是想让你们自己去试试,我在文章里有几处错误
(找错是一种能力,Bruce大师的错误我就发现了几处),你如果自己跟踪过程序
就能明白错在什么地方(可以给个提醒,我说rtc开头的引入函数被直接调用?
我说__vbaAbsI4比rtcAbsVar快4倍?不要完全相信我的结论,自己去实验。那几个
错误正是给那些懒汉们一点教训)。
(to to hx(乱云山风)你觉得有错真不容易,我们就是要有这种不相信任何未经自己
验证的技术之精神,如果你能发现我还不知道的错误,我另开贴子给你分!)
当然要我自己给我的猜想的可信度打80分,但是这里面有意外,有些没有以__vba开头
的函数也一样没有什么COM调用,一样经过优化,我下面要说的VarPtr就是这样一个不
能再优化的函数。还有,虽然rtc开头的函数大部分由于COM调用而变慢,我们也应该
相信VB开发小组这样做有他们的道理,因为他们要考虑到比我们更多的情况,更可况
VB根本就建COM之上,有些东西想不用COM都不行。千万不要因为多用了一点额外的CPU
时间,就否定一切COM调用,VB的成功有一半功劳要给COM开发小组,我们用的CInt,
CLong之类的函数不过是VB开发小组对COM自动化数据类型转换函数一层薄薄的包装。
VB和COM的高度合作是任何语言无法比拟的,这是他的优点。所以,能够被改写的以
rtc开头的函数,你的确可以改写而获得性能上的些许提高,但你可能失去更多,比如
VB文档里经常说的VB的稳定性,尤其在多线程环境里。我提出这个问题,只是想引起大
家的思考,和对VB原理的探索,另外也是想哗众取宠而已。第2个问题,我本想在第二篇文章里讲,不过写文章不象写贴子,你可以顺着写,不改。
我和第一篇文章就花了我两个整天,而现在我白天还要上班(不喜欢的工作也要干,
因为我父母看不惯我整天在家里玩电脑),所以文章可能会更慢点,不过我不会食言。
VarPtr, StrPtr, ObjPtr我现在告诉大家它们根本就是一个函数,在C里的样子是这样
的:
long VarPtr(void* pv){
return (long)pv;
}
仅此而已,不信你用我在第一篇文章里提供的察看类型库函数对应MSVBVM60.DLL中
输出函数序号的小程序看看。它们的序号都是644, 对应的在DLL中的函数就是一个
VarPtr,注意它没有用__vba开头,但我们来看看的汇编代码就知道它已经够优化了:
j = VarPtr(i)004020FC lea eax,[i]
004020FF push eax '参数中的地址值压栈。
00402100 call ___vba@00194188 (004011c2) '调用输入函数
00402105 mov dword ptr [j],eax___vba@00194188: '跳转到输入函数,就是VarPtr的地址。
004011C2 jmp dword ptr [__imp_@__vba@00194188 (0040108c)]'下面就是真正的VarPtr
660E2C86 mov eax,dword ptr [esp+4]
660E2C8A ret 4 '弹出压栈的参数的地址并返回。
最后是一句: lea dword ptr[j], eax 相比其它VB函数,这是有限几个我用汇编看得懂的非常清晰的函数。
如果你用j = StrPtr(strTmp)来看,仅仅是前两句压栈的方式不同,
变成了push dword ptr[strTmp],所以同一个函数因为VB对其参数
处理不同而有不同的功能,懂汇编的从上面就能看出,ValPtr返回
的东西,是一个地址值,这个地址值所对应的指针所指之处和用StrPtr
返回的指针所指是相同的。再看看下面的类型库,就知道为什么VB
会这么做,根本就不是VB对VarPtr做了什么处理,不过是类型库定义
上的小技巧而已,对第3个问题的解答同样需要API定义上的小技巧。
这几个XxxPtr在类型库中是这样的:
[entry(0x60000006), hidden]
long _stdcall VarPtr([in] void* Ptr);
[entry(0x60000007), hidden]
long _stdcall StrPtr([in] BSTR Ptr);
[entry(0x60000008), hidden]
long _stdcall ObjPtr([in] IUnknown* Ptr);
看看StrPtr,其中BSTR就是C里面的char*或unsigned short, 但由于
压的是指针所指地址,因为返回的strTmp[0]的地址。
怎么样,我不再多说了,你可以想一想。我们传的是地址,API需要的是地址,为什么我们不能给Unicode API
传地址,比如我们用SetWindowTextW为什么就不行,连Bruce大师都说
不可以,其实Bruce大师想复杂了或者说Bruce大师产生了盲点,它认为
为,API里定义的字符串是String, 而VB会对String进行Unicode到Ansi的
转换,因此不用库型库你不可能调用Unicode的API,但是为什么我们非要
给API传BSTR的String?我试过用ByRef的String,但该死的VB还是进行了
转换,我在VC的DLL里收到还是一个指向临时ANSI字串的指针,我也一时
产生盲点,连传指向String的指针的指针都要转换,看来是没办法了。
盲点就是我们为什么非要在传String时传String, 我们对自定义类型不是
一直传一个代表地址值的long型吗?有时候问题简单得你一时想不到。
同时,在问题1中我们知道StrPtr我们可以改写。结合问题1和上面的提示
你们能不能想出问题3的解决方案呢?算我布置的一个作业。
这个方案非常有实用价值,因为当程序使用有字串的API,面又在
Win2000下运行时,VB会从U转到A,但Win2000又要从A转到U,两次不必要
的转换开销很大,这个方案的价值就是:一我们可以象VC里一样通过不同
编译参数编译不同平台的版本,二是再动动脑,我们还可以在运行时决定
用什么的版本,虽然这样程序会大点,但能在两个平台上都高效运行。
另这个方法不知是不是我第一个想出来,我也不敢把这个方法打上我的
商标,但希望你们能自己想出来,我思考着我快乐着。 我又花了这么多时间,呵呵,文章可能又要出慢点了。
StrPtr:字符串的地址
ObjPtr:对象的地址