How to use the compositing effect of WinRT Visual Layer? (Apply to Win32 window)

MuYu 41 Reputation points
2023-05-12T03:58:20.62+00:00

My initial idea was to apply transparent and blurry background effects to Win32 windows.

On this road, I know WinUI3 is very useful. Brush an acrylic brush on the window.

But I cannot understand that Visual Studio has built too many files, and I cannot double-click the exe file to run it. I can only run it through Visual Studio. In addition, the exe file is large and uses many DLL files, and I don't know what these are.

Then I knew that WinUI was also implemented based on the Visual layer. Perhaps the effect achieved by WinUI3 can be directly achieved through the visualization layer.

I have tested that WinRT can be called through C++ to apply Visual layer to Win32 windows, but I see that Composition effects require the use of the Microsoft.Graphics.Canvas.Effects namespace. https://learn.microsoft.com/en-us/windows/uwp/composition/composition-effects

I did not find the path to this namespace in the Windows SDK.

I have tried using Visual Studio to use this namespace and found that they are all temporary generated files under the current project.

I saw composer.TryCreateBlurredWallpaperBackdropBrush(); It seems possible to directly obtain a Brush that can be applied to SpriteVisual, but once it runs, it will cause the window to disappear here and the process will exit after a period of time. https://learn.microsoft.com/en-us/uwp/api/windows.ui.composition.compositor.trycreateblurredwallpaperbackdropbrush?view=winrt -22621

// main.cpp
#include "mine.h"

#pragma comment(lib, "windowsapp")
#pragma comment(lib, "User32.lib")

#include <DispatcherQueue.h>

char* getLastErrorString(DWORD error_id) {
    TCHAR* buffer;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, error_id, 0, (LPTSTR)&buffer, 0, NULL);
    LocalFree(buffer);
    return buffer;
}

auto CreateDispatcherQUeueController(){
	DispatcherQueueOptions options {
		sizeof(DispatcherQueueOptions),
		DQTYPE_THREAD_CURRENT,
		DQTAT_COM_STA
	};
	winrt::Windows::System::DispatcherQueueController controller{nullptr};
	CreateDispatcherQueueController(options, reinterpret_cast<ABI::Windows::System::IDispatcherQueueController**>(winrt::put_abi(controller)));
	return controller;
}

winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget CreateDesktopWindowTarget(winrt::Windows::UI::Composition::Compositor const& compositor, HWND window) {
	auto interop = compositor.as<ABI::Windows::UI::Composition::Desktop::ICompositorDesktopInterop>();
	winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget target{nullptr};
	interop->CreateDesktopWindowTarget(window, true, reinterpret_cast<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget**>(winrt::put_abi(target)));
	return target;
}

class mWindow:public DesktopWindow<mWindow> {
	public:
		mWindow() {
			WNDCLASSEX wcex = {sizeof(WNDCLASSEX)};
			wcex.style = CS_HREDRAW | CS_VREDRAW;
			wcex.lpfnWndProc = WndProc;
			wcex.hInstance = GetModuleHandle(nullptr);
			wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
			wcex.lpszClassName = "mWindow";
			RegisterClassEx(&wcex);
			CreateWindowEx(0, wcex.lpszClassName, "Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wcex.hInstance, this);
		}
		void PrepareVisuals() {
			auto compositor = winrt::Windows::UI::Composition::Compositor();
			m_CompositionTarget = CreateDesktopWindowTarget(compositor, m_window);

			auto visual = compositor.CreateSpriteVisual();
			m_CompositionTarget.Root(visual);
			visual.RelativeSizeAdjustment({1.0f, 1.0f});
			visual.Brush(compositor.CreateColorBrush({0xFF, 0xEF, 0x88, 0x02}));

			MessageBox(nullptr, "Create...", "Comment", MB_OK);
			auto br = compositor.TryCreateBlurredWallpaperBackdropBrush(); // Will cause abnormal exit

			MessageBox(nullptr, "Create...", "Comment", MB_OK); // Not Running
		}
	private:
		winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_CompositionTarget{nullptr};
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{
    winrt::init_apartment(winrt::apartment_type::single_threaded);
    auto controller = CreateDispatcherQUeueController();

    mWindow window;
    window.PrepareVisuals();
    MSG message;

    while (GetMessage(&message, nullptr, 0, 0)) {
		TranslateMessage(&message);
        DispatchMessage(&message);
    }
}
//------------------------
// mine.h
#ifdef __mine
#else
#define __mine
#include <winrt/Windows.UI.Composition.Desktop.h>
#include <winrt/Windows.UI.Composition.h>
#include <windows.ui.composition.interop.h>
#include <Windows.h>
#include <windowsx.h>

template <typename Type>
class DesktopWindow {
	public:
	static LRESULT WINAPI WndProc (HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
		if (WM_NCCREATE == message) {
			Type* that = static_cast<Type*>(reinterpret_cast<CREATESTRUCT *>(lparam)->lpCreateParams);
			that->m_window = window;
			SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(that));
		} else if (Type* that = reinterpret_cast<Type*>(GetWindowLongPtr(window, GWLP_USERDATA))) {
			return that->MessageHandler(message, wparam, lparam);
		}
		return DefWindowProc(window, message, wparam, lparam);
	}
	LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept {
		switch(message) {
			case WM_DESTROY:
				PostQuitMessage(0);
				return 0;
		}
		return DefWindowProc(m_window, message, wparam, lparam);
	}
	protected:
	HWND m_window = nullptr;
};
#endif
//--------------------------
// makefile
CC 			:= cl.exe
MAINOBJECT 	:= main.exe
CLFLAGS 	:= /Gy /Os /MD /EHsc /std:c++17 /link /subsystem:windows /out:$(MAINOBJECT) 
OBJFLAGS 	:= /Gy /Os /MD /EHsc /std:c++17 /c /Fo:

OBJS := $(patsubst %.cpp, %.obj, $(wildcard *.cpp))

.SUFFIXES:.ao .S .asm .bin .cpp .o .obj

$(MAINOBJECT): $(OBJS)
	$(CC) $^ $(CLFLAGS)
.cpp.obj:
	$(CC) $(OBJFLAGS)$@ $<
clean:
	rm $(OBJS) $(MAINOBJECT)

I know that DirectX and Gdi+ can do it, but I don't know how to get the pixel content behind the window.

There are some quiestion now .

  1. I can use Visual layer, but when I run this code compositor.TryCreateBlurredWallpaperBackdropBrush(); It will be abnormal exit.
    • Is there a problem with which step I took... ?
  2. And also, Using compositing effects seems to require accessing namespaces "Microsoft", But I don't know where to found the header files about it.
    • Is other ways to use it ?
  3. If using other methods,
    • how to obtain the image behind the window to synthesize the desired effect ?
Windows development Windows API - Win32
Developer technologies C++
{count} votes

Accepted answer
  1. Castorix31 90,521 Reputation points
    2023-05-12T09:55:44.6333333+00:00

    On this road, I know WinUI3 is very useful. Brush an acrylic brush on the window. But I cannot understand that Visual Studio has built too many files, and I cannot double-click the exe file to run it.

    Use Unpackaged App and you will be able to launch it by double-clicking on the .exe

    Otherwise, you can use XAML Islands :

    #include <windows.h>
    #include <tchar.h>
    
    //[C++][Language][C++ Language Standard] ISO C++17 Standard (/std:c++17)
    //[C++][Language][Conformance mode] No (/permissive)
    
    // For older SDK versions
    #pragma comment (lib, "windowsapp")
    
    #include <winrt/base.h>
    #include <winrt/Windows.UI.Xaml.Hosting.h>
    #include <winrt/Windows.UI.Xaml.Controls.h>
    #include <winrt/Windows.UI.Xaml.Media.h>
    #include <winrt/Windows.Foundation.h>
    #include <winrt/Windows.Foundation.Collections.h>
    #include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
    
    using namespace winrt;
    using namespace Windows::UI::Xaml::Hosting;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Numerics;
    
    HINSTANCE hInst;
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    int nWidth = 600, nHeight = 400;
    #define IDC_STATIC 10
    #define IDC_BUTTON 11
    HWND g_hWndXamlIsland = NULL;
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
    {
    	hInst = hInstance;
    	WNDCLASSEX wcex =
    	{
    		sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
    		LoadCursor(NULL, IDC_ARROW), (HBRUSH)/*(COLOR_WINDOW + 1)*/GetStockObject(BLACK_BRUSH), NULL, TEXT("WindowClass"), NULL,
    	};
    	if (!RegisterClassEx(&wcex))
    		return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    	int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
    	HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
    	if (!hWnd)
    		return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    
    	init_apartment(apartment_type::single_threaded);
    	const auto wxm = Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();
    	Windows::UI::Xaml::Hosting::DesktopWindowXamlSource dwxs = {};
    	const auto interop = dwxs.as<IDesktopWindowXamlSourceNative>();
    	check_hresult(interop->AttachToWindow(hWnd));
    	interop->get_WindowHandle(&g_hWndXamlIsland);
    	RECT rect;
    	GetClientRect(hWnd, &rect);
    	SetWindowPos(g_hWndXamlIsland, NULL, 0, 0, rect.right, rect.bottom, SWP_SHOWWINDOW);
    
    	Windows::UI::Xaml::Controls::Grid gridRoot = {};
    	Windows::UI::Xaml::Media::AcrylicBrush ab = {};
    	ab.TintOpacity(0.1f);
    	ab.TintColor(Windows::UI::Colors::Blue());
    	ab.BackgroundSource(Windows::UI::Xaml::Media::AcrylicBackgroundSource::HostBackdrop);
    	gridRoot.Background(ab);
    
    	Windows::UI::Xaml::Controls::TextBlock tb;
    	tb.Text(L"This is a TextBlock in XAML");
    	tb.Foreground(SolidColorBrush(Windows::UI::Colors::Yellow()));
    	tb.VerticalAlignment(Windows::UI::Xaml::VerticalAlignment::Center);
    	tb.HorizontalAlignment(Windows::UI::Xaml::HorizontalAlignment::Center);
    	tb.FontSize(40);
    	gridRoot.Children().Append(tb);
    	gridRoot.UpdateLayout();
    	dwxs.Content(gridRoot);
    
    	MSG msg;
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return (int)msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	switch (message)
    	{
    	case WM_CREATE:
    	{
    		return 0;
    	}
    	break;	
    	case WM_SIZE:
    	{
    		MoveWindow(g_hWndXamlIsland, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
    		return 0;
    	} 
    	break;
    	case WM_DESTROY:
    	{
    		PostQuitMessage(0);
    		return 0;
    	}
    	break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    

    Manifest :

    <?xml version="1.0" encoding="utf-8"?>
    <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
      <assemblyIdentity version="1.0.0.0" name="WinUI3_Test.app"/>
    	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    		<security>
    			<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
    				<!-- UAC Manifest Options
                 If you want to change the Windows User Account Control level replace the 
                 requestedExecutionLevel node with one of the following.
    
            <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
            <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
            <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
    
                Specifying requestedExecutionLevel element will disable file and registry virtualization. 
                Remove this element if your application requires this virtualization for backwards
                compatibility.
            -->
    				<requestedExecutionLevel level="asInvoker" uiAccess="false" />
    			</requestedPrivileges>
    		</security>
    	</trustInfo>
    	
      <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
          <!-- The combination of below two tags have the following effect:
               1) Per-Monitor for >= Windows 10 Anniversary Update
               2) System < Windows 10 Anniversary Update
          -->
          <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
          <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
        </windowsSettings>
      </application>
    	
      <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
          <!-- A list of the Windows versions that this application has been tested on and is
               is designed to work with. Uncomment the appropriate elements and Windows will 
               automatically selected the most compatible environment. -->
    
          <!-- Windows Vista -->
          <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
    
          <!-- Windows 7 -->
          <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
    
          <!-- Windows 8 -->
          <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
    
          <!-- Windows 8.1 -->
          <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
    
          <!-- Windows 10 -->
          <maxversiontested Id="10.0.19041.0"/>
          <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    
        </application>
      </compatibility>
    </assembly>
    
    

    User's image


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.