1、错误处理
GetLastError,SetLastError,这两个函数与线程相关的。参数DWORD有分段的含义,比如第29位为0表示Microsoft定义的错误码,为1表示客户端定义的错误码
错误翻译FormatMessage,参数里面也可以指定语言版本
2、字符和字符串
2.1 字符编码:Unicode,ANSI
实现Unicode的方式也有UTF8,UTF16,UTF32
比较常用的是UTF8和UTF16。比如同一个字"中",用utf8编码,char szChinese[];每个char里面存储的值和用utf16编码,存储的值是不同的
string只是一个内存区,具体存储一个汉字,使用何种编码需要上层指定好,然会赋值给char或string
2.2 Unicode和ANSI转换
MultiByteToWideChar // 多字节转换为宽字节,宽字节即Unicode,使用wchar
WideCharToMultiByte
2.3 判断文本内容是Unicode
IsTextUnicode,该函数只是去猜测文本内容
3、内核对象
1. 每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问。这个内存块是一个数据结构,其成员维护着与对象相关的信息。
2. 内核对象,内核对象的所有者是操作系统内核,而非进程。内核对象的生命周期可能长于创建它的进程。操作系统使用计数来确定当前有多少个进程正在使用特定的内核对象。
2.1 创建内核对象:CreateThread,CreateFile,CreateFileMapping等。参数SECURITY_ATTRIBUTES 标记了安全性相关属性。每个创建内核对象的函数都有该参数。
2.2 关闭内核对象:CloseHandle.
2.3 内核对象句柄表。当创建一个句柄时,会将其插入到内核对象句柄表中,用户使用create**返回的句柄值时,系统会查找句柄表,如果查找为空,则会返回无效句柄的错误。当调用closehandle时,会清除句柄表中的记录项,但不一定会销毁该内核对象,因为有一个计数器存在,当计数器值未0(没有任何进程拥有该内核对象了)的时候才销毁,但记录肯定会清除,因为句柄表是和进程相关的。
每个进程都有自己的句柄表,即调用CloseHandle是与调用者的进程相关的,如果传了一个其它进程的Handle过来,有可能关闭失败,也有可能刚好关错了。
进程间共享内核对象的方法:,共享后计数会+1的
句柄继承:handel的值是相同的
进程名:handle的值是不同的,但是指向同一个内核对象
复制对象句柄:
2.4 使用对象句柄继承。只有在进程间有一个父-子关系的时候,才可以使用对象句柄继承。
A:父继承必须向系统指定它希望这个对象的句柄是可继承的
B:子进程在创建的时候,向系统声明继承父进程中可继承的句柄。
如果子进程在创建后,父进程再创建新的内核句柄,子进程是没有继承到的。只在创建子进程的时候,才会遍历父进程中可继承的句柄并进行复制到自己的句柄表中。
2.5 改变句柄的标志:SetHandleInfomation
2.6 为对象命名:使用名称实现进程共享内核对象,B共享A的并且B不一定是A的子进程。但是B和A的句柄值可能是不一样的,只是它们使用了不同的值指向了同一块内核对象。如进程A创建了一个mutex,进程B调用CreateMutex会返回一个新值,调用错误码会得到already_exists。
打开句柄:Open*,可以使用打开句柄直接得到A创建的句柄,而不需要使用create。Open返回的句柄值和进程A中create的句柄值是一样的。而create是不一样的(Create会返回一个新值,该值也指向了B的Mutex)。
不同用户登录同一个pc,是在不同的session下的,此时使用进程名也是访问不导的,他们在不通的内核对象的命名空间中
获取进程ID:GetCurrentProcessId,根据进程ID获得当前会话id--ProcessIdToSessionId。因为一个进程可能在多个会话中运行,需要确认该进程在哪个会话中。有一些内核对象是在全局命名空间,有一些是在自己内部的命名空间,创建时通过Global\MyName或Local\MyName进行设置。
复制对象句柄:DuplicateHandle,在不同进程之间复制句柄,复制句柄的目的主要是对句柄权限赋值和修改
BOOL WINAPI DuplicateHandle(
__in HANDLE hSourceProcessHandle,
__in HANDLE hSourceHandle,
__in HANDLE hTargetProcessHandle,
__out LPHANDLE lpTargetHandle,
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in DWORD dwOptions
);
将hSourceHandle复制给lpTargetHandle这个值,在源进程中不要关闭lpTargetHandle,因为目标进程会使用该值的。
hSourceProcessHandle:源进程内核句柄(即负责传递内核对象句柄的进程句柄)
hSourceHandle:要传递的内核对象句柄
hTargetProcessHandle:目标进程内核句柄
lpTargetHandle:接收内核对象句柄的地址(先随便声明一个HANDLE)
dwDesiredAccess:TargetHandle句柄使用何种访问掩码(这个掩码是在句柄表中的一项)
bInheritHandle:是否拥有继承
dwOptions:当设DUPLICATE_SAME_ACCESS时,表示于源的内核对象所有标志一样,此时wDesiredAccess可标志为0
当设DUPLICATE_CLOSE_SOURCE时,传输完后,关闭源中的内核对象句柄
此函数能否成功调用还要看你是否有足够的权限去操作目标进程
通常目标进程的内核句柄是利用OpenProcess()得到的
HANDLE WINAPI OpenProcess(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in DWORD dwProcessId
);
dwDesiredAccess:决定你拥有该进程的操作权限,如果要成功用到则要填PROCESS_ALL_ACCESS或PROCESS_DUP_HANDLE
bInheritHandle:是否可继承
dwProcessId:这个ID可在资源管理器中找到,当然,我不提倡在哪里得到,或者你可以通过进程间通信的方法把PID从目标进程传给源进程
若DuplicateHandle()能成功执行,则利用进程通信把句柄值TargetHandle传给目标进程,让他知道利用该句柄使用内核对象,即目标进程能够使用lpTargetHandle了
但很多菜鸟们都不知道为何要用复制句柄函数,我利用进程间通信把句柄传给目标进程不就行了吗?
这样的想法就大错特错了,我们表面上是在复制句柄值,实际上是把该句柄在源进程句柄表中的所有项复制到目标进程的句柄表中,而且使该内核对象的计数器+1了,如果只是简单的只传句柄值,目标进程的句柄表中是不会有所增加的(这样系统不知道内核对象被多个进程使用,在关闭时会出问题的)