GDI对象句柄表
存在着GDI对象表,可以通过未有文档的函数GdiQueryTable查询到:此函数存在于GDI32中,
在GDI对象表中,每个Cell应该是一个16字节的结构体:
struct { void *pKernel; USHORT nProcess; USHORT nCount; USHORT nUpper; USHORT nType; void *pUser; }GdiTableCell;
pKernel指向的是内核地址(页面池),每个GDI对象都有GDI对象表指向的内核数据结构
nProcess是进程标识符,说明GDI对象是进程相关的
nCount一般为0,但是对于DDB来说,当它被选入一个DC中,nCount从0变为1,
如果试图把它再选入另一个DC,就会失败,当DDB从第一个DC中取消时,nCount从1变为0, 说明DDB不能一次被选入一个以上的设备上下文
所以做了个小代码测试:
CPaintDC dc(this); CDC MemDC1,MemDC2; MemDC1.CreateCompatibleDC(&dc); MemDC2.CreateCompatibleDC(&dc); CBitmap *pOld = NULL; pOld = MemDC1.SelectObject(&m_ddb); //MemDC1.SelectObject(pOld); pOld = MemDC2.SelectObject(&m_ddb); CRect rcClient; GetClientRect(&rcClient); dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &MemDC2, 0, 0, SRCCOPY);
测试结果是由于MemDC1已选进了m_ddb,所以MemDC2失败了,返回NULL,也没有绘制出来,那么把//MemDC1.SelectObject(pOld);打开,就OK了,
但是对于字体来说,nCount只是一个选择计数,所以把字体选入第二个DC成功后,nCount加1
注意的是这张表是共享的,也就是这张表还包含了其他进程的GDI对象句柄,而进程终止时,GDI会搜索这张表,把有指定进程标识符的对象全部删除掉。