microsoft / Windows.UI.Composition-Win32-Samples

Windows.UI.Composition Win32 Samples
MIT License
469 stars 186 forks source link

cpp\ScreenCaptureforHWND - When the replica window is exactly at the same size and position, the capture not working properly #39

Closed gileli121 closed 5 years ago

gileli121 commented 5 years ago

Steps

In WinMain function, add the following codes:

1) Set hwndTarget window that is the window that we want to create replica for (capture it Example:

HWND hwndTarget = (HWND)0x00000000000309EC;

2) Register UWP API

    // Init COM
    init_apartment(apartment_type::single_threaded);

    // I don't know why it is but I need this section to make it work
    Windows::System::DispatcherQueueController controller{ nullptr };

    DispatcherQueueOptions options
    {
        sizeof(DispatcherQueueOptions),
        DQTYPE_THREAD_CURRENT,
        DQTAT_COM_STA
    };

3) Register the window class

WNDCLASSEX wcex = {};
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = instance;
    wcex.hIcon = LoadIcon(instance, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = L"ScreenCaptureforHWND";
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
    WINRT_VERIFY(RegisterClassEx(&wcex));

4) Create container window will draw the replica (the captured pixels of the hwndTarget)

RECT rectLayer;
    DwmGetWindowAttribute(hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, &rectLayer, sizeof(RECT));

    HWND hwnd = CreateWindowEx(WS_EX_NOACTIVATE | 0x080000 | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST, wcex.lpszClassName, NULL, 0x80000000,
        rectLayer.left, rectLayer.top, rectLayer.right - rectLayer.left, rectLayer.bottom - rectLayer.top, hwndTarget, NULL, instance, NULL);

    ShowWindow(hwnd, SW_SHOW);

    res = CreateDispatcherQueueController(options, reinterpret_cast<ABI::Windows::System::IDispatcherQueueController * *>(put_abi(controller)));
    if (res != S_OK)
        return -1;

    auto compositor = Compositor();

    auto interop = compositor.as<ABI::Windows::UI::Composition::Desktop::ICompositorDesktopInterop>();
    DesktopWindowTarget desktopWindowTarget{ nullptr };
    res = interop->CreateDesktopWindowTarget(hwnd, true, reinterpret_cast<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget * *>(put_abi(desktopWindowTarget)));
    if (res != S_OK)
        return -1;

    auto containerRoot = compositor.CreateContainerVisual();
    containerRoot.RelativeSizeAdjustment({ 1.0f, 1.0f });

    auto m_root = compositor.CreateSpriteVisual();
    m_root.RelativeSizeAdjustment({ 1, 1 });
    containerRoot.Children().InsertAtTop(m_root);

    //root.Size({ 300,300 });
    desktopWindowTarget.Root(containerRoot);

    winrt::Windows::UI::Composition::SpriteVisual m_renderElement = compositor.CreateSpriteVisual();

    containerRoot.RelativeSizeAdjustment({ 1, 1 });

    m_renderElement.AnchorPoint({ 0.5f, 0.5f });
    m_renderElement.RelativeOffsetAdjustment({ 0.5f, 0.5f, 0 });
    m_renderElement.RelativeSizeAdjustment({ 1, 1 });

    // Create brush for the render element
    auto m_brush = compositor.CreateSurfaceBrush();
    m_renderElement.Brush(m_brush);
    m_brush.HorizontalAlignmentRatio(0.5f);
    m_brush.VerticalAlignmentRatio(0.5f);
    m_brush.Stretch(CompositionStretch::Uniform);

    // Add the render element to the container root
    m_root.Children().InsertAtTop(m_renderElement);

5) Start capture

    auto d3dDevice = CreateD3DDevice();
    auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
    winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device = CreateDirect3DDevice(dxgiDevice.get());

    auto item = CreateCaptureItemForWindow(hwndTarget);

    std::unique_ptr<SimpleCapture> m_capture{ nullptr };
    m_capture = std::make_unique<SimpleCapture>(m_device, item);

    auto surface = m_capture->CreateSurface(compositor);
    m_brush.Surface(surface);

    m_capture->StartCapture();

    //m_capture->TestArea(hwnd2);

    // Message pump
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;

The result is: image

This code reproduced a case that I created a replica window with the exact same size of the hwndTarget and it mirroring the target. It seems that for some reason that if the replica is the exact same size and position, it will capture hwndTarget with some small "window" as I showed in the image in red color..

I attached the full source code that produce the problem.

ScreenCaptureforHWND.zip

Please fix it.

Thanks.

krenner commented 5 years ago

Hi gileli121, Will you share details around the scenario you are trying to create? Based on the end user scenario we could offer advice.

gileli121 commented 5 years ago

I already created the final end result and fixed my problem.

https://windowtop.info/wp-content/uploads/windowtop-aero-example-new-c.gif

Thanks

robmikh commented 5 years ago

I'm going to close this since it looks like it's been addressed. Thanks for sharing, it looks pretty neat!

gileli121 commented 5 years ago

Thanks, I just hope you will not break it in a way that I can't fix it.

We'll take your feedback into account, we'll look into exposing a clear color property in the API. However, it's too late for the next release for us to add such an API, so I can't make any promises.

Please update me about your decision