可可西

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_0WAIT_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_ALLINPUTQS_HOTKEYQS_INPUTQS_KEYQS_MOUSEQS_MOUSEBUTTONQS_MOUSEMOVEQS_PAINTQS_POSTMESSAGEQS_SENDMESSAGEQS_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(
    PSECURITY_ATTRIBUTES psa,
    BOOL bManualReset,
    BOOL bInitialState,
    PCSTR pszName
);

注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(
    PSECURITY_ATTRIBUTES psa,
    BOOL bInitialOwner,
    PCTSTR pszName
);

注:bInitialOwner为Mutex的初始状态;FALSE表示初始状态为未触发状态,线程ID和递归计数都会被设置成0。TRUE表示初始状态为触发状态

使用WaitForSingleObject等候并返回WAIT_OBJECT_0WAIT_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();

仍在运行的时候 进程终止时(ExitProcessTerminateProcess

信号量对象

信号量用来对资源计数。内部包含2个计数器:

最大资源计数器:可使用的最大资源数量

当前资源计数器:当前可用的资源数量

注:当前资源计数器的范围在[0, 最大资源计数器]

 

HANDLE CreateSemaphore(
    PSECURITY_ATTRIBUTES psa,
    LONG lInitialCount,
    LONG lMaximumCount,
    PCTSTR pszName
);

注1:lInitialCount为初始当前资源计数器的值

注2:lMaximumCount为最大资源计数器的值

 

当前资源计数器等于0

 

每使用WaitForSingleObject等候并返回WAIT_OBJECT_0

当前资源计数器减1,当为0时,会将Semaphore置成未触发状态

当前资源计数器大于0

 

BOOL ReleaseSemaphore(
   HANDLE hSemaphore,
   LONG lReleaseCount,
   LPLONG lpPreviousCount
);

 

注1:lReleaseCount需大于0,该值会加到当前资源计数器上

注2:lpPreviousCount返回当前资源计数的原始值,可传入NULL来忽略

线程对象

HANDLE CreateThread();

仍在运行的时候 线程终止时(ExitThreadTerminateThread

等待计时器对象

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

-- 关于程序对资源的使用情况,可以从任务管理器上看到。

 

posted on 2011-07-04 20:03  可可西  阅读(3409)  评论(1编辑  收藏  举报

导航