【UWP】让 UWP 本身托管本身 —— Windows SDK 篇

诗林  金牌会员 | 2025-1-17 18:20:11 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 695|帖子 695|积分 2085

众所周知,UWP 使用的窗口模型是 CoreWindow,但是 UWP 本身只是一个应用模型,所以完全可以创建 win32 窗口,那么我们可以不可以创建一个 win32 窗口,然后像 XAML 岛 (XAML Islands) 一样把 XAML 托管上去呢?本篇将讲述如安在 UWP 创建一个 XAML 岛窗口。

起首,XAML 岛会判断当前的应用模型是否为ClassicDesktop,所以我们需要利用Detours劫持AppPolicyGetWindowingModel方法。具体内容如下:
  1. #r "nuget:Detours.Win32Metadata"
  2. #r "nuget:Microsoft.Windows.CsWin32"
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. using Windows.Win32;
  7. using Windows.Win32.Foundation;
  8. using Windows.Win32.Storage.Packaging.Appx;
  9. using Detours = Microsoft.Detours.PInvoke;
  10. /// <summary>
  11. /// Represents a hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
  12. /// </summary>
  13. public sealed partial class HookWindowingModel : IDisposable
  14. {
  15.     /// <summary>
  16.     /// The value that indicates whether the class has been disposed.
  17.     /// </summary>
  18.     private bool disposed;
  19.     /// <summary>
  20.     /// The reference count for the hook.
  21.     /// </summary>
  22.     private static int refCount;
  23.     /// <summary>
  24.     /// The value that represents the current process token.
  25.     /// </summary>
  26.     private const int currentProcessToken = -6;
  27.     /// <remarks>The original <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.</remarks>
  28.     /// <inheritdoc cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/>
  29.     private static unsafe delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> AppPolicyGetWindowingModel;
  30.     /// <summary>
  31.     /// Initializes a new instance of the <see cref="HookWindowingModel"/> class.
  32.     /// </summary>
  33.     public HookWindowingModel()
  34.     {
  35.         refCount++;
  36.         StartHook();
  37.     }
  38.     /// <summary>
  39.     /// Finalizes this instance of the <see cref="HookWindowingModel"/> class.
  40.     /// </summary>
  41.     ~HookWindowingModel()
  42.     {
  43.         Dispose();
  44.     }
  45.     /// <summary>
  46.     /// Gets the value that indicates whether the hook is active.
  47.     /// </summary>
  48.     public static bool IsHooked { get; private set; }
  49.     /// <summary>
  50.     /// Gets or sets the windowing model to use when the hooked <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function is called.
  51.     /// </summary>
  52.     internal static AppPolicyWindowingModel WindowingModel { get; set; } = AppPolicyWindowingModel.AppPolicyWindowingModel_ClassicDesktop;
  53.     /// <summary>
  54.     /// Starts the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
  55.     /// </summary>
  56.     private static unsafe void StartHook()
  57.     {
  58.         if (!IsHooked)
  59.         {
  60.             using FreeLibrarySafeHandle library = PInvoke.GetModuleHandle("KERNEL32.dll");
  61.             if (!library.IsInvalid && NativeLibrary.TryGetExport(library.DangerousGetHandle(), nameof(PInvoke.AppPolicyGetWindowingModel), out nint appPolicyGetWindowingModel))
  62.             {
  63.                 void* appPolicyGetWindowingModelPtr = (void*)appPolicyGetWindowingModel;
  64.                 delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> overrideAppPolicyGetWindowingModel = &OverrideAppPolicyGetWindowingModel;
  65.                 _ = Detours.DetourRestoreAfterWith();
  66.                 _ = Detours.DetourTransactionBegin();
  67.                 _ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
  68.                 _ = Detours.DetourAttach(ref appPolicyGetWindowingModelPtr, overrideAppPolicyGetWindowingModel);
  69.                 _ = Detours.DetourTransactionCommit();
  70.                 AppPolicyGetWindowingModel = (delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR>)appPolicyGetWindowingModelPtr;
  71.                 IsHooked = true;
  72.             }
  73.         }
  74.     }
  75.     /// <summary>
  76.     /// Ends the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
  77.     /// </summary>
  78.     private static unsafe void EndHook()
  79.     {
  80.         if (--refCount == 0 && IsHooked)
  81.         {
  82.             void* appPolicyGetWindowingModelPtr = AppPolicyGetWindowingModel;
  83.             delegate* unmanaged[Stdcall]<HANDLE, AppPolicyWindowingModel*, WIN32_ERROR> overrideAppPolicyGetWindowingModel = &OverrideAppPolicyGetWindowingModel;
  84.             _ = Detours.DetourTransactionBegin();
  85.             _ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
  86.             _ = Detours.DetourDetach(&appPolicyGetWindowingModelPtr, overrideAppPolicyGetWindowingModel);
  87.             _ = Detours.DetourTransactionCommit();
  88.             AppPolicyGetWindowingModel = null;
  89.             IsHooked = false;
  90.         }
  91.     }
  92.     /// <param name="policy">A pointer to a variable of the <a target="_blank" href="https://docs.microsoft.com/windows/win32/api/appmodel/ne-appmodel-apppolicywindowingmodel">AppPolicyWindowingModel</a> enumerated type.
  93.     /// When the function returns successfully, the variable contains the <see cref="WindowingModel"/> when the identified process is current; otherwise, the windowing model of the identified process.</param>
  94.     /// <remarks>The overridden <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.</remarks>
  95.     /// <inheritdoc cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/>
  96.     [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
  97.     private static unsafe WIN32_ERROR OverrideAppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel* policy)
  98.     {
  99.         if ((int)processToken.Value == currentProcessToken)
  100.         {
  101.             *policy = WindowingModel;
  102.             return WIN32_ERROR.ERROR_SUCCESS;
  103.         }
  104.         return AppPolicyGetWindowingModel(processToken, policy);
  105.     }
  106.     /// <inheritdoc/>
  107.     public void Dispose()
  108.     {
  109.         if (!disposed && IsHooked)
  110.         {
  111.             EndHook();
  112.         }
  113.         GC.SuppressFinalize(this);
  114.         disposed = true;
  115.     }
  116. }
复制代码
准备工作完成,接下来我们就可以创建窗口了,起首我们需要新创建一个线程,CoreWindow 线程无法新建 XAML 岛,新建线程只需要用Thread就行了。
  1. new Thread(() => { ... });
复制代码
起首我们需要创建 XAML 岛,这时我们就需要利用上面劫持器来劫持获取应用模型的方法了。
  1. DesktopWindowXamlSource source;
  2. using (HookWindowingModel hook = new())
  3. {
  4.     source = new DesktopWindowXamlSource();
  5. }
复制代码
微软并没有单独提供一个 Win32 窗口管理的轮子,如果引用 Windows Forms 就太臃肿了,于是我们需要手动制作一个 Window 类:
  1. /// <summary>
  2. /// Represents a system-managed container for the content of an app.
  3. /// </summary>
  4. public partial class DesktopWindow
  5. {
  6.     private bool m_bIsClosed = false;
  7.     private DesktopWindowXamlSource m_source;
  8.     private IDesktopWindowXamlSourceNative m_native;
  9.     private readonly HWND m_hwnd;
  10.     private readonly WNDCLASSEXW m_wndClassEx;
  11.     /// <summary>
  12.     /// Initializes a new instance of the <see cref="DesktopWindow"/> class.
  13.     /// </summary>
  14.     public DesktopWindow()
  15.     {
  16.         m_wndClassEx = RegisterDesktopWindowClass(WNDPROC);
  17.         m_hwnd = CreateDesktopWindow();
  18.     }
  19.     /// <summary>
  20.     /// Gets the event dispatcher for the window.
  21.     /// </summary>
  22.     public CoreDispatcher Dispatcher { get; private set; }
  23.     /// <summary>
  24.     /// Gets the <see cref="DesktopWindowXamlSource"/> to provide XAML for this window.
  25.     /// </summary>
  26.     public DesktopWindowXamlSource WindowXamlSource
  27.     {
  28.         get => m_source;
  29.         private init
  30.         {
  31.             if (m_source != value)
  32.             {
  33.                 m_source = value;
  34.                 if (value != null)
  35.                 {
  36.                     Dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
  37.                     m_native = value.As<IDesktopWindowXamlSourceNative>();
  38.                     m_native.AttachToWindow(m_hwnd);
  39.                     ResizeWindowToDesktopWindowXamlSourceWindowDimensions();
  40.                 }
  41.                 else
  42.                 {
  43.                     m_native = null;
  44.                 }
  45.             }
  46.         }
  47.     }
  48.     /// <summary>
  49.     /// Shows the window and activates it.
  50.     /// </summary>
  51.     public void Show() => _ = PInvoke.ShowWindow(m_hwnd, SHOW_WINDOW_CMD.SW_NORMAL);
  52.     private LRESULT WNDPROC(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam)
  53.     {
  54.         switch (message)
  55.         {
  56.             case PInvoke.WM_PAINT:
  57.                 HDC hdc = PInvoke.BeginPaint(hWnd, out PAINTSTRUCT ps);
  58.                 _ = PInvoke.GetClientRect(hWnd, out RECT rect);
  59.                 _ = PInvoke.FillRect(hdc, rect, new DefaultSafeHandle(PInvoke.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH)));
  60.                 _ = PInvoke.EndPaint(hWnd, ps);
  61.                 return new LRESULT();
  62.             case PInvoke.WM_CLOSE when m_bIsClosed:
  63.                 goto default;
  64.             case PInvoke.WM_CLOSE:
  65.                 m_bIsClosed = true;
  66.                 goto default;
  67.             case PInvoke.WM_SIZE:
  68.                 ResizeWindowToDesktopWindowXamlSourceWindowDimensions();
  69.                 return new LRESULT();
  70.             case PInvoke.WM_CREATE:
  71.                 return new LRESULT();
  72.             case PInvoke.WM_DESTROY:
  73.                 PInvoke.PostQuitMessage(0);
  74.                 return new LRESULT();
  75.             default:
  76.                 return PInvoke.DefWindowProc(hWnd, message, wParam, lParam);
  77.         }
  78.     }
  79.     private void ResizeWindowToDesktopWindowXamlSourceWindowDimensions()
  80.     {
  81.         if (m_bIsClosed) return;
  82.         _ = PInvoke.GetClientRect(m_hwnd, out RECT rect);
  83.         _ = PInvoke.SetWindowPos(
  84.             m_native.WindowHandle,
  85.             new HWND(),
  86.             0, 0,
  87.             rect.Width, rect.Height,
  88.             SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);
  89.     }
  90. }
  91. public partial class DesktopWindow
  92. {
  93.     private static readonly unsafe HINSTANCE g_hInstance = new((void*)Process.GetCurrentProcess().Handle);
  94.     // win32 window class name for top-level WinUI desktop windows
  95.     private const string s_windowClassName = "WinUIDesktopWin32WindowClass";
  96.     // Default window title for top-level WinUI desktop windows
  97.     private const string s_defaultWindowTitle = "WinUI Desktop";
  98.     private static unsafe WNDCLASSEXW RegisterDesktopWindowClass(WNDPROC lpfnWndProc)
  99.     {
  100.         if (!PInvoke.GetClassInfoEx(new DefaultSafeHandle(g_hInstance), s_windowClassName, out WNDCLASSEXW wndClassEx))
  101.         {
  102.             wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);
  103.             wndClassEx.style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW;
  104.             wndClassEx.cbClsExtra = 0;
  105.             wndClassEx.cbWndExtra = 0;
  106.             wndClassEx.hCursor = PInvoke.LoadCursor(new HINSTANCE(), PInvoke.IDC_ARROW);
  107.             wndClassEx.hbrBackground = (HBRUSH)((nint)SYS_COLOR_INDEX.COLOR_WINDOW + 1);
  108.             wndClassEx.hInstance = g_hInstance;
  109.             fixed (char* lps_windowClassName = s_windowClassName)
  110.             {
  111.                 wndClassEx.lpszClassName = lps_windowClassName;
  112.             }
  113.             wndClassEx.lpfnWndProc = lpfnWndProc;
  114.             _ = PInvoke.RegisterClassEx(wndClassEx);
  115.             return wndClassEx;
  116.         }
  117.         return default;
  118.     }
  119.     private static unsafe HWND CreateDesktopWindow() =>
  120.         PInvoke.CreateWindowEx(
  121.             0,                                  // Extended Style
  122.             s_windowClassName,                  // name of window class
  123.             s_defaultWindowTitle,               // title-bar string
  124.             WINDOW_STYLE.WS_OVERLAPPEDWINDOW | WINDOW_STYLE.WS_VISIBLE,  // top-level window
  125.             int.MinValue,                       // default horizontal position
  126.             (int)SHOW_WINDOW_CMD.SW_HIDE,       // If the y parameter is some other value,
  127.                                                 // then the window manager calls ShowWindow with that value as the nCmdShow parameter
  128.             int.MinValue,                       // default width
  129.             int.MinValue,                       // default height
  130.             new HWND(),                         // no owner window
  131.             null,                               // use class menu
  132.             new DefaultSafeHandle(g_hInstance),
  133.             null);
  134.     private partial class DefaultSafeHandle(nint invalidHandleValue, bool ownsHandle) : SafeHandle(invalidHandleValue, ownsHandle)
  135.     {
  136.         public DefaultSafeHandle(nint handle) : this(handle, true) => SetHandle(handle);
  137.         public override bool IsInvalid => handle != nint.Zero;
  138.         protected override bool ReleaseHandle() => true;
  139.     }
  140. }
复制代码
然后我们就可以初始化一个窗口了。
  1. DesktopWindow window = new() { WindowXamlSource = source };
复制代码
末了不要忘了用消息循环保持当火线程,不然这里跑完了窗口就退出了。
  1. MSG msg = new();
  2. while (msg.message != PInvoke.WM_QUIT)
  3. {
  4.     if (PInvoke.PeekMessage(out msg, new HWND(), 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
  5.     {
  6.         _ = PInvoke.DispatchMessage(msg);
  7.     }
  8. }
复制代码
末了把之前的东西组合起来,再加点东西:
  1. /// <summary>
  2. /// Represents a system-managed container for the content of an app.
  3. /// </summary>
  4. public partial class DesktopWindow
  5. {
  6.     private bool m_bIsClosed = false;
  7.     private DesktopWindowXamlSource m_source;
  8.     private IDesktopWindowXamlSourceNative m_native;
  9.     private readonly HWND m_hwnd;
  10.     private readonly WNDCLASSEXW m_wndClassEx;
  11.     /// <summary>
  12.     /// Initializes a new instance of the <see cref="DesktopWindow"/> class.
  13.     /// </summary>
  14.     public DesktopWindow()
  15.     {
  16.         m_wndClassEx = RegisterDesktopWindowClass(WNDPROC);
  17.         m_hwnd = CreateDesktopWindow();
  18.     }
  19.     /// <summary>
  20.     /// Get the handle of the window.
  21.     /// </summary>
  22.     public nint Hwnd => m_hwnd;
  23.     /// <summary>
  24.     /// Gets the event dispatcher for the window.
  25.     /// </summary>
  26.     public CoreDispatcher Dispatcher { get; private set; }
  27.     /// <summary>
  28.     /// Gets or sets the visual root of an application window.
  29.     /// </summary>
  30.     public UIElement Content
  31.     {
  32.         get => WindowXamlSource.Content;
  33.         set => WindowXamlSource.Content = value;
  34.     }
  35.     /// <summary>
  36.     /// Gets or sets the XamlRoot in which this element is being viewed.
  37.     /// </summary>
  38.     [SupportedOSPlatform("Windows10.0.18362.0")]
  39.     public XamlRoot XamlRoot
  40.     {
  41.         get => WindowXamlSource.Content.XamlRoot;
  42.         set => WindowXamlSource.Content.XamlRoot = value;
  43.     }
  44.     /// <summary>
  45.     /// Gets or sets a string used for the window title.
  46.     /// </summary>
  47.     public unsafe string Title
  48.     {
  49.         get
  50.         {
  51.             int windowTextLength = PInvoke.GetWindowTextLength(m_hwnd);
  52.             char* windowText = stackalloc char[windowTextLength + 1];
  53.             _ = PInvoke.GetWindowText(m_hwnd, windowText, windowTextLength + 1);
  54.             return new string(windowText);
  55.         }
  56.         set => _ = PInvoke.SetWindowText(m_hwnd, value);
  57.     }
  58.     /// <summary>
  59.     /// Gets the <see cref="DesktopWindowXamlSource"/> to provide XAML for this window.
  60.     /// </summary>
  61.     public DesktopWindowXamlSource WindowXamlSource
  62.     {
  63.         get => m_source;
  64.         private init
  65.         {
  66.             if (m_source != value)
  67.             {
  68.                 m_source = value;
  69.                 if (value != null)
  70.                 {
  71.                     Dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
  72.                     m_native = value.As<IDesktopWindowXamlSourceNative>();
  73.                     m_native.AttachToWindow(m_hwnd);
  74.                     ResizeWindowToDesktopWindowXamlSourceWindowDimensions();
  75.                 }
  76.                 else
  77.                 {
  78.                     m_native = null;
  79.                 }
  80.             }
  81.         }
  82.     }
  83.     /// <summary>
  84.     /// Occurs when the window has closed.
  85.     /// </summary>
  86.     public event TypedEventHandler<DesktopWindow, object> Closed;
  87.     /// <summary>
  88.     /// Shows the window and activates it.
  89.     /// </summary>
  90.     public void Show() => _ = PInvoke.ShowWindow(m_hwnd, SHOW_WINDOW_CMD.SW_NORMAL);
  91.     /// <summary>
  92.     /// Sets the icon for the window, using the specified icon path.
  93.     /// </summary>
  94.     /// <param name="iconPath">The path of the icon.</param>
  95.     public unsafe void SetIcon(string iconPath)
  96.     {
  97.         fixed (char* ptr = iconPath)
  98.         {
  99.             HANDLE icon = PInvoke.LoadImage(new HINSTANCE(), ptr, GDI_IMAGE_TYPE.IMAGE_ICON, 0, 0, IMAGE_FLAGS.LR_LOADFROMFILE);
  100.             _ = PInvoke.SendMessage(m_hwnd, PInvoke.WM_SETICON, PInvoke.ICON_BIG, new LPARAM((nint)icon.Value));
  101.         }
  102.     }
  103.     private LRESULT WNDPROC(HWND hWnd, uint message, WPARAM wParam, LPARAM lParam)
  104.     {
  105.         switch (message)
  106.         {
  107.             case PInvoke.WM_PAINT:
  108.                 HDC hdc = PInvoke.BeginPaint(hWnd, out PAINTSTRUCT ps);
  109.                 _ = PInvoke.GetClientRect(hWnd, out RECT rect);
  110.                 _ = PInvoke.FillRect(hdc, rect, new DefaultSafeHandle(PInvoke.GetStockObject(GET_STOCK_OBJECT_FLAGS.WHITE_BRUSH)));
  111.                 _ = PInvoke.EndPaint(hWnd, ps);
  112.                 return new LRESULT();
  113.             case PInvoke.WM_CLOSE when m_bIsClosed:
  114.                 goto default;
  115.             case PInvoke.WM_CLOSE:
  116.                 m_bIsClosed = true;
  117.                 Closed?.Invoke(this, null);
  118.                 goto default;
  119.             case PInvoke.WM_SIZE:
  120.                 ResizeWindowToDesktopWindowXamlSourceWindowDimensions();
  121.                 return new LRESULT();
  122.             case PInvoke.WM_CREATE:
  123.                 return new LRESULT();
  124.             case PInvoke.WM_DESTROY:
  125.                 PInvoke.PostQuitMessage(0);
  126.                 return new LRESULT();
  127.             default:
  128.                 return PInvoke.DefWindowProc(hWnd, message, wParam, lParam);
  129.         }
  130.     }
  131.     private void ResizeWindowToDesktopWindowXamlSourceWindowDimensions()
  132.     {
  133.         if (m_bIsClosed) return;
  134.         _ = PInvoke.GetClientRect(m_hwnd, out RECT rect);
  135.         _ = PInvoke.SetWindowPos(
  136.             m_native.WindowHandle,
  137.             new HWND(),
  138.             0, 0,
  139.             rect.Width, rect.Height,
  140.             SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);
  141.     }
  142. }
  143. public partial class DesktopWindow
  144. {
  145.     private static readonly unsafe HINSTANCE g_hInstance = new((void*)Process.GetCurrentProcess().Handle);
  146.     // win32 window class name for top-level WinUI desktop windows
  147.     private const string s_windowClassName = "WinUIDesktopWin32WindowClass";
  148.     // Default window title for top-level WinUI desktop windows
  149.     private const string s_defaultWindowTitle = "WinUI Desktop";
  150.     private static unsafe WNDCLASSEXW RegisterDesktopWindowClass(WNDPROC lpfnWndProc)
  151.     {
  152.         if (!PInvoke.GetClassInfoEx(new DefaultSafeHandle(g_hInstance), s_windowClassName, out WNDCLASSEXW wndClassEx))
  153.         {
  154.             wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);
  155.             wndClassEx.style = WNDCLASS_STYLES.CS_HREDRAW | WNDCLASS_STYLES.CS_VREDRAW;
  156.             wndClassEx.cbClsExtra = 0;
  157.             wndClassEx.cbWndExtra = 0;
  158.             wndClassEx.hCursor = PInvoke.LoadCursor(new HINSTANCE(), PInvoke.IDC_ARROW);
  159.             wndClassEx.hbrBackground = (HBRUSH)((nint)SYS_COLOR_INDEX.COLOR_WINDOW + 1);
  160.             wndClassEx.hInstance = g_hInstance;
  161.             fixed (char* lps_windowClassName = s_windowClassName)
  162.             {
  163.                 wndClassEx.lpszClassName = lps_windowClassName;
  164.             }
  165.             wndClassEx.lpfnWndProc = lpfnWndProc;
  166.             _ = PInvoke.RegisterClassEx(wndClassEx);
  167.             return wndClassEx;
  168.         }
  169.         return default;
  170.     }
  171.     private static unsafe HWND CreateDesktopWindow() =>
  172.         PInvoke.CreateWindowEx(
  173.             0,                                  // Extended Style
  174.             s_windowClassName,                  // name of window class
  175.             s_defaultWindowTitle,               // title-bar string
  176.             WINDOW_STYLE.WS_OVERLAPPEDWINDOW | WINDOW_STYLE.WS_VISIBLE,  // top-level window
  177.             int.MinValue,                       // default horizontal position
  178.             (int)SHOW_WINDOW_CMD.SW_HIDE,       // If the y parameter is some other value,
  179.                                                 // then the window manager calls ShowWindow with that value as the nCmdShow parameter
  180.             int.MinValue,                       // default width
  181.             int.MinValue,                       // default height
  182.             new HWND(),                         // no owner window
  183.             null,                               // use class menu
  184.             new DefaultSafeHandle(g_hInstance),
  185.             null);
  186.     private partial class DefaultSafeHandle(nint invalidHandleValue, bool ownsHandle) : SafeHandle(invalidHandleValue, ownsHandle)
  187.     {
  188.         public DefaultSafeHandle(nint handle) : this(handle, true) => SetHandle(handle);
  189.         public override bool IsInvalid => handle != nint.Zero;
  190.         protected override bool ReleaseHandle() => true;
  191.     }
  192. }
  193. public partial class DesktopWindow
  194. {
  195.     /// <summary>
  196.     /// Create a new <see cref="DesktopWindow"/> instance.
  197.     /// </summary>
  198.     /// <param name="launched">Do something after <see cref="DesktopWindowXamlSource"/> created.</param>
  199.     /// <returns>The new instance of <see cref="DesktopWindow"/>.</returns>
  200.     public static Task<DesktopWindow> CreateAsync(Action<DesktopWindowXamlSource> launched)
  201.     {
  202.         TaskCompletionSource<DesktopWindow> taskCompletionSource = new();
  203.         new Thread(() =>
  204.         {
  205.             try
  206.             {
  207.                 DesktopWindowXamlSource source;
  208.                 using (HookWindowingModel hook = new())
  209.                 {
  210.                     source = new DesktopWindowXamlSource();
  211.                 }
  212.                 DesktopWindow window = new() { WindowXamlSource = source };
  213.                 launched(source);
  214.                 taskCompletionSource.SetResult(window);
  215.                 MSG msg = new();
  216.                 while (msg.message != PInvoke.WM_QUIT)
  217.                 {
  218.                     if (PInvoke.PeekMessage(out msg, new HWND(), 0, 0, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
  219.                     {
  220.                         _ = PInvoke.DispatchMessage(msg);
  221.                     }
  222.                 }
  223.             }
  224.             catch (Exception e)
  225.             {
  226.                 taskCompletionSource.SetException(e);
  227.             }
  228.         })
  229.         {
  230.             Name = nameof(DesktopWindowXamlSource)
  231.         }.Start();
  232.         return taskCompletionSource.Task;
  233.     }
  234. }
复制代码
这样创建的窗口还存在一些问题,但是我不知道该怎么办理,所以该方法还是仅供参考。
末了附上示例应用:https://github.com/wherewhere/CoreAppUWP/tree/wuxc
MUXC 篇:【UWP】让 UWP 本身托管本身 —— Windows App SDK 篇
(由于微软迟迟不正式发布 .NET Core App 对 UWP 的支持,所以本文章实际仍处于未完成状态,源码也暂时未上传至 Github,作者会在微软正式发布之后完善文章并更新源码。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表