XiangXiangRen
总版主
总版主
  • 注册日期2003-02-22
  • 最后登录2015-09-01
  • 粉丝13
  • 关注0
  • 积分1042分
  • 威望472点
  • 贡献值1点
  • 好评度145点
  • 原创分13分
  • 专家分1分
阅读:2701回复:3

Windows驱动编程基础教程(连载7)

楼主#
更多 发布于:2008-05-30 10:10
本文作者是楚狂人,有问题请联系QQ16191935,msn walled_river@hotmail.com

Windows驱动编程基础教程(4.2-4.3)

4.2 注册值的读

    一般使用ZwQueryValueKey来读取注册表中键的值。要注意的是注册表中的值可能有多种数据类型。而且长度也是没有定数的。为此,在读取过程中,就可能要面对很多种可能的情况。ZwQueryValueKey这个函数的原型如下:

    NTSTATUS ZwQueryValueKey(
        IN HANDLE  KeyHandle,
        IN PUNICODE_STRING  ValueName,
        IN KEY_VALUE_INFORMATION_CLASS  KeyValueInformationClass,
        OUT PVOID  KeyValueInformation,
        IN ULONG  Length,
        OUT PULONG  ResultLength
        );

    KeyHandle:这是用ZwCreateKey或者ZwOpenKey所打开的一个注册表键句柄。
    ValueName:要读取的值的名字。
    KeyValueInformationClass:本次查询所需要查询的信息类型。这有如下的三种可能。
    KeyValueBasicInformation:获得基础信息,包含值名和类型。
    KeyValueFullInformation:获得完整信息。包含值名、类型和值的数据。
    KeyValuePartialInformation:获得局部信息。包含类型和值数据。
    很容易看出实际上名字是已知的,获得基础信息是多此一举。同样获得完整信息也是浪费内存空间。因为调用ZwQueryValueKey的目的是为了得到类型和值数据。因此使用KeyValuePartialInformation最常见。当采用KeyValuePartialInformation的时候,一个类型为KEY_VALUE_PARTIAL_INFORMATION的结构将被返回到参数KeyValueInformation所指向的内存中。
    KeyValueInformation:当KeyValueInformationClass被设置为KeyValuePartialInformation时,KEY_VALUE_PARTIAL_INFORMATION结构将被返回到这个指针所指内存中。下面是结构KEY_VALUE_PARTIAL_INFORMATION的原型。

    typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
        ULONG  TitleIndex;            // 请忽略这个成员
        ULONG  Type;                // 数据类型
        ULONG  DataLength;            // 数据长度
        UCHAR  Data[1];              // 可变长度的数据
    }KEY_VALUE_PARTIAL_INFORMATION,*PKEY_VALUE_PARTIAL_INFORMATIO;

    上面的数据类型Type有很多种可能,但是最常见的几种如下:
    REG_BINARY:十六进制数据。
    REG_DWORD:四字节整数。
    REG_SZ:以空结束的Unicode字符串。
    Length:用户传入的输出空间KeyValueInformation的长度。
    ResultLength:返回实际需要的长度。
    返回值:如果说实际需要的长度比Length要大,那么返回STATUS_BUFFER_OVERFLOW或者是STATUS_BUFFER_TOO_SMALL。如果成功读出了全部数据,那么返回STATUS_SUCCESS。其他的情况,返回一个错误码。
    下面请读者考虑如何把上一小节的函数写完整。这其中比较常见的一个问题是在读取注册表键下的值之前,往往不知道这个值有多长。所以有些比较偷懒的程序员总是定义一个足够的大小的空间(比如512字节)。这样的坏处是浪费内存(一般都是在堆栈中定义,而内核编程中堆栈空间被耗尽又是另一个常见的蓝屏问题)。此外也无法避免值实际上大于该长度的情况。为此应该耐心的首先获取长度,然后不足时再动态分配内存进行读取。下面是示例代码:
    
    // 要读取的值的名字
    UNICODE_STRING my_key_name =
        RTL_CONSTANT_STRING(L”SystemRoot”);
    // 用来试探大小的key_infor
    KEY_VALUE_PARTIAL_INFORMATION key_infor;
    // 最后实际用到的key_infor指针。内存分配在堆中
    PKEY_VALUE_PARTIAL_INFORMATION ac_key_infor;
    ULONG ac_length;
    ……
    // 前面已经打开了句柄my_key,下面如此来读取值:
    status = ZwQueryValueKey(
        my_key,
        &my_key_name,
        KeyValuePartialInformation,
        &key_infor,
        sizeof(KEY_VALUE_PARTIAL_INFORMATION),
        &ac_length);
    if(!NT_SUCCESS(status) &&
        status != STATUS_BUFFER_OVERFLOW &&
        status != STATUS_BUFFER_TOO_SMALL)
    {
        // 错误处理
        …    
    }
    // 如果没失败,那么分配足够的空间,再次读取
    ac_key_infor = (PKEY_VALUE_PARTIAL_INFORMATION)
        ExAllocatePoolWithTag(NonpagedPool,ac_length ,MEM_TAG);
    if(ac_key_infor == NULL)
    {
        stauts = STATUS_INSUFFICIENT_RESOURCES;
        // 错误处理
        …
    }
    status = ZwQueryValueKey(
        my_key,
        &my_key_name,
        KeyValuePartialInformation,
        ac_key_infor,
        ac_length,
        &ac_length);
    // 到此为止,如果status为STATUS_SUCCESS,则要读取的数据已经
    // 在ac_key_infor->Data中。请利用前面学到的知识,转换为
    // UNICODE_STRING
    ……

4.3 注册值的写

    实际上注册表的写入比读取要简单。因为这省略了一个尝试数据的大小的过程。直接将数据写入即可。写入值一般使用函数ZwSetValueKey 。这个函数的原型如下:

    NTSTATUS ZwSetValueKey(
        IN HANDLE  KeyHandle,
        IN PUNICODE_STRING  ValueName,
        IN ULONG  TitleIndex  OPTIONAL,
        IN ULONG  Type,
        IN PVOID  Data,
        IN ULONG  DataSize
    );

    其中的TileIndex参数请始终填入0。
    KeyHandle、ValueName、Type这三个参数和ZwQueryValueKey中对应的参数相同。不同的是Data和DataSize。Data是要写入的数据的开始地址,而DataSize是要写入的数据的长度。由于Data是一个空指针,因此,Data可以指向任何数据。也就是说,不管Type是什么,都可以在Data中填写相应的数据写入。
    ZwSetValueKey的时候,并不需要该Value已经存在。如果该Value已经存在,那么其值会被这次写入覆盖。如果不存在,则会新建一个。下面的例子写入一个名字为“Test”,而且值为“My Test Value”的字符串值。假设my_key是一个已经打开的子键的句柄。
    
    UNICODE_STRING name = RTL_CONSTANT_STRING(L”Test”);
    PWCHAR value = { L”My Test Value” };
    …
    // 写入数据。数据长度之所以要将字符串长度加上1,是为了把最后一个空结束符
    // 写入。我不确定如果不写入空结束符会不会有错,有兴趣的读者请自己测试一下。
    status = ZwSetValueKey(my_key,
            &name,0,REG_SZ,value,(wcslen(value)+1)*sizeof(WCHAR));
    if(!NT_SUCCESS(status))
    {
        // 错误处理
        ……
    }
    
    关于注册表的操作就介绍到这里了。如果有进一步的需求,建议读者阅读WDK相关的文档。

最新喜欢:

greenpeacegreenp...
kabunhi
驱动牛犊
驱动牛犊
  • 注册日期2007-06-27
  • 最后登录2016-01-09
  • 粉丝1
  • 关注0
  • 积分461分
  • 威望57点
  • 贡献值0点
  • 好评度46点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2008-05-30 10:23
thank you
microbe
驱动小牛
驱动小牛
  • 注册日期2007-12-10
  • 最后登录2011-01-17
  • 粉丝1
  • 关注0
  • 积分914分
  • 威望420点
  • 贡献值1点
  • 好评度88点
  • 原创分0分
  • 专家分1分
板凳#
发布于:2008-05-30 11:40
3KS,顶起!!
dt1985324
驱动牛犊
驱动牛犊
  • 注册日期2008-05-06
  • 最后登录2009-02-10
  • 粉丝0
  • 关注0
  • 积分16分
  • 威望106点
  • 贡献值1点
  • 好评度20点
  • 原创分0分
  • 专家分0分
地板#
发布于:2008-05-30 16:50
  每篇必顶
期待下文
游客

返回顶部