Created
March 22, 2021 15:38
-
-
Save ADeltaX/aea6aac248604d0cb7d423a61b06e247 to your computer and use it in GitHub Desktop.
DWM Thumbnail/VirtualDesktop IDCompositionVisual example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Unknwn.h> | |
#include <Windows.h> | |
#include <ntstatus.h> | |
#include <winternl.h> | |
#include <wrl\implements.h> | |
#include <comutil.h> | |
#include <dcomp.h> | |
#include <dwmapi.h> | |
#include <dxgi1_3.h> | |
#include <d3d11_2.h> | |
#include <d2d1_2.h> | |
#include <d2d1_2helper.h> | |
//Either use this or add in your linker configuration | |
#pragma comment(lib, "dxgi") | |
#pragma comment(lib, "d3d11") | |
#pragma comment(lib, "d2d1") | |
#pragma comment(lib, "dcomp") | |
#pragma comment(lib, "dwmapi") | |
#pragma comment(lib, "ole32") | |
//-------------------- Definitions | |
enum THUMBNAIL_TYPE | |
{ | |
TT_DEFAULT = 0x0, | |
TT_SNAPSHOT = 0x1, | |
TT_ICONIC = 0x2, | |
TT_BITMAPPENDING = 0x3, | |
TT_BITMAP = 0x4 | |
}; | |
typedef HRESULT(WINAPI* DwmpCreateSharedThumbnailVisual)( | |
IN HWND hwndDestination, | |
IN HWND hwndSource, | |
IN DWORD dwThumbnailFlags, | |
IN DWM_THUMBNAIL_PROPERTIES* pThumbnailProperties, | |
IN VOID* pDCompDevice, | |
OUT VOID** ppVisual, | |
OUT PHTHUMBNAIL phThumbnailId); | |
typedef HRESULT(WINAPI* DwmpQueryWindowThumbnailSourceSize)( | |
IN HWND hwndSource, | |
IN BOOL fSourceClientAreaOnly, | |
OUT SIZE* pSize); | |
typedef HRESULT(WINAPI* DwmpQueryThumbnailType)( | |
IN HTHUMBNAIL hThumbnailId, | |
OUT THUMBNAIL_TYPE* thumbType); | |
//pre-cobalt/pre-iron | |
typedef HRESULT(WINAPI* DwmpCreateSharedVirtualDesktopVisual)( | |
IN HWND hwndDestination, | |
IN VOID* pDCompDevice, | |
OUT VOID** ppVisual, | |
OUT PHTHUMBNAIL phThumbnailId); | |
//cobalt/iron (20xxx+) | |
//No changes except for the function name. | |
typedef HRESULT(WINAPI* DwmpCreateSharedMultiWindowVisual)( | |
IN HWND hwndDestination, | |
IN VOID* pDCompDevice, | |
OUT VOID** ppVisual, | |
OUT PHTHUMBNAIL phThumbnailId); | |
//pre-cobalt/pre-iron | |
typedef HRESULT(WINAPI* DwmpUpdateSharedVirtualDesktopVisual)( | |
IN HTHUMBNAIL hThumbnailId, | |
IN HWND* phwndsInclude, | |
IN DWORD chwndsInclude, | |
IN HWND* phwndsExclude, | |
IN DWORD chwndsExclude, | |
OUT RECT* prcSource, | |
OUT SIZE* pDestinationSize); | |
//cobalt/iron (20xxx+) | |
//Change: function name + new DWORD parameter. | |
//Pass "1" in dwFlags. Feel free to explore other flags. | |
typedef HRESULT(WINAPI* DwmpUpdateSharedMultiWindowVisual)( | |
IN HTHUMBNAIL hThumbnailId, | |
IN HWND* phwndsInclude, | |
IN DWORD chwndsInclude, | |
IN HWND* phwndsExclude, | |
IN DWORD chwndsExclude, | |
OUT RECT* prcSource, | |
OUT SIZE* pDestinationSize, | |
IN DWORD dwFlags); | |
#define DWM_TNP_FREEZE 0x100000 | |
#define DWM_TNP_ENABLE3D 0x4000000 | |
#define DWM_TNP_DISABLE3D 0x8000000 | |
#define DWM_TNP_FORCECVI 0x40000000 | |
#define DWM_TNP_DISABLEFORCECVI 0x80000000 | |
//We also need these if you want to use MultiWindow/VirtualDesktop. | |
enum WINDOWCOMPOSITIONATTRIB | |
{ | |
WCA_UNDEFINED = 0x0, | |
WCA_NCRENDERING_ENABLED = 0x1, | |
WCA_NCRENDERING_POLICY = 0x2, | |
WCA_TRANSITIONS_FORCEDISABLED = 0x3, | |
WCA_ALLOW_NCPAINT = 0x4, | |
WCA_CAPTION_BUTTON_BOUNDS = 0x5, | |
WCA_NONCLIENT_RTL_LAYOUT = 0x6, | |
WCA_FORCE_ICONIC_REPRESENTATION = 0x7, | |
WCA_EXTENDED_FRAME_BOUNDS = 0x8, | |
WCA_HAS_ICONIC_BITMAP = 0x9, | |
WCA_THEME_ATTRIBUTES = 0xA, | |
WCA_NCRENDERING_EXILED = 0xB, | |
WCA_NCADORNMENTINFO = 0xC, | |
WCA_EXCLUDED_FROM_LIVEPREVIEW = 0xD, | |
WCA_VIDEO_OVERLAY_ACTIVE = 0xE, | |
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 0xF, | |
WCA_DISALLOW_PEEK = 0x10, | |
WCA_CLOAK = 0x11, | |
WCA_CLOAKED = 0x12, | |
WCA_ACCENT_POLICY = 0x13, | |
WCA_FREEZE_REPRESENTATION = 0x14, | |
WCA_EVER_UNCLOAKED = 0x15, | |
WCA_VISUAL_OWNER = 0x16, | |
WCA_HOLOGRAPHIC = 0x17, | |
WCA_EXCLUDED_FROM_DDA = 0x18, | |
WCA_PASSIVEUPDATEMODE = 0x19, | |
WCA_LAST = 0x1A, | |
}; | |
typedef struct WINDOWCOMPOSITIONATTRIBDATA | |
{ | |
WINDOWCOMPOSITIONATTRIB Attrib; | |
void* pvData; | |
DWORD cbData; | |
}; | |
typedef BOOL(WINAPI* SetWindowCompositionAttribute)( | |
IN HWND hwnd, | |
IN WINDOWCOMPOSITIONATTRIBDATA* pwcad); | |
//------------------------- Getting functions | |
DwmpQueryThumbnailType lDwmpQueryThumbnailType; | |
DwmpCreateSharedThumbnailVisual lDwmpCreateSharedThumbnailVisual; | |
DwmpQueryWindowThumbnailSourceSize lDwmpQueryWindowThumbnailSourceSize; | |
//PRE-IRON | |
DwmpCreateSharedVirtualDesktopVisual lDwmpCreateSharedVirtualDesktopVisual; | |
DwmpUpdateSharedVirtualDesktopVisual lDwmpUpdateSharedVirtualDesktopVisual; | |
//20xxx+ | |
DwmpCreateSharedMultiWindowVisual lDwmpCreateSharedMultiWindowVisual; | |
DwmpUpdateSharedMultiWindowVisual lDwmpUpdateSharedMultiWindowVisual; | |
SetWindowCompositionAttribute lSetWindowCompositionAttribute; | |
bool InitPrivateDwmAPIs() | |
{ | |
auto dwmapiLib = LoadLibrary(L"dwmapi.dll"); | |
if (!dwmapiLib) | |
return false; | |
lDwmpQueryThumbnailType = (DwmpQueryThumbnailType)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(114)); | |
lDwmpCreateSharedThumbnailVisual = (DwmpCreateSharedThumbnailVisual)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(147)); | |
lDwmpQueryWindowThumbnailSourceSize = (DwmpQueryWindowThumbnailSourceSize)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(162)); | |
//PRE-IRON | |
lDwmpCreateSharedVirtualDesktopVisual = (DwmpCreateSharedVirtualDesktopVisual)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(163)); | |
lDwmpUpdateSharedVirtualDesktopVisual = (DwmpUpdateSharedVirtualDesktopVisual)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(164)); | |
//20xxx+ | |
lDwmpCreateSharedMultiWindowVisual = (DwmpCreateSharedMultiWindowVisual)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(163)); | |
lDwmpUpdateSharedMultiWindowVisual = (DwmpUpdateSharedMultiWindowVisual)GetProcAddress(dwmapiLib, MAKEINTRESOURCEA(164)); | |
if (false) //Just a placeholder, don't. | |
return false; | |
return true; | |
} | |
bool InitPrivateUser32APIs() | |
{ | |
auto user32Lib = LoadLibrary(L"user32.dll"); | |
if (!user32Lib) | |
return false; | |
lSetWindowCompositionAttribute = (SetWindowCompositionAttribute)GetProcAddress(user32Lib, "SetWindowCompositionAttribute"); | |
if (!lSetWindowCompositionAttribute) | |
return false; | |
return true; | |
} | |
//--------------------- Create Device func | |
using namespace Microsoft::WRL; | |
ComPtr<ID3D11Device> direct3dDevice; | |
ComPtr<IDXGIDevice2> dxgiDevice; | |
ComPtr<ID2D1Factory2> d2dFactory2; | |
ComPtr<IDCompositionDesktopDevice> dcompDevice; | |
bool CreateDevice() | |
{ | |
if (D3D11CreateDevice(0, //Adapter | |
D3D_DRIVER_TYPE_HARDWARE, | |
NULL, | |
D3D11_CREATE_DEVICE_BGRA_SUPPORT, | |
NULL, | |
0, | |
D3D11_SDK_VERSION, | |
direct3dDevice.GetAddressOf(), | |
nullptr, | |
nullptr | |
) != S_OK) | |
{ | |
//Maybe try creating with D3D_DRIVER_TYPE_WARP before returning false. | |
//Always listen to device changes. | |
return false; | |
} | |
if (direct3dDevice->QueryInterface(dxgiDevice.GetAddressOf()) != S_OK) | |
{ | |
return false; | |
} | |
if (D2D1CreateFactory( | |
D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED, | |
__uuidof(ID2D1Factory2), | |
(void**)d2dFactory2.GetAddressOf()) != S_OK) | |
{ | |
return false; | |
} | |
if (DCompositionCreateDevice3( | |
dxgiDevice.Get(), | |
__uuidof(dcompDevice), | |
(void**)dcompDevice.GetAddressOf()) != S_OK) | |
{ | |
return false; | |
} | |
return true; | |
} | |
//------------------------ Create window func | |
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (message) | |
{ | |
case WM_CLOSE: | |
PostQuitMessage(0); | |
break; | |
default: | |
break; | |
} | |
return DefWindowProc(hwnd, message, wParam, lParam); | |
} | |
HWND CreateWindowInternal(LPCWSTR lptitleName, LPCWSTR lpclassName, DWORD dwExStyle, DWORD dwStyle) | |
{ | |
HINSTANCE hInstance = GetModuleHandle(NULL); | |
WNDCLASS wc = { }; | |
wc.lpfnWndProc = MyWndProc; | |
wc.hInstance = hInstance; | |
wc.cbWndExtra = 0; | |
wc.lpszClassName = lpclassName; | |
if (!RegisterClass(&wc)) | |
return nullptr; | |
const auto hwndParent = CreateWindowEx(dwExStyle, lpclassName, lptitleName, | |
dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, 1000, 640, | |
nullptr, nullptr, hInstance, NULL); | |
//This part is needed ONLY if you plan on using MultiWindow/VirtualDesktop functions. | |
BOOL enable = TRUE; | |
WINDOWCOMPOSITIONATTRIBDATA wData{}; | |
wData.Attrib = WCA_EXCLUDED_FROM_LIVEPREVIEW; | |
wData.pvData = &enable; | |
wData.cbData = sizeof(BOOL); | |
lSetWindowCompositionAttribute(hwndParent, &wData); | |
if (!hwndParent) | |
return nullptr; | |
return hwndParent; | |
} | |
void DemoCreateWindowThumbnail(HWND myWnd, IDCompositionVisual2** ppWindowVisual) | |
{ | |
//A window target of your choice | |
HWND targetWindow = (HWND)FindWindow(L"Shell_TrayWnd", NULL); | |
//Query the exact thumbnail source size | |
SIZE windowSize{}; | |
lDwmpQueryWindowThumbnailSourceSize(targetWindow, FALSE, &windowSize); | |
//Pretty explanatory and also documented by Microsoft | |
DWM_THUMBNAIL_PROPERTIES thumb{}; | |
thumb.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_RECTSOURCE | DWM_TNP_OPACITY | DWM_TNP_ENABLE3D; | |
thumb.opacity = 255; | |
thumb.fVisible = TRUE; | |
thumb.fSourceClientAreaOnly = FALSE; | |
thumb.rcDestination = RECT{ 0, 0, windowSize.cx, windowSize.cy }; | |
thumb.rcSource = RECT{ 0, 0, windowSize.cx, windowSize.cy }; | |
HTHUMBNAIL hThumbWindow; | |
//Create the shared visual! | |
auto res = lDwmpCreateSharedThumbnailVisual(myWnd, targetWindow, 2, &thumb, | |
dcompDevice.Get(), (void**)ppWindowVisual, &hThumbWindow); | |
} | |
void DemoCreateMultiWindowThumbnail(HWND myWnd, IDCompositionVisual2** ppVirtualDesktopVisual) | |
{ | |
HTHUMBNAIL hThumbVirtualDesktop; | |
auto virtualDeskRes = lDwmpCreateSharedMultiWindowVisual(myWnd, dcompDevice.Get(), | |
(void**)ppVirtualDesktopVisual, &hThumbVirtualDesktop); | |
auto monitorSize = RECT{ 0, 0, 1920, 1080 }; | |
auto targetSize = SIZE{ 960, 540 }; | |
HWND hwndTest = (HWND)0x1; //Exclude from the list what you want to exclude | |
HWND* excludeArray = new HWND[1]; | |
excludeArray[0] = hwndTest; | |
//The include list is useless as it will include every window in every case. | |
//You can only play with the exclusion list. | |
auto virtualDesktopUpdate = lDwmpUpdateSharedMultiWindowVisual(hThumbVirtualDesktop, NULL, 0, | |
excludeArray, 1, &monitorSize, &targetSize, 1); //Last parameter has to be "1" | |
//use lDwmpUpdateSharedVirtualDesktopVisual for 19043 or older | |
} | |
int APIENTRY wWinMain(HINSTANCE hInstance, | |
HINSTANCE hPrevInstance, | |
LPWSTR lpCmdLine, | |
int nCmdShow) | |
{ | |
if (!InitPrivateDwmAPIs() || | |
!InitPrivateUser32APIs() || | |
!CreateDevice()) | |
return 0; | |
auto myWnd = CreateWindowInternal(L"Noice window", L"DwmThumbExampleClass", WS_EX_OVERLAPPEDWINDOW | WS_EX_NOREDIRECTIONBITMAP, WS_OVERLAPPEDWINDOW); | |
ShowWindow(myWnd, SW_SHOW); | |
//ComPtr<IDCompositionVisual2> thumbVisual; | |
//DemoCreateWindowThumbnail(myWnd, thumbVisual.GetAddressOf()); | |
ComPtr<IDCompositionVisual2> thumbVisual; | |
DemoCreateMultiWindowThumbnail(myWnd, thumbVisual.GetAddressOf()); | |
//Create the target for the window. | |
ComPtr<IDCompositionTarget> dcompTarget; | |
if (dcompDevice->CreateTargetForHwnd(myWnd, FALSE, dcompTarget.GetAddressOf()) != S_OK) | |
return 0; | |
//You can either set the virtual desktop visual as root or create a new visual and set the content. | |
if (dcompTarget->SetRoot(thumbVisual.Get()) != S_OK) | |
return 0; | |
//Remember to commit. | |
if (dcompDevice->Commit() != S_OK) | |
return 0; | |
MSG msg{}; | |
while (GetMessage(&msg, nullptr, 0, 0)) | |
{ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
//ComPtr automatically releases for you. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment