A quick way to convert your Win32 VCXPROJ files into WinUI3 and have both versions in the same code.
Background
This is the sequel of my previous article and aims to help the C++ Win32 developer migrate to WinUI3 offered in Windows 10. You have two options to do this:
- Don't change your project and call the Bootstrapper API to enable WinUI3.
- Advantages:
- You only have one executable.
- Disadvantages:
- You have to install the Windows APP SDK redistributable depending on the WindowsAPP SDK version. This requires administrator rights. If the user somehow removes this from the control panel or there is any incompatibility with any newer version, you are totally screwed, for it won't reinstall again unless cleanly removed by some PowerShell magic. If your app wants to use a newer WindowsAPP SDK version, you have to reinstall the redistributable.
- You have to create another WinUI project to compile Xaml files
- Xaml compiled files (xbf) cannot have XAML-level bindings. You practically need to do everything in code.
- Create another VCXPROJ and set it to "Self Contained". This will put all files needed to the output directory and you can redistribute them.
- Advantages
- No admin required, you manage the redistributable files yourself.
- You have all the XAML tools.
- Disadvantages
- If running for Windows 7, you have to have two executables. The executable created by the "Self Contained" project is not compatible with Windows < 10.
So, let's go for the second method, of course.
Preparing the Win32 Project
- Switch it to C++17 or greater
- Change the precompiled header (or create if it's not there) to pch.h/pch.cpp. This is not required, but it will save you time because everything default-generated by Visual Studio has pch.h in it.
Using the Converter
The converter is a XML tool that creates modifications in an existing Win32 project:
- Loads the vcxproj
- Changes the "
ToolsVersion
" to 15
- Updates your PCH.h to include:
#ifdef Win32_WINUI3
#undef GetCurrentTime
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.Data.h>
#include <winrt/Microsoft.UI.Xaml.Interop.h>
#include <microsoft.ui.xaml.window.h>
#include <winrt/Microsoft.UI.Xaml.Markup.h>
#include <winrt/Microsoft.UI.Xaml.Media.h>
#include <winrt/Microsoft.UI.Xaml.Navigation.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
#include <winrt/Microsoft.UI.Xaml.Media.h>
#include <winrt/Microsoft.UI.Xaml.Media.Imaging.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <wil/cppwinrt_helpers.h>
#include <appmodel.h>
#endif
- Installs NuGet packages if not there. At the time of this writing, these are the latest versions, change them when they get updated:
const char* p1v = "2.0.240111.5";
const char* p2v = "1.0.231216.1";
const char* p3v = "10.0.22621.2428";
const char* p4v = "1.4.231219000";
- Create the default
Package.appxmanifest
- Change the intermediate build directory so WinUI files are built in a new directory
- Adds preprocessor definition
Win32_WINUI3
- Puts the global variables in the project to work with the WinUI project template:
<CppWinRTOptimized>true</CppWinRTOptimized>
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
<AppContainerApplication>false</AppContainerApplication>
<AppxPackage>true</AppxPackage>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
<UseWinUI>true</UseWinUI>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<WindowsPackageType>None</WindowsPackageType>
<EnableMsixTooling>true</EnableMsixTooling>
- Adds the Nuget Stuff:
<Import Project="packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Import Project="packages\Microsoft.WindowsAppSDK.1.4.231219000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('packages\Microsoft.WindowsAppSDK.1.4.231219000\build\native\Microsoft.WindowsAppSDK.props')" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\Microsoft.WindowsAppSDK.1.4.231219000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('packages\Microsoft.WindowsAppSDK.1.4.231219000\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
</ImportGroup>
- Adds the default assets and copies the pngs:
<Image Include="Assets\LockScreenLogo.scale-200.png" />
<Image Include="Assets\SplashScreen.scale-200.png">
<DeploymentContent>true</DeploymentContent>
</Image>
<Image Include="Assets\Square150x150Logo.scale-200.png" />
<Image Include="Assets\Square44x44Logo.scale-200.png" />
<Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Image Include="Assets\StoreLogo.png" />
<Image Include="Assets\Wide310x150Logo.scale-200.png" />
- Creates App.xaml, App.xaml.h and App.xaml.cpp:
="1.0"="utf-8"
<Application
x:Class="App2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App2">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
#pragma once
#include "App.xaml.g.h"
namespace winrt::%s::implementation
{
struct App : AppT<App>
{
App();
void OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&);
private:
winrt::Microsoft::UI::Xaml::Window window{ nullptr };
};
}
#include "pch.h"
#include "App.xaml.h"
// #include "MainWindow.xaml.h"
using namespace winrt;
using namespace Microsoft::UI::Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace winrt::%s::implementation
{
App::App()
{
#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException([](IInspectable const&, UnhandledExceptionEventArgs const& e)
{
if (IsDebuggerPresent())
{
auto errorMessage = e.Message();
__debugbreak();
}
});
#endif
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
void App::OnLaunched([[maybe_unused]] LaunchActivatedEventArgs const& e)
{
// window = make<MainWindow>();
// window.Activate();
}
}
Finally, it saves the new project as a Win32ProjectWUI.vcxproj, which you can add to the solution.
Adding WinUI Stuff
The project still runs WinMain
and your Win32 code. To add e.g., a Window
- Add New Item -> WinUI -> Blank Window
- Edit the XAML, IDL, H and CPP as usual to create the window
- Do the
WinMain init
stuff in WinUI style instead:
WinMain
#ifdef Win32_WINUI3
{
void (WINAPI *pfnXamlCheckProcessRequirements)();
auto module = ::LoadLibrary(L"Microsoft.ui.xaml.dll");
if (module)
{
pfnXamlCheckProcessRequirements = reinterpret_cast<decltype
(pfnXamlCheckProcessRequirements)>(GetProcAddress
(module, "XamlCheckProcessRequirements"));
if (pfnXamlCheckProcessRequirements)
{
(*pfnXamlCheckProcessRequirements)();
}
::FreeLibrary(module);
}
}
winrt::init_apartment(winrt::apartment_type::single_threaded);
::winrt::Microsoft::UI::Xaml::Application::Start(
[](auto&&)
{
::winrt::make<::winrt::your_project_namespace::implementation::App>();
});
This Start()
call will only return when there are no windows visible. So now, you can edit the app.xaml.h to include, for example, blankwindow.xaml.h and to show the window in the OnLaunched
method:
window = make<winrt::your_namespace::BlankWindow>();
window.Activate();
The Code
The code contains a sample Win32 project and the converter. The converter will create a WUI vcxproj file which you can add to the project and work to it as described.
Have fun!
History
- 21st January, 2024: First release