一段.NET调用WINAPI实现截图代码的BUG。
由于工作需要,在网上找到一段关于.NET调用WIN API实现截图的代码。
public static Bitmap GetDesktopImage()
{
//In size variable we shall keep the size of the screen.
SIZE size;
//Here we get the handle to the desktop device context.
IntPtr hDC = PlatformInvokeUSER32.GetDC(PlatformInvokeUSER32.GetDesktopWindow());

//Here we make a compatible device context in memory for screen device context.
IntPtr hMemDC = PlatformInvokeGDI32.createCompatibleDC(hDC);
//We pass SM_CXSCREEN constant to GetSystemMetrics to get the X coordinates of screen.
size.cx = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CXSCREEN);

//We pass SM_CYSCREEN constant to GetSystemMetrics to get the Y coordinates of screen.
size.cy = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CYSCREEN);
//We create a compatible bitmap of screen size and using screen device context.
m_HBitmap = PlatformInvokeGDI32.createCompatibleBitmap(hDC, size.cx, size.cy);

//As m_HBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
if (m_HBitmap!=IntPtr.Zero)
{
//Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
IntPtr hOld = (IntPtr) PlatformInvokeGDI32.selectObject(hMemDC, m_HBitmap);
//We copy the Bitmap to the memory device context.
PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0,size.cx,size.cy, hDC, 0, 0, PlatformInvokeGDI32.SRCCOPY);
//We select the old bitmap back to the memory device context.
PlatformInvokeGDI32.selectObject(hMemDC, hOld);
//We delete the memory device context.
PlatformInvokeGDI32.deleteDC(hMemDC);
//We release the screen device context.
PlatformInvokeUSER32.ReleaseDC(PlatformInvokeUSER32.GetDesktopWindow(), hDC);
//Image is created by Image bitmap handle and returned.
return System.Drawing.Image.FromHbitmap(m_HBitmap);
}
//If m_HBitmap is null retunrn null.
return null;
}

实际运行40次左右就出现错误,CodeError=8,用VC错误代码查看器查看显示为:内存不足。
问题出现在return System.Drawing.Image.FromHbitmap(m_HBitmap); 这一句话。
.NET调用WINAPI创建了一个DC资源后,直接返回到托管代码,并没有显式释放DC资源,运行多次后,系统DC资源被用尽,所以报错。
具体修改方法是利用WIN API的deleteObject(InPtr)释放DC资源。
将如下代码:
return System.Drawing.Image.FromHbitmap(m_HBitmap);

修改为:
Bitmap bmp = System.Drawing.Image.FromHbitmap(m_HBitmap);
PlatformInvokeGDI32.deleteObject(m_HBitmap);
return bmp;
这样问题就解决了。使用.NET 调用WIN API 时一定要小心系统资源释放的问题,平时用惯了.NET的垃圾收集往往会忽视,导致
程序稳定性很差。
public static Bitmap GetDesktopImage()
{
//In size variable we shall keep the size of the screen.
SIZE size;
//Here we get the handle to the desktop device context.
IntPtr hDC = PlatformInvokeUSER32.GetDC(PlatformInvokeUSER32.GetDesktopWindow()); 
//Here we make a compatible device context in memory for screen device context.
IntPtr hMemDC = PlatformInvokeGDI32.createCompatibleDC(hDC);
//We pass SM_CXSCREEN constant to GetSystemMetrics to get the X coordinates of screen.
size.cx = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CXSCREEN);
//We pass SM_CYSCREEN constant to GetSystemMetrics to get the Y coordinates of screen.
size.cy = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CYSCREEN);
//We create a compatible bitmap of screen size and using screen device context.
m_HBitmap = PlatformInvokeGDI32.createCompatibleBitmap(hDC, size.cx, size.cy);
//As m_HBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
if (m_HBitmap!=IntPtr.Zero)
{
//Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
IntPtr hOld = (IntPtr) PlatformInvokeGDI32.selectObject(hMemDC, m_HBitmap);
//We copy the Bitmap to the memory device context.
PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0,size.cx,size.cy, hDC, 0, 0, PlatformInvokeGDI32.SRCCOPY);
//We select the old bitmap back to the memory device context.
PlatformInvokeGDI32.selectObject(hMemDC, hOld);
//We delete the memory device context.
PlatformInvokeGDI32.deleteDC(hMemDC);
//We release the screen device context.
PlatformInvokeUSER32.ReleaseDC(PlatformInvokeUSER32.GetDesktopWindow(), hDC);
//Image is created by Image bitmap handle and returned.
return System.Drawing.Image.FromHbitmap(m_HBitmap);
}
//If m_HBitmap is null retunrn null.
return null;
} 
实际运行40次左右就出现错误,CodeError=8,用VC错误代码查看器查看显示为:内存不足。
问题出现在return System.Drawing.Image.FromHbitmap(m_HBitmap); 这一句话。
.NET调用WINAPI创建了一个DC资源后,直接返回到托管代码,并没有显式释放DC资源,运行多次后,系统DC资源被用尽,所以报错。
具体修改方法是利用WIN API的deleteObject(InPtr)释放DC资源。
将如下代码:
return System.Drawing.Image.FromHbitmap(m_HBitmap); 
修改为:
Bitmap bmp = System.Drawing.Image.FromHbitmap(m_HBitmap);
PlatformInvokeGDI32.deleteObject(m_HBitmap);
return bmp;这样问题就解决了。使用.NET 调用WIN API 时一定要小心系统资源释放的问题,平时用惯了.NET的垃圾收集往往会忽视,导致
程序稳定性很差。

浙公网安备 33010602011771号