以上代码初始化了一个SECURITY_ATTRIBUTES结构,表明使用默认安全性来创建此对象,且返回的对象时可继承的。

  句柄表的每个记录中还有一个指明该句柄是否可继承的标志位,如果在创建内核对象的时候将NULL作为PSECURITY_ATTRIBUTES的参数传入,则返回的句柄是不可继承的,标志位为0。

  下一步是由父进程创建子进程,这是通过CreateProcess实现的,此函数第四章会详细介绍,此处仅仅注意bInheritHandles参数。如果在创建进程时,此参数被设为false,则表明不希望子进程继承父进程句柄表中的可继承句柄。如为true,则表明希望子进程继承父进程句柄表中的可继承句柄。注意只有可继承句柄才可以被继承。

  新创建的进程句柄表为空,由于我们希望它继承父进程句柄表,此时系统会遍历父进程句柄表,对它的每一个项进行检查,将所有的可继承的句柄的项全部复制到子进程的句柄表中。在子进程的句柄表中,复制项的位置与它在父进程句柄表中的位置是完全一样的,这是非常重要的。它意味着在父进程和子进程中,对一个内核对象进行标识的句柄是完全一样的。除了复制句柄表,系统还会递增每个可继承句柄的使用计数。为了销毁内核对象,父进程和子进程必须都不再使用才可以。这可以通过CloseHandle和进程终止来实现。注意:句柄进程仅仅发生在进程刚被创建时,如果此后父进程又创建了新的内核对象,那么此时子进程不会继承这些新创建的内核对象句柄。

  如果父进程创建了一个内核对象,得到一个不可继承的句柄,但是后来父进程又希望后来创建的子进程继承它,这怎么办呢?这可以通过使用SetHandleInformation修改内核对象句柄的继承标志 。它需三个参数,第一个标识了一个有效句柄,第二个标识想更改哪些标识。第三个标识指出想把它设成什么。这个标识可以是

  HANDLE_FLAG_INHERI,//打开句柄继承标识。

  HANDLE_FLAG_PROTECT_FROM_CLOSE//不允许关闭句柄。

  GetHandleInformation可以用来返回指定句柄的当前标识。

  跨进程共享内核对象方法之二:命名对象

  许多对象都可以进行命名,但并不是全部。因此该方法有一定局限性。有些创建内核对象的函数都有一个指定内核对象名称的参数,如果传入NULL,则会创建一个匿名的内核对象。如果不为NULL,则应该传入一个一''结尾的字符串。所有这些命名对象共享一个名字空间。即使它们类型不同,如果已存在同名对象,创建会失败。

  一旦一个命名的内核对象被创建,其他进程(不仅仅是子进程)可以通过调用Open*或是Create*函数来访问它。当使用Create*函数时,系统会检查是否存在一个传给此函数的名字,如果确实存在一个这样的对象,内核执行安全检查,验证调用者是否有足够的安全权限。如果是,系统会在此进程的句柄表中查找空白记录项,并将其初始化为指向已存在的命名的内核对象。两个进程的句柄不一定相同,这没有任何影响。由于内核对象被再一次引用,所以其引用计数会被递增。

  为了防止在创建一个命名对象时,仅仅打开了一个现有的而不是新建的,可以在创建后调用GetLastError获得详细信息。

  使用Open*函数可以打开已存在的命名内核对象,如果没有找到这个名称的内核对象将返回NULL。如果找到这个名称的内核对象,但类型不同,函数仍返回NULL。只有当名称相同且类型相同的情况下系统才会进一步检查访问权限。如果有权访问,系统会更新此进程的句柄表,并递增内核对象的引用计数。在Open*函数中也可以指定此句柄的继承性。

  Open*和Create*的区别:如果对象不存在,Create*会创建它,Open*将会调用失败。

  我们经常使用命名的内核对象来防止运行一个程序的多个实例。可以在main函数中建立一个命名对象,返回后调用GetLastError如果GetLastError返回ERROR_ALREADY_EXISTS表明此程序的另一个实例在运行。

  关于终端服务命名空间不再介绍,只需知道它是为了防止命名内核对象命名冲突而设计的。以后有需要的可以仔细研究下。