ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【UWP】在 UWP 中使用 Windows App SDK [打印本页]

作者: 美食家大橙子    时间: 2024-10-18 14:32
标题: 【UWP】在 UWP 中使用 Windows App SDK
众所周知,WAS (Windows App SDK,俗称 WinUI3)在刚开始是支持 UWP 的,甚至最早只支持 UWP,但是微软在正式版发布前删除了对 UWP 的支持,不外真的删除了吗?初生之鸟2023年10月发现在 VS 调试下无视报错继承运行可以正常在 UWP 加载 WAS。随着 WAS 的开源,WAS 阻止在 UWP 上运行的缘故原由也被找到,至此各人终于找到在 UWP 上使用 WAS 的方法了。
WAS 阻止在 UWP 上运行的方法很简单,就是检查注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WinUI\Xaml\EnableUWPWindow是否为00000001,如果不是就直接报错。
Window_Partial.cpp#L80-L114
  1. // ----------------------------------------------------------------------
  2. //                               IWindow
  3. // ----------------------------------------------------------------------
  4. Window::Window()
  5. {
  6.     // The first window created internally by DXamlCore _must_ be a UWP Window.  DXamlCore
  7.     // requires and controls the lifetime of a hidden UWP Microsoft.UI.Xaml.Window.
  8.     // note that this Window instance will be the 'real' window for UWP instances, but
  9.     // serves as a dummy for all other instances. dummy behavior is deprecated and being removed.
  10.     auto dxamlCore = DXamlCore::GetCurrent();
  11.     Window* window = dxamlCore->GetDummyWindowNoRef();
  12.     if (!window)
  13.     {
  14.         // Do a runtime check to see if UWP should be enabled
  15.         static auto runtimeEnabledFeatureDetector = RuntimeFeatureBehavior::GetRuntimeEnabledFeatureDetector();
  16.         auto UWPWindowEnabled = runtimeEnabledFeatureDetector->IsFeatureEnabled(RuntimeEnabledFeature::EnableUWPWindow);
  17.         // WinUI UWP
  18.         if (!UWPWindowEnabled && DXamlCore::GetCurrent()->GetHandle()->GetInitializationType() != InitializationType::IslandsOnly)
  19.         {
  20.             ::RoOriginateError(
  21.                 E_NOT_SUPPORTED,
  22.                 wrl_wrappers::HStringReference(
  23.                 L"WinUI: Error creating an UWP Window. Creating an UWP window is not allowed."
  24.                 ).Get());
  25.             XAML_FAIL_FAST();
  26.         }
  27.         m_spWindowImpl = std::make_shared<UWPWindowImpl>(this);
  28.     }
  29.     else
  30.     {
  31.         m_spWindowImpl = std::make_shared<DesktopWindowImpl>(this);
  32.     }
  33. }
复制代码
Window_Partial.cpp#L80-L114
  1. { L"EnableUWPWindow", RuntimeEnabledFeature::EnableUWPWindow, false, 0, 0 }
复制代码
所以我们只需要修改注册表就行了。
  1. Windows Registry Editor Version 5.00
  2. [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WinUI\Xaml]
  3. "EnableUWPWindow"=dword:00000001
复制代码
但是随处修改注册表并不是一个好主意,于是初生之鸟便提出利用Detours来劫持读取注册表的方法:HookCoreAppWinUI
我们将其翻译成 C#,再加一些小修改,便能得出如下内容:
  1. #r "nuget:Detours.Win32Metadata"
  2. #r "nuget:Microsoft.Windows.CsWin32"
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7. using Windows.Win32;
  8. using Windows.Win32.Foundation;
  9. using Windows.Win32.System.Registry;
  10. using Detours = Microsoft.Detours.PInvoke;
  11. /// <summary>
  12. /// Represents a hook for getting the value of the <c>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WinUI\Xaml\EnableUWPWindow</c> registry key always returning <see langword="00000001"/>.
  13. /// </summary>
  14. public partial class HookRegistry : IDisposable
  15. {
  16.     /// <summary>
  17.     /// The value that indicates whether the class has been disposed.
  18.     /// </summary>
  19.     private bool disposed;
  20.     /// <summary>
  21.     /// The reference count for the hook.
  22.     /// </summary>
  23.     private static int refCount;
  24.     /// <summary>
  25.     /// The dictionary that maps the <see cref="HKEY"/> to a value that indicates whether the key is a real key.
  26.     /// </summary>
  27.     private static readonly Dictionary<HKEY, bool> xamlKeyMap = [];
  28.     /// <summary>
  29.     /// The object used to synchronize access to the <see cref="xamlKeyMap"/> dictionary.
  30.     /// </summary>
  31.     private static readonly object locker = new();
  32.     /// <remarks>The original <see cref="PInvoke.RegOpenKeyEx(HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*)"/> function.</remarks>
  33.     /// <inheritdoc cref="PInvoke.RegOpenKeyEx(HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*)"/>
  34.     private static unsafe delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> RegOpenKeyExW;
  35.     /// <remarks>The original <see cref="PInvoke.RegCloseKey(HKEY)"/> function.</remarks>
  36.     /// <inheritdoc cref="PInvoke.RegCloseKey(HKEY)"/>
  37.     private static unsafe delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> RegCloseKey;
  38.     /// <remarks>The original <see cref="PInvoke.RegQueryValueEx(HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*)"/> function.</remarks>
  39.     /// <inheritdoc cref="PInvoke.RegQueryValueEx(HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*)"/>
  40.     private static unsafe delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> RegQueryValueExW;
  41.     /// <summary>
  42.     /// Initializes a new instance of the <see cref="HookRegistry"/> class.
  43.     /// </summary>
  44.     public HookRegistry()
  45.     {
  46.         refCount++;
  47.         StartHook();
  48.     }
  49.     /// <summary>
  50.     /// Finalizes this instance of the <see cref="HookRegistry"/> class.
  51.     /// </summary>
  52.     ~HookRegistry()
  53.     {
  54.         Dispose();
  55.     }
  56.     /// <summary>
  57.     /// Gets the value that indicates whether the hook is active.
  58.     /// </summary>
  59.     public static bool IsHooked { get; private set; }
  60.     /// <summary>
  61.     /// Starts the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
  62.     /// </summary>
  63.     private static unsafe void StartHook()
  64.     {
  65.         if (!IsHooked)
  66.         {
  67.             using FreeLibrarySafeHandle library = PInvoke.GetModuleHandle("ADVAPI32.dll");
  68.             if (!library.IsInvalid
  69.                 && NativeLibrary.TryGetExport(library.DangerousGetHandle(), "RegOpenKeyExW", out nint regOpenKeyExW)
  70.                 && NativeLibrary.TryGetExport(library.DangerousGetHandle(), nameof(PInvoke.RegCloseKey), out nint regCloseKey)
  71.                 && NativeLibrary.TryGetExport(library.DangerousGetHandle(), "RegQueryValueExW", out nint regQueryValueExW))
  72.             {
  73.                 void* regOpenKeyExWPtr = (void*)regOpenKeyExW;
  74.                 void* regCloseKeyPtr = (void*)regCloseKey;
  75.                 void* regQueryValueExWPtr = (void*)regQueryValueExW;
  76.                 delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> overrideRegOpenKeyExW = &OverrideRegOpenKeyExW;
  77.                 delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> overrideRegCloseKey = &OverrideRegCloseKey;
  78.                 delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> overrideRegQueryValueExW = &OverrideRegQueryValueExW;
  79.                 _ = Detours.DetourRestoreAfterWith();
  80.                 _ = Detours.DetourTransactionBegin();
  81.                 _ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
  82.                 _ = Detours.DetourAttach(ref regOpenKeyExWPtr, overrideRegOpenKeyExW);
  83.                 _ = Detours.DetourAttach(ref regCloseKeyPtr, overrideRegCloseKey);
  84.                 _ = Detours.DetourAttach(ref regQueryValueExWPtr, overrideRegQueryValueExW);
  85.                 _ = Detours.DetourTransactionCommit();
  86.                 RegOpenKeyExW = (delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR>)regOpenKeyExWPtr;
  87.                 RegCloseKey = (delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR>)regCloseKeyPtr;
  88.                 RegQueryValueExW = (delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR>)regQueryValueExWPtr;
  89.                 IsHooked = true;
  90.             }
  91.         }
  92.     }
  93.     /// <summary>
  94.     /// Ends the hook for the <see cref="PInvoke.AppPolicyGetWindowingModel(HANDLE, AppPolicyWindowingModel*)"/> function.
  95.     /// </summary>
  96.     public static unsafe void EndHook()
  97.     {
  98.         if (--refCount == 0 && IsHooked)
  99.         {
  100.             void* regOpenKeyExWPtr = RegOpenKeyExW;
  101.             void* regCloseKeyPtr = RegCloseKey;
  102.             void* regQueryValueExWPtr = RegQueryValueExW;
  103.             delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*, WIN32_ERROR> overrideRegOpenKeyExW = &OverrideRegOpenKeyExW;
  104.             delegate* unmanaged[Stdcall]<HKEY, WIN32_ERROR> overrideRegCloseKey = &OverrideRegCloseKey;
  105.             delegate* unmanaged[Stdcall]<HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*, WIN32_ERROR> overrideRegQueryValueExW = &OverrideRegQueryValueExW;
  106.             _ = Detours.DetourTransactionBegin();
  107.             _ = Detours.DetourUpdateThread(PInvoke.GetCurrentThread());
  108.             _ = Detours.DetourDetach(&regOpenKeyExWPtr, overrideRegOpenKeyExW);
  109.             _ = Detours.DetourDetach(&regCloseKeyPtr, overrideRegCloseKey);
  110.             _ = Detours.DetourDetach(&regQueryValueExWPtr, overrideRegQueryValueExW);
  111.             _ = Detours.DetourTransactionCommit();
  112.             RegOpenKeyExW = null;
  113.             RegCloseKey = null;
  114.             RegQueryValueExW = null;
  115.             IsHooked = false;
  116.         }
  117.     }
  118.     /// <remarks>The overridden <see cref="PInvoke.RegOpenKeyEx(HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*)"/> function.</remarks>
  119.     /// <inheritdoc cref="PInvoke.RegOpenKeyEx(HKEY, PCWSTR, uint, REG_SAM_FLAGS, HKEY*)"/>
  120.     [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
  121.     private static unsafe WIN32_ERROR OverrideRegOpenKeyExW(HKEY hKey, PCWSTR lpSubKey, uint ulOptions, REG_SAM_FLAGS samDesired, HKEY* phkResult)
  122.     {
  123.         WIN32_ERROR result = RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
  124.         if (hKey == HKEY.HKEY_LOCAL_MACHINE && lpSubKey.ToString().Equals(@"Software\Microsoft\WinUI\Xaml", StringComparison.OrdinalIgnoreCase))
  125.         {
  126.             if (result == WIN32_ERROR.ERROR_FILE_NOT_FOUND)
  127.             {
  128.                 HKEY key = new(HANDLE.INVALID_HANDLE_VALUE);
  129.                 xamlKeyMap[key] = false;
  130.                 *phkResult = key;
  131.                 result = WIN32_ERROR.ERROR_SUCCESS;
  132.             }
  133.             else if (result == WIN32_ERROR.ERROR_SUCCESS)
  134.             {
  135.                 xamlKeyMap[*phkResult] = true;
  136.             }
  137.         }
  138.         return result;
  139.     }
  140.     /// <remarks>The overridden <see cref="PInvoke.RegCloseKey(HKEY)"/> function.</remarks>
  141.     /// <inheritdoc cref="PInvoke.RegCloseKey(HKEY)"/>
  142.     [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
  143.     private static unsafe WIN32_ERROR OverrideRegCloseKey(HKEY hKey)
  144.     {
  145.         bool isXamlKey;
  146.         lock (locker)
  147.         {
  148.             if (isXamlKey = xamlKeyMap.TryGetValue(hKey, out bool isRealKey))
  149.             {
  150.                 xamlKeyMap.Remove(hKey);
  151.             }
  152.             return isXamlKey
  153.                 ? isRealKey
  154.                     ? RegCloseKey(hKey) // real key
  155.                     : WIN32_ERROR.ERROR_SUCCESS // simulated key
  156.                 : hKey == HANDLE.INVALID_HANDLE_VALUE
  157.                     ? WIN32_ERROR.ERROR_INVALID_HANDLE
  158.                     : RegCloseKey(hKey);
  159.         }
  160.     }
  161.     /// <remarks>The overridden <see cref="PInvoke.RegQueryValueEx(HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*)"/> function.</remarks>
  162.     /// <inheritdoc cref="PInvoke.RegQueryValueEx(HKEY, PCWSTR, uint*, REG_VALUE_TYPE*, byte*, uint*)"/>
  163.     [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
  164.     private static unsafe WIN32_ERROR OverrideRegQueryValueExW(HKEY hKey, PCWSTR lpValueName, [Optional] uint* lpReserved, [Optional] REG_VALUE_TYPE* lpType, [Optional] byte* lpData, [Optional] uint* lpcbData)
  165.     {
  166.         if (lpValueName.Value != default && lpValueName.ToString().Equals("EnableUWPWindow", StringComparison.OrdinalIgnoreCase))
  167.         {
  168.             lock (locker)
  169.             {
  170.                 if (xamlKeyMap.TryGetValue(hKey, out bool isRealKey))
  171.                 {
  172.                     WIN32_ERROR result;
  173.                     if (isRealKey)
  174.                     {
  175.                         // real key
  176.                         result = RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
  177.                         if (result == WIN32_ERROR.ERROR_SUCCESS && lpData != default)
  178.                         {
  179.                             *lpData = 1;
  180.                         }
  181.                         else if (result == WIN32_ERROR.ERROR_FILE_NOT_FOUND)
  182.                         {
  183.                             if (lpData == default && lpcbData != default)
  184.                             {
  185.                                 *lpcbData = sizeof(int);
  186.                                 result = WIN32_ERROR.ERROR_SUCCESS;
  187.                             }
  188.                             else if (lpData != default && lpcbData != default)
  189.                             {
  190.                                 if (*lpcbData >= sizeof(int))
  191.                                 {
  192.                                     *lpData = 1;
  193.                                     result = WIN32_ERROR.ERROR_SUCCESS;
  194.                                 }
  195.                                 else
  196.                                 {
  197.                                     result = WIN32_ERROR.ERROR_MORE_DATA;
  198.                                 }
  199.                             }
  200.                         }
  201.                     }
  202.                     else
  203.                     {
  204.                         // simulated key
  205.                         result = WIN32_ERROR.ERROR_FILE_NOT_FOUND;
  206.                         if (lpData == default && lpcbData != default)
  207.                         {
  208.                             *lpcbData = sizeof(int);
  209.                             result = WIN32_ERROR.ERROR_SUCCESS;
  210.                         }
  211.                         else if (lpData != default && lpcbData != default)
  212.                         {
  213.                             if (*lpcbData >= sizeof(int))
  214.                             {
  215.                                 *lpData = 1;
  216.                                 result = WIN32_ERROR.ERROR_SUCCESS;
  217.                             }
  218.                             else
  219.                             {
  220.                                 result = WIN32_ERROR.ERROR_MORE_DATA;
  221.                             }
  222.                         }
  223.                     }
  224.                     return result;
  225.                 }
  226.             }
  227.         }
  228.         return RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
  229.     }
  230.     /// <inheritdoc/>
  231.     public void Dispose()
  232.     {
  233.         if (!disposed && IsHooked)
  234.         {
  235.             EndHook();
  236.         }
  237.         GC.SuppressFinalize(this);
  238.         disposed = true;
  239.     }
  240. }
复制代码
随后我们只需要在入口点创建App时进行劫持就行了。
  1. private static bool IsSupportCoreWindow
  2. {
  3.     get
  4.     {
  5.         try
  6.         {
  7.             RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\WinUI\Xaml");
  8.             return registryKey?.GetValue("EnableUWPWindow") is > 0;
  9.         }
  10.         catch
  11.         {
  12.             return false;
  13.         }
  14.     }
  15. }
  16. private static void Main()
  17. {
  18.     ComWrappersSupport.InitializeComWrappers();
  19.     HookRegistry hookRegistry = null;
  20.     try
  21.     {
  22.         if (!IsSupportCoreWindow)
  23.         {
  24.             hookRegistry = new HookRegistry();
  25.         }
  26.         XamlCheckProcessRequirements();
  27.         Application.Start(p =>
  28.         {
  29.             DispatcherQueueSynchronizationContext context = new(DispatcherQueue.GetForCurrentThread());
  30.             SynchronizationContext.SetSynchronizationContext(context);
  31.             _ = new App();
  32.         });
  33.     }
  34.     finally
  35.     {
  36.         hookRegistry?.Dispose();
  37.     }
  38. }
复制代码
固然想要自界说入口函数,我们需要在csproj加上界说。
  1. <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
复制代码
同时还要记得在清单中明白入口点。
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Package ...>
  3.   ...
  4.   <Applications>
  5.     <Application ...
  6.       EntryPoint="明确的入口点">
  7.       ...
  8.     </Application>
  9.   </Applications>
  10.   ...
  11. </Package>
复制代码
随后我们就可以正常的使用 UWP/WAS 了。
最后附上示例应用:https://github.com/wherewhere/CoreAppUWP/tree/muxc

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




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4