进线程, 进线程句柄, 进线程ID, 句柄与ID间转换, 伪句柄, 伪句柄转为句柄

原文请参考:http://www.cnblogs.com/zhcncn/articles/2781333.html

1. 线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的.....(说不清楚了),线程句柄是一个内核对象。我们可以通过句柄来操作线程,但是线程的生命周期和线程句柄的生命周期不一样的。线程的生命周期就是线程函数从开始执行到return,线程句柄的生命周期是从CreateThread返回到你CloseHandle()。

2. 所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了。

3. 如果你CreateThread以后需要对这个线程做一些操作,比如改变优先级,被其他线程等待,强制TermateThread等,就要保存这个句柄,使用完了再CloseHandle。如果你开了一个线程,而不需要对它进行如何干预,CreateThread后直接CloseHandle就行了。所以CloseHandel(ThreadHandle );只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对应的线程做任何干预了。并没有结束线程。如果你觉得多了一个变量,也可以写为:CloseHandel(CreateThread(NULL,0,.....));

4. CloseHandle的功能是关闭一个打开的对象句柄,该对象句柄可以是线程句柄,也可以是进程、信号量等其他内核对象的句柄,而ExitThread的功能是终止一个线程,它所接受的参数是一个线程的退出码。 通过调用CloseHandle可以告知系统,已经完成了对某一内核对象的操作,该函数首先检查调用进程的句柄表,来确认进程是否对该句柄所指向的对象有访问权,如果句柄无效则返回FALSE,如果有效,系统将得到该内核对象的数据结构的地址,把结构中的使用计数成员减1,如果计数变为0,则将从内核中释放该内核对象。如果计数还未到0,就意味着还有其他的进程在使用这个内核对象,那么它就不会被释放。

5. ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除

进程句柄 与 进程ID

进程是一种内核对象,每个内核对象实际上是由内核分配的一块内存,而且只能由内核来访问。这一内存块是一个数据结构,它的成员包含有关于该对象的信息。因为内核对象只能由内核访问,所以应用程序不可能在内存中定位这些数据结构和直接改变它们的内容。为了达到操纵这些内核对象的目的,win32API以良好的方式提供了一组操纵这些结构的函数,对内核对象的访问总是通过这些函数.

当调用创建内核对象的函数时,函数返回一个标志该对象的句柄,它是一个32位的数值,可以被进程中的任意线程使用,可以把它传给各种WIN32函数,这样系统就知道想要操纵的是哪一个内核对象。

------------------------------------------------------------------------------------------------------------------
creating a new process causes the system to create a process kernel object 
and a thread kernel object. At creation time, the system gives each object 
an initial usage count of 1. Then, just before CreateProcess returns, the 
function opens the process object and the thread object and places the 
process-relative handles for each in the hProcess and hThread members of 
the PROCESS_INFORMATION structure. When CreateProcess opens these objects 
internally, the usage count for each becomes 2.

当创建进程后, CreateThread返回的PROCESS_INFORMATION structure中进程, 线程的引用计数不是1,而是2。
创建新的进程后,记数初始化为1,而函数需要返回进程内核对象的句柄,相当于打开一次新创建的类核对象,记数再加1.
------------------------------------------------------------------------------------------------------------------

而进程被创建时,系统会赋给它一个唯一的标识符,就是进程ID。进程ID是唯一的, 系统中运行的其他进程不会有相同的ID值. ,进程句柄被打开一次就有一个,同一个进程ID可以有多个进程句柄.

获取当前进线程ID可使用API: GetCurrentProcessId(), GetCurrentThreadId()

进程句柄->进程ID

(refer to: http://www.usidcbbs.com/simple/?t5426.html)
(1)用户态:在用户态下面比较容易,直接通过GetProcessId,传入进程句柄,就返回句柄对应的ID
(2)内核态:在内核态下面,需要调用Undocument API ZwQueryInformationProcess,根据API名字,我们很容易能明白该API的目的,无非就是查询对应进程的一些其它信息,传入的参数为ProcessID,然后ProcessClass填入0,则返回进程的基本信息,其返回结构如下:

复制代码
typedef struct _PROCESS_BASIC_INFORMATION { 
    PVOID Reserved1; 
    PPEB PebBaseAddress; 
    PVOID Reserved2[2]; 
    ULONG_PTR UniqueProcessId; 
    PVOID Reserved3; 
} PROCESS_BASIC_INFORMATION;
复制代码

其中,UnicodeProcessId就是句柄对应的进程ID.

另外, 最后一个字段Reserved3就是Parent进程的ID. 关于如何"通过子进程ID, 获取父进程的ID,然后通过父进程ID来获取父进程Handle"的方法,可参考: (http://blog.163.com/wangninghaha@126/blog/static/204531052012827111415247/)

进程ID->进程句柄

这个过程就比较直接了,无论在用户态还是在内核态,打开进程,就能够获取进程的句柄了.

HANDLE GetProcessHandle(int nID)  //通过进程ID获取进程句柄
{
    return OpenProcess(PROCESS_ALL_ACCESS, FALSE, nID);
}

进程名->进程句柄

复制代码
HANDLE GetProcessHandle(LPCTSTR pName)  //通过进程名获取进程句柄
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnapshot) {
        return NULL;
    }
    PROCESSENTRY32 pe = { sizeof(pe) };
    BOOL fOk;
    for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe)) {
        if (!_tcscmp(pe.szExeFile, pName)) {
            CloseHandle(hSnapshot);
            return GetProcessHandle(pe.th32ProcessID);
        }
    }
    return NULL;
}
复制代码

伪句柄 pseudohandle 和句柄的转换

(refer to:

1. http://www.cnblogs.com/Kernone/archive/2009/08/18/1549286.html

2. http://blog.sina.com.cn/s/blog_4b226b9201011bu6.html )

在使用很多函数的时候,我们都需要获得一个对象的句柄,而某些函数,如GetCurrentProcess() / GetCurrentThread() 返回的是伪句柄 pseudohandle。

所谓伪句柄,即指向当前线程或者进程的句柄,并不是真正意义上的句柄。它并不创建句柄,同时也不增加引用计数,因次调用CloseHandle()不作任何处理。

它本身就只指向调用它的主调进程或线程。会因为调用者的不同而改变,比如:调用者A使用一个伪句柄,这个句柄指向调用者A,而调用者A将该句柄传递给调用者X,则这个句柄就指向调用者X。

进程的伪句柄总是0xffffffff,而线程的伪句柄总是0xfffffffe。

通过使用DuplicateHandle这个强大的函数,可以将伪句柄转换为真正的句柄。

函数原型: 

复制代码
BOOL DuplicateHandle(
  HANDLE hSourceProcessHandle,  // handle to source process
  HANDLE hSourceHandle,         // handle to duplicate
  HANDLE hTargetProcessHandle,  // handle to target process
  LPHANDLE lpTargetHandle,      // duplicate handle
  DWORD dwDesiredAccess,        // requested access
  BOOL bInheritHandle,          // handle inheritance option
  DWORD dwOptions               // optional actions
);
复制代码

参数:

1> hSourceProcessHandle:拥有待复制HANDLE的进程句柄;

2> hSourceHandle:待复制的HANDLE;

3> hTargetProcessHandle:接收复制后HANDLE的进程句柄;

4> lpTargetHandle:指向 存储复制后HANDLE的 地址;如果取值为NULL,函数复制句柄,但不会返回复制后的句柄

5> dwDesiredAccess:指明对新HANDLE的访问权限;

6> bInheritHandle:指明新HANDLE是否可被继承;

7> dwOptions:指明可选的行为;可以取值为0,也可以取如下值的任意结合:

DUPLICATE_CLOSE_SOURCE:关闭掉原HANDLE;

DUPLICATE_SAME_ACCESS:忽略dwDesiredAccess指明的对新HANDLE的访问权限;新句柄具有同原句柄相同的访问权限;

返回值:

如果成功,返回非零值,即TRUE;

如果失败,返回0,即FALSE;(可以调用GetLastError()获取详细错误信息)

注意:

1. 原句柄和复制后的句柄是类似引用的关系,两者指向同一内核对象,对原句柄或复制后的句柄,其中任何一个的改变,都会导致另外一个的改变

2. DuplicateHandle()会递增特定对象的使用计数,因此当完成对复制对象句柄的使用时,应该调用CloseHandle()关闭句柄,从而递减对象的使用计数;

3. lpTargetHandle所指向的句柄跟CreateThread()时创建的句柄不一定相同

示例:

HANDLE hPseudoThread=GetCurrentThread();  
HANDLE hProcess=GetCurrentProcess();  
HANDLE hRealThread=NULL;  
DuplicateHandle(hProcess, hPseudoThread, hProcess, &hRealThread, 0, false, 0);  

调试内容:

hPseudoThread: 0xfffffffe void *

hProcess: 0xffffffff void *

hRealThread: 0x00000ed8 void *


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值