在另一个进程的窗口句柄(HWND)上绘制位图。

huangapple go评论48阅读模式
英文:

Draw BITMAP over Window Handle (HWND) of another process

问题

以下是代码部分的中文翻译:

我已经使用BitBlt截取了整个屏幕的屏幕截图,并且它确实是一个有效的BITMAP,因为在将其写入文件后,我可以清晰地看到它。

假设我有计算器的窗口句柄。我希望能够不断地使用BITMAP(包含桌面截图)BitBlt到计算器窗口,这样每当计算器处于打开状态时,无论它是背景窗口(可见或被其他窗口覆盖)还是前景窗口 - 它将始终显示最新的截图,而不是默认的计算器应用程序及其按钮。

我拍了桌面的截图(位图),并期望使用StretchDIBits来覆盖目标应用程序的区域与我的位图。然而,当计算器被打开时,它什么也不发生,它不会被桌面截图替换,尽管StretchDIBits函数仍然返回复制到目标的扫描线数。

void* calculator_hwnd = (void*) FindWindowA("ApplicationFrameWindow", "Calculator");
void* composition_window = GetWindowDC((HWND)GetDesktopWindow());
if (composition_window)
{
    void* composition_environment = 0;
    if ((composition_environment = CreateCompatibleDC((HDC)composition_window)))
    {
        int window_x_axis = GetSystemMetrics(SM_CXVIRTUALSCREEN);
        int window_y_axis = GetSystemMetrics(SM_CYVIRTUALSCREEN);
        void* bitmap = CreateCompatibleBitmap((HDC)composition_window, window_x_axis, window_y_axis);
        if (bitmap)
        {
            void* composition_bitmap = SelectObject((HDC)composition_environment, bitmap);
            int status = PrintWindow((HWND) GetDesktopWindow(), (HDC)composition_environment, 0x00000002);
            if (!status)
                BitBlt((HDC)composition_environment, 0, 0, window_x_axis, window_y_axis, (HDC)composition_window, 0, 0, SRCCOPY);

            BITMAPINFO* pbi = CreateBitmapInfoStruct((HBITMAP)bitmap);

            BITMAPINFOHEADER* pbih = (PBITMAPINFOHEADER)pbi;
            unsigned char* lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

            GetDIBits((HDC)composition_environment, (HBITMAP)bitmap, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS);

            void* calculator_window_environment = GetDC((HWND)calculator_hwnd);
             
            StretchDIBits((HDC)calculator_window_environment, 0, 0, window_x_axis, window_y_axis, 0, 0, window_x_axis, window_y_axis, lpBits, pbi, DIB_RGB_COLORS, SRCCOPY);

            GlobalFree(lpBits);

            SelectObject((HDC)composition_environment, composition_bitmap);
            DeleteObject(bitmap);
        }
    }
    DeleteDC((HDC)composition_environment);
    ReleaseDC((HWND)GetDesktopWindow(), (HDC)composition_window);
}

这是我获取CreateBitmapInfoStruct的地方。

这个问题说我尝试的操作是可能的,但没有解释如何操作 - 有人可以根据我的代码回答吗?

英文:

I have bitblt a screenshot of the entire screen, and its indeed a valid BITMAP since after writing it to a file, I can clearly see its there.

Lets say I have the window handle of Calculator. I want to be able to constantly BitBlt the BITMAP (containing the desktop screenshot) onto the window of Calculator so that whenever calculator is open, whether its the background window (visible or overlapped by other windows) or foreground - It will always show the latest screenshot instead of the default Calculator application & its buttons.

I took a screenshot (bitmap) of the desktop and expected to use StretchDIBits to cover the region of the target application with my bitmap. However, when calculator is opened, nothing happens to it/it doesnt get replaced by the screenshot of the desktop, although the StretchDIBits function still returns the number of scan lines copied to the destination.

void* calculator_hwnd = (void*) FindWindowA("ApplicationFrameWindow", "Calculator");
void* composition_window = GetWindowDC((HWND)GetDesktopWindow());
if (composition_window)
{
	void* composition_environment = 0;
	if ((composition_environment = CreateCompatibleDC((HDC)composition_window)))
	{
		int window_x_axis = GetSystemMetrics(SM_CXVIRTUALSCREEN);
		int window_y_axis = GetSystemMetrics(SM_CYVIRTUALSCREEN);
		void* bitmap = CreateCompatibleBitmap((HDC)composition_window, window_x_axis, window_y_axis);
		if (bitmap)
		{
			void* composition_bitmap = SelectObject((HDC)composition_environment, bitmap);
			int status = PrintWindow((HWND) GetDesktopWindow(), (HDC)composition_environment, 0x00000002);
			if (!status)
				BitBlt((HDC)composition_environment, 0, 0, window_x_axis, window_y_axis, (HDC)composition_window, 0, 0, SRCCOPY);

			BITMAPINFO* pbi = CreateBitmapInfoStruct((HBITMAP)bitmap);

			BITMAPINFOHEADER* pbih = (PBITMAPINFOHEADER)pbi;
			unsigned char* lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

			GetDIBits((HDC)composition_environment, (HBITMAP)bitmap, 0, (WORD)pbih->biHeight, lpBits, pbi,
							DIB_RGB_COLORS);

		    void* calculator_window_environment = GetDC((HWND)calculator_hwnd);
			 
            StretchDIBits((HDC)calculator_window_environment, 0, 0, window_x_axis, window_y_axis, 0, 0, window_x_axis, window_y_axis, lpBits, pbi, DIB_RGB_COLORS, SRCCOPY);

			GlobalFree(lpBits);

			SelectObject((HDC)composition_environment, composition_bitmap);
			DeleteObject(bitmap);
		}
	}
	DeleteDC((HDC)composition_environment);
	ReleaseDC((HWND)GetDesktopWindow(), (HDC)composition_window);
}

This is where I got CreateBitmapInfoStruct

This question says what I am trying to do is possible, but doesn't explain how - can anyone answer that with respect to my code?

答案1

得分: 2

这不受操作系统支持。窗口渲染由窗口所有者(即调用CreateWindowEx的线程)自行决定,没有外部线程或进程可以挂接到任何自定义点。

虽然您可以渲染到属于其他窗口的设备上下文,但没有可靠的方法让您知道何时需要更新,也没有办法让目标窗口知道它不应该覆盖刚刚渲染的窗口内容的一部分或全部内容。

这就是GDI渲染所能达到的程度,它主要是为了与未更新以利用桌面合成(在Windows Vista中引入)的应用程序兼容而支持的。在这种模型中,应用程序可以选择渲染窗口内容,而无需提供重定向表面(如果您希望BitBlt/StretchBlt到设备上下文,则需要提供重定向表面)。

因此,实质上,没有可靠的方法让您在未创建的窗口上进行渲染。常见的解决方法是创建一个(部分透明的)覆盖窗口,然后将其移动以使其与应该增强的窗口保持同步。这无疑会导致用户体验非常差,因为您的内容不可避免地会滞后于用户移动窗口。

英文:

This isn't supported by the OS. Window rendering is at the discretion of the window owner (i.e. the thread that called CreateWindowEx), and there aren't any customization points external threads or processes can hook into.

While you can render into a device context that belongs to a foreign window, there's no way for you to (reliably) find out when you need to update, nor is there any way to let the destination know that it shouldn't overwrite part or all of the window contents you just rendered.

That's as far as GDI rendering goes, which is really only supported for compatibility with applications that haven't been updated to take advantage of desktop composition (introduced in Windows Vista). With this model, applications can choose to render window contents without ever providing a redirection surface (which is required if you wish to BitBlt/StretchBlt into a device context).

So, in essence, there's no way for you to reliably render onto a window you did not create. The common workaround is to create a (partially transparent) overlay window, which you would then move around to keep it in sync with the window it is supposed to augment. This is undoubtedly going to create a pretty poor user experience, as your contents are inevitably going to lag behind as the user moves the window.

huangapple
  • 本文由 发表于 2023年2月10日 15:12:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75407949.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定