驅動開發之字串操作
WDM驅動程式可以使用下面4種格式的字串:
- Unicode串,由UNICODE_STRING結構描述,包含16位字元。Unicode有足夠的程式碼空間編碼地球上所有語言的字型。
- ANSI串,由ANSI_STRING結構描述,包含8位字元。另一種OEM_STRING串與其相似,也是用8位字元描述串。兩者的不同是,OEM串中的字元字型由當前內碼表決定,而ANSI串的字元字型不依賴任何內碼表。WDM驅動程式不必處理OEM串,因為它們只能來自使用者模式,在驅動程式看到這些串之前,它們已經被某些核心模式部件轉換成Unicode串。
- 空結尾的字串。你可以用普通的C語法表達串常量,例如,"Hello, world!"
- 空結尾的寬字元(WCHAR型別)串,用C語法也可以表達寬串常量,例如,L"Goodbye, cruel world!",該串看起來象Unicode串常量,但是,最後從文字編輯器出來的串實際僅使用了Windows ANSI字符集中ASCII和Latin1區(0020-007F和00A0-00FF)中的字元。
UNICODE_STRING和ANSI_STRING有相同的資料結構。Buffer
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength
PCHAR Buffer;
} ANSI_STRING, *PANSI_STRING;
表3-7列出了用於處理Unicode和ANSI字串的服務函式。我還對應列出了一些標準C執行時間庫函式,這些函式在核心模式中也存在,它們可以處理常規的C樣式串。雖然它們從來沒有在DDK文件中公開過,但標準DDK頭中有這些函式的宣告,而且連線庫中也包含了它們,所以沒有理由不使用它們。
表3-7. 串處理函式
操作 | ANSI串函式 | Unicode串函式 |
---|---|---|
Length | strlen | wcslen |
Concatenate | strcat, strncat | wcscat, wcsncat, RtlAppendUnicodeStringToString, RtlAppendUnicodeToString |
Copy | strcpy, strncpy, RtlCopyString | wcscpy, wcsncpy, RtlCopyUnicodeString |
Reverse | _strrev | _wcsrev |
Compare | strcmp, strncmp, _stricmp, _strnicmp, RtlCompareString, RtlEqualString | wcscmp, wcsncmp, _wcsicmp, _wcsnicmp, RtlCompareUnicodeString, RtlEqualUnicodeString, RtlPrefixUnicodeString |
Initialize | _strset, _strnset, RtlInitAnsiString, RtlInitString | _wcsnset, RtlInitUnicodeString |
Search | strchr, strrchr, strspn, strstr | wcschr, wcsrchr, wcsspn, wcsstr |
Upper/lowercase | _strlwr, _strupr, RtlUpperString | _wcslwr, _wcsupr, RtlUpcaseUnicodeString |
Character | isdigit, islower, isprint, isspace, isupper, isxdigit, tolower, toupper, RtlUpperChar | towlower, towupper, RtlUpcaseUnicodeChar |
Format | sprintf, vsprintf, _snprintf, _vsnprintf | swprintf, _snwprintf |
String conversion | atoi, atol, _itoa | _itow, RtlIntegerToUnicodeString, RtlUnicodeStringToInteger |
Type conversion | RtlAnsiStringToUnicodeSize, RtlAnsiStringToUnicodeString | RtlUnicodeStringToAnsiString |
Memory release | RtlFreeAnsiString | RtlFreeUnicodeString |
系統DLL還輸出了許多RtlXxx函式,但我僅列出了在DDK標頭檔案中定義了原型的函式,在驅動程式中我們僅應使用這些函式。
分配和釋放串緩衝區
我不將詳細描述這些串處理函式,DDK中的描述已經十分詳細。你可以根據你的串使用經驗使用它們。但這裡有一個問題我要提一下。
你可能經常把UNICODE_STRING或ANSI_STRING型別的串定義成自動變數,或者定義成裝置擴充套件的一部分。而串的資料緩衝區通常是動態分配的記憶體。但有時候你希望使用串常量,記錄誰擁有某個串緩衝區可能是一個問題。考慮下面函式片段:
UNICODE_STRING foo; if (bArriving) RtlInitUnicodeString(&foo, L"Hello, world!"); else { ANSI_STRING bar; RtlInitAnsiString(&bar, "Goodbye, cruel world!"); RtlAnsiStringToUnicodeString(&foo, &bar, TRUE); } ... RtlFreeUnicodeString(&foo); // <--don't do this! |
在第一種情況下,我們為驅動程式中的寬字串常量初始化foo.Length、foo.MaximumLength,和foo.Buffer。在另一種情況下,我們要求系統(使用第三個引數為TRUE的RtlAnsiStringToUnicodeString呼叫)為轉化ANSI串分配記憶體。在第一種情況下,呼叫RtlFreeUnicodeString就是一個錯誤,因為它將無條件地釋放常量串“Hello, world!”佔用的記憶體,由於foo串的Buffer成員指向一個常量串,不是動態分配的記憶體,所以根本不能釋放。在另一種情況下,由於我們指定了TRUE引數,系統為表達轉換結果動態地分配了Unicode串緩衝區,所以我們必須呼叫RtlFreeUnicodeString釋放該緩衝區,以避免記憶體洩漏。
Blob資料(大塊資料)
我從資料庫技術中借來術語“blob”來描述一塊無結構定義的位元組組合。表3-8列出了處理這種資料的函式(包括一些來自標準執行時間庫中的函式),你可以在核心模式中呼叫它們。我假設你能從它們的名字上看出它們的用法。然而,我需要指出一些不明顯的事實:
- 記憶體的“copy”和“move”操作之間的區別在於可否容忍源和目的相重疊。move操作不管源和目的是否重疊。而copy操作在源和目的有任何重疊時不工作。
- “byte”操作和“memory”操作的區別是操作的間隔尺寸。byte操作保證按位元組為單位執行。而memory操作可以在內部使用更大的塊,所有這些塊的和等於指定的位元組數。這個區別會根據平臺的不同而改變,在32位Intel計算機上,byte操作實際上是對應memory操作的巨集。但在Alpha平臺上,RtlCopyBytes與RtlCopyMemory是完全不同的函式。
表3-8. 處理blob資料的服務函式
服務函式或巨集 | 描述 |
---|---|
memchr | 在blob中尋找一個位元組 |
memcpy, RtlCopyBytes, RtlCopyMemory | 複製位元組,不允許重疊 |
memmove, RtlMoveMemory | 複製位元組,允許重疊 |
memset, RtlFillBytes, RtlFillMemory | 用給定的值填充blob |
memcmp, RtlCompareMemory, RtlEqualMemory | 比較兩個blob |
memset, RtlZeroBytes, RtlZeroMemory | blob清零 |