在现有 Win32 应用程序中使用 WinUI3






4.95/5 (18投票s)
如何在不进行打包的情况下,
引言
实话实说。如果微软创造了与纯 Win32 API 兼容的东西,大家仍然会使用这个旧 API,偶尔尝试新界面。因此,他们费尽心机让我难以使用他们发明的任何新东西。
UWP 未能在 Win32 中普及。 他们尝试了 XAML Islands - 我创建了一个小型库(尝试)使用它。它不支持所有控件,即使是支持的控件也充满了糟糕的 Bug。不能用于正式的生产环境。没关系。
我曾认为唯一的方法是使用两个可执行文件。然而,Sota Nakamura 的一篇精彩文章指引了我正确的方向。你可以有一个仍然兼容 Windows 7 的应用程序,并且可以立即使用 WinUI3。
所以,让我向你展示我是如何做到的。这是我的普通 Win32 Direct2D
应用Turbo Play的截图,以及一个 WinUI3
的“是、否、取消”对话框。
这是这个项目的一个小型演示。一个 Win32 对话框调用一个 WinUI
对话框。
WUI3 项目
- 在 Visual Studio 中创建一个新的“打包的 WinUI 3 空白应用”项目(在“桌面”类别下)。
- 关闭项目,并用文本编辑器打开 .vcxproj 文件。
- 将
<AppxPackage>true</AppxPackage>
更改为<AppxPackage>false</AppxPackage>
。 - 在第一个
<PropertyGroup Label="Globals">
之后添加元素<WindowsPackageType>None</WindowsPackageType>
。 - 用 Visual Studio 重新打开项目。
- 转到工具 -> NuGet 包管理器 -> 管理解决方案的 NuGet 程序包,然后更新四个程序包。这可确保使用最新的
WinUI3
库版本。
这可确保你创建的是一个“未打包”的项目。这个项目的唯一用途是编译你的 XAML 文件为 XBF 文件。除此之外没有其他用途。
编辑你的 MainWindow.xaml 文件(或你添加到其中的任何其他 XAML 文件)并进行编译。你应该会在 \x64\Debug\embed 目录下看到 MainWindow.xbf。
普通应用程序
-
创建一个新的标准 Win32 项目。
-
将标准设置为 C++ 17。
-
可选地将其设置为静态(无 DLL 运行时)并添加
#pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
用于样式。
-
使用 Nuget 安装
WindowsAppSDK
和Microsoft.Windows.CppWinRT
。 -
添加一个 RC 文件,其中将包含 xbf 文件作为资源。
L1 DATA "..\\wui3\\x64\\debug\\embed\\MainWindow.xbf"
- 间接调用引导程序。
CoInitializeEx(0, COINIT_APARTMENTTHREADED); PACKAGE_VERSION pg = {}; typedef HRESULT (__stdcall* mi)( UINT32 majorMinorVersion, PCWSTR versionTag, PACKAGE_VERSION minVersion); const wchar_t* dll = L"Microsoft.WindowsAppRuntime.Bootstrap.dll"; auto hL = LoadLibrary(dll); mi M = (mi)GetProcAddress(hL, "MddBootstrapInitialize"); if (!M) return E_FAIL; auto hr = M(0x00010003, L"", pg);
现在引导程序已加载。
创建“App”类
让我们首先看看如何构建资源。你必须向加载器提供 ms-appx://local/<path> 文件名,其中包含你保存在 RC 文件中的所有 XBF 资源。
void BuildURLS()
{
urls.clear();
wchar_t x[200] = {};
auto tf = TempFile4(0,0,0);
tf += L".xbf";
ExtractResourceToFile(GetModuleHandle(0), L"L1", L"DATA", tf.c_str());
swprintf_s(x, 200, L"ms-appx://local/%s", tf.c_str());
urls.push_back(x);
}
现在让我们看看 App
类。
bool FirstRun = 1;
class AppL : public ApplicationT<AppL, IXamlMetadataProvider>
{
XamlControlsXamlMetaDataProvider provider;
public:
std::vector<std::wstring> urls;
std::vector<Window> windows;
void BuildURLS() {...}
void L2()
{
BuildURLS();
if (FirstRun)
{
Resources().MergedDictionaries().Append(XamlControlsResources());
for (size_t i = 0; i < urls.size(); i++)
{
windows.emplace_back(Window());
}
for (size_t i = 0; i < urls.size(); i++)
{
Application::LoadComponent(windows[i], Uri(urls[i].c_str()));
}
FirstRun = 0;
windows[0].Activate();
}
else
{
for (size_t i = 0; i < urls.size(); i++)
{
windows[i].Activate();
windows[i].Activated = 1;
}
}
}
void OnLaunched(LaunchActivatedEventArgs const&)
{
L2();
}
IXamlType GetXamlType(TypeName const& type)
{
return provider.GetXamlType(type);
}
IXamlType GetXamlType(hstring const& fullname)
{
return provider.GetXamlType(fullname);
}
com_array<XmlnsDefinition> GetXmlnsDefinitions()
{
return provider.GetXmlnsDefinitions();
}
};
首先,你使用 IXamlMetadataProvider
的回调来为 WinUI3 提供“视觉样式”(如果这样做,你将获得旧的 UWP 样式)。
其次,当 OnLaunch
被调用时,你必须加载所有窗口,因为你不能多次调用 Resources().MergedDictionaries().Append(XamlControlsResources());
。无论你稍后 Activate()
还是隐藏/显示你的窗口,都取决于你,但必须立即加载到所有窗口。
在你的 WinMain
中现在
auto app3 = make<AppL>();
Application::Start([&](auto&&) {
app3;
});
只要有窗口可见,这就不会返回。如果返回,你可以再次调用它。
安装可再发行组件
对于未打包的应用程序,在运行它之前,你必须以管理员身份安装可再发行组件。从这个链接获取可再发行组件。
你已准备就绪并正在运行!
更多想法
你还不能在 XAML 中定义事件。你必须手动设置它们。
Panel p = Content().as<Panel>();
p.FindName(L"myButton").as<Button>().Click([&]
(IInspectable const&, RoutedEventArgs const&)
{
MessageBox(0, L"Clicked", 0, 0);
});
你可以子类化 WinUI 窗口以在自己的 Window PRoc 中手动处理消息。
// Subclass
auto n = as <IWindowNative>();
if (n)
{
n->get_WindowHandle(&hwnd);
old = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)NEWP);
}
之后,你可以像使用任何普通窗口一样使用 SendMessage
。
自动调整窗口大小
auto co = Content();
Panel sp = FindElementByName(L"rsp2").as<Panel>();
if (!Activated)
{
sp.SizeChanged([&](winrt::Windows::Foundation::IInspectable const& sender,
winrt::Windows::Foundation::IInspectable)
{
OnChangeSize(sender, false);
});
你想让窗口自动适应,所以我把一个第二个 StackPanel 放在第一个里面,名字叫 rsp2
,并在它上面触发一个 OnChangeSize
。
void OnChangeSize(winrt::Windows::Foundation::IInspectable const& sender, bool f)
{
auto dlg = sender.as<winrt::Microsoft::UI::Xaml::Controls::StackPanel>();
auto strn = dlg.Name();
float xy = GetDpiForWindow(hwnd) / 96.0f;
auto wi5 = dlg.ActualWidth() * xy;
auto he5 = dlg.ActualHeight() * xy;
wi5 += GetThemeSysSize(0, SM_CXBORDER);
he5 += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME) +
GetSystemMetrics(SM_CYEDGE) * 2;
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, (int)wi5, (int)he5,
SWP_SHOWWINDOW | SWP_NOMOVE);
CenterWindow2(hwnd);
}
将 Win32 控件托管到 WinUI3 窗口中
这个很有帮助。你必须将窗口设置为 WS_EX_LAYERED
,然后调用 SetLayeredWindowAttributes(hwnd, 0, (BYTE)(255 * 100 / 100), LWA_ALPHA);
这仍然需要一些工作(例如,加速转发等),但它存在一个 Bug(WinUI3 控件无法绘制在你的 Win32 窗口之上 - 菜单有问题)。
基本规则是,Win32 窗口将覆盖任何 WinUI3 控件。这意味着你必须将任何 WinUI 放置在主窗口的顶部、左侧、右侧、底部,并将任何 Win32 HWND
放在里面。我为菜单所做的是处理菜单项的点击并在其下方显示一个普通的 HMENU
- 呃,太丑了,但目前我无能为力。
这是具有完整 WinUI3 模式的 Turbo Play。
旧文章
仅供参考,了解使用 Winui3 的旧方法。现在不需要了。
启动一个不可见的应用程序
WinUI3
应用程序只能有一个窗口,一旦它被销毁,你就无法再次创建它。因此,你的 WinUI3
应用程序将只有一个窗口,其中包含一个大的 XAML,用于显示你所有的 UI 并按需切换它们。
在你的 OnLaunched
事件中,执行以下操作。
auto ew = make<MainWindow>();
window = ew;
window.Activate();
if (window)
{
auto n = window.as<IWindowNative>();
if (n)
{
n->get_WindowHandle(&mw);
if (mw)
{
ShowWindow(mw, SW_HIDE);
auto ew2 = window.as<MainWindow>();
ew2->Subclass();
std::thread t(tx, this);
t.detach();
}
}
}
你需要获取一个 HWND
以供以后在进程间使用(保存在 mw
中),然后你需要通过新的窗口过程来子类化这个窗口(稍后详述)。
Old = (WNDPROC)GetWindowLongPtr(mw, GWLP_WNDPROC);
SetWindowLongPtr(mw, GWLP_WNDPROC, (LONG_PTR)NEWW_WP);
mwt = this;
然后,你想要创建一个线程来等待你的 Win32 应用程序的触发。
void tx(App* app)
{
if (!app)
return;
auto w = app->window.as<winrt::wui3::implementation::MainWindow>();
w->Run();
}
我们稍后会研究这个 Run()
函数。
创建 XAML
如前所述,你只能有一个 XAML。所以我在这里创建了两个条目:一个 infobox
(用于替换 MessageBox
)和一个 AskText
对话框。
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
RequestedTheme="Dark" KeyDown="KeyD2">
<!-- Ask Text -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
VerticalAlignment="Center" x:Name="AskText" Visibility="Collapsed"
SizeChanged="szch" Width="500">
<StackPanel MinWidth="500">
<InfoBar Name="AskText_Question" IsOpen="True"
Severity="Informational" Title="" IsIconVisible="False"
IsClosable="False" Message="" />
<TextBox Name="AskText_Response" Margin="15"
Text="" KeyDown="KeyD"/>
<StackPanel Orientation="Horizontal" Margin="15"
HorizontalAlignment="Right">
<Button Content="" Margin="0,0,0,5"
Name="AskText_OK" Click="AskText_ClickOK"
Style="{ThemeResource AccentButtonStyle}" />
<Button Content="" Margin="15,0,0,5"
Name="AskText_Cancel" Click="AskText_ClickCancel" />
</StackPanel>
</StackPanel>
</StackPanel>
<!-- Message -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
VerticalAlignment="Center" x:Name="Message1" Visibility="Collapsed"
SizeChanged="szch" Width="500">
<StackPanel MinWidth="500">
<InfoBar MinHeight="100" Name="Message1_Question"
IsOpen="True" Severity="Informational" Title=""
IsIconVisible="False" IsClosable="False" Message="" />
<StackPanel Orientation="Horizontal" Margin="15"
HorizontalAlignment="Left">
<Button Content="" Margin="0,0,0,5"
Name="Message1_OK" Click="AskText_ClickCancel"
Style="{ThemeResource AccentButtonStyle}" />
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
这两个 StackPanel
都设置了 Visibility="Collapsed"
和 SizeChanged="szch"
,因为我们需要调整主窗口的大小以使其与第一个调用时 StackPanel
自动调整的大小相同。
等待触发
现在让我们看看那个 Run
函数。它将等待我们的主可执行文件发送通知以显示对话框。
首先,我们检查 HwndOfWin32App
。如果由于任何原因它失效,WinUI3
应用程序将终止。然后,我们也向 Win32
应用程序返回我们自己的 HWND
句柄,并触发一个等待事件,以便我们的 Win32 应用程序知道 WinUI
应用程序已准备好。对于所有这些通信,我使用了我的 USM 库(包含在此存储库中)。
然后我们等待主应用程序的触发。如果超时并且 hwnd
不再有效,我们将退出,否则继续等待。
如果我们收到触发,我们将读取一个 DIALOGID
(在 common.h 中找到,我已经定义了 DIALOGID_ASKTEXT
和 DIALOGID_MESSAGE
),目前是这样。这还包括读取一个 XML string
(使用我自己的xml3all.h库),以便我们知道如何初始化我们的对话框。然后,我们向我们的窗口发送一个 WM_APP
消息来加载它(必须来自主线程!)。
// Create the mutex
u = std::make_shared<USMLIBRARY::usm<>>(usm_cid, 0, 1024 * 1024, 10);
u->Initialize();
u->ReadData((char*)&HwndOfWin32App, 8, 0);
SetTimer(mw, 1, 500, 0);
u->WriteData((char*)&mw, sizeof(HWND), 0, 0);
SetEvent(u->hEventAux1);
for (;;)
{
auto j = u->NotifyWrite(true, 5000);
if (j == WAIT_TIMEOUT)
{
if (!IsWindow((HWND)HwndOfWin32App))
break;
}
else
{
auto id = DIALOGID_NONE;
auto rd = u->BeginRead();
if (!rd)
continue;
unsigned long long xlen = 0;
memcpy(&id, rd + 0, sizeof(id));
memcpy(&xlen, rd + sizeof(id), sizeof(xlen));
std::vector<char> xmld;
if (xlen < (1024 * 1024))
{
xmld.resize(xlen + 1);
memcpy(xmld.data(), rd + sizeof(id) + sizeof(xlen), xlen);
}
u->EndRead();
if (xlen < (1024 * 1024))
SendMessage(mw, WM_APP, id, (LPARAM)xmld.data());
}
}
if (mw)
PostMessage(mw, WM_CLOSE, 0xFEFEFEFE, 0);
如果出现问题,我们发送一个 WM_CLOSE
消息以及 0xFEFEFEFE
标志来终止应用程序。我们需要这个标志,因为如果用户按下X按钮,会发送一个没有这个标志的 WM_CLOSE
,我们不能将其传递给旧的窗口过程(否则应用程序会崩溃)。
运行对话框
WM_APP
处理程序将使用传递的 ID
以及任何初始化 XML string
调用 RunDialog()
。
unsigned long long id = mm - WM_APP;
XML3::XML xx;
if (ll)
xx = (char*)ll;
current_id = id;
AskText().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
Message1().Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
if (id == DIALOGID_ASKTEXT)
{
XML3::XML* x = (XML3::XML*)&xx;
Title(x->GetRootElement().vv("title").GetWideValue().c_str());
AskText_Question().Title(x->GetRootElement().vv
("t0").GetWideValue().c_str());
AskText_Question().Message(x->GetRootElement().vv
("t1").GetWideValue().c_str());
AskText_Response().Text(x->GetRootElement().vv
("t2").GetWideValue().c_str());
AskText_Response().PlaceholderText
(x->GetRootElement().vv("t3").GetWideValue().c_str());
if (x->GetRootElement().vv("big").GetValueInt())
{
AskText_Response().TextWrapping
(winrt::Microsoft::UI::Xaml::TextWrapping::Wrap);
AskText_Response().AcceptsReturn(true);
AskText_Response().MinHeight(100);
}
AskText_Response().SelectAll();
AskText_OK().Content(box_value(x->GetRootElement().vv
("tOK").GetWideValue().c_str()));
AskText_Cancel().Content(box_value(x->GetRootElement().vv
("tCancel").GetWideValue().c_str()));
SetWindowPos(mw, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW |
SWP_NOMOVE | SWP_NOSIZE);
auto dlg = AskText();
dlg.Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
if (ChangedSizeOnce[current_id])
{
OnChangeSize(dlg, 1);
}
}
if (id == DIALOGID_MESSAGE1)
{
XML3::XML* x = (XML3::XML*)&xx;
Title(x->GetRootElement().vv("title").GetWideValue().c_str());
Message1_Question().Title(x->GetRootElement().vv
("t0").GetWideValue().c_str());
Message1_Question().Message(x->GetRootElement().vv
("t1").GetWideValue().c_str());
Message1_OK().Content(box_value(x->GetRootElement().vv
("tOK").GetWideValue().c_str()));
SetWindowPos(mw, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW |
SWP_NOMOVE | SWP_NOSIZE);
auto dlg = Message1();
dlg.Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
if (ChangedSizeOnce[current_id])
{
OnChangeSize(dlg, 1);
}
}
我们在这里做什么?
- 我们隐藏所有内容。
- 我们使用传递的 XML
string
来初始化对话框中的元素。 - 我们显示对话框。
SetWindowPos(mw, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW |
SWP_NOMOVE | SWP_NOSIZE);
auto dlg = AskText();
dlg.Visibility(winrt::Microsoft::UI::Xaml::Visibility::Visible);
if (ChangedSizeOnce[current_id])
{
OnChangeSize(dlg, 1);
}
是的,我们需要将其设置为 TopMost
确实很丑陋,但我们希望它隐藏我们的主 Win32
应用程序。另外,如果这是我们第一次显示(ChangedSizeOnce[current_id] == 0
),那么我们将等待控件调整大小,以便调用我们的 szch
。如果这不是我们第一次调用它,我们必须通过手动调用 OnChangeSize
并传递一个强制参数来自己调整它。
更改大小
void MainWindow::OnChangeSize(winrt::Windows::Foundation::IInspectable const& sender,
bool f) {
if (f)
ChangedSizeOnce[current_id] = 0;
if (ChangedSizeOnce[current_id])
return;
ChangedSizeOnce[current_id] = 1;
auto dlg = sender.as<winrt::Microsoft::UI::Xaml::Controls::StackPanel>();
float xy = GetDpiForWindow(mw) / 96.0f;
auto wi5 = dlg.ActualWidth() * xy;
auto he5 = dlg.ActualHeight() * xy;
wi5 += GetThemeSysSize(0, SM_CXBORDER);
he5 += GetSystemMetrics(SM_CYCAPTION) +
GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE) * 2;
SetWindowPos(mw, HWND_TOPMOST, 0, 0, (int)wi5, (int)he5,
SWP_SHOWWINDOW | SWP_NOMOVE);
CenterWindow(mw);
如果我们正在强制调整大小,或者这是第一次,我们将获取 DPI 和主题边框长度,然后将 WinUI3
应用程序的主窗口调整为与加载的对话框大小匹配。
用户按下 X、ESC 或 Enter 键
if (mm == WM_CLOSE && ww != 0xFEFEFEFE)
{
XML3::XML x;
if (mwt->current_id == DIALOGID_ASKTEXT)
{
auto dlg = mwt->AskText();
dlg.Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
if (mwt->current_id == DIALOGID_MESSAGE1)
{
auto dlg = mwt->Message1();
dlg.Visibility(winrt::Microsoft::UI::Xaml::Visibility::Collapsed);
}
mwt->Cancel(x);
return 0;
}
除非我们使用了 0xFEFEFEFE
标志,否则当用户按下X时,我们不希望应用程序关闭。相反,我们关闭对话框并调用 Cancel()
,它会调用 Off()
。
按键也发生类似情况,无论是针对(整个对话框的)通用 StackPanel
,还是当光标在 TextBox
内时。
void MainWindow::KeyD(winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs r)
{
auto k = r.Key();
if (k == winrt::Windows::System::VirtualKey::Enter)
{
if (current_id == DIALOGID_MESSAGE1)
{
Microsoft::UI::Xaml::RoutedEventArgs a;
AskText_ClickCancel(sender, a);
}
}
if (k == winrt::Windows::System::VirtualKey::Escape)
{
if (current_id == DIALOGID_MESSAGE1)
{
Microsoft::UI::Xaml::RoutedEventArgs a;
AskText_ClickCancel(sender, a);
}
}
}
void MainWindow::KeyD2(winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs r)
{
auto k = r.Key();
if (k == winrt::Windows::System::VirtualKey::Enter)
{
if (current_id == DIALOGID_ASKTEXT)
{
Microsoft::UI::Xaml::RoutedEventArgs a;
AskText_ClickOK(sender, a);
}
if (current_id == DIALOGID_MESSAGE1)
{
Microsoft::UI::Xaml::RoutedEventArgs a;
AskText_ClickCancel(sender, a);
}
}
if (k == winrt::Windows::System::VirtualKey::Escape)
{
XML3::XML x;
Cancel(x);
}
}
通知调用者
Off()
函数将任何 XML 信息写入共享内存。
void MainWindow::Off(XML3::XML& x)
{
ShowWindow(mw, SW_HIDE);
auto s = x.Serialize();
auto rd = u->BeginWrite();
if (!rd)
return;
unsigned long long xlen = s.length();
memcpy(rd, &xlen, sizeof(xlen));
memcpy(rd + sizeof(xlen), s.data(), s.length());
u->EndWrite();
current_id = DIALOGID_NONE;
SetEvent(u->hEventAux2);
}
Win32 项目
例如,调用 AskText
对话框。
auto id = DIALOGID_ASKTEXT;
const char* x1 = R"(<?xml?><e title="Ask" t0="This is bold title"
t1="Enter value:" t2="100" t3="Placeholder text"
tOK="OK" tCancel="Cancel" />)";
unsigned long long wl = strlen(x1);
std::vector<char> what(1000);
memcpy(what.data(), &id, sizeof(id));
memcpy(what.data() + sizeof(id), &wl, sizeof(wl));
memcpy(what.data() + sizeof(id) + sizeof(wl), x1, wl);
ResetEvent(u.hEventAux1);
ResetEvent(u.hEventAux2);
u.WriteData((char*)what.data(), sizeof(id) + sizeof(wl) + wl, 0);
HANDLE h2[2] = { u.hEventAux1,u.hEventAux2 };
for (;;)
{
auto wi = WaitForMultipleObjects(2, h2, false, 2000);
if (wi == WAIT_OBJECT_0)
{
wmsg(hh);
continue; // OK, dialog is showing
}
if (wi != (WAIT_OBJECT_0 + 1))
{
SetOffWUI3();
// Fail here
}
break; // dialog ended
}
unsigned long long how = 0;
what.clear();
auto rd = u.BeginRead();
memcpy(&how, rd, sizeof(how));
if (how < 1024 * 1024)
{
what.resize(how);
memcpy(what.data(), rd + sizeof(how), how);
}
u.EndRead();
XML3::XML x;
what.resize(what.size() + 1);
x = what.data();
// Parse x to check return values of the dialogs
这是代码中比较糟糕的部分。你借助 USM 库发送 XML 信息,现在我有一个等待循环。
HANDLE h2[2] = { u.hEventAux1,u.hEventAux2 };
for (;;)
{
auto wi = WaitForMultipleObjects(2, h2, false, 2000);
if (wi == WAIT_OBJECT_0)
{
wmsg(hh);
continue; // OK, dialog is showing
}
if (wi != (WAIT_OBJECT_0 + 1))
{
SetOffWUI3();
// Fail here
}
break; // dialog ended
}
void wmsg(HWND hh)
{
MSG msg;
if (GetMessage(&msg, hh, 0, 0))
{
if (msg.message == WM_LBUTTONDOWN || msg.message == WM_LBUTTONUP ||
msg.message == WM_RBUTTONDOWN || msg.message == WM_RBUTTONUP ||
msg.message == WM_MOUSEMOVE || msg.message == WM_KEYDOWN ||
msg.message == WM_KEYUP || msg.message == WM_PAINT)
return;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
如果第一个事件被触发,这意味着对话框仍然处于活动状态。但在这种情况下,我们不仅会等待,还会收到一条消息,这样看起来我们的等待应用程序并没有卡住,但我们也想处理任何鼠标/键盘消息。
如果第二个事件被触发,则表示用户已关闭对话框。因此,我们可以从共享内存中读取输出。
如果发生超时,则表示 WinUI3
应用程序已崩溃/已停止运行/等等。因此,我们可以回退到 Win32
对话框。
打包
在 x64\release\wui3 中,有 wui3.exe、wui3.winmd、resources.pri、其他资产和 Microsoft.WindowsAppRuntime.Bootstrap.dll。将整个 wui3 目录与你的应用程序一起打包和分发。
代码
GIT 存储库包含带有两个可执行项目(项目)的解决方案。尽情享受吧!
历史
- 2023 年 4 月 26 日:新的文章,无需第二个可执行文件。
- 2023 年 4 月 24 日:首次发布。