1. 程式人生 > >驅動開發之字串操作

驅動開發之字串操作

WDM驅動程式可以使用下面4種格式的字串:

  • Unicode串,由UNICODE_STRING結構描述,包含16位字元。Unicode有足夠的程式碼空間編碼地球上所有語言的字型。
  • ANSI串,由ANSI_STRING結構描述,包含8位字元。另一種OEM_STRING串與其相似,也是用8位字元描述串。兩者的不同是,OEM串中的字元字型由當前內碼表決定,而ANSI串的字元字型不依賴任何內碼表。WDM驅動程式不必處理OEM串,因為它們只能來自使用者模式,在驅動程式看到這些串之前,它們已經被某些核心模式部件轉換成Unicode串。
  • 空結尾的字串。你可以用普通的C語法表達串常量,例如,"Hello, world!"
    ,該串使用了型別為CHAR的8位字元,這些字元被假定屬於ANSI字符集。串常量中的字元來自你建立源程式所用的編輯器。如果你的編輯器需要依賴當前內碼表才能顯示編輯視窗中的字元字型,那麼應該注意,這些字元在Windows ANSI字符集中可能會有不同的含義。
  • 空結尾的寬字元(WCHAR型別)串,用C語法也可以表達寬串常量,例如,L"Goodbye, cruel world!",該串看起來象Unicode串常量,但是,最後從文字編輯器出來的串實際僅使用了Windows ANSI字符集中ASCII和Latin1區(0020-007F和00A0-00FF)中的字元。

UNICODE_STRING和ANSI_STRING有相同的資料結構。Buffer

域指向包含串資料的緩衝區。MaximumLength給出了緩衝區的長度,Length提供串的當前長度,它不考慮任何可能存在的空結束符。length域的單位是位元組,對於UNICODE_STRING結構也是這樣。

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.Lengthfoo.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平臺上,RtlCopyBytesRtlCopyMemory是完全不同的函式。

表3-8. 處理blob資料的服務函式

服務函式或巨集 描述
memchr 在blob中尋找一個位元組
memcpy, RtlCopyBytes, RtlCopyMemory 複製位元組,不允許重疊
memmove, RtlMoveMemory 複製位元組,允許重疊
memset, RtlFillBytes, RtlFillMemory 用給定的值填充blob
memcmp, RtlCompareMemory, RtlEqualMemory 比較兩個blob
memset, RtlZeroBytes, RtlZeroMemory blob清零