原文:http://hi.baidu.com/hack_oldwolf/blog/item/03c2dba88fad05bcca130c47.html
1、使用字符串结构
=================================================================
UNICODE_STRING
UNICODE_STRING结构体是用来定义UNICODE字符串的。
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING * PUNICODE_STRING;
结构体变量说明:
Length
存储在缓冲器中字符串的长度。
MaximumLength
缓冲器的最大长度。
Buffer
指向一个包含宽字符字符串的缓冲区。
=================================================================
由于UNICODE_STRING中Buffer不一定是以空终止符结尾的。所以下面的方法都是错误的:
UNICODE_STRING str;
len = wcslen( str.Buffer); //试图求长度
DbgPrint( "%ws" , str.Buffer); //试图调试输出str.Buffer中的内容
出错的原因也很简单,wcslen和DbgPrint都认为'/0' 是字符串结束符
=================================================================
2 、字符串的初始化
错误的初始化方法:
UNICODE_STRING str;
wcscpy( str.Buffer, L"my first string!" ); //错误:Buffer是一个未初始化的指针
str.Length = str.MaximumLength = wcslen( L"my first string!" ) * sizeof ( WCHAR);
正确的初始化方式是:
UNICODE_STRING str;
str.Buffer = L"my first string!" ;
str.Length = str.MaximumLength = wcslen( L"my first string!" ) * sizeof ( WCHAR);
上面初始化字符串的方法相对比较繁琐,在头文件ntdef.h中有一个宏,可简单初始化子符串。
#include < ntdef.h>
UNICODE_STRING str = RTL_CONSTANT_STRING( L"my first string!" );
但是RTL_CONSTANT_STRING这个宏只能在定义的时候使用,为了随时初始化字符串,我们使用RtlInitUnicodeString.定义方法如下:
UNICODE_STRING str;
RtlInitUnicodeString(& str, L"my first string!" );
或者使用RtlInitEmptyString:
UNICODE_STRING str;
WCHAR wchStr[ 256 ];
RtlInitEmptyString(& str, wchStr, sizeof ( WCHAR) * 256 );
3 、字符串拷贝
在内核编程中,普通的wcscpy函数已经不行了。我们用RtlCopyUnicodeString函数来拷贝。拷贝的时候要注意目的字符串的Buffer必须要有足够的空间,否则会丢失数据,但系统不会崩溃。
例子:
UNICODE_STRING dst;
WCHAR dst_buf[ 256 ];
UNICODE_STRING src = RTL_CONSTANT_STRING( L"my first string!" );
RtlInitEmptyString(& dst, dst_buf, sizeof ( WCHAR) * 256 );
RtlCopyUnicodeString(& dst, & src);
4 、字符串的连接
内核中的Unicode字符串连接不难,重要的是保证目标字符串的空间大小。
实例:
NTSTATUS status
UNICODE_STRING dst;
WCHAR dst_buf[ 256 ];
UNICODE_STRING src = RTL_CONSTANT_STRING( L"my first string!" );
RtlInitEmptyString(& dst, dst_buf, sizeof ( WCHAR) * 256 );
RtlCopyUnicodeString(& dst, & src);
status = RtlAppendUnicodeToString(& dst, L"my second string!" );
if ( status == STATUS_SUCCESS)
{
……
}
这里在介绍个连接字符串的函数:
NTSTATUS
RtlAppendUnicodeStringToString(
IN OUT PUNICODE_STRING Destination, //指向目的字符串的缓冲区
IN PUNICODE_STRING Source //指向源字符串的缓冲区
);
NTSTATUS是一个返回值类型,如果成功就返回STATUS_SUCCESS,否则返回一个错误码。如果目标字符串的空间不足,则返回一个STATUS_BUFFER_TOO_SAMLL。
5 、字符串的打印
NTSTATUS
RtlStringCbPrintfW(
OUT LPWSTR pszDest, //指向目的Unicode字符串
IN size_t cbDest, //目的缓冲区的大小,Unicode的*sizeof(WCHAR)。
IN LPCWSTR pszFormat,
...
);
使用该函数的时候要包含ntstrsate.h和ntstrsate.lib。
实例:
int const arraysize = 30 ;
WCHAR pszDest[ arraysize];
size_t cbDest = arraysize * sizeof ( WCHAR);
LPCWSTR pszFormat = L"%wZ %d + %d + %d." ;
WCHAR * pszTxt = L"my first string!" ;
NTSTATUS status = RtlStringCBPrintfW( pszDest, cdDest, pszFormat, pszTxt, 1 , 2 , 3 );
RtlStringCBPrintfW在目标缓冲区内存不足的时候依然可以打印,但是多余的部分被借去了,此时的status的返回值为STATUS_BUFFRT_OVERFLOW。
打印UNICODE_STRING类型的指针时,在不确定字符串是以空终止符结尾的情况下,最好用% wZ, 否则也可以使用% ws和% s。
驱动中还可以调用DbgPrint() 函数来打印调试信息。
但是DbgPrint() 不管在发行版还是调试版本中都会有效,一般我们都使用宏KdPrint() 来输出调试字符串。
使用KdPrint() 函数的时候要使用双括号来包含参数。