Closed gileli121 closed 5 years ago
Do you have a repo I can clone? I'm unsure what other changes you've made. For example, I'm not sure if you're calling init_apartment, the only code you've shown has it commented out and that's a big red flag.
Hi, This time I changed only the main.cpp file to this:
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
#include "App.h"
#include "SimpleCapture.h"
#include <ShObjIdl.h>
#include "Win32WindowEnumeration.h"
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Composition::Desktop;
// Direct3D11CaptureFramePool requires a DispatcherQueue
auto CreateDispatcherQueueController()
{
namespace abi = ABI::Windows::System;
DispatcherQueueOptions options
{
sizeof(DispatcherQueueOptions),
DQTYPE_THREAD_CURRENT,
DQTAT_COM_STA
};
Windows::System::DispatcherQueueController controller{ nullptr };
check_hresult(CreateDispatcherQueueController(options, reinterpret_cast<abi::IDispatcherQueueController**>(put_abi(controller))));
return controller;
}
DesktopWindowTarget CreateDesktopWindowTarget(Compositor const& compositor, HWND window)
{
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = compositor.as<abi::ICompositorDesktopInterop>();
DesktopWindowTarget target{ nullptr };
check_hresult(interop->CreateDesktopWindowTarget(window, true, reinterpret_cast<abi::IDesktopWindowTarget**>(put_abi(target))));
return target;
}
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow);
auto g_app = std::make_shared<App>();
auto g_windows = EnumerateWindows();
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
class TestClass
{
public:
void Test(HINSTANCE instance)
{
// Init COM
init_apartment(apartment_type::single_threaded);
// Create the window
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));
HWND hwnd = CreateWindow(
L"ScreenCaptureforHWND",
L"ScreenCaptureforHWND",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
instance,
NULL);
WINRT_VERIFY(hwnd);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Create combo box
HWND comboBoxHwnd = CreateWindow(
WC_COMBOBOX,
L"",
CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
10,
10,
200,
200,
hwnd,
NULL,
instance,
NULL);
WINRT_VERIFY(comboBoxHwnd);
// Populate combo box
for (auto& window : g_windows)
{
SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str());
}
//SendMessage(comboBoxHwnd, CB_SETCURSEL, 0, 0);
// Create a DispatcherQueue for our thread
auto controller = CreateDispatcherQueueController();
// Initialize Composition
auto compositor = Compositor();
auto target = CreateDesktopWindowTarget(compositor, hwnd);
auto root = compositor.CreateContainerVisual();
root.RelativeSizeAdjustment({ 1.0f, 1.0f });
target.Root(root);
// Enqueue our capture work on the dispatcher
auto queue = controller.DispatcherQueue();
auto success = queue.TryEnqueue([=]() -> void
{
g_app->Initialize(root);
});
WINRT_VERIFY(success);
}
};
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow)
{
TestClass test;
test.Test(instance);
// Message pump
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if (HIWORD(wParam) == CBN_SELCHANGE)
{
auto index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
auto window = g_windows[index];
g_app->StartCapture(window.Hwnd());
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}
My goal is that it will work while the message loop will be outside. It doesn't. This time, it throws exception and the program crashed here:
I think that it is the same problem.. just this time I did not removed code that throws exception and stop the execution.
Notice that it will work in one of the following ways:
If I move the code
// Message pump
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
To the end of the Test
function in TestClass
If I move all the code in Test
function to WinMain
function so it will be before the message loop.
I don't know why it is that.. what make it fail.. it is very strage.
My desing in my software is that some logic must be in class. The logic that creates the UI will be in class. something like this
And the message loop will be outside the class - in the WinMain
function.
I can't continue to work with the API unless the problem will solved. I tried a lot to figure out what the problem is and the only thing I found is that the message loop code should be in the same code block that create the UI.
Please help me to find the solution.
Thanks.
In addition the previeus message, I could reproduce the exact same problem if you change in the last changed code I post here the lines:
// Enqueue our capture work on the dispatcher
auto queue = controller.DispatcherQueue();
auto success = queue.TryEnqueue([=]() -> void
{
g_app->Initialize(root);
});
WINRT_VERIFY(success);
to
// Enqueue our capture work on the dispatcher
auto queue = controller.DispatcherQueue();
g_app->Initialize(root);
//WINRT_VERIFY(success);
Next, you will see that if you don't add code
// Message pump
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Right after this (and this UI code is inside function in class), and that message loop code is written only in WinMain
function after test.Test()
call, then it will not draw the capture. This is what I got in my original example (not an exception). And it will draw the capture if you add these lines right after it
I found an ugly workaround - I use std:: thread with lamda...
Inside TestClass
, inside Test
function, I warp the ui code with
std::thread uiThread([&]
{
// UI code
// Message loop
MSG msg;
while (!killThread && GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
});
uiThread.detach();
In WinMain
function, I also write message loop that will be this:
// Message pump
MSG msg;
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Here is the full code
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
#include "App.h"
#include "SimpleCapture.h"
#include <ShObjIdl.h>
#include "Win32WindowEnumeration.h"
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Composition::Desktop;
// Direct3D11CaptureFramePool requires a DispatcherQueue
auto CreateDispatcherQueueController()
{
namespace abi = ABI::Windows::System;
DispatcherQueueOptions options
{
sizeof(DispatcherQueueOptions),
DQTYPE_THREAD_CURRENT,
DQTAT_COM_STA
};
Windows::System::DispatcherQueueController controller{ nullptr };
check_hresult(CreateDispatcherQueueController(options, reinterpret_cast<abi::IDispatcherQueueController**>(put_abi(controller))));
return controller;
}
DesktopWindowTarget CreateDesktopWindowTarget(Compositor const& compositor, HWND window)
{
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = compositor.as<abi::ICompositorDesktopInterop>();
DesktopWindowTarget target{ nullptr };
check_hresult(interop->CreateDesktopWindowTarget(window, true, reinterpret_cast<abi::IDesktopWindowTarget**>(put_abi(target))));
return target;
}
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow);
auto g_app = std::make_shared<App>();
auto g_windows = EnumerateWindows();
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
class TestClass
{
public:
std::atomic<bool> killThread = false;
void Test(HINSTANCE instance)
{
std::thread uiThread([&]
{
// Init COM
init_apartment(apartment_type::single_threaded);
// Create the window
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));
HWND hwnd = CreateWindow(
L"ScreenCaptureforHWND",
L"ScreenCaptureforHWND",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
instance,
NULL);
WINRT_VERIFY(hwnd);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Create combo box
HWND comboBoxHwnd = CreateWindow(
WC_COMBOBOX,
L"",
CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
10,
10,
200,
200,
hwnd,
NULL,
instance,
NULL);
WINRT_VERIFY(comboBoxHwnd);
// Populate combo box
for (auto& window : g_windows)
{
SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str());
}
//SendMessage(comboBoxHwnd, CB_SETCURSEL, 0, 0);
// Create a DispatcherQueue for our thread
auto controller = CreateDispatcherQueueController();
// Initialize Composition
auto compositor = Compositor();
auto target = CreateDesktopWindowTarget(compositor, hwnd);
auto root = compositor.CreateContainerVisual();
root.RelativeSizeAdjustment({ 1.0f, 1.0f });
target.Root(root);
// Enqueue our capture work on the dispatcher
auto queue = controller.DispatcherQueue();
g_app->Initialize(root);
//WINRT_VERIFY(success);
MSG msg;
while (!killThread && GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
});
uiThread.detach();
}
};
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow)
{
// Init COM
init_apartment(apartment_type::single_threaded);
TestClass test;
test.Test(instance);
// Message pump
MSG msg;
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)0;
}
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if (HIWORD(wParam) == CBN_SELCHANGE)
{
auto index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
auto window = g_windows[index];
g_app->StartCapture(window.Hwnd());
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}
The question is, is this is the best solution? I don't know
Couple things:
As it stands, the code blows up with RO_E_CLOSED, which means the object has already been cleaned up. It appears you aren't keeping any of the objects you're creating in the Test method alive. Make them members of the class. Think of these WinRT objects as smart pointers, they'll get cleaned up when they go out of scope and they won't leak unless you mess with the ref count.
After I made those changes it all worked:
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
#include "App.h"
#include "SimpleCapture.h"
#include <ShObjIdl.h>
#include "Win32WindowEnumeration.h"
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Composition::Desktop;
// Direct3D11CaptureFramePool requires a DispatcherQueue
auto CreateDispatcherQueueController()
{
namespace abi = ABI::Windows::System;
DispatcherQueueOptions options
{
sizeof(DispatcherQueueOptions),
DQTYPE_THREAD_CURRENT,
DQTAT_COM_STA
};
Windows::System::DispatcherQueueController controller{ nullptr };
check_hresult(CreateDispatcherQueueController(options, reinterpret_cast<abi::IDispatcherQueueController * *>(put_abi(controller))));
return controller;
}
DesktopWindowTarget CreateDesktopWindowTarget(Compositor const& compositor, HWND window)
{
namespace abi = ABI::Windows::UI::Composition::Desktop;
auto interop = compositor.as<abi::ICompositorDesktopInterop>();
DesktopWindowTarget target{ nullptr };
check_hresult(interop->CreateDesktopWindowTarget(window, true, reinterpret_cast<abi::IDesktopWindowTarget * *>(put_abi(target))));
return target;
}
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow);
auto g_app = std::make_shared<App>();
auto g_windows = EnumerateWindows();
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
class TestClass
{
public:
void Test(HINSTANCE instance)
{
// Create the window
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));
HWND hwnd = CreateWindow(
L"ScreenCaptureforHWND",
L"ScreenCaptureforHWND",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
instance,
NULL);
WINRT_VERIFY(hwnd);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Create combo box
HWND comboBoxHwnd = CreateWindow(
WC_COMBOBOX,
L"",
CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE,
10,
10,
200,
200,
hwnd,
NULL,
instance,
NULL);
WINRT_VERIFY(comboBoxHwnd);
// Populate combo box
for (auto& window : g_windows)
{
SendMessage(comboBoxHwnd, CB_ADDSTRING, 0, (LPARAM)window.Title().c_str());
}
// Initialize Composition
m_compositor = Compositor();
m_target = CreateDesktopWindowTarget(m_compositor, hwnd);
m_root = m_compositor.CreateContainerVisual();
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f });
m_target.Root(m_root);
g_app->Initialize(m_root);
}
private:
Compositor m_compositor{ nullptr };
CompositionTarget m_target{ nullptr };
ContainerVisual m_root{ nullptr };
};
int CALLBACK WinMain(
HINSTANCE instance,
HINSTANCE previousInstance,
LPSTR cmdLine,
int cmdShow)
{
// Init COM
init_apartment(apartment_type::single_threaded);
// Create a DispatcherQueue for our thread
auto controller = CreateDispatcherQueueController();
TestClass test;
test.Test(instance);
// Message pump
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if (HIWORD(wParam) == CBN_SELCHANGE)
{
auto index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
auto window = g_windows[index];
g_app->StartCapture(window.Hwnd());
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return 0;
}
WOW, I did what you said and now my code works! I tried to solve it so hard.
Thank you very much!
One more question:
What does the following lines doing:
// Init COM
init_apartment(apartment_type::single_threaded);
// Create a DispatcherQueue for our thread
auto controller = CreateDispatcherQueueController();
Thanks.
Hi again, I just found a bug that if you create a WS_POPUP
window that is positioned exactly at the location of the window to capture, and the window will display the captured window you will get the following bug:
To reproduce the bug you need:
1) Create WS_POPUP
window with the RECT rect
that you get by calling to
RECT rect;
DwmGetWindowAttribute(hwndCaptureWindow, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT));
Then use rect
to create the window at the exact location of the window you capture that is hwndCaptureWindow
2) Enable for the window you created the capture using the new API so that the window you created will display hwndCaptureWindow
.
This will happen when the window is positioned exactly where hwndCaptureWindow
positioned.
As you can see in the gif, when I move the window it will not happen.
It will also not happen if I don't create the window exactly where the hwndCaptureWindow
is positioned
No problem!
init_apartment
is really a wrapper around RoInitialize, which is required to be called in order to use Windows Runtime objects (per thread). You may be able to get things to work without it, but you'll see all sorts of lifetime bugs. UWP applications do this automatically for you, but Win32 applications have to do this explicitly.
The DispatcherQueueController
is needed because the Direct3D11CaptureFramePool
uses the DispatcherQueue
to do callbacks. This way you're always called back on the thread that created the frame pool. It's also required to use any of the Windows.UI.Composition
APIs.
If you didn't want to take the DispatcherQueue
dependency, you can create your frame pool using CreateFreeThreaded
instead of Create
. This will remove the requirement of a DispatcherQueue
, but it will also mean you will be responsible for threading. The OnFrameArrived
callback will fire on an arbitrary thread, not the thread you created the frame pool on, when you create it this way. However you wouldn't be able to use any of the Composition APIs to present your content without a dispatcher queue anyway.
I'll take a look to see if I reproduce the bug you seem to have found. Does it only happen when the window you create is styled with WS_POPUP
?
What build of the OS are you running? I'm unable to reproduce this bug on 18362.239 (Go to Settings -> System -> About -> Windows specifications -> OS build).
My version is 1903. Maybe I should open it as another issue. It does not matter. I found a workaround... Please open another issue here and I will respond there.
I am looking for a way to draw the captured image with Alpha channel.
Currently, I know how to do it with BitBlt
function. But BitBlt
is slow.
My setup is that I create WS_POPUP
window with hbrBackground = (HBRUSH)CreateSolidBrush(RGB(121, 122, 123));
This is the full window class:
//Step 1: Registering the Window Class
wndclassex.cbSize = sizeof(WNDCLASSEX);
wndclassex.style = 0;
wndclassex.lpfnWndProc = WndProc;
wndclassex.cbClsExtra = 0;
wndclassex.cbWndExtra = 0;
wndclassex.hInstance = hInstance;
wndclassex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclassex.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclassex.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(121, 122, 123));
wndclassex.lpszMenuName = NULL;
wndclassex.lpszClassName = ccharClassName;
wndclassex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wndclassex)) return -1;
Next, I create WS_POPUP
window called 'hwndDisplay' and set it to be with this style:
SetWindowLong(hwndDisplay, GWL_EXSTYLE, GetWindowLong(hwndDisplay, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
SetLayeredWindowAttributes(hwndDisplay, RGB(121, 122, 123), 0, LWA_COLORKEY);
Then I use 'BitBlt' function that draws a processed frame. Each pixel with the color of (121,122,123) will be transparent (in a way that you see what behind the window). This is how it works.
But this trick does not work with hardware drawing as in the example. Do you know how to do it?
I tried to modify each pixel that is at position 4...
for (int point = 0; point < xSize * ySize * 4; point += 4)
{
bits[point+4] = 128; // Edit the alpha channel
}
I expected that
m_swapChain->Present1(1, 0, &presentParameters);
will not ignore the alpha channel. but it did not worked..
bits
is what I get from by using the code:
// First verify that we can map the texture
D3D11_TEXTURE2D_DESC desc;
backBuffer->GetDesc(&desc);
// translate texture format to WIC format. We support only BGRA and ARGB.
GUID wicFormatGuid;
switch (desc.Format) {
case DXGI_FORMAT_R8G8B8A8_UNORM:
wicFormatGuid = GUID_WICPixelFormat32bppRGBA;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
wicFormatGuid = GUID_WICPixelFormat32bppBGRA;
break;
default:
return;
}
// Get the device context
ID3D11Device* d3dDevice;
backBuffer->GetDevice(&d3dDevice);
ID3D11DeviceContext* d3dContext;
d3dDevice->GetImmediateContext(&d3dContext);
// Create new textrue with CPU Access right
ID3D11Texture2D* texTemp = NULL;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
HRESULT hr2 = d3dDevice->CreateTexture2D(
&desc,
NULL,
&texTemp
);
if (hr2 != S_OK)
return;
m_d3dContext->CopyResource(texTemp, frameSurface.get());
// map the texture
ID3D11Texture2D* mappedTexture;
D3D11_MAPPED_SUBRESOURCE mapInfo;
ZeroMemory(&mapInfo, sizeof(D3D11_MAPPED_SUBRESOURCE));
HRESULT hr = d3dContext->Map(texTemp, 0, D3D11_MAP_READ,0,&mapInfo);
bits
is stored in mapInfo.pData;
I am doing this:
byte* bits= (byte*)mapInfo.pData;
Next I try to do some processing on it and copy it back to backBuffer
using m_d3dContext->CopyResource(backBuffer, texTemp);
I know that I am doing it right because I saw edited pixels on the screen. So pixels alpha 128 must be exists.. It just that the device ignoring it.
I want that it will not ignore this value.
Do you know how to solve it?
Thanks.
Unfortunately, I’m unsure how to solve your transparency issue. I’m going to close this issue since the original issue has been addressed. Best of luck.
I solved the transparency issue. You need to change the code that set m_swapChain
variable to this:
captureItem = CreateCaptureItemForWindow(hwndTarget);
d3dDevice = CreateD3DDevice();
dxgiDevice = d3dDevice.as<IDXGIDevice>();
device = CreateDirect3DDevice(dxgiDevice.get());
IDXGIAdapter* dxgi_adapter;
dxgiDevice->GetAdapter(&dxgi_adapter);
IDXGIFactory2* dxgi_factory;
dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
RECT rectLayer;
DwmGetWindowAttribute(hwndDisplay, DWMWA_EXTENDED_FRAME_BOUNDS, &rectLayer, sizeof(RECT));
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = rectLayer.right- rectLayer.left;
desc.Height = rectLayer.bottom - rectLayer.top;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.BufferCount = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
desc.Flags = 0;
HRESULT hr = dxgi_factory->CreateSwapChainForComposition(
d3dDevice.get(), &desc, nullptr, m_swapChain.put());
I found the reference here: https://chromium.googlesource.com/chromium/src.git/+/62.0.3178.1/gpu/ipc/service/direct_composition_child_surface_win.cc
I don't know what this code does but I found the pattern I was looking for and I connected the dots, edited the example again and it works!
Thanks anyway
Hello, after I remix the example cpp/ScreenCaptureforHWND to this code:
I found something very strange.
If you change the code to this:
Then the window will not draw the screenshot of the target window to capture. The only difference here is that the while loop with the GetMessage is out of the Test function inside the class.
Now, If I move the while loop to the end of the Test function, it will work. I have no idea why it is that..
It is blocking me from using the API.
Please help me to solve this problem. I want that the message loop will be outside of the class while it still draw the window screenshot.
Thanks