使用 PrintWindow 截图后,WinAPI Window 不再更新
WinAPI Window does not update anymore after taking screenshot with PrintWindow
截屏后,我正在使用 PrintWindow(). The application contains a listview and after some time, the list does not update anymore. Only when I select an entry in the list, does it update the name of that entry. My assumption is that the ListView's window does not get invalidated somehow, but that's just a guess. I tried calling InvalidateRect() 截屏应用程序,但这也无济于事。
我认为这一定是资源泄漏的原因,但我将所有需要的资源封装在一个 class 中,它会自动处理释放它们。
struct DrawingSurface
{
DrawingSurface(const DrawingSurface&) = delete;
DrawingSurface& operator=(const DrawingSurface&) = delete;
// Window is a custom class, but it's not really important here.
DrawingSurface(const Window& window)
: hwnd(window.handle()), pixels(0), windowDC(0), memoryDC(0), bitmap(0), previous(0)
{
// Get window size.
Rect clientRect = window.getClientRect();
width = clientRect.width();
height = clientRect.height();
// Create DCs.
windowDC = ::GetDC(window.handle());
if(windowDC == NULL)
return;
memoryDC = ::CreateCompatibleDC(windowDC);
if(memoryDC == NULL)
return;
// Create bitmap.
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = width * height * 4;
bitmap = ::CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&pixels, 0, 0);
if(bitmap == NULL)
return;
previous = ::SelectObject(memoryDC, bitmap);
}
~DrawingSurface()
{
if(windowDC != NULL)
::ReleaseDC(hwnd, windowDC);
if(previous != NULL && previous != HGDI_ERROR && memoryDC != NULL)
::SelectObject(memoryDC, previous);
if(memoryDC != NULL)
::DeleteDC(memoryDC);
if(bitmap != NULL)
::DeleteObject(bitmap);
}
bool valid() const
{
return width * height > 0
&& previous != NULL
&& previous != HGDI_ERROR
&& windowDC != NULL
&& memoryDC != NULL
&& bitmap != NULL;
}
int width, height;
HWND hwnd;
HDC windowDC;
HDC memoryDC;
HBITMAP bitmap;
RGBQUAD* pixels;
BITMAPINFO bitmapInfo;
private:
HGDIOBJ previous;
};
然后我用这个绘图界面用这个功能截屏:
bool Screenshot::take(const Window& window)
{
m_width = 0; m_height = 0;
DrawingSurface surface(window);
if(!surface.valid())
return false;
if(PrintWindow(surface.hwnd, surface.memoryDC, PW_CLIENTONLY) == 0)
return false;
if(GdiFlush() == 0)
return false;
// Set attributes.
m_hwnd = surface.hwnd;
m_width = surface.width;
m_height = surface.height;
// Copy pixels.
m_pixels.resize(surface.width * surface.height, { 0, 0, 0, 0 });
memcpy(&m_pixels[0], surface.pixels, surface.bitmapInfo.bmiHeader.biSizeImage);
return true;
}
我看不出我可以在这里泄漏任何资源。为什么我上面描述的可能会发生任何其他想法?
感谢任何帮助,谢谢。
谢谢大家的回答。我现在能够自己解决这个问题,这个问题确实与屏幕截图功能无关。相反,我 post 向 ListView 发送了一条 WM_KEYDOWN
消息,但也忘记了 post WM_KEYUP
。这使 ListView 处于无效状态,因此它不再更新。
再次感谢您的所有回答,尤其感谢您帮助验证没有资源泄漏。
截屏后,我正在使用 PrintWindow(). The application contains a listview and after some time, the list does not update anymore. Only when I select an entry in the list, does it update the name of that entry. My assumption is that the ListView's window does not get invalidated somehow, but that's just a guess. I tried calling InvalidateRect() 截屏应用程序,但这也无济于事。
我认为这一定是资源泄漏的原因,但我将所有需要的资源封装在一个 class 中,它会自动处理释放它们。
struct DrawingSurface
{
DrawingSurface(const DrawingSurface&) = delete;
DrawingSurface& operator=(const DrawingSurface&) = delete;
// Window is a custom class, but it's not really important here.
DrawingSurface(const Window& window)
: hwnd(window.handle()), pixels(0), windowDC(0), memoryDC(0), bitmap(0), previous(0)
{
// Get window size.
Rect clientRect = window.getClientRect();
width = clientRect.width();
height = clientRect.height();
// Create DCs.
windowDC = ::GetDC(window.handle());
if(windowDC == NULL)
return;
memoryDC = ::CreateCompatibleDC(windowDC);
if(memoryDC == NULL)
return;
// Create bitmap.
ZeroMemory(&bitmapInfo, sizeof(BITMAPINFO));
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = width;
bitmapInfo.bmiHeader.biHeight = -height;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = width * height * 4;
bitmap = ::CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, (void**)&pixels, 0, 0);
if(bitmap == NULL)
return;
previous = ::SelectObject(memoryDC, bitmap);
}
~DrawingSurface()
{
if(windowDC != NULL)
::ReleaseDC(hwnd, windowDC);
if(previous != NULL && previous != HGDI_ERROR && memoryDC != NULL)
::SelectObject(memoryDC, previous);
if(memoryDC != NULL)
::DeleteDC(memoryDC);
if(bitmap != NULL)
::DeleteObject(bitmap);
}
bool valid() const
{
return width * height > 0
&& previous != NULL
&& previous != HGDI_ERROR
&& windowDC != NULL
&& memoryDC != NULL
&& bitmap != NULL;
}
int width, height;
HWND hwnd;
HDC windowDC;
HDC memoryDC;
HBITMAP bitmap;
RGBQUAD* pixels;
BITMAPINFO bitmapInfo;
private:
HGDIOBJ previous;
};
然后我用这个绘图界面用这个功能截屏:
bool Screenshot::take(const Window& window)
{
m_width = 0; m_height = 0;
DrawingSurface surface(window);
if(!surface.valid())
return false;
if(PrintWindow(surface.hwnd, surface.memoryDC, PW_CLIENTONLY) == 0)
return false;
if(GdiFlush() == 0)
return false;
// Set attributes.
m_hwnd = surface.hwnd;
m_width = surface.width;
m_height = surface.height;
// Copy pixels.
m_pixels.resize(surface.width * surface.height, { 0, 0, 0, 0 });
memcpy(&m_pixels[0], surface.pixels, surface.bitmapInfo.bmiHeader.biSizeImage);
return true;
}
我看不出我可以在这里泄漏任何资源。为什么我上面描述的可能会发生任何其他想法?
感谢任何帮助,谢谢。
谢谢大家的回答。我现在能够自己解决这个问题,这个问题确实与屏幕截图功能无关。相反,我 post 向 ListView 发送了一条 WM_KEYDOWN
消息,但也忘记了 post WM_KEYUP
。这使 ListView 处于无效状态,因此它不再更新。
再次感谢您的所有回答,尤其感谢您帮助验证没有资源泄漏。