windows资源管理(内核对象/GDI对象/user对象)
在使用c++进行windows编程时,程序员除了管理使用new/malloc动态在堆上分配出来的内存外,还需要对windows的内核对象、GDI对象和user对象进行管理,这些对象使用句柄来标识,通过操作这些句柄就使用不同的资源对象。和堆内存一样,程序员也需要管理这些对象资源,以免造成系统资源泄漏。
句柄(HANDLE)是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。句柄实际上是一种指向某种资源的指针,但与指针又有所不同:指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。
【内核对象、GDI对象和user对象之间的区别】
内核对象是不属于进程的,是属于windows内核的。进程只有一个内核对象句柄表,用来存放其内核对象句柄。所以,多个进程可以同时使用一个内核对象 -- 只要有handle即可。
对于GDI对象和user对象,他们是一个进程内部拥有的东西,不会被多个进程共有。GDI对象与绘图相关,而user与交互相关。
------
内核对象的直接拥有者是操作系统内核,所有进程共享这些内核对象,因此要有一种机制保证内核对象的正确构建、销毁,Windows采用引用计数的技术;内核对象维护着一个引用计数成员。一个进程创建了一个内核对象,对象的引用计数为1,如果该对象又被另外的进程共享,每多一个进程,引用计数会加1,当一个进程调用CloseHandle函数后,引用计数会减1,如果引用计数变为0,操作系统会销毁该内核对象。引用计数实现上跟COM计数的引用计数类似。内核对象使用完之后,要调用CloseHandle。这个函数的作用就是将该内核对象的引用计数 - 1,当这个计数为0的时候,会被操作系统销毁。
GDI对象和user对象的销毁不需用调用CloseHandle。每一个GDI对象和user对象的销毁都有其对应的Destroy或Delete方法。
------
线程状态转换图
注:当线程正在等待未触发的内核对象时,则会进入等待(不可调度);当内核对象被触发时,则会进入就绪(可调度)。
线程等待函数
Sleep
void Sleep( DWORD dwMilliseconds );
让当前线程睡dwMilliseconds毫秒
让出cpu,让当前线程进入等待(不可调度)
传入0时,表示切掉当前线程的时间片(即让线程进入就绪)
WaitForSingleObject
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
);
让当前线程睡觉,并等待某个指定内核对象句柄
说明:该函数在以下2种情况下返回
a. 等待的句柄对象处于触发态
b. 超时
注1:等待的句柄对象已经处于触发态,线程是不会进入等待状态的,直接返回WAIT_OBJECT_0。
注2:dwMilliseconds设置为INFINITE(0xFFFFFFFF),表示永不超时;设置为0,会立即返回不会等待。
该函数失败返回 WAIT_FAILED 例如:传入无效的hHandle句柄
成功有3种状态:
(1) 等待的目标(核心对象)变成触发态。这种情况下返回值为WAIT_OBJECT_0
(2) 核心对象变成触发态之前,等待时间终了。这种情况下返回值为WAIT_TIMEOUT
(3) 如果一个拥有Mutex的线程结束之前没有释放mutex,则传回WAIT_ABANDONED
WaitForMultipleObjects
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in the handle array
CONST HANDLE *lpHandles, // pointer to the object-handle array
BOOL bWaitAll, // wait flag
DWORD dwMilliseconds // time-out interval in milliseconds
);
让当前线程睡觉,并等待指定句柄对象数组
说明: nCount 表示lpHandles做指的handles数组的个数。最大容量为MAXINUM_WAIT_OBJECTS(即64个)。这些handles不需要为相同的类型。
lpHandles 指向一个由对象handles组成的数组。这些handles不需要为相同的类型。
bWaitAll 如果此为TRUE,表示所有的handles都必须触发,此函数才返回;否则只要任何一个handle触发时就返回。
dwMillisenconds 等待时间。当该时间终了,即使没有任何handle触发,此函数也会返回。
该函数失败返回 WAIT_FAILED 例如:传入无效的hHandle句柄
成功有3种状态:
(1) 如果bWaitAll是TRUE,返回值为WAIT_OBJECT_0
(2) 如果bWaitAll是FALSE,那么返回值减去WAIT_OBJECT_0,就表示数组中的哪一个handle被触发了。(如果同时有多个handle处于触发态,则返回在数组中下标最小的那一个。)
(3) 如果等待的handles中有一个拥有Mutex的线程结束之前没有释放mutex,则传回WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1
MsgWaitForMultipleObjects
DWORD MsgWaitForMultipleObjects(
DWORD nCount, // number of handles in the handle array
LPHANDLE pHandles, // pointer to the object-handle array
BOOL bWaitAll, // wait for all or wait for one
DWORD dwMilliseconds, // time-out interval in milliseconds
DWORD dwWakeMask // type of input events to wait for
);
等待核心对象数组和消息
说明:与WaitForMultipleObects相比较,MsgWaitForMultipleObjects有一些额外的返回值意义,为了表示“消息到达队列”,返回值将是WAIT_OBJECT_0 + nCount。
dwMakeMask 可以是QS_ALLINPUT、QS_HOTKEY、QS_INPUT、QS_KEY、QS_MOUSE、QS_MOUSEBUTTON、QS_MOUSEMOVE、QS_PAINT、QS_POSTMESSAGE、QS_SENDMESSAGE、QS_TIMER。
注意的几点:
(1) 在收到WM_QUIT之后,Windows仍然会传送消息给你。如果在WM_QUIT之后,必须继续处理你的消息,否则窗口会变得反应迟钝,而且没有重绘能力。
(2) MsgWaitForMultipleObjects不允许handles数组中有缝隙产生。所以当某个handle被触发时,应该在下一次调用MsgWaitForMultipleObjects之前先把handles数组做了整理、紧压,不能仅仅把handle设置为NULL。
(3) 如果有另一个线程更改了对象数组,而那时你正在等待的,那么需要一种方法,可以强迫MsgWaitForMultipleObjects返回,并重新开始,以包含这个新的handle。【可以使用WM_THREADCOUNT消息】
## 内核对象 ##
内核对象内部有1个布尔变量来保存其状态:未触发(nonsignaled,FALSE,未激发)和触发(signaled,TRUE,激发)。
操作系统通过一些API让内核对象在这两种状态之间进行转换。
核心对象 |
说明 |
未触发(nonsignaled,FALSE) |
触发(signaled,TRUE) |
事件对象 |
HANDLE CreateEvent( 注1:bManualReset为TRUE表示为手动重置事件; 为FALSE表示为自动重置事件。操作系统将所有等待该Event线程切换到就绪后,会自动调用ResetEvent将Event设置成未触发状态。因此,开发者自己不需要显示地调用ResetEvent。 注2:bInitialState为Event的初始状态;FALSE表示初始状态为未触发状态,TRUE表示初始状态为触发状态 |
BOOL ResetEvent(HANDLE hEvent); |
BOOL SetEvent(HANDLE hEvent); |
文件对象 |
HANDLE CreateFile(); |
有待处理的I/O请求的时候 | I/O请求完成的时候 |
文件映射对象 |
HANDLE CreateFileMapping(); |
||
I/O完成对象 |
HANDLE CreateIoCompletionPort(); |
||
作业对象 |
HANDLE CreateJobObject(); |
作业尚未超时的时候 | 作业超时的时候 |
邮件槽对象 |
HANDLE CreateMailslot(); |
||
互斥对象 |
互斥对象用来确保一个线程独占一个资源的访问。 内部包含一个线程ID和一个递归计数。 线程ID为0时,表明该互斥对象不被任何线程占用,处于触发状态。为非0时,为当前占用该互斥对象的线程ID,处于未触发状态。 递归计数为当前线程ID占用该互斥对象的次数。 HANDLE CreateMutex( 注:bInitialOwner为Mutex的初始状态;FALSE表示初始状态为未触发状态,线程ID和递归计数都会被设置成0。TRUE表示初始状态为触发状态 |
使用WaitForSingleObject等候并返回WAIT_OBJECT_0或WAIT_ABANDONED时,会将Mutex设置成未触发状态
注1:WAIT_OBJECT_0表示上次Mutex被正确的释放 注2:WAIT_ABANDONED表示上次Mutex没有调用ReleaseMutex进行释放 |
没有被任何线程拥有,Mutex就处于触发状态 BOOL ReleaseMutex(HANDLE hMutex);
注1:每调用1次ReleaseMutex,递归计数就-1 注2:如果线程成功等待Mutex不止一次,那么必须调用相同的次数的ReleaseMutex,使得递归计数为0。当递归次数为0时,函数还会将线程ID设置成0,这样就进入了触发状态。 |
管道对象 |
BOOL CreatePipe(); |
||
进程对象 |
BOOL CreateProcess(); |
仍在运行的时候 | 进程终止时(ExitProcess、TerminateProcess) |
信号量对象 |
信号量用来对资源计数。内部包含2个计数器: 最大资源计数器:可使用的最大资源数量 当前资源计数器:当前可用的资源数量 注:当前资源计数器的范围在[0, 最大资源计数器]
HANDLE CreateSemaphore( 注1:lInitialCount为初始当前资源计数器的值 注2:lMaximumCount为最大资源计数器的值
|
当前资源计数器等于0
每使用WaitForSingleObject等候并返回WAIT_OBJECT_0 当前资源计数器减1,当为0时,会将Semaphore置成未触发状态 |
当前资源计数器大于0
BOOL ReleaseSemaphore(
注1:lReleaseCount需大于0,该值会加到当前资源计数器上 注2:lpPreviousCount返回当前资源计数的原始值,可传入NULL来忽略 |
线程对象 |
HANDLE CreateThread(); |
仍在运行的时候 | 线程终止时(ExitThread、TerminateThread) |
等待计时器对象 |
HANDLE CreateWaitableTimer(); |
BOOL CancelWaitableTimer(HANDLE hTimer); | 时间到的时候(SetWaitableTimer) |
## GDI对象 ##
GDI对象 |
产生方法 |
销毁方法 |
位图(HBITMAP) |
CreateBitmap,CreateBitmapIndirect, CreateCompatibleBitmap,CreateDIBitmap, CreateDIBSection,CreateDiscardableBitmap |
DeleteObject |
画刷(HBRUSH) |
CreateBrushIndirect,CreateDIBPatternBrush, CreateDIBPatternBrushPt,CreateHatchBrush, CreatePatternBrush,CreateSolidBrush |
DeleteObject |
设备上下文(HDC) |
CreateDC |
DeleteDC,ReleaseDC |
字体(HFONT) |
CreateFont,CreateFontIndirect |
DeleteObject |
内存DC(HDC) |
CreateCompatibleDC |
DeleteDC |
调色板(HPALETTE) |
CreatePalette |
DeleteObject |
画笔(HPEN) |
CreatePen,CreatePenIndirect |
DeleteObject |
区域(HRGN) |
CombineRgn,CreateEllipticRgn, CreateEllipticRgnIndirect,CreatePolygonRgn, CreatePolyPolygonRgn,CreateRectRgn, CreateRectRgnIndirect,CreateRoundRectRgn, |
DeleteObject |
## USER对象 ##
user对象 |
产生方法 |
销毁方法 |
快捷键(HACCEL) |
CreateAcceleratorTable |
DestroyAcceleratorTable |
Caret |
CreateCaret |
DestoryCaret |
光标(HCURSOR) |
CreateCursor,LoadCursor,LoadImage |
DestoryCursor |
钩子(HHOOK) |
SetWindowsHookEx |
UnhookWindowsHookEx |
图标(HICON) |
CreateIconIndirect,LoadIcon,LoadImage |
DestroyIcon |
菜单(HMENU) |
CreateMenu,CreatePopupMenu LoadMenu,LoadMenuIndirect |
DeleteMenu |
窗体(HWND) |
CreateWindow,CreateWindowEx, CreateDialogParam,CreateMDIWindow |
DestroyWindow |
HDWP |
BeginDeferWindowPos |
EndDeferWindowPos |
-- 关于程序对资源的使用情况,可以从任务管理器上看到。