是否可以屏幕捕获特定窗口(也可能是另一个进程的窗口)?目前我正在捕获特定显示器的整个桌面,但我真正想要的是捕获特定窗口的内容(无论其位置如何)。
hl0ma9xz1#
是的。所有你需要的是获得句柄到窗口,你想捕捉和使用WinAPI函数PrintWindow例如:
// Get the window handle of calculator application. HWND hWnd = ::FindWindow( 0, _T( "Calculator" )); // Take screenshot. PrintWindow( hWnd, getDC(hWnd), 0 );
Here您有打印窗口文档。
8nuwlpux2#
是的,就像捕获全屏一样简单。你只需要在所需的窗口上使用GetWindowDC()而不是GetDesktopWindow(),然后从那里到你的目标DC使用BitBlt()。你也可以通过使用GetWindowRect()获得正确的大小。请注意,此方法还允许您从隐藏/覆盖的窗口中进行捕获,而带有边框的完整屏幕截图则不能。有关更多详细信息,请参见this question。
GetWindowDC()
GetDesktopWindow()
BitBlt()
GetWindowRect()
zc0qhyus3#
有一个新的API在Windows 10中winrt/Windows.Graphics.Capture.h这里有一个非常复杂的例子,由微软:https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/ScreenCaptureforHWND我想创建一些脏的例子(非常脏),在我的例子中,你有一个简单的函数,它获取一个窗口的HWND并捕获它,然后将其保存为ScreenShot.bmp注意该函数错误处理不好,欢迎您改进!注:
winrt/Windows.Graphics.Capture.h
HWND
ScreenShot.bmp
WDA_NONE
编码:
#include <iostream> #include <Windows.h> #include <dxgi.h> #include <inspectable.h> #include <dxgi1_2.h> #include <d3d11.h> #include <winrt/Windows.Foundation.h> #include <winrt/Windows.System.h> #include <winrt/Windows.Graphics.Capture.h> #include <windows.graphics.capture.interop.h> #include <windows.graphics.directx.direct3d11.interop.h> #include <roerrorapi.h> #include <shlobj_core.h> #include <dwmapi.h> #pragma comment(lib,"Dwmapi.lib") #pragma comment(lib,"windowsapp.lib") void CaptureWindow(HWND hwndTarget) { // Init COM winrt::init_apartment(winrt::apartment_type::multi_threaded); // Create Direct 3D Device winrt::com_ptr<ID3D11Device> d3dDevice; winrt::check_hresult(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0,D3D11_SDK_VERSION, d3dDevice.put(), nullptr, nullptr)); winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device; const auto dxgiDevice = d3dDevice.as<IDXGIDevice>(); { winrt::com_ptr<::IInspectable> inspectable; winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), inspectable.put())); device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>(); } auto idxgiDevice2 = dxgiDevice.as<IDXGIDevice2>(); winrt::com_ptr<IDXGIAdapter> adapter; winrt::check_hresult(idxgiDevice2->GetParent(winrt::guid_of<IDXGIAdapter>(), adapter.put_void())); winrt::com_ptr<IDXGIFactory2> factory; winrt::check_hresult(adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void())); ID3D11DeviceContext* d3dContext = nullptr; d3dDevice->GetImmediateContext(&d3dContext); RECT rect{}; DwmGetWindowAttribute(hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT)); const auto size = winrt::Windows::Graphics::SizeInt32{rect.right - rect.left, rect.bottom - rect.top}; winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_framePool = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create( device, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, size); const auto activationFactory = winrt::get_activation_factory< winrt::Windows::Graphics::Capture::GraphicsCaptureItem>(); auto interopFactory = activationFactory.as<IGraphicsCaptureItemInterop>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem captureItem = {nullptr}; interopFactory->CreateForWindow(hwndTarget, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), reinterpret_cast<void**>(winrt::put_abi(captureItem))); auto isFrameArrived = false; winrt::com_ptr<ID3D11Texture2D> texture; const auto session = m_framePool.CreateCaptureSession(captureItem); m_framePool.FrameArrived([&](auto& framePool, auto&) { if (isFrameArrived) return; auto frame = framePool.TryGetNextFrame(); struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) IDirect3DDxgiInterfaceAccess : ::IUnknown { virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0; }; auto access = frame.Surface().as<IDirect3DDxgiInterfaceAccess>(); access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void()); isFrameArrived = true; return; }); session.IsCursorCaptureEnabled(false); session.StartCapture(); // Message pump MSG msg; clock_t timer = clock(); while (!isFrameArrived) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) DispatchMessage(&msg); if (clock() - timer > 20000) { // TODO: try to make here a better error handling return; } } session.Close(); D3D11_TEXTURE2D_DESC capturedTextureDesc; texture->GetDesc(&capturedTextureDesc); capturedTextureDesc.Usage = D3D11_USAGE_STAGING; capturedTextureDesc.BindFlags = 0; capturedTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; capturedTextureDesc.MiscFlags = 0; winrt::com_ptr<ID3D11Texture2D> userTexture = nullptr; winrt::check_hresult(d3dDevice->CreateTexture2D(&capturedTextureDesc, NULL, userTexture.put())); d3dContext->CopyResource(userTexture.get(), texture.get()); D3D11_MAPPED_SUBRESOURCE resource; winrt::check_hresult(d3dContext->Map(userTexture.get(), NULL, D3D11_MAP_READ, 0, &resource)); BITMAPINFO lBmpInfo; // BMP 32 bpp ZeroMemory(&lBmpInfo, sizeof(BITMAPINFO)); lBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lBmpInfo.bmiHeader.biBitCount = 32; lBmpInfo.bmiHeader.biCompression = BI_RGB; lBmpInfo.bmiHeader.biWidth = capturedTextureDesc.Width; lBmpInfo.bmiHeader.biHeight = capturedTextureDesc.Height; lBmpInfo.bmiHeader.biPlanes = 1; lBmpInfo.bmiHeader.biSizeImage = capturedTextureDesc.Width * capturedTextureDesc.Height * 4; std::unique_ptr<BYTE> pBuf(new BYTE[lBmpInfo.bmiHeader.biSizeImage]); UINT lBmpRowPitch = capturedTextureDesc.Width * 4; auto sptr = static_cast<BYTE*>(resource.pData); auto dptr = pBuf.get() + lBmpInfo.bmiHeader.biSizeImage - lBmpRowPitch; UINT lRowPitch = std::min<UINT>(lBmpRowPitch, resource.RowPitch); for (size_t h = 0; h < capturedTextureDesc.Height; ++h) { memcpy_s(dptr, lBmpRowPitch, sptr, lRowPitch); sptr += resource.RowPitch; dptr -= lBmpRowPitch; } // Save bitmap buffer into the file ScreenShot.bmp WCHAR lMyDocPath[MAX_PATH]; winrt::check_hresult(SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, lMyDocPath)); std::wstring lFilePath = L"ScreenShot.bmp"; FILE* lfile = nullptr; if (auto lerr = _wfopen_s(&lfile, lFilePath.c_str(), L"wb"); lerr != 0) return; if (lfile != nullptr) { BITMAPFILEHEADER bmpFileHeader; bmpFileHeader.bfReserved1 = 0; bmpFileHeader.bfReserved2 = 0; bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lBmpInfo.bmiHeader.biSizeImage; bmpFileHeader.bfType = 'MB'; bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, lfile); fwrite(&lBmpInfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, lfile); fwrite(pBuf.get(), lBmpInfo.bmiHeader.biSizeImage, 1, lfile); fclose(lfile); } return; }
使用方法:
auto targetHwnd = FindWindow(L"Notepad",NULL); // Or user any other HWND CaptureWindow(targetHwnd); // If it worked, it will create ScreenShot.bmp file
j5fpnvbx4#
当我想在记事本上绘制游戏画面时,使用以下方法只能显示黑色画面。
hdcGame := GetDC(hwndGame) BitBlt(hdcNotpad, 0, 0, rectGame.Width(), rectGame.Height(), hdcGame, 0, 0, SRCCOPY )
因此,我尝试@Roman R.的建议,捕获桌面DCx 1 m0n1x,然后计算屏幕的目标位置GetWindowRect,并仅绘制该位置。BitBlt或StretchBlt
下面的代码由go实现。
// draw the calculator's screen on the notepad func Example_captureWindow() { user32dll := w32.NewUser32DLL() gdi32dll := w32.NewGdi32DLL() hwndScreen := user32dll.GetDesktopWindow() hdcScreen := user32dll.GetDC(hwndScreen) defer user32dll.ReleaseDC(hwndScreen, hdcScreen) // release when exit function hwndCalculator := user32dll.FindWindow("ApplicationFrameWindow", "小算盤") hwndNotepad := user32dll.FindWindow("Notepad", "") hdcNotepad := user32dll.GetWindowDC(hwndNotepad) defer user32dll.ReleaseDC(hwndNotepad, hdcNotepad) var rectNotepad w32.RECT var rectC w32.RECT gdi32dll.SetStretchBltMode(hdcNotepad, w32.HALFTONE) user32dll.GetWindowRect(hwndNotepad, &rectNotepad) user32dll.GetWindowRect(hwndCalculator, &rectC) gdi32dll.StretchBlt( hdcNotepad, rectNotepad.Width()/4, rectNotepad.Height()/4, rectNotepad.Width()/2, rectNotepad.Height()/2, // draw screen on the center hdcScreen, rectC.Left, rectC.Top, rectC.Width(), rectC.Height(), w32.SRCCOPY, ) }
1.每0.1秒捕获一次Calculator's屏幕,然后在记事本上绘制:源链路1.按F9可以捕获特定设备屏幕并将其显示在记事本上。源链接
4条答案
按热度按时间hl0ma9xz1#
是的。所有你需要的是获得句柄到窗口,你想捕捉和使用WinAPI函数PrintWindow例如:
Here您有打印窗口文档。
8nuwlpux2#
是的,就像捕获全屏一样简单。你只需要在所需的窗口上使用
GetWindowDC()
而不是GetDesktopWindow()
,然后从那里到你的目标DC使用BitBlt()
。你也可以通过使用GetWindowRect()
获得正确的大小。请注意,此方法还允许您从隐藏/覆盖的窗口中进行捕获,而带有边框的完整屏幕截图则不能。
有关更多详细信息,请参见this question。
zc0qhyus3#
有一个新的API在Windows 10中
winrt/Windows.Graphics.Capture.h
这里有一个非常复杂的例子,由微软:https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/ScreenCaptureforHWND我想创建一些脏的例子(非常脏),在我的例子中,你有一个简单的函数,它获取一个窗口的
HWND
并捕获它,然后将其保存为ScreenShot.bmp
注意该函数错误处理不好,欢迎您改进!
注:
WDA_NONE
显示关联(https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity)编码:
使用方法:
j5fpnvbx4#
当我想在记事本上绘制游戏画面时,使用以下方法只能显示黑色画面。
因此,我尝试@Roman R.的建议,捕获桌面DCx 1 m0n1x,然后计算屏幕的目标位置GetWindowRect,并仅绘制该位置。BitBlt或StretchBlt
示例
下面的代码由go实现。
其他示例:
1.每0.1秒捕获一次Calculator's屏幕,然后在记事本上绘制:源链路
1.按F9可以捕获特定设备屏幕并将其显示在记事本上。源链接