首页 > 解决方案 > How to get the "display stream" of a MS Windows window?

问题描述

I have a program (we will call it the "virtual screen") that create a full screen window and start arbitrary programs, and with the help of hooks (CBTProc) get handles to windows that started programs create. From those handles I retrieve the content of the windows (using GetDIBits) and displays it in the "virtual screen" window.
Currently, this "virtual screen" copy content of windows and then redraw them, which make it work, sort of like a mirroring software.
Here is how I get the content of a window:

struct WindowContent {
    void *pixel;
    int width;
    int height;
};
WindowContent getWindowContent(HWND hWnd, int height, int width)
{
    WindowContent content;
    WINDOWINFO windowInfo;
    GetWindowInfo(hWnd, &windowInfo);
    content.height = windowInfo.rcClient.right - windowInfo.rcClient.left;
    content.width = windowInfo.rcClient.bottom - windowInfo.rcClient.top;
    HDC hdc = GetDC(hWnd);
    HDC captureHdc = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, content.width, content.height);
    HGDIOBJ oldHdc = SelectObject(captureHdc, hBitmap);
    BitBlt(captureHdc, 0, 0, content.width, content.height, hdc, 0, 0, SRCCOPY|CAPTUREBLT);
    SelectObject(captureHdc, oldHdc);
    DeleteDC(captureHdc);
    BITMAPINFO outputBitmapInfo = {};
    outputBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    GetDIBits(hdc, hBitmap, 0, 0, NULL, &outputBitmapInfo, DIB_RGB_COLORS);
    content.pixel = (BYTE *)malloc(outputBitmapInfo.bmiHeader.biSizeImage);
    outputBitmapInfo.bmiHeader.biCompression = BI_RGB;  
    outputBitmapInfo.bmiHeader.biBitCount = 32;
    GetDIBits(hdc, hBitmap, 0, outputBitmapInfo.bmiHeader.biHeight, content.pixel, &outputBitmapInfo, DIB_RGB_COLORS);
    return content;
}

My question is, how do I remove the copying part, how can I make an area on my "virtual screen" the window output for those programs ?
Emphasis on the fact that I'm trying to make created windows be the area on the "virtual screen", I don't want an additional window hidden or living on the desktop. In my research, I've looked into Windows DWM DLLs and found some undocumented function (SignalRedirectionStartComplete or MilConnection_CreateChannel) which names look linked to what I want to do, but I don't think I should use them, as they are undocumented. Also, the code is using Win32 API but I don't mind using another Windows API or another language (C#, DX* ...).

Forgot to mention, I have already thought about using the DWM thumbnail stuff, but it's not that reliable enough for what I'm trying to do. As far as I understand Windows 10 uses DX under the hood for all display output, for GDI, and even for Vulkan / OpenGL programs, and someone used it to make a lib that gets DX 10 texture from a window (). Is it possible to make something similar, that, for a specific HWND, set its "output" to a texture or some region in memory (swapchain redirection ?) instead of the screen and then display the output in another program (in my case, on the "virtual screen" window) ?

标签: c++cwindowswinapi

解决方案


DWM 是一个值得一看的地方。它有一些记录在案的功能,可以让你至少更接近你想要的东西。

您可以使用 .将您的窗口注册为“缩略图”查看器DwmRegisterThumbnail。然后你(可能)调用DwmUpdateThumbnailProperties告诉它如何绘制到你的窗口(例如,设置不透明度、要绘制的矩形,以及是否绘制整个源窗口,或者只是它的客户区)。完成后,您调用DwmUnregisterThumbnail以取消显示它。

虽然这只是更接近您想要的——它消除了您必须将位图从源复制到您自己的窗口中——但这就是它所做的一切。目标应用程序仍将在其他地方运行自己的窗口。

如果您想要更多地隐藏应用程序,您可以创建另一个桌面,并在该桌面上显示应用程序。如果您不提供切换桌面的方法,则可以很好地隐藏它。另一方面,有一些外部工具可以让用户更改桌面,让他们直接看到应用程序。

另一个需要考虑的途径(对于应用程序的子集)是 COM。COM 支持的部分内容是让 COM 服务器在某些 COM 客户端窗口的框架内显示其输出。这几乎不再是以前的大事了,但是(我相信)支持它的所有代码仍然可用。但是,它只适用于专门为支持它而编写的应用程序(老实说,这并不是一个巨大的数字)。用于此的代码也不是微不足道的。


推荐阅读