UNICODE_STRING的初始化
从UNICODE_STRING结构体的三个成员可以看到,其包含的是字符串的指针,字符串的实际长度和字符串指针的内存空间长。
学过C语言的我们都知道,要对一个指针的内存进行赋值,就必须确定这个指针指向的内存空间可用,否则会导致内存使用异常。在应用层中会导致应用程序异常,而在驱动中会导致蓝屏。
我们们这里使用以下的代码是完全错误的,内核会立刻崩溃:
#define MYSTRING L"www.pnpon.com"
UNICODE_STRING str;
wcscpy(str.Buffer,MYSTRING);
str.Length = str.MaximumLength = wcslen(MYSTRING) * sizeof(WCHAR);
上面的这段代码相当于定义了一个指针,但并没有对这个指针进行初所以系统会使用默认的值进行填充。str.Buffer只是一个未初始化的指针。它并没有指向有意义的空间。
Rlease/Free编译环境下为NULL,所以wcscpy其实是对NULL地址复制数据。
其相当于我们在应用层如下的代码:
#define MYSTRING L"www.pnpon.com"
char * pBuffer;
wcscpy(str.Buffer,MYSTRING);
pBuffer并未始始化,所以对其指向的内存赋值会引起内存写异常。
手动初始化UNICODE_STRING
以下的方法是正确初始化UNICODE_STRING:
#define MYSTRING L"www.pnpon.com"
// 先定义后,再定义空间
UNICODE_STRING str;
str.Buffer = MYSTRING;
str.Length = str.MaximumLength = wcslen(MYSTRING) * sizeof(WCHAR);
在上面的代码中,指针Buffer进行了初始化赋值,这个指针指向的全局常量地址空间,所以这一段地址空间只能读,不可写。
如果我们强行对其地址修改,会触发系统的异常,导致蓝屏。
下在我们来使用另一种方式来初始化UNICODE_STRING。
UNICODE_STRING str = {
sizeof(L"www.pnpon.com") – sizeof((L"www.pnpon.com")[0]),
sizeof(L"www.pnpon.com"),
L"www.pnpon.com"};
上面一段代码相当于分别对结构体的三个变量进行初始化:
对于UNICODE_STRING的Length,相当于
str.Length = sizeof(L"www.pnpon.com") – sizeof((L"www.pnpon.com")[0]);
- sizeof(L”www.pnpon.com”) 计算该字符串所占的内存空间,这里包括L’\0’。
- sizeof((L”www.pnpon.com”)[0])相当于取的是这个字符串的第一个字符长度,这里因为宽字节,所以是2,两者相减,就计算出了字符串的长度。这里看到确实是以字节为单位计算的,而不是以宽字节的长度为单位的。
而对于UNIOCDE_STRING的MaximumLength成同,则直接使用sizeof计算其所占内存空间。所以这里也就包括了L’\0’2个字节的长度。
对于UNIOCDE_STRING的Buffer成员,直接使用常量地址空间的地址赋值。
使用RtlInitUnicodeString和RTL_CONSTANT_STRING初始化字符串
通过上面的代码看到,要初始化一个UNICODE_STRING,简直不要太复杂,而且很容易由于书写错误引起错误而导致蓝屏。
这里就不得不说一下微软简易太贴心了,它把以上的代码封装成了一个宏RTL_CONSTANT_STRING,我们可以直接使用这个宏来初始化常量字符串。
#define RTL_CONSTANT_STRING(s) \
{ \
sizeof( s ) - sizeof( (s)[0] ), \
sizeof( s ) / sizeof(_RTL_CONSTANT_STRING_type_check(s)), \
_RTL_CONSTANT_STRING_remove_const_macro(s) \
}
所以上面的代码可以简化为:
#include <ntdef.h>
UNICODE_STRING str = RTL_CONSTANT_STRING(L"www.pnpon.com");
要使用宏RTL_CONSTANT_STRING,必须包括头文件ntdef.h,因为这个宏是在ntdef.h头文件中定义的。
这里我们再介绍一个函数可以使用RtlInitUnicodeString函数,这个函数可以随时初始化一个字符串。
这只能在定义这个字符串的时候使用。为了随时初始化一个字符串,可以使用RtlInitUnicodeString。示例如下:
UNICODE_STRING str;
RtlInitUnicodeString(&str,L"www.pnpon.com");
...
//可以再次初始化
RtlInitUnicodeString(&str,L"pnpon内核开发");
关于ANSI_STRING
ANSI_STRING字符串也有相对应的宏和初始函数,分别为RTL_CONSTANT_STRING和RtlInitString。
ANSI_STRING str = RTL_CONSTANT_STRING("www.pnpon.com");
RtlInitAnsiString(&str, "www.pnpon.com");
用本小节的方法初始化的字符串,不用担心内存释放方面的问题。因为我们并没有分配任何内存,因为这些内存都是使用的是常量地址空间。