Skip to content

Instantly share code, notes, and snippets.

@ADeltaX
Created June 18, 2021 11:46
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ADeltaX/c0e565a50d2cedb62dab5c13674cb8b5 to your computer and use it in GitHub Desktop.
Save ADeltaX/c0e565a50d2cedb62dab5c13674cb8b5 to your computer and use it in GitHub Desktop.
DWM Thumbnail/VirtualDesktop USING Windows.UI.Composition
#include <Unknwn.h>
#include <Windows.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>
//For this part you need C++/WinRT extension
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Composition.Desktop.h>
//This is for ABI
#include <windows.ui.composition.interop.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")
//using namespace
using namespace winrt;
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Composition::Desktop;
using namespace winrt::Windows::UI::Core;
//-------------------- 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
//-------- CoreDispatcher
//Windows::UI::Core::IInternalCoreDispatcherStatic (14393+)
DECLARE_INTERFACE_IID_(IInternalCoreDispatcherStatic, IInspectable, "4B4D0861-D718-4F7C-BEC7-735C065F7C73")
{
STDMETHOD(GetForCurrentThread)(
winrt::Windows::UI::Core::CoreDispatcher* ppDispatcher
) PURE;
STDMETHOD(GetOrCreateForCurrentThread)(
winrt::Windows::UI::Core::CoreDispatcher* ppDispatcher
) PURE;
};
//-------- Interop Composition interfaces
//Windows::UI::Composition::HwndTarget (14393-15063)
DECLARE_INTERFACE_IID_(HwndTarget, IUnknown, "6677DA68-C80C-407A-A4D2-3AA118AD7C46")
{
STDMETHOD(GetRoot)(THIS_
OUT ABI::Windows::UI::Composition::IVisual** value) PURE;
STDMETHOD(SetRoot)(THIS_
IN ABI::Windows::UI::Composition::IVisual * value) PURE;
};
//Windows::UI::Composition::InteropCompositorTarget
DECLARE_INTERFACE_IID_(InteropCompositionTarget, IUnknown, "EACDD04C-117E-4E17-88F4-D1B12B0E3D89")
{
STDMETHOD(SetRoot)(THIS_
IN IDCompositionVisual * visual) PURE;
};
//Windows::UI::Composition::IInteropCompositorPartner
DECLARE_INTERFACE_IID_(IInteropCompositorPartner, IUnknown, "e7894c70-af56-4f52-b382-4b3cd263dc6f")
{
STDMETHOD(MarkDirty)(THIS_) PURE;
STDMETHOD(ClearCallback)(THIS_) PURE;
STDMETHOD(CreateManipulationTransform)(THIS_
IN IDCompositionTransform * transform,
IN REFIID iid,
OUT VOID * *result) PURE;
STDMETHOD(RealClose)(THIS_) PURE;
};
//Windows.UI.Composition.IInteropCompositorPartnerCallback
DECLARE_INTERFACE_IID_(IInteropCompositorPartnerCallback, IUnknown, "9bb59fc9-3326-4c32-bf06-d6b415ac2bc5")
{
STDMETHOD(NotifyDirty)(THIS_) PURE;
STDMETHOD(NotifyDeferralState)(THIS_
bool deferRequested) PURE;
};
//Windows::UI::Composition::IInteropCompositorFactoryPartner
DECLARE_INTERFACE_IID_(IInteropCompositorFactoryPartner, IInspectable, "22118adf-23f1-4801-bcfa-66cbf48cc51b")
{
STDMETHOD(CreateInteropCompositor)(THIS_
IN IUnknown* renderingDevice,
IN IInteropCompositorPartnerCallback* callback,
IN REFIID iid,
OUT VOID** instance
) PURE;
STDMETHOD(CheckEnabled)(THIS_
OUT bool* enableInteropCompositor,
OUT bool* enableExposeVisual
) PURE;
};
//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<ID2D1Device> d2dDevice;
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 (d2dFactory2->CreateDevice(
dxgiDevice.Get(),
d2dDevice.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, SIZE* thumbSize)
{
//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);
*thumbSize = 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, SIZE* thumbSize)
{
HTHUMBNAIL hThumbVirtualDesktop;
auto virtualDeskRes = lDwmpCreateSharedMultiWindowVisual(myWnd, dcompDevice.Get(),
(void**)ppVirtualDesktopVisual, &hThumbVirtualDesktop);
auto monitorSize = RECT{ 0, 0, 1920, 1080 };
auto targetSize = SIZE{ 960, 540 };
*thumbSize = targetSize;
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
}
//------------------------ Windows.UI.Composit* / IDComposition* interop
bool InitializeInteropCompositor(HWND hwnd, IUnknown* d2dDevice, Compositor* compositor, IUnknown** compositionTarget, ContainerVisual* rootVisual)
{
auto interopCompositorFactory = winrt::get_activation_factory<Compositor, IInteropCompositorFactoryPartner>();
com_ptr<IInteropCompositorPartner> interopCompositor;
auto interopRes = interopCompositorFactory->CreateInteropCompositor(d2dDevice, NULL, winrt::guid_of<IInteropCompositorPartner>(), interopCompositor.put_void());
if (interopRes != S_OK)
return false;
//Get as Compositor and as IDCompositionDevice
auto m_compositor = interopCompositor.as<Compositor>();
dcompDevice = interopCompositor.as<IDCompositionDesktopDevice>().detach();
//Create a target for our window
com_ptr<IDCompositionTarget> dcompTarget;
auto res = dcompDevice->CreateTargetForHwnd(hwnd, TRUE, dcompTarget.put());
if (res != S_OK)
return false;
//Create a container visual to hold all our visuals and then set it as root.
//InteropCompositionTarget derives from DesktopWindowTarget which ultimately derives from CompositionTarget - 16299+
//InteropCompositionTarget derives from HwndTarget - 14393-15063
auto containerVisual = m_compositor.CreateContainerVisual();
auto compTarget = dcompTarget.try_as<CompositionTarget>();
if (compTarget)
{
compTarget.Root(containerVisual);
*compositionTarget = compTarget.as<IUnknown>().detach();
}
else
{
//We are on 15063 or 14393
//Get "raw" pointer to IVisual
winrt::com_ptr<ABI::Windows::UI::Composition::IVisual> visualAbi;
winrt::get_unknown(containerVisual)->QueryInterface(visualAbi.put());
auto hwndTarget = dcompTarget.as<HwndTarget>();
hwndTarget->SetRoot(visualAbi.get());
*compositionTarget = hwndTarget.as<IUnknown>().detach();
}
*compositor = m_compositor;
*rootVisual = containerVisual;
return true;
}
Visual DCompVisualToVisual(Compositor compositor, IDCompositionVisual2* dcompVisual, SIZE dcompVisualSize)
{
winrt::com_ptr<IDCompositionVisual2> dcompVisualContainer;
compositor.as<IDCompositionDesktopDevice>()->CreateVisual(dcompVisualContainer.put());
dcompVisualContainer->AddVisual(dcompVisual, TRUE, nullptr);
auto visualContainer = dcompVisualContainer.as<Visual>();
visualContainer.Size({ (float)dcompVisualSize.cx, (float)dcompVisualSize.cy });
return visualContainer;
}
void CreateAnimationForVisual(Compositor compositor, Visual visual)
{
auto animationScale = compositor.CreateVector3KeyFrameAnimation();
auto spring = compositor.CreateCubicBezierEasingFunction({ .41F, .51999998F }, { .00F, .94F });
animationScale.InsertKeyFrame(.15F, { 1.F, 1.F, 1.F }, spring);
animationScale.InsertKeyFrame(.3F, { .5F, .5F, .5F }, spring);
animationScale.InsertKeyFrame(.6F, { .5F, .5F, .5F }, spring);
animationScale.InsertKeyFrame(.85F, { 1.F, 1.F, 1.F }, spring);
animationScale.Duration(std::chrono::milliseconds(3000));
animationScale.IterationBehavior(AnimationIterationBehavior::Forever);
visual.CenterPoint({ visual.Size() / 2, 0.f });
visual.StartAnimation(L"Scale", animationScale);
}
CoreDispatcher GetOrCreateCoreDispatcher()
{
//CoreDispatcher internally creates a DispatcherQueue if it doesn't exist (and if you are on 15063 or newer)
auto dispatcher = winrt::try_get_activation_factory<CoreDispatcher, IInternalCoreDispatcherStatic>();
//On 10586 there isn't a way to create a CoreDispatcher (unless you create a CoreWindow, lol.)
if (!dispatcher)
return nullptr;
CoreDispatcher coreDispatcher{ nullptr };
dispatcher->GetOrCreateForCurrentThread(&coreDispatcher);
return coreDispatcher;
}
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nCmdShow)
{
init_apartment();
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);
//Create Compositor, ContainerVisual, *target, and set dcompDevice.
Compositor compositor{ nullptr };
ContainerVisual root{ nullptr };
IUnknown* target{ nullptr };
auto resInit = InitializeInteropCompositor(myWnd, d2dDevice.Get(), &compositor, &target, &root);
ComPtr<IDCompositionVisual2> thumbVisual;
SIZE thumbVisualSize{};
DemoCreateWindowThumbnail(myWnd, thumbVisual.GetAddressOf(), &thumbVisualSize);
//ComPtr<IDCompositionVisual2> thumbVisual;
//SIZE thumbVisualSize{};
//DemoCreateMultiWindowThumbnail(myWnd, thumbVisual.GetAddressOf(), &thumbVisualSize);
//Wrap the IDCompositionVisual into a Windows.UI.Composition.Visual and insert it into the Composition root
auto thumbCompositionVisual = DCompVisualToVisual(compositor, thumbVisual.Get(), thumbVisualSize);
root.Children().InsertAtTop(thumbCompositionVisual);
//Set an animation for the visual!
CreateAnimationForVisual(compositor, thumbCompositionVisual);
CoreDispatcher coreDispatcher = GetOrCreateCoreDispatcher();
coreDispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
//ComPtr automatically releases for you.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment