refactor: 重新架构

This commit is contained in:
刘旭 2022-05-12 15:30:33 +08:00
commit 4bc871fbee
8 changed files with 199 additions and 300 deletions

View file

@ -149,7 +149,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="XamlHost.h" />
<ClInclude Include="XamlApp.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -162,7 +162,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="XamlHost.cpp" />
<ClCompile Include="XamlApp.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -18,7 +18,7 @@
<ClInclude Include="pch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="XamlHost.h">
<ClInclude Include="XamlApp.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
@ -35,7 +35,7 @@
<ClCompile Include="pch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="XamlHost.cpp">
<ClCompile Include="XamlApp.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>

145
src/Magpie/XamlApp.cpp Normal file
View file

@ -0,0 +1,145 @@
#include "pch.h"
#include "XamlApp.h"
#include <winrt/Windows.UI.Core.h>
#include <CoreWindow.h>
ATOM RegisterWndClass(
HINSTANCE hInstance,
LRESULT (*wndProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam),
const wchar_t* className
) {
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = wndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = className;
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
RTL_OSVERSIONINFOW GetOSVersion() {
HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
if (!hNtDll) {
return {};
}
auto rtlGetVersion = (LONG(WINAPI*)(PRTL_OSVERSIONINFOW))GetProcAddress(hNtDll, "RtlGetVersion");
if (rtlGetVersion == nullptr) {
return {};
}
RTL_OSVERSIONINFOW version{};
version.dwOSVersionInfoSize = sizeof(version);
rtlGetVersion(&version);
return version;
}
bool XamlApp::Initialize(HINSTANCE hInstance, const wchar_t* className, const wchar_t* title) {
RegisterWndClass(hInstance, _WndProcStatic, className);
auto osVersion = GetOSVersion();
bool isWin11 = osVersion.dwMajorVersion == 10 && osVersion.dwMinorVersion == 0 && osVersion.dwBuildNumber >= 22000;
_hwndXamlHost = CreateWindowEx(isWin11 ? WS_EX_NOREDIRECTIONBITMAP | WS_EX_DLGMODALFRAME : 0,
className, isWin11 ? L"" : title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1000, 700,
nullptr, nullptr, hInstance, nullptr);
if (!_hwndXamlHost) {
return false;
}
if (isWin11) {
constexpr const DWORD DWMWA_MICA_EFFECT = 1029;
BOOL value = TRUE;
DwmSetWindowAttribute(_hwndXamlHost, DWMWA_MICA_EFFECT, &value, sizeof(value));
}
return true;
}
void XamlApp::Show(winrt::Windows::UI::Xaml::UIElement xamlElement) {
_xamlElement = xamlElement;
// 在 Win10 上可能导致任务栏出现空的 DesktopWindowXamlSource 窗口
// 见 https://github.com/microsoft/microsoft-ui-xaml/issues/6490
ShowWindow(_hwndXamlHost, SW_SHOW);
_xamlSource = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource();
_xamlSourceNative2 = _xamlSource.as<IDesktopWindowXamlSourceNative2>();
auto interop = _xamlSource.as<IDesktopWindowXamlSourceNative>();
interop->AttachToWindow(_hwndXamlHost);
interop->get_WindowHandle(&_hwndXamlIsland);
_xamlSource.Content(xamlElement);
_OnResize();
// 防止关闭时出现 DesktopWindowXamlSource 窗口
auto coreWindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread();
if (coreWindow) {
HWND hwndDWXS;
coreWindow.as<ICoreWindowInterop>()->get_WindowHandle(&hwndDWXS);
ShowWindow(hwndDWXS, SW_HIDE);
}
}
int XamlApp::Run() {
MSG msg;
// 主消息循环
while (GetMessage(&msg, nullptr, 0, 0)) {
if (_xamlSource) {
BOOL processed = FALSE;
HRESULT hr = _xamlSourceNative2->PreTranslateMessage(&msg, &processed);
if (SUCCEEDED(hr) && processed) {
continue;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void XamlApp::_OnResize() {
RECT clientRect;
GetClientRect(_hwndXamlHost, &clientRect);
SetWindowPos(_hwndXamlIsland, NULL, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_SHOWWINDOW);
}
LRESULT XamlApp::_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_SIZE:
_OnResize();
return 0;
case WM_SETFOCUS:
if (_hwndXamlIsland) {
SetFocus(_hwndXamlIsland);
}
return 0;
case WM_DESTROY:
_xamlSourceNative2 = nullptr;
_xamlSource.Close();
_xamlSource = nullptr;
_xamlElement = nullptr;
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

38
src/Magpie/XamlApp.h Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include "pch.h"
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
class XamlApp {
public:
XamlApp() = default;
XamlApp(const XamlApp&) = delete;
XamlApp(XamlApp&&) = delete;
static XamlApp& Get() {
static XamlApp instance;
return instance;
}
bool Initialize(HINSTANCE hInstance, const wchar_t* className, const wchar_t* title);
void Show(winrt::Windows::UI::Xaml::UIElement xamlElement);
int Run();
private:
void _OnResize();
static LRESULT _WndProcStatic(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
return Get()._WndProc(hWnd, msg, wParam, lParam);
}
LRESULT _WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
winrt::Windows::UI::Xaml::UIElement _xamlElement{ nullptr };
HWND _hwndXamlHost = NULL;
HWND _hwndXamlIsland = NULL;
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _xamlSource{ nullptr };
winrt::com_ptr<IDesktopWindowXamlSourceNative2> _xamlSourceNative2;
};

View file

@ -1,101 +0,0 @@
#include "pch.h"
#include "XamlHost.h"
#include <winrt/Windows.UI.Core.h>
#include <CoreWindow.h>
namespace winrt {
using namespace Windows::UI::Xaml::Hosting;
}
void XamlHost::Attach(HWND parent, winrt::Windows::UI::Xaml::UIElement xamlElement) {
if (HasAttach()) {
Detach();
}
_xamlSource = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource();
_xamlSourceNative2 = _xamlSource.as<IDesktopWindowXamlSourceNative2>();
auto interop = _xamlSource.as<IDesktopWindowXamlSourceNative>();
interop->AttachToWindow(parent);
_hwndParent = parent;
interop->get_WindowHandle(&_hwndXamlIsland);
_xamlSource.Content(xamlElement);
_OnResize();
// 防止关闭时出现 DesktopWindowXamlSource 窗口
auto coreWindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread();
if (coreWindow) {
HWND hwndDWXS;
coreWindow.as<ICoreWindowInterop>()->get_WindowHandle(&hwndDWXS);
ShowWindow(hwndDWXS, SW_HIDE);
}
}
void XamlHost::Detach() {
if (!HasAttach()) {
return;
}
_xamlSource.Close();
_xamlSource = nullptr;
}
bool XamlHost::PreHandleMessage(const MSG& msg) {
if (!HasAttach()) {
return false;
}
BOOL processed = FALSE;
HRESULT hr = _xamlSourceNative2->PreTranslateMessage(&msg, &processed);
if (FAILED(hr)) {
processed = FALSE;
}
if (processed) {
return true;
}
return false;
}
void XamlHost::HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (!HasAttach()) {
return;
}
switch (message) {
case WM_SIZE:
_OnResize();
break;
case WM_SETFOCUS:
if (_hwndXamlIsland) {
SetFocus(_hwndXamlIsland);
}
break;
case WM_DESTROY:
Detach();
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
}
default:
break;
}
}
void XamlHost::_OnResize() {
if (!HasAttach()) {
return;
}
RECT clientRect;
GetClientRect(_hwndParent, &clientRect);
SetWindowPos(_hwndXamlIsland, NULL, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_SHOWWINDOW);
}

View file

@ -1,37 +0,0 @@
#pragma once
#include "pch.h"
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
class XamlHost {
public:
XamlHost() = default;
XamlHost(const XamlHost&) = delete;
XamlHost(XamlHost&&) = delete;
~XamlHost() {
Detach();
}
bool HasAttach() const {
return !!_xamlSource;
}
void Attach(HWND parent, winrt::Windows::UI::Xaml::UIElement xamlElement);
void Detach();
bool PreHandleMessage(const MSG& msg);
void HandleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
void _OnResize();
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _xamlSource{ nullptr };
winrt::com_ptr<IDesktopWindowXamlSourceNative2> _xamlSourceNative2;
HWND _hwndXamlIsland = NULL;
HWND _hwndParent = NULL;
};

View file

@ -2,157 +2,22 @@
//
#include "pch.h"
#include "XamlHost.h"
#include "XamlApp.h"
// 全局变量:
HINSTANCE hInst; // 当前实例
const wchar_t* szTitle = L"Magpie"; // 标题栏文本
const wchar_t* szWindowClass = L"Magpie_XamlHost"; // 主窗口类名
winrt::Magpie::App::App hostApp{ nullptr };
winrt::Magpie::App::MainPage _myUserControl{ nullptr };
XamlHost xamlHost;
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow
) {
winrt::init_apartment(winrt::apartment_type::single_threaded);
MyRegisterClass(hInstance);
auto& app = XamlApp::Get();
app.Initialize(hInstance, L"Magpie_XamlHost", L"Magpie");
// 执行应用程序初始化:
if (!InitInstance(hInstance, nCmdShow)) {
return FALSE;
}
winrt::Magpie::App::App hostApp = winrt::Magpie::App::App{};
app.Show(winrt::Magpie::App::MainPage());
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0)) {
if (xamlHost.PreHandleMessage(msg)) {
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
RTL_OSVERSIONINFOW GetOSVersion() {
HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
if (!hNtDll) {
return {};
}
auto rtlGetVersion = (LONG(WINAPI*)(PRTL_OSVERSIONINFOW))GetProcAddress(hNtDll, "RtlGetVersion");
if (rtlGetVersion == nullptr) {
return {};
}
RTL_OSVERSIONINFOW version{};
version.dwOSVersionInfoSize = sizeof(version);
rtlGetVersion(&version);
return version;
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
hInst = hInstance; // Store instance handle in our global variable
auto osVersion = GetOSVersion();
bool isWin11 = osVersion.dwMajorVersion == 10 && osVersion.dwMinorVersion == 0 && osVersion.dwBuildNumber >= 22000;
HWND hWnd = CreateWindowEx(isWin11 ? WS_EX_NOREDIRECTIONBITMAP | WS_EX_DLGMODALFRAME : 0, szWindowClass, isWin11 ? L"" : szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 1000, 700, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
return FALSE;
}
if (isWin11) {
constexpr const DWORD DWMWA_MICA_EFFECT = 1029;
BOOL value = TRUE;
DwmSetWindowAttribute(hWnd, DWMWA_MICA_EFFECT, &value, sizeof(value));
}
// 在 Win10 上可能导致任务栏出现空的 DesktopWindowXamlSource 窗口
// 见 https://github.com/microsoft/microsoft-ui-xaml/issues/6490
hostApp = winrt::Magpie::App::App{};
ShowWindow(hWnd, nCmdShow);
xamlHost.Attach(hWnd, winrt::Magpie::App::MainPage());
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
xamlHost.HandleMessage(hWnd, message, wParam, lParam);
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
return app.Run();
}

View file

@ -38,17 +38,10 @@
#define NOMCX
#include <windows.h>
#include <windowsx.h>
#include <dwmapi.h>
// C++ 运行时头文件
#include <string>
#include <memory>
#include <cstdlib>
#include <functional>
#include <algorithm>
#include <string_view>
#include <span>
// C++/WinRT 头文件
#include <winrt/base.h>
@ -61,7 +54,3 @@
#pragma comment(lib, "dwmapi.lib")
using namespace std::literals::string_literals;
using namespace std::literals::string_view_literals;