其它技術

求取變數在記憶體中的位置,這是 Visual Basic 內建函數,但是微軟不建議使用

函數名 引數 說明
VarPtr Any 求取任一變數在記憶體中的位置,使用者自訂型態也用這個
StrPtr String 求取字串在記憶體中的位置
ObjPtr UnKnown 求取任一物件在記憶體中的位置

MSChart 物件結構圖:為配合使用 MSChart 物件,不得不自行繪製其結構圖,畫這張圖就花了一天,被老闆譏為浪費時間,希望各位網友不要再白花時間了

最佳化

尋找字串變數在字串中的位置:

尋找字串變數在字串中的位置時,若字串可能不分大小寫,可以用字串比較或全部轉成小寫後尋找,全部轉成小寫後再尋找可節省 30 % 的時間

tLoc = InStr(1, OldStr, FindStr, vbTextCompare)
tLoc = InStr(LCase(OldStr), LCase(FindStr)) ' 較快

取得中英混合字串長度 (中文字串當成 2)

經常用來取的中英混合字串長度的方式有兩種:

  1. tLen = lstrlen(myString) ' Windows API
  2. tLen = LenB(StrConv(myString, vbFromUnicode))

其速度比為

字串長度 <256 >256
法 1 80% 120%
法 2 100% 100%

當字串長度小於 256 個字元時,使用 Windows API 較快,當字串長度大於 256 個字元時,使用 Visual Basic 函數較快,而使用哪種方式求取字串長度視使用狀況而定

字串測試報告

對於字串到底在記憶體中是如何的存在呢?在引數傳遞中又是如何傳遞呢?很多人大概跟我一樣好奇,首先先以 myStrPtr 這個函數模擬對於 StrPtr 這個函數的動作:

Public Function myStrPtr(lpString As Variant) As Long

ReDim tBytes(1 To 16) As Byte
Dim tLong As Long

CopyMemory tBytes(1), lpString, 16
CopyMemory tLong, tBytes(9), 4
CopyMemory myStrPtr, ByVal tLong, 4

End Function

不管是 String 或是 Variant 的字串型態,先強迫轉換為可取得 Pointer 內容的 Variant 資料型態,透過第 9 ~ 12 位元的指標指向原 String 的指標位置,再將指標位置的內容轉換為 String 的指標

那麼在這個記憶體位置內的內容又是如何表示的呢?在 Visual Basic 的相關技術文件均記載著是以 UniCode 的型式存在,我們來看看範例 "Basic 培基語言" 在記憶體內的 16 進位值:

42 0 61 0 73 0 69 0 63 0 20 0 F9 57 FA 57 9E 8A 0 8A

以上 42 0 就是 "B" 的 UniCode 值, F9 57 就是 "培" 的 UniCode 值

對於 Visual Basic 中的字串傳遞大概沒問題了,那麼對於呼叫 Windows API 又是如何運作的呢?我們以三種宣告引數方式及兩種傳遞引數方式測試:

Dim tStr As String : tStr = "Basic 培基語言"

引數宣告 傳址呼叫 (ByRef) 傳值呼叫 (ByVal)
lpString As Any 指向 ANSI 字串的指標 ANSI 字串的指標
lpString As String 指向 ANSI 字串的指標 ANSI 字串的指標
ByVal lpString As String ANSI 字串的指標 ANSI 字串的指標

什麼是 ANSI 字串呢?一般我們存在檔案中的格式就是所謂的 ANSI 字串,我們來看看範例 "Basic 培基語言" 在記憶體內的 16 進位值:

42 61 73 69 63 20 B0 F6 B0 F2 BB 79 A8 A5

以上 42 就是 "B" 的 ANSI 值, B0 F6 就是 "培" 的 ANSI 值 (= Asc("培"))

由以上總結,在傳值呼叫中, Visual Basic 會將 tStr 內容由 UniCode 碼轉換為 ANSI 碼,並將指向這個位置的指標傳遞給 DLL ,而傳址呼叫會將指向字串的指標位置傳給 DLL ,若要取得 tStr 的 ANSI 的位元陣列可用三種方式:

Dim tBytes() As Byte
tBytes = StrConv(tStr, vbFromUnicode)
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, lpString As Any, ByVal cbCopy As Long)

ReDim tBytes(1 To 14) As Byte
Dim tlng As Long
CopyMemory tlng, tStr, 4
CopyMemory tBytes(1), ByVal tlng, 14

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, lpString As Any, ByVal cbCopy As Long)

ReDim tBytes(1 To 14) As Byte
Dim tlng As Long
CopyMemory tBytes(1), ByVal tStr, 14

由上大改可以概知 Visual Basic 在傳遞字串的轉移方式,另外對於使用者自訂型態中的字串來說,傳遞的是指向 ANSI 字串的指標,也就是說 Visual Basic 在傳遞使用者自訂型態前還是會自動做轉換處理

註:宣告引數為 lpString As String 在傳遞上並不穩定,下面兩種方式居然得到兩種不同內容,在使用上請自行注意:

Declare Sub CopyMemoryStr Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, lpString As String, ByVal cbCopy As Long)

ReDim tBytes(1 To 14) As Byte
Dim tlng As Long


CopyMemoryStr tBytes(1), tStr, 14 ' 傳遞內容與宣告成為 lpString As Any 相同


' 只是在下面加一個 CopyMemoryStr tlng, tStr, 4 敘述,卻完全不知道在傳什麼,傳遞內容完全不同
CopyMemoryStr tBytes(1), tStr, 14
CopyMemoryStr tlng, tStr, 4

轉載 MSDN 文章

以下資訊版權屬 Microsoft 所有,文章是 MSDN 裡的,我只是抓回來自己參考並提供網友參考

螢幕保護程式

經運用本文內容後,完成的螢幕保護程式並支援 Windows 內建密碼保護功能

預覽列印程式