UNICODE_STRING的打印
字符串的连接另一种常见的情况是字符串和数字的组合。有时数字需要被转换为字符串。有时需要把若干个数字和字符串混合组合起来。这往往用于打印日志的时候。日志中可能含有文件名、时间、和行号,以及其他的信息。
熟悉C语言的读者会使用sprintf。这个函数的宽字符版本为swprintf。该函数在驱动开发中依然可以使用,但是不安全。微软建议使用RtlStringCbPrintfW来代替它。RtlStringCbPrintfW需要包含头文件ntstrsafe.h。在连接的时候,还需要连接库ntsafestr.lib。
下面的代码生成一个字符串,字符串中包含文件的路径,和这个文件的大小。
#include <ntstrsafe.h>
// 任何时候,假设文件路径的长度为有限的都是不对的。应该动态的分配
// 内存。但是动态分配内存的方法还没有讲述,所以这里再次把内存空间
// 定义在局部变量中,也就是所谓的“在栈中”
WCHAR buf[512] = { 0 };
UNICODE_STRING dst;
NTSTATUS status;
……
// 字符串初始化为空串。缓冲区长度为512*sizeof(WCHAR)
RtlInitEmptyString(dst,dst_buf,512*sizeof(WCHAR));
// 调用RtlStringCbPrintfW来进行打印
status = RtlStringCbPrintfW(
dst->Buffer,L”file path = %wZ file size = %d \r\n”,
&file_path,file_size);
// 这里调用wcslen没问题,这是因为RtlStringCbPrintfW打印的
// 字符串是以空结束的。
dst->Length = wcslen(dst->Buffer) * sizeof(WCHAR);
RtlStringCbPrintfW在目标缓冲区内存不足的时候依然可以打印,但是多余的部分被截去了。返回的status值为STATUS_BUFFER_OVERFLOW。调用这个函数之前很难知道究竟需要多长的缓冲区。一般都采取倍增尝试。每次都传入一个为前次尝试长度为2倍长度的新缓冲区,直到这个函数返回STATUS_SUCCESS为止。
值得注意的是UNICODE_STRING类型的指针,用%wZ打印可以打印出字符串。在不能保证字符串为空结束的时候,必须避免使用%ws或者%s。其他的打印格式字符串与传统C语言中的printf函数完全相同。可以尽情使用。
另外就是常见的输出打印。printf函数只有在有控制台输出的情况下才有意义。在驱动中没有控制台。但是Windows内核中拥有调试信息输出机制。可以使用特殊的工具查看打印的调试信息(请参阅附录1“WDK的安装与驱动开发的环境配置”)。
驱动中可以调用DbgPrint()函数来打印调试信息。这个函数的使用和printf基本相同。但是格式字符串要使用宽字符。DbgPrint()的一个缺点在于,发行版本的驱动程序往往不希望附带任何输出信息,只有调试版本才需要调试信息。但是DbgPrint()无论是发行版本还是调试版本编译都会有效。为此可以自己定义一个宏:
#if DBG
KdPrint(a) DbgPrint##a
#else
KdPrint (a)
#endif
不过这样的后果是,由于KdPrint (a)只支持1个参数,因此必须把DbgPrint的所有参数都括起来当作一个参数传入。导致KdPrint看起来很奇特的用了双重括弧:
// 调用KdPrint来进行输出调试信息
status = KdPrint((L"file path = %wZ file size = %d \r\n",&file_path,file_size));
这个宏没有必要自己定义,WDK包中已有。所以可以直接使用KdPrint来代替DbgPrint取得更方便的效果。