例如:Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal (not currently supported), Date, String (for variable-length strings), String * length (for fixed-length strings), Object, Variant, a user-defined type, or an object type.
等等,有没有类似于vc的sizeof函数得出某一数据类型所占用的内存大小呢?
非常感谢

解决方案 »

  1.   

    integer   2字节
    long       4
    single     4
    double      8
    变长string    10 + 字符串长度
    定长string    实际长度
    date        8
    currency      8
    variant      >=16
    boolean      2
    object     对象变量  4
               对象     视情况了
      

  2.   

    谢谢csdngoodnight,请问我自己定义的类的长度怎么计算呢?
    另外adodb的connection.command,recordset等等怎么计算呢?
    有没有类似于len的统一方法,或者通过调试工具、第三方工具来查看某一个变量的内存?
      

  3.   

    另外Collection的长度怎么计算呢?
    非常感谢!!!!!
      

  4.   

    应该用lenb(得知变量占用记忆体几个Byte)函数:
    Option ExplicitPrivate Sub Command1_Click()
        Dim i As Integer
        Dim j As Long
        Dim k As Double
        Dim l As Single
        MsgBox "Integer size is " + CStr(LenB(i))
        MsgBox "long size is " + CStr(LenB(j))
        MsgBox "Double size is " + CStr(LenB(k))
        MsgBox "Single size is " + CStr(LenB(l))
        '其余的类似写
    End Sub对于字串的处理不能单纯用lenb实现,原因是vb以Unicode方式编码,无论中英文,每个字符它都认为占2个字节
    可以这么处理:
    LenB(StrConv("中英Mixed", vbFromUnicode))对于集合(不支持lenb,但集合中的元素支持lenb),可先取得元素个数,然后分别计算每个元素所占内存,最后累加
      

  5.   

    关于字符串的长度不同意楼上的说法:LenB(StrConv("中英Mixed", vbFromUnicode))只是求得当它转换成ANSI字符串时的长度,它本身在内存中以UNICODE存在,因此,它的长度是lenB(myString);还有,VB字符串的头部存放的是一个长度,由于有32K的限制,所以可以肯定是个integer,但不知csdngoodnight(居然比我还快,你真行!) 的10+实际长度是如何得来的。就算加上它的那个指针,也就是6+实际长度。望指教!对于集合的长度,也值得推敲,每个元素的长度之和就是集合的长度?那索引之类的信息在哪里?似乎不妥啊?
      

  6.   

    根据VC和Delphi的说法,自己定义的类的长度等于各个成员变量的总和加上虚函数表的长度,如果没有虚函数,那么自己定义的类的长度等于各个成员变量的总和。VB的没看过介绍。
      

  7.   

    可变长度字符串是一个指向字符的指针。
    第一个字符前是此字符串以字节表示的长度,类型是Long。csdngoodnight(居然比我还快,你真行!) 所说的"10+实际长度"
    大概是:(指针(4) + 字符串长度(4) + 字符串结尾符(2) + 实际长度) == (10 + 实际长度)字符串结尾符等于 0,用来表示字符串的终止。因为是Unicode,所以长度是2。定长字符串其实和普通的c下的short数组一样。
      

  8.   

    在多数代码优化技术中都包括了从代码中删除不必要的元素,而下面元素的长度和数量是无须限制便可删除的:  1.标志符名称  2.注释  3.空行  其他元素,如变量、窗体和过程,确实要占用内存的一部分空间,最好将它们精简以使效率更高。当应用程序作为一个.exe文件运行时,有几项技术可用于缩小应用程序所需内存。以下技术可缩小代码大小:  1.减小加载窗体数目   2.减小控件数目   3.用标签代替文本框   4.保持数据在磁盘文件和资源中,并且只在最需要时才加载   5.组织模块   6.考虑替换Variant数据类型   7.使用动态数组,并在删除时回收内存空间   8.消除死代码和无用的变量   9.回收被字符串和对象变量用过的空间  一、每一个加载的窗体,无论是与否,都要占用一定数量的内存(其数量随着窗体上控件的类型和数量,以及窗体上位图的大小等不同而有所改变)。所以,为了节约内存,我们只在需要时才加载窗体,不需要时卸载窗体(而不是隐藏窗体)。记住:任何对窗体的引用,和对用New声明的窗体变量的引用,都会导致VB加载该窗体。  当使用Unload方法卸载窗体时,只能卸载释放掉部分窗体所占的空间。若想释放所有空间,可用关键字Nothing使窗体的引用无效,如:  Set From=Nothing   二、设计应用程序时,窗体应尽量少用控件。实际的限制取决于控件的类型和系统。实际上,含有大量控件的窗体将运行缓慢。一项与之相关的技术就是:在设计时,尽可能使用控件数组,而不是在窗体上放置大量同类型的控件。  三、标签控件占用的Windows资源比文本框少,因此,在可能的情况下,应使用标签代替文本框。  例如,当窗体上需要一个隐藏的控件保存文本时,使用标签应该更为有效。如果是需要大量文本域的数据输入窗体,也可以用这种技术优化。这时,我们可以针对每一个域创建一个标签,并使用单文本框输入,在LostFocus事件中移动它到下一个标签的位置:
    Prirate Sub Label1_LostFocus 
    Update Label1
    Label1.Option=Text1.text
    Text1.Move Label2.LeftLabel2.Top
    Text1.Text1 contents
    Text1.Text=Label2.Caption
    End Sub    通过适当设置BackColor和BorderStyle的属性,可以使标签看起来像文本框,虽说这会需要更多的代码,但对于含有大量文本域的窗体,它可以大大地减少资源的使用。  四、在设计中,直接放入应用程序的数据(像属性或代码中的文字字符串和数值)将增加运行时程序占用的内存。运行时从磁盘文件和资源中加载数据可以减少内存占用。这对大的位图和字符串特别有价值。  五、VB只在需要时才加载模块,即当代码调用模块中的一个过程时,模块才被加载到内存中。若从未调用一特定模块中的过程,VB决不会加载该模块。因此,尽量把相关的过程放在同一个模块中,让VB只在需要时才加载模块。  六、Variant数据使用极其灵活,但占用内存大。所以,我们应考虑用其他数据类型代替Variant变量,特别是替代Variant变量数组。  每一个Variant占用16个字节,而Integer占用2个字节,Double占用8个字节,长字符串变量占用4个字节,加上字符串中每一个字符占用1个字节,共5个字节。但Variant字符串变量会占用16个字节,再加上字符串中每一个字符占用一个字节。因为它们太大,消耗堆栈空间特别快,所以特烦人,尤其是在作局部变量或过程的参数时。但有些情况下,Variant变量的灵活性又能给程序带来极大的方便,所以,用不用这项优化技术应根据应用程序的情况来作取舍。  七、这项技术相信大家并不陌生。动态数组可用Erase或ReDim Preserve收回数组所占用的内存。例如:Erase MyArray,Erase用于完全删除数组;ReDim Preserve则只缩短数组而不丢失其内容:  ReDim Preserve MyArray10smallernum  删除固定数组不能收回它占用的内存空间,只可简单地清除数组内每一个元素。如果元素是字符串,或包含字符串或数组的Variant变量,那删除数组可回收这些字符串或Variant变量所占用的内存,而不是数组本身所占用的内存。  八、当过程结束时,可自动回收(非静态)局部字符串和数组变量所占用的内存空间。但是,全局、模块级的字符串和数组变量一直存活到整个程序结束。要使应用程序尽量小,应尽可能地回收这些变量所占用的空间。  九、再开发和修改程序时,可能遗留了一些死代码——代码中的一个完整过程,而它并没有被任何地方调用,也可能声明了一些不用的变量。虽然再创建.exe文件时,VB确实可删除无用的常数,但不能删除无用的变量和死代码。  要消除死代码,可在”编辑“菜单中,使用”查找“命令搜索特定变量的引用;或者,当每个模块都含有Option Explicit语句时,通过删除或注释该变量的声明,并运行应用程序,可迅速发现变量是否被调用,若被调用,则VB会出错,否则就没被使用。
      

  9.   

    VirtualQueryEx和ReadProcessMemory/WriteProcessMemory的使用。本来只是想看看ReadProcessMemory/WriteProcessMemory函数怎么使用,于是就想到编写一个类似游戏修改器的东西,但搜索4G的地址空间显然不现实,于是求助于VirtualQueryEx。先说VirtualQueryEx,这个函数的作用是查询指定进程中某个地址的信息,如哪些内存是空闲的,哪些是被占用区等等,它在VB自带的API浏览器里的声明是Public Declare Function VirtualQueryEx Lib "kernel32" Alias "VirtualQueryEx" (ByVal hProcess As Long, lpAddress As Any, lpBuffer As MEMORY_BASIC_INFORMATION, ByVal dwLength As Long) As Long
    其中lpAddress是要查询的地址值,它用了Any类型,而且是引用传参。这样当然有它的道理,但我需要查某个指定地址(例如0x123456)的信息就显得麻烦了,因为这个参数在C语言里是个void型的指针,这是个32位的值,因此我决定把它改为ByVal lpAddress as Long。另外,像MEM_COMMIT = &H1000&这些常用的常数在VB自带的API浏览器里居然没有,真是可气!只好自己加了。先举个例子吧,看。这个函数是求某个进程占用内存的大小。Public Function GetTotalCommittedMemory(ByVal ProcessId As Long) As Long
    Dim hProcess As Long
    Dim mi As MEMORY_BASIC_INFORMATION
    Dim pAddr As Long
    Dim dwTotalCommit As Long
    Dim ret As Long
    Dim miLen As Long
    miLen = Len(mi)
    dwTotalCommit = 0'这是结果
    pAddr = 0'这个时查询起始地址,设为0,即进程虚拟地址开始处。hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, ProcessId)'首先打开进程供查询信息
    ret = VirtualQueryEx(hProcess, pAddr, mi, miLen)'从起始地址开始查询
    Do While (ret = miLen)
    If mi.State = MEM_COMMIT Then'如果State是MEM_COMMIT则表明这块内存被占用,RegionSize是这一块内存的大小,这一块内存的State都一样
    dwTotalCommit = dwTotalCommit + mi.RegionSize
    End If
    pAddr = mi.BaseAddress + mi.RegionSize'跳过已经查询过的内存块,到未被查询的内存地址起始处
    ret = VirtualQueryEx(hProcess, pAddr, mi, miLen)'再次查询,直到查询失败(所有可查询地址都已经查过了)
    Loop
    GetTotalCommittedMemory = dwTotalCommitEnd Function下面再说ReadProcessMemory,这个函数从指定进程中指定地址读出指定长度的内容。它在VB自带的API浏览器里的声明是Public Declare Function ReadProcessMemory Lib "kernel32" Alias "ReadProcessMemory" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long同样,为了使用方便,lpBaseAddress As Any被我改成了ByVal lpBaseAddress As Long,然后讲一下在进程内存里查找数据的技术。首先,要打开进程,由于要用ReadProcessMemory读出数据,所以权限要大一些。下面这个打开进程的方式具备了查询、读和写的权限hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, 0, ProcessId)然后就要结合上面的程序来搜索了。只有当内存是处于被占用状态时才去读取其中的内容,而忽略空闲状态的内存。程序我就不在这儿写了,和上面那段差不多。只是把dwTotalCommit = dwTotalCommit + mi.RegionSize换成了读取内存以及搜索这一块内存的函数而已。还有WriteProcessMemory,这个函数向指定进程中指定地址写入指定长度的内容。同样,lpBaseAddress As Any被我改成了ByVal lpBaseAddress As Long,这个函数的使用很简单,我就不再赘述了。
      

  10.   

    使用 Visual Basic 通过 32 位 地址访问内存    wxj_lake(翻译)  
      http://www.csdn.net/develop/Read_Article.asp?Id=12939使用 Visual Basic 通过 32 位 地址访问内存2001年7月6日马尼拉,菲律宾作者:Chris Vega [[email protected]]  当我们谈论“真的”指针和内存地址,我们大都会想到 Visual Basic 的局限性,比如,由于 VB 没有作为变量声明的指针数据类型,它不能直接访问内存。当某些场合需要一个变量的“地址”而不是它的值的时候,这一点混淆就显得特别明显。例如,那个变量位于内存(当前进程、其它进程或者动态链接库的虚拟空间)中的何处。  是的,VB 确实“没有”指针变量,但是你是否曾试过将一个正规的 VB 数据类型转变为一个指针?你是否认为这是不可能的?好吧,还是再想一下,因为在 Visual Basic 中(从发行版本5开始),Microsoft 提供了一系列便利的函数以将你的正规变量转换为指针,它们是:1] VarPtr - 返回一个变量或者数组元素的地址
       StrPtr - 返回字符串的地址  Visual Basic 中除字符串以外的变量,都位于它的内存位置上,你可以通过调用 VarPtr 函数获取这个变量的地址。字符串实际上是作为 BSTR 储存的,这是一个指向“字符数组的指针”的指针,这样你就需要 StrPtr 以得到“字符数组的指针”的地址,而不是用 VarPtr 获得 BSTR 的地址。范例:
     Dim ptrMyPointer As Long
     Dim intMyInteger As Integer
     Dim strMyString As String * 25 ' 这就是一个调用
     ptrMyPointer = VarPtr(intMyInteger)
     ' 将内存中 intMyInteger 这个变量的32位地址赋予 ptrMyPointer strMyString = "变量的地址:" & Hex(ptrMyPointer)
     MsgBox strMyString
     ' 这是另一个调用
     ptrMyPointer = StrPtr(strMyString)
     ' 给出字符数组首元素的地址,例如,字符串的第一个字母。
    2] VarPtrArray - 返回变量数组的地址
       VarPtrStringArray - 返回字符传数组的地址  Visual Basic 中数组被包存在 SAFEARRAY 结构中,你需要使用 VarPtrArray 函数以获取数组的地址,但是在使用该函数之前,你必须手工把它从 msvbvm50.dll 中声明到 VB 程序中。范例: ' 对于 VB 5
     ' ========
     Declare Function VarPtrArray Lib "msvbvm50.dll" Alias "VarPtr" (Var() as Any) As Long ' 对于 VB 6
     ' ========
     Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() as Any) As Long  ' 调用 Dim lngSafeArrayAddress As Long
     Dim lngArrayOfLongs(6) As Long
     Dim i As Long Randomize
     For i = 0 to 6
      lngArrayOfLongs = Int(Rnd * &HFFFF)
     Next lngSafeArrayAddress = VarPtrArray(lngArrayOfLongs())  ' 返回数组 lngArrayOfLongs 的安全地址,s of an Array , you
     ' 你可以将这些地址用于快速排序或其它用途。
      

  11.   

    事实上,VarPtrStringArray 更难以用于你的程序中,因为你需要创建一个类型库并手工将该类型库引用到 VB 程序中。要做一个类型库,你需要一个 MIDL 编译器,它是一个用于将 *.odl 文件编译成类型库的命令行工具。
      对于 VB5,创建一个文本文件并且保存为 VB5StrPtr.odl,加入以下内容:----------开始剪切--------------------------------------------------
    #define RTCALL _stdcall
    [uuid(6E814F00-7439-11D2-98D2-00C04FAD90E7),lcid (0), version(5.0), helpstring("VarPtrStringArray Support for VB5")]
    library PtrLib
    {
     importlib ("stdole2.tlb");
     [dllname("msvbvm50.dll")]
     module ArrayPtr
       {
        [entry("VarPtr")]
        long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);
       }

    ----------结束剪切-------------------------------------------------  用以下命令编译: MIDL /t VB5StrPtr.odl  对于 VB6,创建一个文本文件并且保存为 VB6StrPtr.odl,加入以下内容:-----------开始剪切--------------------------------------------------
    #define RTCALL _stdcall
    [uuid(C6799410-4431-11d2-A7F1-00A0C91110C3),lcid (0), version(6.0), helpstring("VarPtrStringArray Support for VB6")]
    library PtrLib
    {
     importlib ("stdole2.tlb");
     [dllname("msvbvm60.dll")]
     module ArrayPtr
       {
        [entry("VarPtr")]
        long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);
       }

    ----------结束剪切-------------------------------------------------  用以下命令编译: MIDL /t VB6StrPtr.odl
      现在,你有了类型库,将该类型库引用到 VB 程序中,然后你可以用以下方式获取字符串数组:Dim MyArrayOfStrings(3) As String
    Dim AddressOfArray As Long
    MyArrayOfStrings(0)="Chris"
    MyArrayOfStrings(1)="Vega"
    MyArrayOfStrings(2)="[email protected]"' 调用
    AddressOfArray = VarPtrStringArray ( MyArrayOfStrings() ) ' 给出数组首元素的地址,而且是该首元素的第一个字符,
    ' 例如,这里是字符“C”在内存中的地址
    ' *** 怎样?你没有 MIDL 编译器?或者你不愿麻烦自己创建类型库并手工引用?
    ' 这里有一种足够容易的简单方法。
    ' 因为 StrPtr 函数具有获得字符串地址的能力,而字符串数组的元素全部都是字符串,
    ' 所以你应该清楚了,你可以对数组的首元素进行这个调用AddressOfArray = StrPtr ( MyArrayOfStrings(0) )' 返回更上述方法完全相同的结果 3] ObjPtr - 返回一个对象的地址  面向对象编程由众多对象组成,而这些对象和它的众多属性一起保存在内存中,作为一种结构化的布局。你需要调用 ObjPtr 函数来获取它的位置。范例:
     ' 你要知道 Form1 处于内存何处,本方法告诉你它在线程中的地址 Dim objMyObject As New Form1
     MsgBox "Form1 位于:" & Hex( ObjPtr( objMyObject ) )   好,到此为止你一定在想:无论如何,怎么才能把这些地址变成实际有用的东西呢?其实如果你这样想答案就很清楚了:地址是一个内存中的位置,而你的变量中保存的就是一个内存中的位置,并且有它本身在内存中的位置。搞糊涂了?我们让它简单一点,你可以简单的认为这个地址是保存数据的位置,数据是可读写的,而你需要通过这个地址来读写它。唔,Visual Basic 能够做这种事情吗?  不能,如果你只是简单的考虑 Visual Basic 的能力的话,但是你的程序可以使用 API 函数。我现在谈到的 API 是从 KERNEL32.DLL 输出的运行库,名为 RtlMoveMemory 和 RtlCopyMemory。  它太吸引人了。首先我们已经找到了通过把变量转变为指针来得到内存地址的方法,现在我们又有了读写这些地址所指内容的方法。但是只要将这两个声明中的任一个加入你的程序中,而不是全部,因为它们的功能是一样的。我建议使用第二个,因为它被所有的 Windows 系统支持,而 RtlCopyMemory 则不然。
      

  12.   

    Private Declare Sub CopyMemory _
                         Lib "kernel32" Alias _
                         "RtlCopyMemory" _
                         (Destination As Any, _
                          Source As Any, _
                          ByVal length As Long)
     ' RtlCopyMemory 将一块内存的内容复制到另一块中。 ' 或者 Private Declare Sub CopyMemory _
                         Lib "kernel32" Alias _
                         "RtlMoveMemory" _
                         (Destination As Any, _
                          Source As Any, _
                          ByVal length As Long) ' RtlMoveMemory 可以向前或向后移动内存,匹配的或不匹配的,
     ' 以 4字节的块为单位,后面为所有保留的字节。参数: Destination 
      指向要移动的目标。 Source 
      指向要复制的内存。 Length 
      指定要复制的字节数。
      为了使它更容易使用,你可以把下面内容复制粘贴到 modMemory.bas 中:------------开始剪切------------------------------------------------------------------ Attribute VB_Name = "modMemory"
     ' =============================================================================
     ' 复制内存 API
     ' =============================================================================
     Private Declare Sub CopyMemory _
                         Lib "kernel32" Alias _
                         "RtlMoveMemory" _
                         (Destination As Any, _
                          Source As Any, _
                          ByVal length As Long)
                         
     ' =============================================================================
     ' 数据长度
     ' =============================================================================
     Public Enum e_BinaryData
         DefineByte = 1                          '  8 位数据
         DefineWord = 2                          ' 16 位数据
         DefineDoubleWord = 4                    ' 32 位数据
         DefineQuadWord = 8                      ' 64 位数据
     End Enum ' =============================================================================
     ' 允许直接读 MemPointer 指向的内存
     ' 用和 Asm 一样的字节数定义 (DB, DW, DD, DX)
     ' =============================================================================
     Function ReadMem(ByVal MemPointer As Long, _
                      SizeInBytes As e_BinaryData)
         Select Case SizeInBytes
             Case DefineByte
                 Dim DB As Byte
                 CopyMemory DB, ByVal MemPointer, 1
                 ReadMem = DB
             Case DefineWord
                 Dim DW As Integer
                 CopyMemory DW, ByVal MemPointer, 2
                 ReadMem = DW
             Case DefineDoubleWord
                 Dim DD As Long
                 CopyMemory DD, ByVal MemPointer, 4
                 ReadMem = DD
             Case DefineQuadWord
                 Dim DX As Double
                 CopyMemory DX, ByVal MemPointer, 8
                 ReadMem = DX
         End Select
     End Function ' =============================================================================
     ' 允许直接写 MemPointer 指向的内存
     ' 用和 Asm 一样的字节数定义 (DB, DW, DD, DX)
     ' =============================================================================
     Sub WriteMem(ByVal MemPointer As Long, _
                  SizeInBytes As e_BinaryData, _
                  ByVal DataToWrite)
         CopyMemory ByVal MemPointer, VarPtr(DataToWrite), SizeInBytes
     End Sub------------结束剪切---------------------------------------------------------------
    用例:通过内存为变量赋值: Dim ptrVariable As Long
     Dim xCounter As Long ptrVariable = VarPtr(ptrVariable)
     WriteMem ptrVariable, DefineWord, &HFFFF 
     ' 与 ptrVariable = &HFFFF 等价
    读内存的内容,使用: ptrVariable = ReadMem(ptrVariable, DefineWord)
      现在我们能够获得指针并访问它们了。但是如果你一步步跟着以上步骤看下来,你可能奇怪一条原本的 Visual Basic 赋值操作比这里介绍的直接内存赋值操作快得多。然而本文旨在指出可以使用 Visual Basic 访问内存,而这一点的主要意义不仅在于读取和分析变量,接下来,你可以通过获得内存地址简单地处理运行的 DLL。同时利用 modMemory.bas 和 PE (Portable Executable) 文件格式的知识,你可以分析 DLL 主体,看看它们是如何处理的。最好的是,可以获取它所有输出函数的列表;差点忘记,可以把它们 spy 出来或者干脆获取函数体的副本进行反汇编,比低级语言访问更多的内容,这也是 C 语言被称为工业标准的原因;现在你可以书写跟 C 表现相同的 Visual Basic 程序,祝你好运!