核心模式下的字串操作

whatday發表於2013-07-27
1)ASCII字串和寬字串   

在應用程式中使用兩種字元:
a) char型字串負責記錄ANSI字符集,它是指向一個char陣列的指標,每個char型變數大小是一個位元組,字串是以0標誌字串結束的;
b) wchar_t型的寬字串負責描述unicode字符集,它是指向一個wchar_t陣列的指標,wchar_t字元大小為兩個位元組,字串以0標誌字串結束。   
例:
ANSI字元構造如下:   char *str1 = "ASCE";   
UNICODE字元構造如下:wchar_t *str2 = L"ASCE";   
注:在構造字串時使用關鍵字“L”,編譯器會自動生成所需要的寬字元   
  
在驅動開發中,DDK將char和wchar_t替換成CHAR和WCHAR。
驅動程式中使用KdPrint巨集列印ASCII字串和寬字串:   

例:

    CHAR *string1 = "ASCE";
    KdPrint(("%s\n", string1)); //注意是小寫%s

    WCHAR *string2 = L"ASCE";
    KdPrint(("%S\n", string2)); 

2)ANSI_STRING字串與UNICODE_STRING字串  
DDK不鼓勵程式設計師使用C語言的字串,主要是因為標準C字串處理函式容易導致緩衝區溢位等錯誤。應該使用DDK自定義的字串: 
這個結構對ASCII字串進行封裝: 
 typedef struct _STRING {  
      USHORT Length; //字元的長度,單位是位元組
      USHORT MaximumLength; //整個字串緩衝區的最大長度
      PCHAR Buffer; //緩衝區的指標
    } ANSI_STRING, *PANSI_STRING;
標準C字串中,如果緩衝區長度是N,則只能容納N-1個字元的字串,最後一個位元組儲存NULL;
而在STRING字串中,緩衝區大小是MaximumLength,最大字串長度可以是MaximumLength,而不是MaximumLength-1。   


DDK將寬字串封裝成UNICODE_STRING資料結構:    

typedef struct _UNICODE_STRING {
      USHORT Length; //字元的長度,單位是位元組。如果是N個字元,那麼Length等於N的2倍
      USHORT MaximumLength; //整個字串緩衝區的最大長度,單位是位元組
      PWSTR Buffer; //緩衝區的指標
    } UNICODE_STRING, *PUNICODE_STRING;
與ANSI_STRING一樣,UNICODE_STRING字串不是以NULL為結束標誌的。  
 
列印這兩種字串方法如下: 
    ANSI_STRING ansiString;
    //此處略去對ansiString的初始化
    KdPrint(("%Z\n", &ansiString)); //注意是%Z
    UNICODE_STRING uniString;
    //此處略去對uniString的初始化
    KdPrint(("%wZ\n", &uniString));

3)字元的初始化和銷燬  
 
ANSI_STRING字串和UNICODE_STRING字串使用前需要進行初始化,有兩種方法構造這個資料結構:  

(1)使用DDK提供的函式:  

這種初始化的優點是操作簡單,用完後不用清理記憶體。但有一個問題,就是如果修改SourceString,同時會導致DestinationString字元發生變化:

    初始化ANSI_STRING字串:    
    VOID RtlInitAnsiString(     
      __out PANSI_STRING DestinationString, //要初始化的ANSI_STRING字串   
      __in_opt PCSZ SourceString //字串的內容
    );  

    初始化UNICODE_STRING字串:
    VOID RtlInitUnicodeString(
      __out PUNICODE_STRING DestinationString, //要初始化的UNICODE_STRING字串
      __in_opt PCWSTR SourceString //字串的內容
    );
      
    ANSI_STRING ansiString;
    CHAR *string = "asce";
    //初始化ANSI_STRING字串
    RtlInitAnsiString(&ansiString, string);
    KdPrint(("ansiString: %Z\n", &ansiString));
      
    //改變string
    string[0] = 'a';
    string[1] = 's';      
    string[2] = 'c';      
    string[3] = 'e';
      
    //改變string的同時ansiString也改變了    
    KdPrint(("ansiString: %Z\n", &ansiString));



(2)程式設計師自己申請記憶體,並初始化記憶體,當不用字串時,需要回收字串佔用的記憶體:  

    #define BUFFER_SIZE 1024    
    UNICODE_STRING UnicodeString = {0};      
    //設定緩衝區大小     
    UnicodeString.MaximumLength = BUFFER_SIZE;   
    //分配記憶體      
    UnicodeString.Buffer = (PWSTR)ExAllocatePool(PagedPool, BUFFER_SIZE);     
    WCHAR *wideString = L"ASCE";             
    //設定字串長度,因為是寬字元,所以是字元長度的倍      
    UnicodeString.Length = 2*wcslen(wideString);      
    //保證緩衝區足夠大,否則程式終止      
    ASSERT(UnicodeString.MaximumLength >= UnicodeString.Length);     
    //記憶體複製      
    RtlCopyString(UnicodeString.Buffer, wideString, UnicodeString.Length);          
    KdPrint(("UnicodeString: %wZ\n", &UnicodeString));  
    //清理記憶體
    ExFreePool(UnicodeString.Buffer);      
    UnicodeString.Buffer = NULL;      
    UnicodeString.Length = UnicodeString.MaximumLength = 0;
      
    對於最後一步清理記憶體,DDK給出了簡化函式,分別是RtlFreeAnsiString和RtlFreeUnicodeString,這兩個函式內部呼叫了ExFreePool去回收記憶體的。

4)字串複製  
DDK提供針對ANSI_STRING字串和UNICODE_STRING字串的複製字串函式:  

    VOID RtlCopyString(
      __out PSTRING DestinationString, //目的字串
      __in_opt const STRING *SourceString //源字串
    );
      
    VOID RtlCopyUnicodeString(
      __inout PUNICODE_STRING DestinationString, //目的字串
      __in_opt PCUNICODE_STRING SourceString //源字串
    );

下面程式碼說明了RtlCopyUnicodeString函式的使用:  
    #define BUFFER_SIZE 1024
    //初始化UnicodeString1
    UNICODE_STRING UnicodeString1;
    RtlInitUnicodeString(&UnicodeString1, L"ASCE");
    //初始化UnicodeString2
    UNICODE_STRING UnicodeString2 = {0};
    UnicodeString2.Buffer = (PWSTR)ExAllocatePool(PagedPool, BUFFER_SIZE);
    UnicodeString2.MaximumLength = BUFFER_SIZE; 
    //將初始化UnicodeString1複製到UnicodeString2 
    RtlCopyUnicodeString(&UnicodeString2, &UnicodeString1);
    //分別顯示UnicodeString1和UnicodeString2
    KdPrint(("UnicodeString1: %wZ\n", &UnicodeString1));
    KdPrint(("UnicodeString2: %wZ\n", &UnicodeString2));
    //銷燬UnicodeString2,注意:UnicodeString1不用銷燬
    RtlFreeUnicodeString(&UnicodeString2);

5)字串比較  
DDK提供了對ANSI_STRING字串和UNICODE_STRING字串的相關比較函式:

    LONG RtlCompareString(
      __in const STRING *String1, //要比較的第一個字串
      __in const STRING *String2, //要比較的第二個字串
      __in BOOLEAN CaseInSensitive //是否對大小寫敏感
    );
      
    LONG RtlCompareUnicodeString(
      __in PCUNICODE_STRING String1, //要比較的第一個字串
      __in PCUNICODE_STRING String2, //要比較的第二個字串
      __in BOOLEAN CaseInSensitive //是否對大小寫敏感
    );
DDK同時提供了RtlEqualString和RtlEqualUnicodeString函式,返回為非零代表相等,零代表不相等:
    BOOLEAN RtlEqualString(
      __in const STRING *String1,
      __in const STRING *String2,
      __in BOOLEAN CaseInSensitive
      
    );
      
    BOOLEAN RtlEqualUnicodeString(
      __in PCUNICODE_STRING String1,
      __in PCUNICODE_STRING String2,
      __in BOOLEAN CaseInSensitive
    );

函式例項: 
    //初始化UnicodeString1
    UNICODE_STRING UnicodeString1;
    RtlInitUnicodeString(&UnicodeString1, L"ASCE");    
      
    //初始化UnicodeString2
    UNICODE_STRING UnicodeString2;
    RtlnitUnicodeString(&UnicodeString2, L"ASCE BOY");  
      
    //判斷字串是否相等
    if(RtlEqualUnicodeString(&UnicodeString1, &UnicodeString2, TRUE))
    {
             KdPrint(("UnicodeString1 and UnicodeString2 are equal\n"));
    }
      
    else
    {
             KdPrint(("UnicodeString1 and UnicodeString2 are not euqal\n"));
    }

6)字串轉化成大寫  

DDK提供的將ANSI_STRING字串和UNICODE_STRING字串轉換成大寫的函式如下:  

VOID RtlUpperString(  
  __inout  PSTRING DestinationString, //目的字串  
  __in     const STRING *SourceString //源字串  
);  
 
NTSTATUS RtlUpcaseUnicodeString(  
  __inout  PUNICODE_STRING DestinationString, //目的字串  
  __in     PCUNICODE_STRING SourceString, //源字串  
  __in     BOOLEAN AllocateDestinationString //是否為目的字串分配記憶體,  
//目的字串和源字串可以是同一個字串  
);   
例項程式碼:  

//初始化UnicodeString1  
UNICODE_STRING UnicodeString;  
RtlInitUnicodeString(&UnicodeString, L"ASCE BOY");  
 
//變化前  
KdPrint(("UnicodeString: %wZ\n", &UnicodeString));  
 
//轉化成大寫  
RtlUpcaseUnicodeString(&UnicodeString, &UnicodeString, FALSE);  
 
//變化後  
KdPrint(("UnicodeString: %wZ\n", &UnicodeString));   

7)字串與整型數字相互轉換  
將UNICODE_STRING字串轉換成整數:  

NTSTATUS RtlUnicodeStringToInteger(  
  __in      PCUNICODE_STRING String, //需要轉換的字串  
  __in_opt  ULONG Base, //轉換的數的進位制(如2、8、10、16)  
  __out     PULONG Value //需要轉換的數字  
);  
 
   
 
將整數轉換成UNICODE_STRING字串:  
 
NTSTATUS RtlIntegerToUnicodeString(  
  __in      ULONG Value, //需要轉換的數字  
  __in_opt  ULONG Base, //轉換的數的進位制(2、8、10、16)  
  __inout   PUNICODE_STRING String //需要轉換的字串  
);    

例項程式碼如下:  

#define BUFFER_SIZE 1024  
 
//字串轉換成數字  
UNICODE_STRING UnicodeString;  
RtlInitUnicodeString(&UnicodeString, L"-100");  
 
ULONG lNumber;  
NTSTATUS nStatus = RtlUnicodeStringToInteger(&UnicodeString, 10, &lNumber);  
 
if(NT_SUCCESS(nStatus))  
{  
         KdPrint(("Conver to integer successfully\n"));  
         KdPrint(("Result : %d\n", lNumber));  
}  
else 
{  
         KdPrint(("Conver to integer failed\n"));  
}  
 
//將數字轉換成字串  
UNICODE_STRING UnicodeStringSec = {0};  
UnicodeStringSec.Buffer = (PWSTR)ExAllocatePool(PagedPool, BUFFER_SIZE);  
UnicodeStringSec.MaximumLength = BUFFER_SIZE;  
nStatus = RtlIntegerToUnicodeString(200, 10, &UnicodeStringSec);  
 
if(NT_SUCCESS(nStatus))  
{  
         KdPrint(("Cover to string successfully\n"));  
         KdPrint(("Result : %wZ\n", &UnicodeStringSec));  
}  
else 
{  
         KdPrint(("Conver to string failed\n"));  
}  
   
//銷燬UnicodeStringSec,注意:UnicodeString不用銷燬  
RtlFreeUnicodeString(&UnicodeStringSec);  


8)ANSI_STRING字串與UNICODE_STRING字串的轉換 

將UNICODE_STRING字串轉換成ANSI_STRING字串:

NTSTATUS RtlUnicodeStringToAnsiString(  
  __inout  PANSI_STRING DestinationString, //需要被轉換的字串  
  __in     PCUNICODE_STRING SourceString, //需要轉換的源字串  
  __in     BOOLEAN AllocateDestinationString //是否需要對被轉換字串分配記憶體  
);  
 
將ANSI_STRING字串轉換成UNICODE_STRING字串:  
 
NTSTATUS RtlAnsiStringToUnicodeString(  
  __inout  PUNICODE_STRING DestinationString, //需要被轉換的字串  
  __in     PCANSI_STRING SourceString, //需要轉換的源字串  
  __in     BOOLEAN AllocateDestinationString //是否需要對被轉換字串分配記憶體  
);   

例項程式碼如下:
//將UNICODE_STRING字串轉換成ANSI_STRING字串  
UNICODE_STRING UnicodeString;  
RtlInitUnicodeString(&UnicodeString, L"ASCE BOY");  

ANSI_STRING AnsiString;  
NTSTATUS nStatus = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);  
 
if(NT_SUCCESS(nStatus))  
{  
         KdPrint(("Conver successfully\n"));  
         KdPrint(("Result:%Z\n", &AnsiString));  
}  
else 
{  
         KdPrint(("Conver failed\n"));  
}  
 
//銷燬AnsiString  
RtlFreeAnsiString(&AnsiString);  
 
//將ANSI_STRING字串轉換成UNICODE_STRING字串  
ANSI_STRING AnsiStringSec;  
RtlInitString(&AnsiStringSec, "ASCE BOY");  
 
UNICODE_STRING UnicodeStringSec;  
nStatus = RtlAnsiStringToUnicodeString(&UnicodeStringSec, &AnsiStringSec, TRUE);  
 
if(NT_SUCCESS(nStatus))  
{  
         KdPrint(("Conver successfully\n"));  
         KdPrint(("Result: %wZ\n", &UnicodeStringSec));  
}  
else 
{  
         KdPrint(("Conver failed\n"));  
}  
 
//銷燬UnicodeStringSec  
RtlFreeUnicodeString(&UnicodeStringSec);   

相關文章