gpt4 book ai didi

c++ - 捕获任何 Windows 应用程序的屏幕?

转载 作者:可可西里 更新时间:2023-11-01 11:45:29 42 4
gpt4 key购买 nike

我正在尝试编写一个 Windows C++ 程序,该程序将尝试从屏幕上当前显示的任何内容中挑选出感兴趣的颜色。

我尝试了以下 GDI、Direct3D9 和 Direct3D11 DXGI 示例,它们所有似乎都用于捕获 Windows 桌面和/或我自己的应用程序自己的输出。当我启动全屏 Direct3D 游戏时,我似乎最终得到了一些空白像素数据。

必须可以实现这一点,否则 OBS Studio、FRAPS 等将无法像它们那样透明地工作。

我知道我可以尝试对 OBS Studio 进行逆向工程,但是有人有更简洁的 C++ 解决方案来将任意 Windows 应用程序的视频输出捕获为某种像素缓冲区吗?

编辑:我还应该提到捕获常规桌面窗口似乎可行。全屏游戏给我带来了麻烦。

编辑:一位评论者请求我的 GDI 代码。这是我的 GDI 和 D3D9 代码。如您所见,我根据发现的相互矛盾的示例尝试了一些变体:

std::wstring GetScreenColor(COLORREF& colorRef)
{
std::wstring retVal;

//const int desktopWidth(GetDeviceCaps(desktopHdc, HORZRES));
//const int desktopHeight(GetDeviceCaps(desktopHdc, VERTRES));
// const int desktopWidth(GetSystemMetrics(SM_CXVIRTUALSCREEN));
// const int desktopHeight(GetSystemMetrics(SM_CYVIRTUALSCREEN));
const int desktopWidth(GetSystemMetrics(SM_CXSCREEN));
const int desktopHeight(GetSystemMetrics(SM_CYSCREEN));
HWND desktopHwnd(GetDesktopWindow());
// HDC desktopHdc(GetDC(NULL));
HDC desktopHdc(GetDC(desktopHwnd));
HDC myHdc(CreateCompatibleDC(desktopHdc));
const HBITMAP desktopBitmap(CreateCompatibleBitmap(desktopHdc, desktopWidth, desktopHeight));
SelectObject(myHdc, desktopBitmap);
// BitBlt(myHdc, 0, 0, desktopWidth, desktopHeight, desktopHdc, 0, 0, SRCCOPY);
BitBlt(myHdc, 0, 0, desktopWidth, desktopHeight, desktopHdc, 0, 0, SRCCOPY | CAPTUREBLT);
//SelectObject(myHdc, hOld);
ReleaseDC(NULL, desktopHdc);

BITMAPINFO bitmapInfo = { 0 };
bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
bitmapInfo.bmiHeader.biWidth = desktopWidth;
bitmapInfo.bmiHeader.biHeight = -desktopHeight;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 24;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = 0;

// TODO: use a persistent buffer?
const unsigned long numPixels(desktopHeight * desktopWidth);
ColorBGRS* rawPixels(new ColorBGRS[numPixels]);
if (!GetDIBits(myHdc, desktopBitmap, 0, desktopHeight, rawPixels, &bitmapInfo, DIB_RGB_COLORS))
{
delete[] rawPixels;
ReleaseDC(desktopHwnd, desktopHdc);
DeleteDC(myHdc);
DeleteObject(desktopBitmap);
return L"GetDIBits() failed";
}

unsigned long redSum(0);
unsigned long greenSum(0);
unsigned long blueSum(0);

for (unsigned long index(0); index < numPixels; ++index)
{
blueSum += rawPixels[index].blue;
greenSum += rawPixels[index].green;
redSum += rawPixels[index].red;
}

const unsigned long redAverage(redSum / numPixels);
const unsigned long blueAverage(blueSum / numPixels);
const unsigned long greenAverage(greenSum / numPixels);

colorRef = RGB(redAverage, greenAverage, blueAverage);

delete[] rawPixels;
ReleaseDC(desktopHwnd, desktopHdc);
DeleteDC(myHdc);
DeleteObject(desktopBitmap);

return std::wstring();
}

std::wstring GetScreenColor2(COLORREF& colorRef)
{
IDirect3D9* d3d9(Direct3DCreate9(D3D_SDK_VERSION));
if (!d3d9)
{
d3d9->Release();
return L"Direct3DCreate9() failed";
}

D3DDISPLAYMODE d3dDisplayMode;
if (FAILED(d3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3dDisplayMode)))
{
return L"GetAdapterDisplayMode() failed";
}

D3DPRESENT_PARAMETERS d3dPresentParams;
ZeroMemory(&d3dPresentParams, sizeof(D3DPRESENT_PARAMETERS));
d3dPresentParams.Windowed = TRUE;
d3dPresentParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dPresentParams.BackBufferFormat = d3dDisplayMode.Format;
d3dPresentParams.BackBufferCount = 1;
d3dPresentParams.BackBufferHeight = d3dDisplayMode.Height;
d3dPresentParams.BackBufferWidth = d3dDisplayMode.Width;
d3dPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
//d3dPresentParams.SwapEffect = D3DSWAPEFFECT_COPY;
d3dPresentParams.hDeviceWindow = NULL; //hWnd;
d3dPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
d3dPresentParams.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

IDirect3DDevice9* d3d9Device(0);
if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dPresentParams.hDeviceWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dPresentParams, &d3d9Device)))
{
d3d9->Release();
return L"CreateDevice() failed";
}

IDirect3DSurface9* d3d9Surface(0);
// if (FAILED(d3d9Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &d3d9Surface))) return L"GetBackBuffer() failed";
if (FAILED(d3d9Device->CreateOffscreenPlainSurface(d3dDisplayMode.Width, d3dDisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &d3d9Surface, NULL)))
// if (FAILED(d3d9Device->CreateOffscreenPlainSurface(d3dDisplayMode.Width, d3dDisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &d3d9Surface, NULL)))
{
d3d9Device->Release();
d3d9->Release();
return L"CreateOffscreenPlainSurface() failed";
}

if (FAILED(d3d9Device->GetFrontBufferData(0, d3d9Surface)))
{
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();
return L"GetFrontBufferData() failed";
}

D3DLOCKED_RECT d3dLockedRect;
if (FAILED(d3d9Surface->LockRect(&d3dLockedRect, 0, D3DLOCK_NO_DIRTY_UPDATE |
D3DLOCK_NOSYSLOCK |
D3DLOCK_READONLY)))
{
d3d9Surface->UnlockRect();
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();
return L"LockRect() failed";
}

const unsigned long numPixels(d3dDisplayMode.Height * d3dDisplayMode.Width);
BYTE* rawPixels((BYTE*)(d3dLockedRect.pBits));
colorRef = RGB(*(rawPixels + 2), *(rawPixels + 1), *(rawPixels));

d3d9Surface->UnlockRect();
d3d9Surface->Release();
d3d9Device->Release();
d3d9->Release();

return std::wstring();
}

最佳答案

Desktop Duplication API自 Windows 8 以来,它能够录制全屏应用程序,如游戏。我最近做了this library对于我的一个项目,您也许可以作为引用。只有在您的情况下,您才需要从纹理中获取原始像素数据,而不是将它们写入视频或图像。

编辑:添加了一个在失去访问权限时重新初始化的小例子。

{
CComPtr<ID3D11Device> pDevice;
CComPtr<IDXGIOutputDuplication> pDeskDupl;
//(...)create devices and duplication interface etc here..
InitializeDesktopDupl(pDevice, &pDeskDupl, &OutputDuplDesc);
while(true) //capture loop gist.
{
IDXGIResource *pDesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
RtlZeroMemory(&FrameInfo, sizeof(FrameInfo));
// Get new frame
HRESULT hr = pDeskDupl->AcquireNextFrame(
99,//timeout in ms
&FrameInfo,
&pDesktopResource);

if (hr == DXGI_ERROR_ACCESS_LOST) {
pDeskDupl->ReleaseFrame();
pDeskDupl.Release();
pDesktopResource->Release();

hr = InitializeDesktopDupl(pDevice, &pDeskDupl, &OutputDuplDesc);
if(FAILED(hr)){
//Check if everything is OK before continuing
}
}
}
}
HRESULT InitializeDesktopDupl(ID3D11Device *pDevice, IDXGIOutputDuplication **ppDesktopDupl, DXGI_OUTDUPL_DESC *pOutputDuplDesc)
{
*ppDesktopDupl = NULL;
*pOutputDuplDesc;

// Get DXGI device
CComPtr<IDXGIDevice> pDxgiDevice;
CComPtr<IDXGIOutputDuplication> pDeskDupl = NULL;
DXGI_OUTDUPL_DESC OutputDuplDesc;

HRESULT hr = pDevice->QueryInterface(IID_PPV_ARGS(&pDxgiDevice));

if (FAILED(hr)) { return hr; }
// Get DXGI adapter
CComPtr<IDXGIAdapter> pDxgiAdapter;
hr = pDxgiDevice->GetParent(
__uuidof(IDXGIAdapter),
reinterpret_cast<void**>(&pDxgiAdapter));
pDxgiDevice.Release();
if (FAILED(hr)) { return hr; }
// Get output
CComPtr<IDXGIOutput> pDxgiOutput;
hr = pDxgiAdapter->EnumOutputs(
m_DisplayOutput,
&pDxgiOutput);
if (FAILED(hr)) { return hr; }
pDxgiAdapter.Release();

CComPtr<IDXGIOutput1> pDxgiOutput1;

hr = pDxgiOutput->QueryInterface(IID_PPV_ARGS(&pDxgiOutput1));
if (FAILED(hr)) { return hr; }
pDxgiOutput.Release();

// Create desktop duplication
hr = pDxgiOutput1->DuplicateOutput(
pDevice,
&pDeskDupl);
if (FAILED(hr)) { return hr; }
pDxgiOutput1.Release();

// Create GUI drawing texture
pDeskDupl->GetDesc(&OutputDuplDesc);

pDxgiOutput1.Release();

*ppDesktopDupl = pDeskDupl;
(*ppDesktopDupl)->AddRef();
*pOutputDuplDesc = OutputDuplDesc;
return hr;
}

关于c++ - 捕获任何 Windows 应用程序的屏幕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46639349/

42 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com