diff --git a/.gitignore b/.gitignore
index ab792ee69..22296c86c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ ReleaseHant/
/output/*.ime
/output/*.txt
/output/*.bat
+/output/*.config
/output/data/essay.txt
/output/data/opencc/
/output/data/*.yaml
@@ -44,4 +45,5 @@ arm64x_wrapper/*.o
arm64x_wrapper/*.dll
arm64x_wrapper/*.ime
arm64x_wrapper/*.lib
-arm64x_wrapper/*.exp
\ No newline at end of file
+arm64x_wrapper/*.exp
+/packages
\ No newline at end of file
diff --git a/Weasel.Setup/Localization/Resources.Designer.cs b/Weasel.Setup/Localization/Resources.Designer.cs
new file mode 100644
index 000000000..5ec4ca74d
--- /dev/null
+++ b/Weasel.Setup/Localization/Resources.Designer.cs
@@ -0,0 +1,117 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace Weasel.Setup.Localization {
+ using System;
+
+
+ ///
+ /// 一个强类型的资源类,用于查找本地化的字符串等。
+ ///
+ // 此类是由 StronglyTypedResourceBuilder
+ // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+ // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+ // (以 /str 作为命令选项),或重新生成 VS 项目。
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// 返回此类使用的缓存的 ResourceManager 实例。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Weasel.Setup.Localization.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 重写当前线程的 CurrentUICulture 属性,对
+ /// 使用此强类型资源类的所有资源查找执行重写。
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// 查找类似 Error 的本地化字符串。
+ ///
+ internal static string STR_ERROR {
+ get {
+ return ResourceManager.GetString("STR_ERROR", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Install 的本地化字符串。
+ ///
+ internal static string STR_INSTALL {
+ get {
+ return ResourceManager.GetString("STR_INSTALL", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Installation success 的本地化字符串。
+ ///
+ internal static string STR_INSTALL_SUCCESS {
+ get {
+ return ResourceManager.GetString("STR_INSTALL_SUCCESS", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 OK 的本地化字符串。
+ ///
+ internal static string STR_OK {
+ get {
+ return ResourceManager.GetString("STR_OK", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Change the user folder location successfully 的本地化字符串。
+ ///
+ internal static string STR_UF_CHANGE_SUCCESS {
+ get {
+ return ResourceManager.GetString("STR_UF_CHANGE_SUCCESS", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Uninstall successfully 的本地化字符串。
+ ///
+ internal static string STR_UNINSTALL_SUCCESS {
+ get {
+ return ResourceManager.GetString("STR_UNINSTALL_SUCCESS", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Weasel.Setup/Localization/Resources.resx b/Weasel.Setup/Localization/Resources.resx
new file mode 100644
index 000000000..6c89c2f2f
--- /dev/null
+++ b/Weasel.Setup/Localization/Resources.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Install
+
+
+ OK
+
+
+ Error
+
+
+ Installation success
+
+
+ Uninstall successfully
+
+
+ Change the user folder location successfully
+
+
\ No newline at end of file
diff --git a/Weasel.Setup/Localization/Resources.zh-Hans.resx b/Weasel.Setup/Localization/Resources.zh-Hans.resx
new file mode 100644
index 000000000..5132eddce
--- /dev/null
+++ b/Weasel.Setup/Localization/Resources.zh-Hans.resx
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 安装
+
+
+ 确定
+
+
+ 出错了
+
+
+ 可以使【小狼毫】写字了 :)
+
+
+ 小狼毫 :)
+
+
+ 更改用户文件夹位置成功 :)
+
+
diff --git a/Weasel.Setup/Localization/Resources.zh-Hant.resx b/Weasel.Setup/Localization/Resources.zh-Hant.resx
new file mode 100644
index 000000000..ba779971d
--- /dev/null
+++ b/Weasel.Setup/Localization/Resources.zh-Hant.resx
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 安裝
+
+
+ 確認
+
+
+ 出錯了
+
+
+ 可以使【小狼毫】寫字了 :)
+
+
+ 小狼毫 :)
+
+
+ 更改用戶資料夾位置成功 :)
+
+
\ No newline at end of file
diff --git a/Weasel.Setup/PInvoke.cs b/Weasel.Setup/PInvoke.cs
new file mode 100644
index 000000000..c0e98d403
--- /dev/null
+++ b/Weasel.Setup/PInvoke.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Weasel.Setup
+{
+ internal class PInvoke
+ {
+ [Flags]
+ public enum ILOT
+ {
+ ILOT_UNINSTALL = 0x00000001,
+ }
+
+ [DllImport("input.dll", SetLastError = false, ExactSpelling = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool InstallLayoutOrTip([In, MarshalAs(UnmanagedType.LPWStr)] string psz, [In] ILOT dwFlags);
+
+
+ [Flags]
+ public enum MoveFileFlags
+ {
+ MOVEFILE_REPLACE_EXISTING = 0x1,
+ MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
+ }
+
+
+ [DllImport("kernel32.dll", EntryPoint = "MoveFileExW", SetLastError = true, CharSet = CharSet.Unicode)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool MoveFileEx(string src, string dest, MoveFileFlags flags);
+
+ [Flags]
+ public enum PROCESS_DPI_AWARENESS
+ {
+ PROCESS_DPI_UNAWARE = 0,
+ PROCESS_SYSTEM_DPI_AWARE,
+ PROCESS_PER_MONITOR_DPI_AWARE,
+ }
+
+ [DllImport("Shcore.dll", SetLastError = true)]
+ public static extern int SetProcessDpiAwareness(PROCESS_DPI_AWARENESS type);
+ }
+}
diff --git a/Weasel.Setup/Program.cs b/Weasel.Setup/Program.cs
new file mode 100644
index 000000000..a87ed2c7e
--- /dev/null
+++ b/Weasel.Setup/Program.cs
@@ -0,0 +1,214 @@
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Security.Principal;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Weasel.Setup
+{
+ internal class Program
+ {
+ private static bool IsRunAsAdmin()
+ {
+ var identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+
+ private static void RunAsAdmin(string arg)
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = Application.ExecutablePath,
+ Arguments = arg,
+ Verb = "RunAs",
+ UseShellExecute = true,
+ };
+ Process.Start(info);
+ }
+
+ ///
+ /// 应用程序的主入口点。
+ ///
+ [STAThread]
+ static void Main(string[] args)
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+
+ Utils.App.SetProcessApiAwareness();
+
+ var appCulture = Utils.App.CultureInfo;
+ Thread.CurrentThread.CurrentCulture = appCulture;
+ Thread.CurrentThread.CurrentUICulture = appCulture;
+
+ Run(string.Join(" ", args));
+ }
+
+ private static readonly string WEASEL_PROG_REG_KEY = @"SOFTWARE\Rime\Weasel";
+ private static readonly string WEASEL_UPDATE_REG_KEY = $@"{WEASEL_PROG_REG_KEY}\Updates";
+
+ private static void Run(string arg)
+ {
+ try
+ {
+ if (arg.StartsWith("/userdir:")) // 设置用户目录
+ {
+ var dir = arg.Substring(arg.IndexOf(':') + 1);
+ if (dir != null)
+ {
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\RimeUserDir", dir);
+ }
+ return;
+ }
+ else
+ {
+ switch (arg) // 无需管理员权限的操作
+ {
+ case "/ls": // 简体中文
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\Language", "chs");
+ return;
+ case "/lt": // 繁体中文
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\Language", "cht");
+ return;
+ case "/le": // 英语
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\Language", "eng");
+ return;
+ case "/eu": // 启用更新
+ Registry.CurrentUser.SetValue($@"{WEASEL_UPDATE_REG_KEY}\CheckForUpdates", "1");
+ return;
+ case "/du": // 禁用更新
+ Registry.CurrentUser.SetValue($@"{WEASEL_UPDATE_REG_KEY}\CheckForUpdates", "0");
+ return;
+ case "/toggleime":
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\ToggleImeOnOpenClose", "yes");
+ return;
+ case "/toggleascii":
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\ToggleImeOnOpenClose", "no");
+ return;
+ case "/testing": // 测试通道
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\UpdateChannel", "testing");
+ return;
+ case "/release": // 正式通道
+ Registry.CurrentUser.SetValue($@"{WEASEL_PROG_REG_KEY}\UpdateChannel", "release");
+ return;
+ default: // 需要管理员权限的操作
+ if (!IsRunAsAdmin())
+ {
+ RunAsAdmin(arg);
+ Application.Exit();
+ return;
+ }
+ switch (arg)
+ {
+ case "/u": // 卸载
+ Setup.Uninstall(true);
+ return;
+ case "/s": // 简体中文安装
+ Setup.NormalInstall(false);
+ return;
+ case "/t": // 繁体中文安装
+ Setup.NormalInstall(true);
+ return;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ // 自定义安装
+ CustomInstall(arg == "/i");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, Localization.Resources.STR_ERROR);
+ }
+ }
+
+ private static void CustomInstall(bool isInstalling)
+ {
+ var isSilentMode = false;
+ var isInstalled = Setup.IsWeaselInstalled;
+
+ bool isHant = false;
+ string userDir = string.Empty;
+ using (var user = Registry.CurrentUser.OpenSubKey(WEASEL_PROG_REG_KEY))
+ {
+ if (user != null)
+ {
+ userDir = Convert.ToString(user.GetValue("RimeUserDir"));
+ var value = user.GetValue("Hant");
+ if (value != null && value is int)
+ {
+ isHant = Convert.ToBoolean(value);
+ if (isInstalling) isSilentMode = true;
+ }
+ }
+ }
+
+ if (!isSilentMode)
+ {
+ var dialog = new SetupOptionDialog
+ {
+ IsInstalled = isInstalled,
+ IsHant = isHant,
+ UserDir = userDir
+ };
+ if (DialogResult.OK == dialog.ShowDialog())
+ {
+ isInstalled = dialog.IsInstalled;
+ isHant = dialog.IsHant;
+ userDir = dialog.UserDir;
+
+ using (var user = Registry.CurrentUser.CreateSubKey(WEASEL_PROG_REG_KEY))
+ {
+ user.SetValue($@"{WEASEL_PROG_REG_KEY}\RimeUserDir", userDir);
+ user.SetValue($@"{WEASEL_PROG_REG_KEY}\Hant", isHant ? 1 : 0);
+ }
+ }
+ else
+ {
+ if (!isInstalling)
+ {
+ Application.Exit();
+ return;
+ }
+ }
+ }
+ if (!isInstalled)
+ {
+ Setup.NormalInstall(isHant, isSilentMode);
+ }
+ else
+ {
+ var installDir = Path.GetDirectoryName(Application.ExecutablePath); ;
+ Task.Run(() =>
+ {
+ ExecProcess(Path.Combine(installDir, "WeaselServer.exe"), "/q");
+ Task.Delay(500);
+
+ ExecProcess(Path.Combine(installDir, "WeaselServer.exe"), string.Empty);
+ Task.Delay(500);
+
+ ExecProcess(Path.Combine(installDir, "WeaselDeployer.exe"), "/deploy");
+ });
+ MessageBox.Show(Localization.Resources.STR_UF_CHANGE_SUCCESS);
+ }
+ }
+
+ private static void ExecProcess(string path, string args)
+ {
+ var info = new ProcessStartInfo
+ {
+ FileName = path,
+ Arguments = args,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Normal,
+ };
+ Process.Start(info);
+ }
+ }
+}
diff --git a/Weasel.Setup/Setup.cs b/Weasel.Setup/Setup.cs
new file mode 100644
index 000000000..2d1d71a30
--- /dev/null
+++ b/Weasel.Setup/Setup.cs
@@ -0,0 +1,273 @@
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using TSF.InteropTypes;
+using TSF.TypeLib;
+
+namespace Weasel.Setup
+{
+ public static class Setup
+ {
+ private static readonly Guid CLSID_TEXT_SERVICE;
+ private static readonly Guid GUID_PROFILE;
+
+ private static readonly string PSZTITLE_HANS;
+ private static readonly string PSZTITLE_HANT;
+
+ private static readonly string RIME_ROOT_REG_KEY;
+ private static readonly string WEASEL_PROG_REG_KEY;
+ private static readonly string WEASEL_SERVER_EXE;
+ private static readonly string WEASEL_WER_REG_KEY;
+
+ static Setup()
+ {
+ CLSID_TEXT_SERVICE = Guid.Parse("{A3F4CDED-B1E9-41EE-9CA6-7B4D0DE6CB0A}");
+ GUID_PROFILE = Guid.Parse("{3D02CAB6-2B8E-4781-BA20-1C9267529467}");
+
+ PSZTITLE_HANS = $"0804:{CLSID_TEXT_SERVICE:B}{GUID_PROFILE:B}";
+ PSZTITLE_HANT = $"0404:{CLSID_TEXT_SERVICE:B}{GUID_PROFILE:B}";
+
+ RIME_ROOT_REG_KEY = @"SOFTWARE\Rime";
+ WEASEL_PROG_REG_KEY = $@"{RIME_ROOT_REG_KEY}\Weasel";
+ WEASEL_SERVER_EXE = "WeaselServer.exe";
+
+ WEASEL_WER_REG_KEY = $@"SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\{WEASEL_SERVER_EXE}";
+ }
+
+ public static bool IsWeaselInstalled
+ {
+ get
+ {
+ var sys32Dir = Environment.GetFolderPath(Environment.SpecialFolder.System);
+ var weaselPath = Path.Combine(sys32Dir, "weasel.dll");
+ return File.Exists(weaselPath);
+ }
+ }
+
+ public static void NormalInstall(bool isHant, bool isSilentMode = true)
+ {
+ // 安装 IME 文件
+ var productDir = Path.GetDirectoryName(Application.ExecutablePath);
+ var imeSrcPath = Path.Combine(productDir, "weasel.dll");
+ InstallImeFiles(imeSrcPath, (libPath) =>
+ {
+ UpdateServiceState(libPath, true, isHant);
+ });
+
+ // 登记注册表
+ using (var local = Registry.LocalMachine.CreateSubKey(WEASEL_PROG_REG_KEY))
+ {
+ local.SetValue("WeaselRoot", productDir);
+ local.SetValue("ServerExecutable", WEASEL_SERVER_EXE);
+ }
+
+ // 启用键盘布局和文本服务
+ var psz = isHant ? PSZTITLE_HANT : PSZTITLE_HANS;
+ PInvoke.InstallLayoutOrTip(psz, 0);
+
+ // 收集用户模式转储
+ // https://learn.microsoft.com/zh-cn/windows/win32/wer/collecting-user-mode-dumps
+ using (var local = Registry.LocalMachine.CreateSubKey(WEASEL_WER_REG_KEY))
+ {
+ local.SetValue("DumpFolder", Utils.LogPath);
+ local.SetValue("DumpType", 0);
+ local.SetValue("CustomDumpFlags", 0);
+ local.SetValue("DumpCount", 10);
+ }
+
+ if (!isSilentMode)
+ {
+ MessageBox.Show(Localization.Resources.STR_INSTALL_SUCCESS);
+ }
+ }
+
+ public static void Uninstall(bool isSilentMode)
+ {
+ // 停用键盘布局和文本服务
+ var isHant = Convert.ToBoolean(
+ Registry.CurrentUser.GetValue($@"{WEASEL_PROG_REG_KEY}\Hant")
+ );
+ var psz = isHant ? PSZTITLE_HANT : PSZTITLE_HANS;
+ PInvoke.InstallLayoutOrTip(psz, PInvoke.ILOT.ILOT_UNINSTALL);
+
+ UninstallImeFiles("weasel.dll", (imePath) =>
+ {
+ UpdateServiceState(imePath, enable: false, isHant);
+ });
+
+ // 清理注册表
+ using (var local = Registry.LocalMachine)
+ {
+ local.DeleteSubKeyTree(RIME_ROOT_REG_KEY, throwOnMissingSubKey: false);
+ local.DeleteSubKey(WEASEL_WER_REG_KEY, throwOnMissingSubKey: false);
+ }
+
+ if (!isSilentMode)
+ {
+ MessageBox.Show(Localization.Resources.STR_UNINSTALL_SUCCESS);
+ }
+ }
+
+ private static void InstallImeFiles(string srcPath, Action updateService)
+ {
+ var baseName = Path.GetFileName(srcPath);
+ var sys32Dir = Environment.GetFolderPath(Environment.SpecialFolder.System);
+ var destPath = Path.Combine(sys32Dir, baseName);
+
+ if (Environment.Is64BitOperatingSystem)
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
+ {
+ var sysarm32Dir = Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.Windows),
+ "SysArm32"
+ );
+ if (Directory.Exists(sysarm32Dir))
+ {
+ // 如果支持 ARM32 子系统,则安装 ARM32 版本(Windows 11 24H2 之前)
+ var arm32SrcPath = srcPath.Insert(srcPath.LastIndexOf('.'), "ARM");
+ var arm32DestPath = Path.Combine(sysarm32Dir, baseName);
+ if (File.Exists(arm32SrcPath))
+ {
+ Utils.CopyFile(arm32SrcPath, arm32DestPath);
+ updateService(arm32SrcPath);
+ }
+ }
+
+ // 安装 ARM64(和 x64)版本库。
+ // ARM64 系统上进程会被重定向至 ARM64X DLL,
+ // 其加载时,ARM64 进程会被重定向到 ARM64 DLL,x64 进程会被重定向到 x64 DLL,
+ // 所以这三个库都要安装
+ var x64SrcPath = srcPath.Insert(srcPath.LastIndexOf('.'), "x64");
+ var x64DestPath = destPath.Insert(srcPath.LastIndexOf('.'), "x64");
+ if (File.Exists(x64SrcPath)) Utils.CopyFile(x64SrcPath, x64DestPath);
+
+ var arm64SrcPath = srcPath.Insert(srcPath.LastIndexOf('.'), "x64");
+ var arm64DestPath = destPath.Insert(srcPath.LastIndexOf('.'), "x64");
+ if (File.Exists(arm64SrcPath)) Utils.CopyFile(arm64SrcPath, arm64DestPath);
+
+ // ARM64X 充当重定向器(转发器),因此我们不必区分其简繁变体
+ var arm64xSrcPath = srcPath.Insert(srcPath.LastIndexOf('.'), "ARM64X");
+ if (File.Exists(arm64xSrcPath))
+ {
+ Utils.CopyFile(arm64xSrcPath, destPath);
+ updateService(destPath);
+ }
+ }
+ else
+ {
+ var sysWow64Dir = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86);
+ var wow64DestPath = Path.Combine(sysWow64Dir, baseName);
+ if (File.Exists(srcPath))
+ {
+ Utils.CopyFile(srcPath, wow64DestPath);
+ updateService(wow64DestPath);
+ }
+ var x64SrcPath = srcPath.Insert(srcPath.LastIndexOf('.'), "x64");
+ if (File.Exists(x64SrcPath))
+ {
+ Utils.CopyFile(x64SrcPath, destPath);
+ updateService(destPath);
+ }
+ }
+ }
+ else
+ {
+ if (File.Exists(srcPath))
+ {
+ File.Copy(srcPath, destPath, true);
+ updateService(destPath);
+ }
+ }
+ }
+
+ private static void UninstallImeFiles(string baseName, Action updateService)
+ {
+ var sys32Dir = Environment.GetFolderPath(Environment.SpecialFolder.System);
+ var imePath = Path.Combine(sys32Dir, baseName);
+
+ if (Environment.Is64BitOperatingSystem)
+ {
+ var sysWow64Dir = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86);
+ var wow64ImePath = Path.Combine(sysWow64Dir, baseName);
+ if (File.Exists(wow64ImePath)) Utils.DeleteFile(wow64ImePath);
+
+ if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
+ {
+ var sysarm32Dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "SysArm32");
+ if (Directory.Exists(sysarm32Dir))
+ {
+ var arm32ImePath = Path.Combine(sysarm32Dir, baseName);
+ if (File.Exists(arm32ImePath))
+ {
+ updateService(arm32ImePath);
+ Utils.DeleteFile(arm32ImePath);
+ }
+ }
+
+ var x64ImePath = imePath.Insert(imePath.LastIndexOf('.'), "x64");
+ if (File.Exists(x64ImePath)) Utils.DeleteFile(x64ImePath);
+
+ var arm64ImePath = imePath.Insert(imePath.LastIndexOf('.'), "ARM64");
+ if (File.Exists(arm64ImePath)) Utils.DeleteFile(arm64ImePath);
+ }
+ else
+ {
+ if (File.Exists(imePath))
+ {
+ updateService(imePath);
+ Utils.DeleteFile(imePath);
+ }
+ }
+ }
+ else
+ {
+ if (File.Exists(imePath))
+ {
+ updateService(imePath);
+ Utils.DeleteFile(imePath);
+ }
+ }
+ }
+
+ private static void UpdateServiceState(string libPath, bool enable, bool isHant)
+ {
+ if (!enable) UpdateProfile(false, isHant);
+ var value = isHant ? "hant" : "hans";
+ Environment.SetEnvironmentVariable("TEXTSERVICE_PROFILE", value);
+ var sysarm32Dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "SysArm32");
+ var regsvr32Path = Directory.Exists(sysarm32Dir)
+ ? Path.Combine(sysarm32Dir, "regsvr32.exe")
+ : "regsvr32.exe";
+ var args = enable ? $"/s \"{libPath}\"" : $"/s /u \"{libPath}\"";
+ var updateInfo = new ProcessStartInfo
+ {
+ FileName = regsvr32Path,
+ Arguments = args,
+ Verb = "open",
+ UseShellExecute = true,
+ };
+ Process.Start(updateInfo).WaitForExit();
+
+ if (enable) UpdateProfile(true, isHant);
+ }
+
+ private static void UpdateProfile(bool enable, bool isHant)
+ {
+ var langId = isHant ? new LangID(0x0404) : new LangID(0x0804);
+ var profiles = new ITfInputProcessorProfiles();
+ if (enable)
+ {
+ profiles.EnableLanguageProfile(CLSID_TEXT_SERVICE, langId, GUID_PROFILE, enable);
+ profiles.EnableLanguageProfileByDefault(CLSID_TEXT_SERVICE, langId, GUID_PROFILE, out _);
+ }
+ else
+ {
+ profiles.RemoveLanguageProfile(CLSID_TEXT_SERVICE, langId, GUID_PROFILE);
+ }
+ }
+ }
+}
diff --git a/Weasel.Setup/SetupOptionDialog.Designer.cs b/Weasel.Setup/SetupOptionDialog.Designer.cs
new file mode 100644
index 000000000..fa482e4fc
--- /dev/null
+++ b/Weasel.Setup/SetupOptionDialog.Designer.cs
@@ -0,0 +1,156 @@
+namespace Weasel.Setup
+{
+ partial class SetupOptionDialog
+ {
+ ///
+ /// 必需的设计器变量。
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// 清理所有正在使用的资源。
+ ///
+ /// 如果应释放托管资源,为 true;否则为 false。
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows 窗体设计器生成的代码
+
+ ///
+ /// 设计器支持所需的方法 - 不要修改
+ /// 使用代码编辑器修改此方法的内容。
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SetupOptionDialog));
+ this.inputLangGroup = new System.Windows.Forms.GroupBox();
+ this.chtRadio = new System.Windows.Forms.RadioButton();
+ this.chsRadio = new System.Windows.Forms.RadioButton();
+ this.userFolderGroup = new System.Windows.Forms.GroupBox();
+ this.selectButton = new System.Windows.Forms.Button();
+ this.customPathBox = new System.Windows.Forms.TextBox();
+ this.customFolderRadio = new System.Windows.Forms.RadioButton();
+ this.defaultFolderRadio = new System.Windows.Forms.RadioButton();
+ this.confirmButton = new System.Windows.Forms.Button();
+ this.removeButton = new System.Windows.Forms.Button();
+ this.inputLangGroup.SuspendLayout();
+ this.userFolderGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // inputLangGroup
+ //
+ this.inputLangGroup.Controls.Add(this.chtRadio);
+ this.inputLangGroup.Controls.Add(this.chsRadio);
+ resources.ApplyResources(this.inputLangGroup, "inputLangGroup");
+ this.inputLangGroup.Name = "inputLangGroup";
+ this.inputLangGroup.TabStop = false;
+ //
+ // chtRadio
+ //
+ resources.ApplyResources(this.chtRadio, "chtRadio");
+ this.chtRadio.Name = "chtRadio";
+ this.chtRadio.TabStop = true;
+ this.chtRadio.UseVisualStyleBackColor = true;
+ //
+ // chsRadio
+ //
+ resources.ApplyResources(this.chsRadio, "chsRadio");
+ this.chsRadio.Name = "chsRadio";
+ this.chsRadio.TabStop = true;
+ this.chsRadio.UseVisualStyleBackColor = true;
+ //
+ // userFolderGroup
+ //
+ this.userFolderGroup.Controls.Add(this.selectButton);
+ this.userFolderGroup.Controls.Add(this.customPathBox);
+ this.userFolderGroup.Controls.Add(this.customFolderRadio);
+ this.userFolderGroup.Controls.Add(this.defaultFolderRadio);
+ resources.ApplyResources(this.userFolderGroup, "userFolderGroup");
+ this.userFolderGroup.Name = "userFolderGroup";
+ this.userFolderGroup.TabStop = false;
+ //
+ // selectButton
+ //
+ resources.ApplyResources(this.selectButton, "selectButton");
+ this.selectButton.Name = "selectButton";
+ this.selectButton.UseVisualStyleBackColor = true;
+ this.selectButton.Click += new System.EventHandler(this.SelectButton_Click);
+ //
+ // customPathBox
+ //
+ resources.ApplyResources(this.customPathBox, "customPathBox");
+ this.customPathBox.Name = "customPathBox";
+ //
+ // customFolderRadio
+ //
+ resources.ApplyResources(this.customFolderRadio, "customFolderRadio");
+ this.customFolderRadio.Name = "customFolderRadio";
+ this.customFolderRadio.TabStop = true;
+ this.customFolderRadio.UseVisualStyleBackColor = true;
+ this.customFolderRadio.CheckedChanged += new System.EventHandler(this.CustomFolderRadio_CheckedChanged);
+ //
+ // defaultFolderRadio
+ //
+ resources.ApplyResources(this.defaultFolderRadio, "defaultFolderRadio");
+ this.defaultFolderRadio.Name = "defaultFolderRadio";
+ this.defaultFolderRadio.TabStop = true;
+ this.defaultFolderRadio.UseVisualStyleBackColor = true;
+ this.defaultFolderRadio.CheckedChanged += new System.EventHandler(this.DefaultFolderRadio_CheckedChanged);
+ //
+ // confirmButton
+ //
+ resources.ApplyResources(this.confirmButton, "confirmButton");
+ this.confirmButton.Name = "confirmButton";
+ this.confirmButton.UseVisualStyleBackColor = true;
+ this.confirmButton.Click += new System.EventHandler(this.ConfirmButton_Click);
+ //
+ // removeButton
+ //
+ resources.ApplyResources(this.removeButton, "removeButton");
+ this.removeButton.Name = "removeButton";
+ this.removeButton.UseVisualStyleBackColor = true;
+ this.removeButton.Click += new System.EventHandler(this.RemoveButton_Click);
+ //
+ // SetupOptionDialog
+ //
+ resources.ApplyResources(this, "$this");
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.removeButton);
+ this.Controls.Add(this.confirmButton);
+ this.Controls.Add(this.userFolderGroup);
+ this.Controls.Add(this.inputLangGroup);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "SetupOptionDialog";
+ this.ShowIcon = false;
+ this.Load += new System.EventHandler(this.InstallOptionDialog_Load);
+ this.inputLangGroup.ResumeLayout(false);
+ this.inputLangGroup.PerformLayout();
+ this.userFolderGroup.ResumeLayout(false);
+ this.userFolderGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.GroupBox inputLangGroup;
+ private System.Windows.Forms.GroupBox userFolderGroup;
+ private System.Windows.Forms.RadioButton chtRadio;
+ private System.Windows.Forms.RadioButton chsRadio;
+ private System.Windows.Forms.RadioButton customFolderRadio;
+ private System.Windows.Forms.RadioButton defaultFolderRadio;
+ private System.Windows.Forms.Button confirmButton;
+ private System.Windows.Forms.Button removeButton;
+ private System.Windows.Forms.TextBox customPathBox;
+ private System.Windows.Forms.Button selectButton;
+ }
+}
+
diff --git a/Weasel.Setup/SetupOptionDialog.cs b/Weasel.Setup/SetupOptionDialog.cs
new file mode 100644
index 000000000..5d829a46e
--- /dev/null
+++ b/Weasel.Setup/SetupOptionDialog.cs
@@ -0,0 +1,89 @@
+using System.Windows.Forms;
+
+namespace Weasel.Setup
+{
+ public partial class SetupOptionDialog : Form
+ {
+ public bool IsInstalled { get; set; }
+ public bool IsHant { get; set; }
+ public string UserDir { get; set; }
+
+ public SetupOptionDialog()
+ {
+ InitializeComponent();
+ }
+
+ private void InstallOptionDialog_Load(object sender, System.EventArgs e)
+ {
+ chsRadio.Checked = !IsHant;
+ chtRadio.Checked = IsHant;
+
+ inputLangGroup.Enabled = !IsInstalled;
+
+ defaultFolderRadio.Checked = string.IsNullOrEmpty(UserDir);
+ customFolderRadio.Checked = !string.IsNullOrEmpty(UserDir);
+ customPathBox.Enabled = !string.IsNullOrEmpty(UserDir);
+ selectButton.Enabled = !string.IsNullOrEmpty(UserDir);
+ customPathBox.Text = UserDir;
+
+ if (IsInstalled)
+ {
+ confirmButton.Text = Localization.Resources.STR_OK;
+ }
+ removeButton.Enabled = IsInstalled;
+ }
+
+ private void DefaultFolderRadio_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if (defaultFolderRadio.Checked)
+ {
+ customPathBox.Text = string.Empty;
+ customPathBox.Enabled = false;
+ selectButton.Enabled = false;
+ }
+ }
+
+ private void CustomFolderRadio_CheckedChanged(object sender, System.EventArgs e)
+ {
+ if (customFolderRadio.Checked)
+ {
+ customPathBox.Enabled = true;
+ selectButton.Enabled = true;
+ }
+ }
+
+ private void ConfirmButton_Click(object sender, System.EventArgs e)
+ {
+ IsHant = chtRadio.Checked;
+ UserDir = customFolderRadio.Checked
+ ? customPathBox.Text
+ : string.Empty;
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+
+ private void SelectButton_Click(object sender, System.EventArgs e)
+ {
+ var folderDialog = new FolderBrowserDialog();
+ var customPath = customPathBox.Text;
+ if (!string.IsNullOrEmpty(customPath))
+ {
+ folderDialog.SelectedPath = customPath;
+ }
+ if (DialogResult.OK == folderDialog.ShowDialog())
+ {
+ customPathBox.Text = folderDialog.SelectedPath;
+ }
+ confirmButton.Focus();
+ }
+
+ private void RemoveButton_Click(object sender, System.EventArgs e)
+ {
+ Setup.Uninstall(false);
+ IsInstalled = false;
+ confirmButton.Text = Localization.Resources.STR_INSTALL;
+ inputLangGroup.Enabled = !IsInstalled;
+ removeButton.Enabled = IsInstalled;
+ }
+ }
+}
diff --git a/Weasel.Setup/SetupOptionDialog.resx b/Weasel.Setup/SetupOptionDialog.resx
new file mode 100644
index 000000000..354f10496
--- /dev/null
+++ b/Weasel.Setup/SetupOptionDialog.resx
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ chtRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ inputLangGroup
+
+
+ 0
+
+
+ chsRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ inputLangGroup
+
+
+ 1
+
+
+
+ 10, 11
+
+
+ 652, 204
+
+
+
+ 0
+
+
+ Input language
+
+
+ inputLangGroup
+
+
+ System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 3
+
+
+ True
+
+
+ 45, 125
+
+
+ 218, 21
+
+
+ 1
+
+
+ Chinese (Traditional)
+
+
+ chtRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ inputLangGroup
+
+
+ 0
+
+
+ True
+
+
+ 45, 63
+
+
+ 209, 21
+
+
+ 0
+
+
+ Chinese (Simplified)
+
+
+ chsRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ inputLangGroup
+
+
+ 1
+
+
+ 495, 137
+
+
+ 112, 33
+
+
+ 3
+
+
+ Select
+
+
+ selectButton
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ userFolderGroup
+
+
+ 0
+
+
+ False
+
+
+ 45, 139
+
+
+ 440, 27
+
+
+ 2
+
+
+ customPathBox
+
+
+ System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ userFolderGroup
+
+
+ 1
+
+
+ True
+
+
+ 45, 93
+
+
+ 164, 21
+
+
+ 1
+
+
+ Custom location
+
+
+ customFolderRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ userFolderGroup
+
+
+ 2
+
+
+ True
+
+
+ 45, 46
+
+
+ 173, 21
+
+
+ 0
+
+
+ Default location
+
+
+ defaultFolderRadio
+
+
+ System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ userFolderGroup
+
+
+ 3
+
+
+ 10, 222
+
+
+ 652, 204
+
+
+ 1
+
+
+ User folder
+
+
+ userFolderGroup
+
+
+ System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 2
+
+
+ 55, 442
+
+
+ 112, 45
+
+
+ 2
+
+
+ Install
+
+
+ confirmButton
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 1
+
+
+ 505, 442
+
+
+ 112, 45
+
+
+ 3
+
+
+ Remove
+
+
+ removeButton
+
+
+ System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 0
+
+
+ True
+
+
+ 9, 17
+
+
+ 675, 510
+
+
+ 宋体, 10pt
+
+
+
+ CenterScreen
+
+
+ Weasel Setup Options
+
+
+ SetupOptionDialog
+
+
+ System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Weasel.Setup/SetupOptionDialog.zh-Hans.resx b/Weasel.Setup/SetupOptionDialog.zh-Hans.resx
new file mode 100644
index 000000000..ff9550065
--- /dev/null
+++ b/Weasel.Setup/SetupOptionDialog.zh-Hans.resx
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ 99, 27
+
+
+ 繁体中文
+
+
+ 99, 27
+
+
+ 简体中文
+
+
+ 输入语言
+
+
+ 用户文件夹
+
+
+ 133, 27
+
+
+ 我来指定位置
+
+
+ 133, 27
+
+
+ 使用默认位置
+
+
+ 安装
+
+
+ 移除
+
+
+ 【小狼毫】配置选项
+
+
+ 选择
+
+
\ No newline at end of file
diff --git a/Weasel.Setup/SetupOptionDialog.zh-Hant.resx b/Weasel.Setup/SetupOptionDialog.zh-Hant.resx
new file mode 100644
index 000000000..a301d86f4
--- /dev/null
+++ b/Weasel.Setup/SetupOptionDialog.zh-Hant.resx
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ 99, 27
+
+
+ 繁體中文
+
+
+ 99, 27
+
+
+ 簡體中文
+
+
+ 輸入語言
+
+
+ 用戶資料夾
+
+
+ 133, 27
+
+
+ 我來指定位置
+
+
+ 133, 27
+
+
+ 使用默認位置
+
+
+ 安裝
+
+
+ 移除
+
+
+ 【小狼毫】配置選項
+
+
+ 選擇
+
+
\ No newline at end of file
diff --git a/Weasel.Setup/Utils.cs b/Weasel.Setup/Utils.cs
new file mode 100644
index 000000000..0c3542628
--- /dev/null
+++ b/Weasel.Setup/Utils.cs
@@ -0,0 +1,117 @@
+using Microsoft.Win32;
+using System;
+using System.IO;
+
+namespace Weasel.Setup
+{
+ internal class Utils
+ {
+ public static CultureInfo CultureInfo
+ {
+ get
+ {
+ var lang = (string)Registry.CurrentUser.GetValue(@"Software\Rime\Weasel\Language", string.Empty);
+ if (!string.IsNullOrEmpty(lang))
+ {
+ switch (lang)
+ {
+ case "chs":
+ return new CultureInfo("zh-Hans");
+ case "cht":
+ return new CultureInfo("zh-Hant");
+ default:
+ return new CultureInfo("en-US");
+ }
+ }
+ else
+ {
+ var current = Thread.CurrentThread.CurrentUICulture.Name;
+ if (current.StartsWith("zh"))
+ {
+ if (current.EndsWith("HK") || current.EndsWith("MO") ||
+ current.EndsWith("TW") || current.EndsWith("Hant"))
+ {
+ return new CultureInfo("zh-Hant");
+ }
+ else
+ {
+ return new CultureInfo("zh-Hans");
+ }
+ }
+ else
+ {
+ return new CultureInfo("en-US");
+ }
+ }
+ }
+ }
+
+ public static void SetProcessApiAwareness()
+ {
+ if (Environment.OSVersion.Version >= new Version(6, 3, 9600)) // Windows 8.1
+ {
+ PInvoke.HIDPI.SetProcessDpiAwareness(PInvoke.HIDPI.PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE);
+ }
+ }
+
+ public static string LogPath
+ {
+ get
+ {
+ var path = Environment.ExpandEnvironmentVariables(@"%TEMP%\rime.weasel");
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ return path;
+ }
+ }
+
+ public static void CopyFile(string src, string dest)
+ {
+ try
+ {
+ File.Copy(src, dest, true);
+ }
+ catch
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ var old = $"{dest}.old.{i}";
+ if (PInvoke.MoveFileEx(dest, old, PInvoke.MoveFileFlags.MOVEFILE_REPLACE_EXISTING))
+ {
+ PInvoke.MoveFileEx(old, null, PInvoke.MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
+ break;
+ }
+ }
+ try
+ {
+ File.Copy(src, dest, true);
+ }
+ catch
+ {
+ }
+ }
+ }
+
+ public static void DeleteFile(string path)
+ {
+ try
+ {
+ File.Delete(path);
+ }
+ catch
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ var old = $"{path}.old.{i}";
+ if (PInvoke.MoveFileEx(path, old, PInvoke.MoveFileFlags.MOVEFILE_REPLACE_EXISTING))
+ {
+ PInvoke.MoveFileEx(old, null, PInvoke.MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Weasel.Setup/Weasel.Setup.csproj b/Weasel.Setup/Weasel.Setup.csproj
new file mode 100644
index 000000000..76df0f454
--- /dev/null
+++ b/Weasel.Setup/Weasel.Setup.csproj
@@ -0,0 +1,20 @@
+
+
+ net48
+ true
+ WinExe
+ AnyCPU
+ $(SolutionDir)output\
+ false
+
+
+ True
+
+
+ True
+
+
+
+
+
+
diff --git a/WeaselSetup/InstallOptionsDlg.cpp b/WeaselSetup/InstallOptionsDlg.cpp
deleted file mode 100644
index 54fce46c8..000000000
--- a/WeaselSetup/InstallOptionsDlg.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "stdafx.h"
-#include "InstallOptionsDlg.h"
-#include
-#include
-#pragma comment(lib, "Shell32.lib")
-
-int uninstall(bool silent);
-
-InstallOptionsDialog::InstallOptionsDialog()
- : installed(false), hant(false), user_dir() {}
-
-InstallOptionsDialog::~InstallOptionsDialog() {}
-
-LRESULT InstallOptionsDialog::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
- cn_.Attach(GetDlgItem(IDC_RADIO_CN));
- tw_.Attach(GetDlgItem(IDC_RADIO_TW));
- remove_.Attach(GetDlgItem(IDC_REMOVE));
- default_dir_.Attach(GetDlgItem(IDC_RADIO_DEFAULT_DIR));
- custom_dir_.Attach(GetDlgItem(IDC_RADIO_CUSTOM_DIR));
- dir_.Attach(GetDlgItem(IDC_EDIT_DIR));
-
- CheckRadioButton(IDC_RADIO_CN, IDC_RADIO_TW,
- (hant ? IDC_RADIO_TW : IDC_RADIO_CN));
- CheckRadioButton(
- IDC_RADIO_DEFAULT_DIR, IDC_RADIO_CUSTOM_DIR,
- (user_dir.empty() ? IDC_RADIO_DEFAULT_DIR : IDC_RADIO_CUSTOM_DIR));
- dir_.SetWindowTextW(user_dir.c_str());
-
- cn_.EnableWindow(!installed);
- tw_.EnableWindow(!installed);
- remove_.EnableWindow(installed);
- dir_.EnableWindow(user_dir.empty() ? FALSE : TRUE);
-
- button_custom_dir_.Attach(GetDlgItem(IDC_BUTTON_CUSTOM_DIR));
- button_custom_dir_.EnableWindow(user_dir.empty() ? FALSE : TRUE);
-
- ok_.Attach(GetDlgItem(IDOK));
- if (installed) {
- CString str;
- str.LoadStringW(IDS_STRING_MODIFY);
- ok_.SetWindowTextW(str);
- }
-
- ime_.Attach(GetDlgItem(IDC_CHECK_INSTIME));
- if (installed)
- ime_.EnableWindow(FALSE);
-
- CenterWindow();
- return 0;
-}
-
-LRESULT InstallOptionsDialog::OnClose(UINT, WPARAM, LPARAM, BOOL&) {
- EndDialog(IDCANCEL);
- return 0;
-}
-
-LRESULT InstallOptionsDialog::OnOK(WORD, WORD code, HWND, BOOL&) {
- hant = (IsDlgButtonChecked(IDC_RADIO_TW) == BST_CHECKED);
- old_ime_support = (IsDlgButtonChecked(IDC_CHECK_INSTIME) == BST_CHECKED);
- if (IsDlgButtonChecked(IDC_RADIO_CUSTOM_DIR) == BST_CHECKED) {
- CStringW text;
- dir_.GetWindowTextW(text);
- user_dir = text;
- } else {
- user_dir.clear();
- }
- EndDialog(IDOK);
- return 0;
-}
-
-LRESULT InstallOptionsDialog::OnRemove(WORD, WORD code, HWND, BOOL&) {
- const bool non_silent = false;
- uninstall(non_silent);
- installed = false;
- ime_.EnableWindow(!installed);
- CString str;
- str.LoadStringW(IDS_STRING_INSTALL);
- ok_.SetWindowTextW(str);
- cn_.EnableWindow(!installed);
- tw_.EnableWindow(!installed);
- remove_.EnableWindow(installed);
- return 0;
-}
-
-LRESULT InstallOptionsDialog::OnUseDefaultDir(WORD, WORD code, HWND, BOOL&) {
- dir_.EnableWindow(FALSE);
- dir_.SetWindowTextW(L"");
- button_custom_dir_.EnableWindow(FALSE);
- return 0;
-}
-
-LRESULT InstallOptionsDialog::OnUseCustomDir(WORD, WORD code, HWND, BOOL&) {
- CShellFileOpenDialog fileOpenDlg(
- NULL, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_PICKFOLDERS);
- CStringW text;
- dir_.GetWindowTextW(text);
- if (!text.IsEmpty()) {
- PIDLIST_ABSOLUTE pidl;
- HRESULT hr = SHParseDisplayName(text, NULL, &pidl, 0, NULL);
- if (SUCCEEDED(hr)) {
- IShellItem* psi;
- hr = SHCreateShellItem(NULL, NULL, pidl, &psi);
- if (SUCCEEDED(hr)) {
- fileOpenDlg.GetPtr()->SetFolder(psi);
- psi->Release();
- }
- CoTaskMemFree(pidl);
- }
- }
- if (fileOpenDlg.DoModal(m_hWnd) == IDOK) {
- CComPtr psi;
- if (SUCCEEDED(fileOpenDlg.GetPtr()->GetResult(&psi))) {
- LPWSTR path;
- if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) {
- dir_.SetWindowTextW(path);
- CoTaskMemFree(path);
- }
- }
- }
- ok_.SetFocus();
- return 0;
-}
diff --git a/WeaselSetup/InstallOptionsDlg.h b/WeaselSetup/InstallOptionsDlg.h
deleted file mode 100644
index 4a7ba6ecb..000000000
--- a/WeaselSetup/InstallOptionsDlg.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#pragma once
-
-#include "resource.h"
-#include
-
-#define MSG_BY_IDS(idInfo, idCap, uType) \
- { \
- CString info, cap; \
- info.LoadStringW(idInfo); \
- cap.LoadStringW(idCap); \
- LANGID langID = GetThreadUILanguage(); \
- MessageBoxExW(NULL, info, cap, uType, langID); \
- }
-
-#define MSG_ID_CAP(info, idCap, uType) \
- { \
- CString cap; \
- cap.LoadStringW(idCap); \
- LANGID langID = GetThreadUILanguage(); \
- MessageBoxExW(NULL, info, cap, uType, langID); \
- }
-
-#define MSG_NOT_SILENT_BY_IDS(silent, idInfo, idCap, uType) \
- { \
- if (!silent) \
- MSG_BY_IDS(idInfo, idCap, uType); \
- }
-#define MSG_NOT_SILENT_ID_CAP(silent, info, idCap, uType) \
- { \
- if (!silent) \
- MSG_ID_CAP(info, idCap, uType); \
- }
-
-template
-LSTATUS SetRegKeyValue(HKEY rootKey,
- const wchar_t* subpath,
- const wchar_t* key,
- const T& value,
- DWORD type,
- bool disable_reg_redirect = false) {
- HKEY hKey;
- auto flag_wow64 = (disable_reg_redirect && is_wow64()) ? KEY_WOW64_64KEY : 0;
- LSTATUS ret =
- RegOpenKeyEx(rootKey, subpath, 0, KEY_ALL_ACCESS | flag_wow64, &hKey);
- if (ret != ERROR_SUCCESS) {
- ret = RegCreateKeyEx(rootKey, subpath, 0, NULL, 0,
- KEY_ALL_ACCESS | flag_wow64, 0, &hKey, NULL);
- if (ret != ERROR_SUCCESS)
- return ret;
- }
- if (ret == ERROR_SUCCESS) {
- DWORD dataSize;
- const BYTE* dataPtr;
- if constexpr (std::is_same::value) {
- dataSize = (value.size() + 1) * sizeof(wchar_t);
- dataPtr = reinterpret_cast(value.c_str());
- } else if constexpr (std::is_same::value) {
- dataSize = (wcslen((wchar_t*)value) + 1) * sizeof(wchar_t);
- dataPtr = reinterpret_cast(value);
- } else {
- dataSize = sizeof(T);
- dataPtr = reinterpret_cast(&value);
- }
- ret = RegSetValueEx(hKey, key, 0, type, dataPtr, dataSize);
- RegCloseKey(hKey);
- }
- return ret;
-}
-
-class InstallOptionsDialog : public CDialogImpl {
- public:
- enum { IDD = IDD_INSTALL_OPTIONS };
-
- InstallOptionsDialog();
- ~InstallOptionsDialog();
-
- bool installed;
- bool hant;
- bool old_ime_support;
- std::wstring user_dir;
-
- protected:
- BEGIN_MSG_MAP(InstallOptionsDialog)
- MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
- MESSAGE_HANDLER(WM_CLOSE, OnClose)
- COMMAND_ID_HANDLER(IDOK, OnOK)
- COMMAND_ID_HANDLER(IDC_REMOVE, OnRemove)
- COMMAND_ID_HANDLER(IDC_RADIO_DEFAULT_DIR, OnUseDefaultDir)
- COMMAND_ID_HANDLER(IDC_RADIO_CUSTOM_DIR, OnUseCustomDir)
- COMMAND_ID_HANDLER(IDC_BUTTON_CUSTOM_DIR, OnUseCustomDir)
- END_MSG_MAP()
-
- LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&);
- LRESULT OnClose(UINT, WPARAM, LPARAM, BOOL&);
- LRESULT OnOK(WORD, WORD code, HWND, BOOL&);
- LRESULT OnRemove(WORD, WORD code, HWND, BOOL&);
- LRESULT OnUseDefaultDir(WORD, WORD code, HWND, BOOL&);
- LRESULT OnUseCustomDir(WORD, WORD code, HWND, BOOL&);
-
- CButton cn_;
- CButton tw_;
- CButton remove_;
- CButton default_dir_;
- CButton custom_dir_;
- CButton ok_;
- CButton ime_;
- CButton button_custom_dir_;
- CEdit dir_;
-};
diff --git a/WeaselSetup/WeaselSetup.cpp b/WeaselSetup/WeaselSetup.cpp
deleted file mode 100644
index 43286ecfb..000000000
--- a/WeaselSetup/WeaselSetup.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-// WeaselSetup.cpp : main source file for WeaselSetup.exe
-//
-
-#include "stdafx.h"
-
-#include "resource.h"
-#include "WeaselUtility.h"
-#include
-
-#include "InstallOptionsDlg.h"
-
-#include
-#pragma comment(lib, "Shcore.lib")
-CAppModule _Module;
-
-static int Run(LPTSTR lpCmdLine);
-static bool IsProcAdmin();
-static int RestartAsAdmin(LPTSTR lpCmdLine);
-
-int WINAPI _tWinMain(HINSTANCE hInstance,
- HINSTANCE /*hPrevInstance*/,
- LPTSTR lpstrCmdLine,
- int /*nCmdShow*/) {
- HRESULT hRes = ::CoInitialize(NULL);
- ATLASSERT(SUCCEEDED(hRes));
-
- AtlInitCommonControls(
- ICC_BAR_CLASSES); // add flags to support other controls
-
- hRes = _Module.Init(NULL, hInstance);
- ATLASSERT(SUCCEEDED(hRes));
-
- LANGID langId = get_language_id();
- SetThreadUILanguage(langId);
- SetThreadLocale(langId);
-
- int nRet = Run(lpstrCmdLine);
-
- _Module.Term();
- ::CoUninitialize();
-
- return nRet;
-}
-int install(bool hant, bool silent, bool old_ime_support);
-int uninstall(bool silent);
-bool has_installed();
-
-static std::wstring install_dir() {
- WCHAR exe_path[MAX_PATH] = {0};
- GetModuleFileNameW(GetModuleHandle(NULL), exe_path, _countof(exe_path));
- std::wstring dir(exe_path);
- size_t pos = dir.find_last_of(L"\\");
- dir.resize(pos);
- return dir;
-}
-
-static int CustomInstall(bool installing) {
- bool hant = false;
- bool silent = false;
- bool old_ime_support = false;
- std::wstring user_dir;
-
- const WCHAR KEY[] = L"Software\\Rime\\Weasel";
- HKEY hKey;
- LSTATUS ret = RegOpenKey(HKEY_CURRENT_USER, KEY, &hKey);
- if (ret == ERROR_SUCCESS) {
- WCHAR value[MAX_PATH];
- DWORD len = sizeof(value);
- DWORD type = 0;
- DWORD data = 0;
- ret =
- RegQueryValueEx(hKey, L"RimeUserDir", NULL, &type, (LPBYTE)value, &len);
- if (ret == ERROR_SUCCESS && type == REG_SZ) {
- user_dir = value;
- }
- len = sizeof(data);
- ret = RegQueryValueEx(hKey, L"Hant", NULL, &type, (LPBYTE)&data, &len);
- if (ret == ERROR_SUCCESS && type == REG_DWORD) {
- hant = (data != 0);
- if (installing)
- silent = true;
- }
- RegCloseKey(hKey);
- }
- bool _has_installed = has_installed();
- if (!silent) {
- InstallOptionsDialog dlg;
- dlg.installed = _has_installed;
- dlg.hant = hant;
- dlg.user_dir = user_dir;
- if (IDOK != dlg.DoModal()) {
- if (!installing)
- return 1; // aborted by user
- } else {
- hant = dlg.hant;
- user_dir = dlg.user_dir;
- old_ime_support = dlg.old_ime_support;
- _has_installed = dlg.installed;
- }
- }
- if (!_has_installed)
- if (0 != install(hant, silent, old_ime_support))
- return 1;
-
- ret = SetRegKeyValue(HKEY_CURRENT_USER, KEY, L"RimeUserDir", user_dir.c_str(),
- REG_SZ, false);
- if (FAILED(HRESULT_FROM_WIN32(ret))) {
- MSG_BY_IDS(IDS_STR_ERR_WRITE_USER_DIR, IDS_STR_INSTALL_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
- ret = SetRegKeyValue(HKEY_CURRENT_USER, KEY, L"Hant", (hant ? 1 : 0),
- REG_DWORD, false);
- if (FAILED(HRESULT_FROM_WIN32(ret))) {
- MSG_BY_IDS(IDS_STR_ERR_WRITE_HANT, IDS_STR_INSTALL_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
- if (_has_installed) {
- std::wstring dir(install_dir());
- std::thread th([dir]() {
- ShellExecuteW(NULL, NULL, (dir + L"\\WeaselServer.exe").c_str(), L"/q",
- NULL, SW_SHOWNORMAL);
- Sleep(500);
- ShellExecuteW(NULL, NULL, (dir + L"\\WeaselServer.exe").c_str(), L"",
- NULL, SW_SHOWNORMAL);
- Sleep(500);
- ShellExecuteW(NULL, NULL, (dir + L"\\WeaselDeployer.exe").c_str(),
- L"/deploy", NULL, SW_SHOWNORMAL);
- });
- th.detach();
- MSG_BY_IDS(IDS_STR_MODIFY_SUCCESS_INFO, IDS_STR_MODIFY_SUCCESS_CAP,
- MB_ICONINFORMATION | MB_OK);
- }
-
- return 0;
-}
-
-LPCTSTR GetParamByPrefix(LPCTSTR lpCmdLine, LPCTSTR prefix) {
- return (wcsncmp(lpCmdLine, prefix, wcslen(prefix)) == 0)
- ? (lpCmdLine + wcslen(prefix))
- : 0;
-}
-
-static int Run(LPTSTR lpCmdLine) {
- constexpr bool silent = true;
- constexpr bool old_ime_support = false;
- bool uninstalling = !wcscmp(L"/u", lpCmdLine);
- if (uninstalling) {
- if (IsProcAdmin())
- return uninstall(silent);
- else
- return RestartAsAdmin(lpCmdLine);
- }
-
- if (auto res = GetParamByPrefix(lpCmdLine, L"/userdir:")) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"RimeUserDir", res, REG_SZ);
- }
-
- if (!wcscmp(L"/ls", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"Language", L"chs", REG_SZ);
- } else if (!wcscmp(L"/lt", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"Language", L"cht", REG_SZ);
- } else if (!wcscmp(L"/le", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"Language", L"eng", REG_SZ);
- }
-
- if (!wcscmp(L"/eu", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel\\Updates",
- L"CheckForUpdates", L"1", REG_SZ);
- }
- if (!wcscmp(L"/du", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel\\Updates",
- L"CheckForUpdates", L"0", REG_SZ);
- }
-
- if (!wcscmp(L"/toggleime", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"ToggleImeOnOpenClose", L"yes", REG_SZ);
- }
- if (!wcscmp(L"/toggleascii", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"ToggleImeOnOpenClose", L"no", REG_SZ);
- }
- if (!wcscmp(L"/testing", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"UpdateChannel", L"testing", REG_SZ);
- }
- if (!wcscmp(L"/release", lpCmdLine)) {
- return SetRegKeyValue(HKEY_CURRENT_USER, L"Software\\Rime\\weasel",
- L"UpdateChannel", L"release", REG_SZ);
- }
-
- if (!IsProcAdmin()) {
- return RestartAsAdmin(lpCmdLine);
- }
-
- bool hans = !wcscmp(L"/s", lpCmdLine);
- if (hans)
- return install(false, silent, old_ime_support);
- bool hant = !wcscmp(L"/t", lpCmdLine);
- if (hant)
- return install(true, silent, old_ime_support);
- bool installing = !wcscmp(L"/i", lpCmdLine);
- return CustomInstall(installing);
-}
-
-// https://learn.microsoft.com/zh-cn/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership
-bool IsProcAdmin() {
- BOOL b = FALSE;
- SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
- PSID AdministratorsGroup;
- b = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
- DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
- &AdministratorsGroup);
-
- if (b) {
- if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) {
- b = FALSE;
- }
- FreeSid(AdministratorsGroup);
- }
-
- return (b);
-}
-
-int RestartAsAdmin(LPTSTR lpCmdLine) {
- SHELLEXECUTEINFO execInfo{0};
- TCHAR path[MAX_PATH];
- GetModuleFileName(GetModuleHandle(NULL), path, _countof(path));
- execInfo.lpFile = path;
- execInfo.lpParameters = lpCmdLine;
- execInfo.lpVerb = _T("runas");
- execInfo.cbSize = sizeof(execInfo);
- execInfo.nShow = SW_SHOWNORMAL;
- execInfo.fMask = SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
- execInfo.hwnd = NULL;
- execInfo.hProcess = NULL;
- if (::ShellExecuteEx(&execInfo) && execInfo.hProcess != NULL) {
- ::WaitForSingleObject(execInfo.hProcess, INFINITE);
- DWORD dwExitCode = 0;
- ::GetExitCodeProcess(execInfo.hProcess, &dwExitCode);
- ::CloseHandle(execInfo.hProcess);
- return dwExitCode;
- }
- return -1;
-}
diff --git a/WeaselSetup/WeaselSetup.h b/WeaselSetup/WeaselSetup.h
deleted file mode 100644
index 99d30f5f1..000000000
--- a/WeaselSetup/WeaselSetup.h
+++ /dev/null
@@ -1 +0,0 @@
-// WeaselSetup.h
diff --git a/WeaselSetup/WeaselSetup.ico b/WeaselSetup/WeaselSetup.ico
deleted file mode 100644
index b2a7c748e..000000000
Binary files a/WeaselSetup/WeaselSetup.ico and /dev/null differ
diff --git a/WeaselSetup/WeaselSetup.rc b/WeaselSetup/WeaselSetup.rc
deleted file mode 100644
index 5a67a1582..000000000
Binary files a/WeaselSetup/WeaselSetup.rc and /dev/null differ
diff --git a/WeaselSetup/WeaselSetup.vcxproj b/WeaselSetup/WeaselSetup.vcxproj
deleted file mode 100644
index a141fc515..000000000
--- a/WeaselSetup/WeaselSetup.vcxproj
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
-
- 17.0
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}
- WeaselSetup
-
-
-
-
- Application
- true
- $(PLATFORM_TOOLSET)
- Unicode
-
-
- Application
- false
- true
- $(PLATFORM_TOOLSET)
- Unicode
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- false
- $(SolutionDir)output\
- $(SolutionDir)msbuild\$(Configuration)\$(Platform)\$(ProjectName)\
-
-
- true
- $(SolutionDir)output\
- $(SolutionDir)msbuild\$(Configuration)\$(Platform)\$(ProjectName)\
-
-
-
- Use
- Level3
- MultiThreaded
- Sync
-
- WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions)
- stdcpp17
- $(SolutionDir)include;$(BOOST_ROOT)
- true
-
-
- Windows
- Imm32.lib;Kernel32.lib;%(AdditionalDependencies)
- $(SolutionDir)output\$(ProjectName)$(TargetExt)
- AsInvoker
- UseLinkTimeCodeGeneration
-
-
- 0x7804
- $(IntDir);$(SolutionDir)\include\wtl
- NDEBUG;%(PreprocessorDefinitions)
-
-
- false
- Win32
- NDEBUG;%(PreprocessorDefinitions)
- WeaselSetup.h
- WeaselSetup_i.c
- WeaselSetup_p.c
- true
- $(IntDir)/WeaselSetup.tlb
-
-
-
- PerMonitorHighDPIAware
-
-
-
-
- Use
- Level3
- MultiThreadedDebug
- EditAndContinue
- EnableFastChecks
- Disabled
- WIN32;_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions)
- stdcpp17
- $(SolutionDir)include;$(BOOST_ROOT)
-
-
-
-
- Windows
- true
- Imm32.lib;Kernel32.lib;%(AdditionalDependencies)
- $(SolutionDir)output\$(ProjectName)$(TargetExt)
- AsInvoker
-
-
-
-
- 0x7804
- $(IntDir);$(SolutionDir)\include\wtl
- _DEBUG;%(PreprocessorDefinitions)
-
-
- false
- Win32
- _DEBUG;%(PreprocessorDefinitions)
- WeaselSetup.h
- WeaselSetup_i.c
- WeaselSetup_p.c
- true
- $(IntDir)/WeaselSetup.tlb
-
-
-
- PerMonitorHighDPIAware
-
-
-
-
-
-
- Create
- Create
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/WeaselSetup/WeaselSetup.vcxproj.filters b/WeaselSetup/WeaselSetup.vcxproj.filters
deleted file mode 100644
index 84e08d0b9..000000000
--- a/WeaselSetup/WeaselSetup.vcxproj.filters
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
- {2df80f89-25ee-49a8-a0bf-2dd38ec3b0b2}
- cpp;c;cxx;def;odl;idl;hpj;bat;asm
-
-
- {30b9a326-3c70-4dd6-99a0-8fc6015e684b}
- h;hpp;hxx;hm;inl;inc
-
-
- {6ed053d1-845d-4449-a84b-43340151cc7c}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest
-
-
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
-
-
- Resource Files
-
-
-
-
- Resource Files
-
-
-
\ No newline at end of file
diff --git a/WeaselSetup/imesetup.cpp b/WeaselSetup/imesetup.cpp
deleted file mode 100644
index ed48aea99..000000000
--- a/WeaselSetup/imesetup.cpp
+++ /dev/null
@@ -1,681 +0,0 @@
-#include "stdafx.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "InstallOptionsDlg.h"
-
-// {A3F4CDED-B1E9-41EE-9CA6-7B4D0DE6CB0A}
-static const GUID c_clsidTextService = {
- 0xa3f4cded,
- 0xb1e9,
- 0x41ee,
- {0x9c, 0xa6, 0x7b, 0x4d, 0xd, 0xe6, 0xcb, 0xa}};
-
-// {3D02CAB6-2B8E-4781-BA20-1C9267529467}
-static const GUID c_guidProfile = {
- 0x3d02cab6,
- 0x2b8e,
- 0x4781,
- {0xba, 0x20, 0x1c, 0x92, 0x67, 0x52, 0x94, 0x67}};
-
-// if in the future, option hant is extended, maybe a function to generate this
-// info is required
-#define PSZTITLE_HANS \
- L"0804:{A3F4CDED-B1E9-41EE-9CA6-7B4D0DE6CB0A}{3D02CAB6-2B8E-4781-BA20-" \
- L"1C9267529467}"
-#define PSZTITLE_HANT \
- L"0404:{A3F4CDED-B1E9-41EE-9CA6-7B4D0DE6CB0A}{3D02CAB6-2B8E-4781-BA20-" \
- L"1C9267529467}"
-#define ILOT_UNINSTALL 0x00000001
-typedef HRESULT(WINAPI* PTF_INSTALLLAYOUTORTIP)(LPCWSTR psz, DWORD dwFlags);
-
-#define WEASEL_WER_KEY \
- L"SOFTWARE\\Microsoft\\Windows\\Windows Error " \
- L"Reporting\\LocalDumps\\WeaselServer.exe"
-
-BOOL copy_file(const std::wstring& src, const std::wstring& dest) {
- BOOL ret = CopyFile(src.c_str(), dest.c_str(), FALSE);
- if (!ret) {
- for (int i = 0; i < 10; ++i) {
- std::wstring old = dest + L".old." + std::to_wstring(i);
- if (MoveFileEx(dest.c_str(), old.c_str(), MOVEFILE_REPLACE_EXISTING)) {
- MoveFileEx(old.c_str(), NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
- break;
- }
- }
- ret = CopyFile(src.c_str(), dest.c_str(), FALSE);
- }
- return ret;
-}
-
-BOOL delete_file(const std::wstring& file) {
- BOOL ret = DeleteFile(file.c_str());
- if (!ret) {
- for (int i = 0; i < 10; ++i) {
- std::wstring old = file + L".old." + std::to_wstring(i);
- if (MoveFileEx(file.c_str(), old.c_str(), MOVEFILE_REPLACE_EXISTING)) {
- MoveFileEx(old.c_str(), NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
- return TRUE;
- }
- }
- }
- return ret;
-}
-
-typedef BOOL(WINAPI* PISWOW64P2)(HANDLE, USHORT*, USHORT*);
-BOOL is_arm64_machine() {
- PISWOW64P2 fnIsWow64Process2 = (PISWOW64P2)GetProcAddress(
- GetModuleHandle(_T("kernel32.dll")), "IsWow64Process2");
-
- if (fnIsWow64Process2 == NULL) {
- return FALSE;
- }
-
- USHORT processMachine;
- USHORT nativeMachine;
-
- if (!fnIsWow64Process2(GetCurrentProcess(), &processMachine,
- &nativeMachine)) {
- return FALSE;
- }
- return nativeMachine == IMAGE_FILE_MACHINE_ARM64;
-}
-
-typedef HRESULT(WINAPI* PISWOWGMS)(USHORT, BOOL*);
-typedef UINT(WINAPI* PGSW64DIR2)(LPWSTR, UINT, WORD);
-INT get_wow_arm32_system_dir(LPWSTR lpBuffer, UINT uSize) {
- PISWOWGMS fnIsWow64GuestMachineSupported = (PISWOWGMS)GetProcAddress(
- GetModuleHandle(_T("kernel32.dll")), "IsWow64GuestMachineSupported");
- PGSW64DIR2 fnGetSystemWow64Directory2W = (PGSW64DIR2)GetProcAddress(
- GetModuleHandle(_T("kernelbase.dll")), "GetSystemWow64Directory2W");
-
- if (fnIsWow64GuestMachineSupported == NULL ||
- fnGetSystemWow64Directory2W == NULL) {
- return 0;
- }
-
- BOOL supported;
- if (fnIsWow64GuestMachineSupported(IMAGE_FILE_MACHINE_ARMNT, &supported) !=
- S_OK) {
- return 0;
- }
-
- if (!supported) {
- return 0;
- }
-
- return fnGetSystemWow64Directory2W(lpBuffer, uSize, IMAGE_FILE_MACHINE_ARMNT);
-}
-
-typedef int (*ime_register_func)(const std::wstring& ime_path,
- bool register_ime,
- bool is_wow64,
- bool is_wowarm,
- bool hant,
- bool silent);
-
-int install_ime_file(std::wstring& srcPath,
- const std::wstring& ext,
- bool hant,
- bool silent,
- ime_register_func func) {
- WCHAR path[MAX_PATH];
- GetModuleFileNameW(GetModuleHandle(NULL), path, _countof(path));
-
- std::wstring srcFileName = L"weasel";
-
- srcFileName += ext;
- WCHAR drive[_MAX_DRIVE];
- WCHAR dir[_MAX_DIR];
- _wsplitpath_s(path, drive, _countof(drive), dir, _countof(dir), NULL, 0, NULL,
- 0);
- srcPath = std::wstring(drive) + dir + srcFileName;
-
- GetSystemDirectoryW(path, _countof(path));
- std::wstring destPath = std::wstring(path) + L"\\weasel" + ext;
-
- int retval = 0;
- // 复制 .dll/.ime 到系统目录
- if (!copy_file(srcPath, destPath)) {
- MSG_NOT_SILENT_ID_CAP(silent, destPath.c_str(), IDS_STR_INSTALL_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
- retval += func(destPath, true, false, false, hant, silent);
- if (is_wow64()) {
- PVOID OldValue = NULL;
- // PW64DW64FR fnWow64DisableWow64FsRedirection =
- // (PW64DW64FR)GetProcAddress(GetModuleHandle(_T("kernel32.dll")),
- // "Wow64DisableWow64FsRedirection"); PW64RW64FR
- // fnWow64RevertWow64FsRedirection =
- // (PW64RW64FR)GetProcAddress(GetModuleHandle(_T("kernel32.dll")),
- // "Wow64RevertWow64FsRedirection");
- if (Wow64DisableWow64FsRedirection(&OldValue) == FALSE) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRCANCELFSREDIRECT,
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- if (is_arm64_machine()) {
- WCHAR sysarm32[MAX_PATH];
- if (get_wow_arm32_system_dir(sysarm32, _countof(sysarm32)) > 0) {
- // Install the ARM32 version if ARM32 WOW is supported (lower than
- // Windows 11 24H2).
- std::wstring srcPathARM32 = srcPath;
- ireplace_last(srcPathARM32, ext, L"ARM" + ext);
-
- std::wstring destPathARM32 = std::wstring(sysarm32) + L"\\weasel" + ext;
- if (!copy_file(srcPathARM32, destPathARM32)) {
- MSG_NOT_SILENT_ID_CAP(silent, destPathARM32.c_str(),
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
- retval += func(destPathARM32, true, true, true, hant, silent);
- }
-
- // Then install the ARM64 (and x64) version.
- // On ARM64 weasel.dll(ime) is an ARM64X redirection DLL (weaselARM64X).
- // When loaded, it will be redirected to weaselARM64.dll(ime) on ARM64
- // processes, and weaselx64.dll(ime) on x64 processes. So we need a total
- // of three files.
-
- std::wstring srcPathX64 = srcPath;
- std::wstring destPathX64 = destPath;
- ireplace_last(srcPathX64, ext, L"x64" + ext);
- ireplace_last(destPathX64, ext, L"x64" + ext);
- if (!copy_file(srcPathX64, destPathX64)) {
- MSG_NOT_SILENT_ID_CAP(silent, destPathX64.c_str(),
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- std::wstring srcPathARM64 = srcPath;
- std::wstring destPathARM64 = destPath;
- ireplace_last(srcPathARM64, ext, L"ARM64" + ext);
- ireplace_last(destPathARM64, ext, L"ARM64" + ext);
- if (!copy_file(srcPathARM64, destPathARM64)) {
- MSG_NOT_SILENT_ID_CAP(silent, destPathARM64.c_str(),
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- // Since weaselARM64X is just a redirector we don't have separate
- // HANS and HANT variants.
- srcPath = std::wstring(drive) + dir + L"weaselARM64X" + ext;
- } else {
- ireplace_last(srcPath, ext, L"x64" + ext);
- }
-
- if (!copy_file(srcPath, destPath)) {
- MSG_NOT_SILENT_ID_CAP(silent, destPath.c_str(), IDS_STR_INSTALL_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
- retval += func(destPath, true, true, false, hant, silent);
- if (Wow64RevertWow64FsRedirection(OldValue) == FALSE) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRRECOVERFSREDIRECT,
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
- }
- return retval;
-}
-
-int uninstall_ime_file(const std::wstring& ext,
- bool silent,
- ime_register_func func) {
- int retval = 0;
- WCHAR path[MAX_PATH];
- GetSystemDirectoryW(path, _countof(path));
- std::wstring imePath(path);
- imePath += L"\\weasel" + ext;
- retval += func(imePath, false, false, false, false, silent);
- delete_file(imePath);
- if (is_wow64()) {
- retval += func(imePath, false, true, false, false, silent);
- PVOID OldValue = NULL;
- if (Wow64DisableWow64FsRedirection(&OldValue) == FALSE) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRCANCELFSREDIRECT,
- IDS_STR_UNINSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- if (is_arm64_machine()) {
- WCHAR sysarm32[MAX_PATH];
- if (get_wow_arm32_system_dir(sysarm32, _countof(sysarm32)) > 0) {
- std::wstring imePathARM32 = std::wstring(sysarm32) + L"\\weasel" + ext;
- retval += func(imePathARM32, false, true, true, false, silent);
- delete_file(imePathARM32);
- }
-
- std::wstring imePathX64 = imePath;
- ireplace_last(imePathX64, ext, L"x64" + ext);
- delete_file(imePathX64);
-
- std::wstring imePathARM64 = imePath;
- ireplace_last(imePathARM64, ext, L"ARM64" + ext);
- delete_file(imePathARM64);
- }
-
- delete_file(imePath);
- if (Wow64RevertWow64FsRedirection(OldValue) == FALSE) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRRECOVERFSREDIRECT,
- IDS_STR_UNINSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
- }
- return retval;
-}
-
-// 注册IME输入法
-int register_ime(const std::wstring& ime_path,
- bool register_ime,
- bool is_wow64,
- bool is_wowarm,
- bool hant,
- bool silent) {
- if (is_wow64) {
- return 0; // only once
- }
-
- const WCHAR KEYBOARD_LAYOUTS_KEY[] =
- L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts";
- const WCHAR PRELOAD_KEY[] = L"Keyboard Layout\\Preload";
-
- if (register_ime) {
- HKL hKL = ImmInstallIME(ime_path.c_str(), get_weasel_ime_name().c_str());
- if (!hKL) {
- // manually register ime
- WCHAR hkl_str[16] = {0};
- HKEY hKey;
- LSTATUS ret = RegOpenKey(HKEY_LOCAL_MACHINE, KEYBOARD_LAYOUTS_KEY, &hKey);
- if (ret == ERROR_SUCCESS) {
- for (DWORD k = 0xE0200000 + (hant ? 0x0404 : 0x0804); k <= 0xE0FF0804;
- k += 0x10000) {
- StringCchPrintfW(hkl_str, _countof(hkl_str), L"%08X", k);
- HKEY hSubKey;
- ret = RegOpenKey(hKey, hkl_str, &hSubKey);
- if (ret == ERROR_SUCCESS) {
- WCHAR imeFile[32] = {0};
- DWORD len = sizeof(imeFile);
- DWORD type = 0;
- ret = RegQueryValueEx(hSubKey, L"Ime File", NULL, &type,
- (LPBYTE)imeFile, &len);
- if (ret = ERROR_SUCCESS) {
- if (_wcsicmp(imeFile, L"weasel.ime") == 0) {
- hKL = (HKL)k; // already there
- }
- }
- RegCloseKey(hSubKey);
- } else {
- // found a spare number to register
- ret = RegCreateKey(hKey, hkl_str, &hSubKey);
- if (ret == ERROR_SUCCESS) {
- const WCHAR ime_file[] = L"weasel.ime";
- RegSetValueEx(hSubKey, L"Ime File", 0, REG_SZ, (LPBYTE)ime_file,
- sizeof(ime_file));
- const WCHAR layout_file[] = L"kbdus.dll";
- RegSetValueEx(hSubKey, L"Layout File", 0, REG_SZ,
- (LPBYTE)layout_file, sizeof(layout_file));
- const std::wstring layout_text = get_weasel_ime_name();
- RegSetValueEx(hSubKey, L"Layout Text", 0, REG_SZ,
- (LPBYTE)layout_text.c_str(),
- layout_text.size() * sizeof(wchar_t));
- RegCloseKey(hSubKey);
- hKL = (HKL)k;
- }
- break;
- }
- }
- RegCloseKey(hKey);
- }
- if (hKL) {
- HKEY hPreloadKey;
- ret = RegOpenKey(HKEY_CURRENT_USER, PRELOAD_KEY, &hPreloadKey);
- if (ret == ERROR_SUCCESS) {
- for (size_t i = 1; true; ++i) {
- std::wstring number = std::to_wstring(i);
- DWORD type = 0;
- WCHAR value[32];
- DWORD len = sizeof(value);
- ret = RegQueryValueEx(hPreloadKey, number.c_str(), 0, &type,
- (LPBYTE)value, &len);
- if (ret != ERROR_SUCCESS) {
- RegSetValueEx(hPreloadKey, number.c_str(), 0, REG_SZ,
- (const BYTE*)hkl_str,
- (wcslen(hkl_str) + 1) * sizeof(WCHAR));
- break;
- }
- }
- RegCloseKey(hPreloadKey);
- }
- }
- }
- if (!hKL) {
- DWORD dwErr = GetLastError();
- WCHAR msg[100];
- CString str;
- str.LoadStringW(IDS_STR_ERRREGIME);
- StringCchPrintfW(msg, _countof(msg), str, hKL, dwErr);
- MSG_NOT_SILENT_ID_CAP(silent, msg, IDS_STR_INSTALL_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
- return 0;
- }
-
- // unregister ime
-
- HKEY hKey;
- LSTATUS ret = RegOpenKey(HKEY_LOCAL_MACHINE, KEYBOARD_LAYOUTS_KEY, &hKey);
- if (ret != ERROR_SUCCESS) {
- MSG_NOT_SILENT_ID_CAP(silent, KEYBOARD_LAYOUTS_KEY,
- IDS_STR_UNINSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- for (int i = 0; true; ++i) {
- WCHAR subKey[16];
- ret = RegEnumKey(hKey, i, subKey, _countof(subKey));
- if (ret != ERROR_SUCCESS)
- break;
-
- // 中文键盘布局?
- if (wcscmp(subKey + 4, L"0804") == 0 || wcscmp(subKey + 4, L"0404") == 0) {
- HKEY hSubKey;
- ret = RegOpenKey(hKey, subKey, &hSubKey);
- if (ret != ERROR_SUCCESS)
- continue;
-
- WCHAR imeFile[32];
- DWORD len = sizeof(imeFile);
- DWORD type = 0;
- ret = RegQueryValueEx(hSubKey, L"Ime File", NULL, &type, (LPBYTE)imeFile,
- &len);
- RegCloseKey(hSubKey);
- if (ret != ERROR_SUCCESS)
- continue;
-
- // 小狼毫?
- if (_wcsicmp(imeFile, L"weasel.ime") == 0) {
- DWORD value;
- swscanf_s(subKey, L"%x", &value);
- UnloadKeyboardLayout((HKL)value);
-
- RegDeleteKey(hKey, subKey);
-
- // 移除preload
- HKEY hPreloadKey;
- ret = RegOpenKey(HKEY_CURRENT_USER, PRELOAD_KEY, &hPreloadKey);
- if (ret != ERROR_SUCCESS)
- continue;
- std::vector preloads;
- std::wstring number;
- for (size_t i = 1; true; ++i) {
- number = std::to_wstring(i);
- DWORD type = 0;
- WCHAR value[32];
- DWORD len = sizeof(value);
- ret = RegQueryValueEx(hPreloadKey, number.c_str(), 0, &type,
- (LPBYTE)value, &len);
- if (ret != ERROR_SUCCESS) {
- if (i > preloads.size()) {
- // 删除最大一号注册表值
- number = std::to_wstring(i - 1);
- RegDeleteValue(hPreloadKey, number.c_str());
- }
- break;
- }
- if (_wcsicmp(subKey, value) != 0) {
- preloads.push_back(value);
- }
- }
- // 重写preloads
- for (size_t i = 0; i < preloads.size(); ++i) {
- number = std::to_wstring(i + 1);
- RegSetValueEx(hPreloadKey, number.c_str(), 0, REG_SZ,
- (const BYTE*)preloads[i].c_str(),
- (preloads[i].length() + 1) * sizeof(WCHAR));
- }
- RegCloseKey(hPreloadKey);
- }
- }
- }
-
- RegCloseKey(hKey);
- return 0;
-}
-
-void enable_profile(BOOL fEnable, bool hant) {
- HRESULT hr;
- ITfInputProcessorProfiles* pProfiles = NULL;
-
- hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL,
- CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles,
- (LPVOID*)&pProfiles);
-
- if (SUCCEEDED(hr)) {
- LANGID lang_id = hant ? 0x0404 : 0x0804;
- if (fEnable) {
- pProfiles->EnableLanguageProfile(c_clsidTextService, lang_id,
- c_guidProfile, fEnable);
- pProfiles->EnableLanguageProfileByDefault(c_clsidTextService, lang_id,
- c_guidProfile, fEnable);
- } else {
- pProfiles->RemoveLanguageProfile(c_clsidTextService, lang_id,
- c_guidProfile);
- }
-
- pProfiles->Release();
- }
-}
-
-// 注册TSF输入法
-int register_text_service(const std::wstring& tsf_path,
- bool register_ime,
- bool is_wow64,
- bool is_wowarm32,
- bool hant,
- bool silent) {
- using RegisterServerFunction = HRESULT(STDAPICALLTYPE*)();
-
- if (!register_ime)
- enable_profile(FALSE, hant);
-
- std::wstring params = L" \"" + tsf_path + L"\"";
- if (!register_ime) {
- params = L" /u " + params; // unregister
- }
- // if (silent) // always silent
- { params = L" /s " + params; }
-
- if (hant) {
- if (!SetEnvironmentVariable(L"TEXTSERVICE_PROFILE", L"hant")) {
- // bad luck
- }
- } else {
- if (!SetEnvironmentVariable(L"TEXTSERVICE_PROFILE", L"hans")) {
- // bad luck
- }
- }
-
- std::wstring app = L"regsvr32.exe";
- if (is_wowarm32) {
- WCHAR sysarm32[MAX_PATH];
- get_wow_arm32_system_dir(sysarm32, _countof(sysarm32));
-
- app = std::wstring(sysarm32) + L"\\" + app;
- }
-
- SHELLEXECUTEINFOW shExInfo = {0};
- shExInfo.cbSize = sizeof(shExInfo);
- shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
- shExInfo.hwnd = 0;
- shExInfo.lpVerb = L"open"; // Operation to perform
- shExInfo.lpFile = app.c_str(); // Application to start
- shExInfo.lpParameters = params.c_str(); // Additional parameters
- shExInfo.lpDirectory = 0;
- shExInfo.nShow = SW_SHOW;
- shExInfo.hInstApp = 0;
- if (ShellExecuteExW(&shExInfo)) {
- WaitForSingleObject(shExInfo.hProcess, INFINITE);
- CloseHandle(shExInfo.hProcess);
- } else {
- WCHAR msg[100];
- CString str;
- str.LoadStringW(IDS_STR_ERRREGTSF);
- StringCchPrintfW(msg, _countof(msg), str, params.c_str());
- // StringCchPrintfW(msg, _countof(msg), L"註冊輸入法錯誤 regsvr32.exe %s",
- // params.c_str()); if (!silent) MessageBoxW(NULL, msg, L"安装/卸載失败",
- // MB_ICONERROR | MB_OK);
- MSG_NOT_SILENT_ID_CAP(silent, msg, IDS_STR_INORUN_FAILED,
- MB_ICONERROR | MB_OK);
- return 1;
- }
-
- if (register_ime)
- enable_profile(TRUE, hant);
-
- return 0;
-}
-
-int install(bool hant, bool silent, bool old_ime_support) {
- std::wstring ime_src_path;
- int retval = 0;
- if (old_ime_support) {
- retval +=
- install_ime_file(ime_src_path, L".ime", hant, silent, ®ister_ime);
- }
- retval += install_ime_file(ime_src_path, L".dll", hant, silent,
- ®ister_text_service);
-
- // 写注册表
- WCHAR drive[_MAX_DRIVE];
- WCHAR dir[_MAX_DIR];
- _wsplitpath_s(ime_src_path.c_str(), drive, _countof(drive), dir,
- _countof(dir), NULL, 0, NULL, 0);
- std::wstring rootDir = std::wstring(drive) + dir;
- rootDir.pop_back();
- auto ret = SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_REG_KEY, L"WeaselRoot",
- rootDir.c_str(), REG_SZ);
- if (FAILED(HRESULT_FROM_WIN32(ret))) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRWRITEWEASELROOT,
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- const std::wstring executable = L"WeaselServer.exe";
- ret = SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_REG_KEY, L"ServerExecutable",
- executable.c_str(), REG_SZ);
- if (FAILED(HRESULT_FROM_WIN32(ret))) {
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_ERRREGIMEWRITESVREXE,
- IDS_STR_INSTALL_FAILED, MB_ICONERROR | MB_OK);
- return 1;
- }
-
- // InstallLayoutOrTip
- // https://learn.microsoft.com/zh-cn/windows/win32/tsf/installlayoutortip
- // example in ref page not right with "*PTF_ INSTALLLAYOUTORTIP"
- // space inside should be removed
- HMODULE hInputDLL = LoadLibrary(TEXT("input.dll"));
- if (hInputDLL) {
- PTF_INSTALLLAYOUTORTIP pfnInstallLayoutOrTip;
- pfnInstallLayoutOrTip =
- (PTF_INSTALLLAYOUTORTIP)GetProcAddress(hInputDLL, "InstallLayoutOrTip");
- if (pfnInstallLayoutOrTip) {
- if (hant)
- (*pfnInstallLayoutOrTip)(PSZTITLE_HANT, 0);
- else
- (*pfnInstallLayoutOrTip)(PSZTITLE_HANS, 0);
- }
- FreeLibrary(hInputDLL);
- }
-
- // https://learn.microsoft.com/zh-cn/windows/win32/wer/collecting-user-mode-dumps
- const std::wstring dmpPathW = WeaselLogPath().wstring();
- // DumpFolder
- SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_WER_KEY, L"DumpFolder",
- dmpPathW.c_str(), REG_SZ, true);
- // dump type 0
- SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_WER_KEY, L"DumpType", 0, REG_DWORD,
- true);
- // CustomDumpFlags, MiniDumpNormal
- SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_WER_KEY, L"CustomDumpFlags", 0,
- REG_DWORD, true);
- // maximium dump count 10
- SetRegKeyValue(HKEY_LOCAL_MACHINE, WEASEL_WER_KEY, L"DumpCount", 10,
- REG_DWORD, true);
-
- if (retval)
- return 1;
-
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_INSTALL_SUCCESS_INFO,
- IDS_STR_INSTALL_SUCCESS_CAP,
- MB_ICONINFORMATION | MB_OK);
- return 0;
-}
-
-int uninstall(bool silent) {
- // 注销输入法
- int retval = 0;
-
- const WCHAR KEY[] = L"Software\\Rime\\Weasel";
- HKEY hKey;
- LSTATUS ret = RegOpenKey(HKEY_CURRENT_USER, KEY, &hKey);
- if (ret == ERROR_SUCCESS) {
- DWORD type = 0;
- DWORD data = 0;
- DWORD len = sizeof(data);
- ret = RegQueryValueEx(hKey, L"Hant", NULL, &type, (LPBYTE)&data, &len);
- if (ret == ERROR_SUCCESS && type == REG_DWORD) {
- HMODULE hInputDLL = LoadLibrary(TEXT("input.dll"));
- if (hInputDLL) {
- PTF_INSTALLLAYOUTORTIP pfnInstallLayoutOrTip;
- pfnInstallLayoutOrTip = (PTF_INSTALLLAYOUTORTIP)GetProcAddress(
- hInputDLL, "InstallLayoutOrTip");
- if (pfnInstallLayoutOrTip) {
- if (data != 0)
- (*pfnInstallLayoutOrTip)(PSZTITLE_HANT, ILOT_UNINSTALL);
- else
- (*pfnInstallLayoutOrTip)(PSZTITLE_HANS, ILOT_UNINSTALL);
- }
- FreeLibrary(hInputDLL);
- }
- }
- RegCloseKey(hKey);
- }
-
- uninstall_ime_file(L".ime", silent, ®ister_ime);
- retval += uninstall_ime_file(L".dll", silent, ®ister_text_service);
-
- // 清除注册信息
- RegDeleteKey(HKEY_LOCAL_MACHINE, WEASEL_REG_KEY);
- RegDeleteKey(HKEY_LOCAL_MACHINE, RIME_REG_KEY);
-
- // delete WER register,
- // "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\Windows Error
- // Reporting\\LocalDumps\\WeaselServer.exe" no WOW64 redirect
-
- auto flag_wow64 = is_wow64() ? KEY_WOW64_64KEY : 0;
- RegDeleteKeyEx(HKEY_LOCAL_MACHINE, WEASEL_WER_KEY, flag_wow64, 0);
- if (retval)
- return 1;
-
- MSG_NOT_SILENT_BY_IDS(silent, IDS_STR_UNINSTALL_SUCCESS_INFO,
- IDS_STR_UNINSTALL_SUCCESS_CAP,
- MB_ICONINFORMATION | MB_OK);
- return 0;
-}
-
-bool has_installed() {
- WCHAR path[MAX_PATH];
- GetSystemDirectory(path, _countof(path));
- std::wstring sysPath(path);
- DWORD attr = GetFileAttributesW((sysPath + L"\\weasel.dll").c_str());
- return (attr != INVALID_FILE_ATTRIBUTES &&
- !(attr & FILE_ATTRIBUTE_DIRECTORY));
-}
diff --git a/WeaselSetup/resource.h b/WeaselSetup/resource.h
deleted file mode 100644
index 3315f0f94..000000000
--- a/WeaselSetup/resource.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//{{NO_DEPENDENCIES}}
-// Microsoft Visual C++ generated include file.
-// Used by WeaselSetup.rc
-//
-#define IDI_WEASELSETUP 107
-#define IDR_WEASELSETUP 107
-#define IDR_MAINFRAME 128
-#define IDS_STRING_INSTALL 129
-#define IDS_STRING_MODIFY 130
-#define IDS_STR_INSTALL_FAILED 131
-#define IDS_STR_INSTALL_SUCCESS_CAP 132
-#define IDS_STR_UNINSTALL_SUCCESS_CAP 133
-#define IDS_STR_UNINSTALL_FAILED 134
-#define IDS_STR_ERRCANCELFSREDIRECT 135
-#define IDS_STR_ERRRECOVERFSREDIRECT 136
-#define IDS_STR_ERRREGIME 137
-#define IDS_STR_ERRREGTSF 138
-#define IDS_STR_ERRREGIMEWRITESVREXE 139
-#define IDS_STR_INORUN_FAILED 140
-#define IDS_STR_ERRWRITEWEASELROOT 141
-#define IDS_STR_INSTALL_SUCCESS_INFO 142
-#define IDS_STR_UNINSTALL_SUCCESS_INFO 143
-#define IDS_STR_ERR_WRITE_USER_DIR 144
-#define IDS_STR_ERR_WRITE_HANT 145
-#define IDS_STR_MODIFY_SUCCESS_INFO 146
-#define IDS_STR_MODIFY_SUCCESS_CAP 147
-#define IDD_INSTALL_OPTIONS 201
-#define IDD_DIALOG1 203
-#define IDC_RADIO_CN 1000
-#define IDC_RADIO_TW 1001
-#define IDC_RADIO_DEFAULT_DIR 1002
-#define IDC_RADIO_CUSTOM_DIR 1003
-#define IDC_EDIT_DIR 1004
-#define IDC_REMOVE 1005
-#define IDC_CHECK1 1006
-#define IDC_CHECK_INSTIME 1006
-#define IDC_BUTTON_CUSTOM_DIR 1007
-
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 205
-#define _APS_NEXT_COMMAND_VALUE 32775
-#define _APS_NEXT_CONTROL_VALUE 1008
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
diff --git a/WeaselSetup/stdafx.cpp b/WeaselSetup/stdafx.cpp
deleted file mode 100644
index af034a721..000000000
--- a/WeaselSetup/stdafx.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// stdafx.cpp : source file that includes just the standard includes
-// WeaselSetup.pch will be the pre-compiled header
-// stdafx.obj will contain the pre-compiled type information
-
-#include "stdafx.h"
diff --git a/WeaselSetup/stdafx.h b/WeaselSetup/stdafx.h
deleted file mode 100644
index 1fc659710..000000000
--- a/WeaselSetup/stdafx.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// stdafx.h : include file for standard system include files,
-// or project specific include files that are used frequently, but
-// are changed infrequently
-//
-
-#pragma once
-
-// Change these values to use different versions
-#define WINVER 0x0603
-#define _WIN32_WINNT 0x0603
-#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
-
-#include
-#include
-
-extern CAppModule _Module;
-
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#if defined _M_IX86
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
-#elif defined _M_X64
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
-#else
- #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
-#endif
diff --git a/WeaselSetup/xmake.lua b/WeaselSetup/xmake.lua
deleted file mode 100644
index 865392e46..000000000
--- a/WeaselSetup/xmake.lua
+++ /dev/null
@@ -1,33 +0,0 @@
-target("WeaselSetup")
- set_kind("binary")
- add_files("./*.cpp")
- add_rules("add_rcfiles", "use_weaselconstants", "subwin")
- add_links("imm32", "kernel32")
-
- set_policy("windows.manifest.uac", "invoker")
- add_files("$(projectdir)/PerMonitorHighDPIAware.manifest")
- add_ldflags("/DEBUG /OPT:REF /OPT:ICF /LARGEADDRESSAWARE /ERRORREPORT:QUEUE")
-
- before_build(function(target)
- local target_dir = path.join(target:targetdir(), target:name())
- if not os.exists(target_dir) then
- os.mkdir(target_dir)
- end
- target:set("targetdir", target_dir)
- local level = target:policy("windows.manifest.uac")
- if level then
- local level_maps = {
- invoker = "asInvoker",
- admin = "requireAdministrator",
- highest = "highestAvailable"
- }
- assert(level_maps[level], "unknown uac level %s, please set invoker, admin or highest", level)
- local ui = target:policy("windows.manifest.uac.ui") or false
- target:add("ldflags", "/manifest:embed", {("/manifestuac:level='%s' uiAccess='%s'"):format(level_maps[level], ui)}, {force = true, expand = false})
- end
- end)
-
- after_build(function(target)
- os.cp(path.join(target:targetdir(), "WeaselSetup.exe"), "$(projectdir)/output")
- os.cp(path.join(target:targetdir(), "WeaselSetup.pdb"), "$(projectdir)/output")
- end)
diff --git a/build.bat b/build.bat
index 152d0de85..cc2da49f9 100644
--- a/build.bat
+++ b/build.bat
@@ -72,7 +72,7 @@ if not defined PLATFORM_TOOLSET (
if defined DEVTOOLS_PATH set PATH=%DEVTOOLS_PATH%%PATH%
set build_config=Release
-set build_option=/t:Build
+set build_option=/t:Build -restore
set build_boost=0
set boost_build_variant=release
set build_data=0
diff --git a/clang-format.sh b/clang-format.sh
index d1b087b8a..31e54577f 100755
--- a/clang-format.sh
+++ b/clang-format.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-WEASEL_SOURCE_PATH="RimeWithWeasel WeaselDeployer WeaselIME WeaselIPC WeaselIPCServer WeaselServer WeaselSetup WeaselTSF WeaselUI include test"
+WEASEL_SOURCE_PATH="RimeWithWeasel WeaselDeployer WeaselIME WeaselIPC WeaselIPCServer WeaselServer WeaselTSF WeaselUI include test"
# clang format options
method="-i"
diff --git a/output/install.nsi b/output/install.nsi
index 92f5a9b60..29b854fde 100644
--- a/output/install.nsi
+++ b/output/install.nsi
@@ -260,7 +260,8 @@ program_files:
${Endif}
${Endif}
- File "WeaselSetup.exe"
+ File "Weasel.Setup.exe"
+ File "TSF.TypeLib.dll"
; shared data files
SetOutPath $INSTDIR\data
File "data\*.yaml"
@@ -287,7 +288,7 @@ program_files:
IfErrors +2 0
StrCpy $R2 "/t"
- ExecWait '"$INSTDIR\WeaselSetup.exe" $R2'
+ ExecWait '"$INSTDIR\Weasel.Setup.exe" $R2'
; Write the uninstall keys for Windows
WriteRegStr HKLM "${REG_UNINST_KEY}" "DisplayName" "$(DISPLAYNAME)"
@@ -350,7 +351,7 @@ Section "Start Menu Shortcuts"
CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORUSERFOLDER).lnk" "$INSTDIR\WeaselServer.exe" "/userdir" "$SYSDIR\shell32.dll" 126
CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORAPPFOLDER).lnk" "$INSTDIR\WeaselServer.exe" "/weaseldir" "$SYSDIR\shell32.dll" 19
CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORUPDATER).lnk" "$INSTDIR\WeaselServer.exe" "/update" "$SYSDIR\shell32.dll" 13
- CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORSETUP).lnk" "$INSTDIR\WeaselSetup.exe" "" "$SYSDIR\shell32.dll" 162
+ CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORSETUP).lnk" "$INSTDIR\Weasel.Setup.exe" "" "$SYSDIR\shell32.dll" 162
CreateShortCut "$SMPROGRAMS\$(DISPLAYNAME)\$(LNKFORUNINSTALL).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
SectionEnd
@@ -363,7 +364,7 @@ Section "Uninstall"
ExecWait '"$INSTDIR\WeaselServer.exe" /quit'
- ExecWait '"$INSTDIR\WeaselSetup.exe" /u'
+ ExecWait '"$INSTDIR\Weasel.Setup.exe" /u'
; Remove registry keys
DeleteRegKey HKLM SOFTWARE\Rime
diff --git a/weasel.sln b/weasel.sln
index c248db400..f94ebbb2a 100644
--- a/weasel.sln
+++ b/weasel.sln
@@ -41,20 +41,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RimeWithWeasel", "RimeWithW
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WeaselDeployer", "WeaselDeployer\WeaselDeployer.vcxproj", "{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WeaselSetup", "WeaselSetup\WeaselSetup.vcxproj", "{39F6E3F5-8F0B-4023-BC40-A66AE6C37095}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Weasel.Setup", "Weasel.Setup\Weasel.Setup.csproj", "{2D028423-929D-4555-A61E-245BD9EC7383}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
+ Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|Any CPU.Build.0 = Debug|x64
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|ARM.ActiveCfg = Debug|ARM
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|ARM.Build.0 = Debug|ARM
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -63,6 +67,8 @@ Global
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|Win32.Build.0 = Debug|Win32
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|x64.ActiveCfg = Debug|x64
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Debug|x64.Build.0 = Debug|x64
+ {FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|Any CPU.ActiveCfg = Release|x64
+ {FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|Any CPU.Build.0 = Release|x64
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|ARM.ActiveCfg = Release|ARM
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|ARM.Build.0 = Release|ARM
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -71,6 +77,8 @@ Global
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|Win32.Build.0 = Release|Win32
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|x64.ActiveCfg = Release|x64
{FF9B3625-BBD6-4972-8F23-7BA57F3127E8}.Release|x64.Build.0 = Release|x64
+ {10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|Any CPU.Build.0 = Debug|x64
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|ARM.ActiveCfg = Debug|ARM
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|ARM.Build.0 = Debug|ARM
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -79,6 +87,8 @@ Global
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|Win32.Build.0 = Debug|Win32
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|x64.ActiveCfg = Debug|x64
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Debug|x64.Build.0 = Debug|x64
+ {10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|Any CPU.ActiveCfg = Release|x64
+ {10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|Any CPU.Build.0 = Release|x64
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|ARM.ActiveCfg = Release|ARM
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|ARM.Build.0 = Release|ARM
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -87,6 +97,8 @@ Global
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|Win32.Build.0 = Release|Win32
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|x64.ActiveCfg = Release|x64
{10B3B8BF-7294-4661-9A8A-2FFC920FA2F4}.Release|x64.Build.0 = Release|x64
+ {CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|Any CPU.Build.0 = Debug|x64
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|ARM.ActiveCfg = Debug|ARM
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|ARM.Build.0 = Debug|ARM
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -95,6 +107,8 @@ Global
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|Win32.Build.0 = Debug|Win32
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|x64.ActiveCfg = Debug|x64
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Debug|x64.Build.0 = Debug|x64
+ {CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|Any CPU.ActiveCfg = Release|x64
+ {CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|Any CPU.Build.0 = Release|x64
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|ARM.ActiveCfg = Release|ARM
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|ARM.Build.0 = Release|ARM
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -103,6 +117,8 @@ Global
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|Win32.Build.0 = Release|Win32
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|x64.ActiveCfg = Release|x64
{CE11A2DF-8D20-4B07-A935-4B0D03F0303D}.Release|x64.Build.0 = Release|x64
+ {A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|Any CPU.Build.0 = Debug|x64
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|ARM.ActiveCfg = Debug|ARM
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|ARM.Build.0 = Debug|ARM
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -111,6 +127,8 @@ Global
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|Win32.Build.0 = Debug|Win32
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|x64.ActiveCfg = Debug|x64
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Debug|x64.Build.0 = Debug|x64
+ {A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|Any CPU.ActiveCfg = Release|x64
+ {A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|Any CPU.Build.0 = Release|x64
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|ARM.ActiveCfg = Release|ARM
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|ARM.Build.0 = Release|ARM
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|ARM64.ActiveCfg = Release|ARM64
@@ -119,84 +137,118 @@ Global
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|Win32.Build.0 = Release|Win32
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|x64.ActiveCfg = Release|x64
{A958FE3D-9F6E-44CB-981E-F2AE181657B3}.Release|x64.Build.0 = Release|x64
+ {A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|Any CPU.Build.0 = Debug|x64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|ARM.ActiveCfg = Debug|ARM
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|Win32.ActiveCfg = Debug|Win32
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|Win32.Build.0 = Debug|Win32
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|x64.ActiveCfg = Debug|x64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Debug|x64.Build.0 = Debug|x64
+ {A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|Any CPU.ActiveCfg = Release|x64
+ {A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|Any CPU.Build.0 = Release|x64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|ARM.ActiveCfg = Release|ARM
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|ARM64.ActiveCfg = Release|ARM64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|Win32.ActiveCfg = Release|Win32
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|Win32.Build.0 = Release|Win32
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|x64.ActiveCfg = Release|x64
{A68C2A5C-D74D-4CB2-9E22-A4D2FC256A2D}.Release|x64.Build.0 = Release|x64
+ {9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|Any CPU.Build.0 = Debug|x64
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|ARM.ActiveCfg = Debug|ARM
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|ARM64.ActiveCfg = Debug|ARM64
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|Win32.ActiveCfg = Debug|Win32
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|Win32.Build.0 = Debug|Win32
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|x64.ActiveCfg = Debug|x64
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Debug|x64.Build.0 = Debug|x64
+ {9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|Any CPU.ActiveCfg = Release|x64
+ {9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|Any CPU.Build.0 = Release|x64
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|ARM.ActiveCfg = Release|ARM
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|ARM64.ActiveCfg = Release|ARM64
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|Win32.ActiveCfg = Release|Win32
{9C1CC4BA-18FF-4890-908E-E545881C5586}.Release|x64.ActiveCfg = Release|x64
+ {26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|Any CPU.Build.0 = Debug|x64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|ARM.ActiveCfg = Debug|ARM
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|ARM64.ActiveCfg = Debug|ARM64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|Win32.ActiveCfg = Debug|Win32
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|Win32.Build.0 = Debug|Win32
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|x64.ActiveCfg = Debug|x64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Debug|x64.Build.0 = Debug|x64
+ {26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|Any CPU.ActiveCfg = Release|x64
+ {26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|Any CPU.Build.0 = Release|x64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|ARM.ActiveCfg = Release|ARM
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|ARM64.ActiveCfg = Release|ARM64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|Win32.ActiveCfg = Release|Win32
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|Win32.Build.0 = Release|Win32
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|x64.ActiveCfg = Release|x64
{26930622-CFA2-43DF-A9F3-4AA4A313B2A4}.Release|x64.Build.0 = Release|x64
+ {CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|Any CPU.Build.0 = Debug|x64
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|ARM.ActiveCfg = Debug|ARM
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|ARM64.ActiveCfg = Debug|ARM64
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|Win32.ActiveCfg = Debug|Win32
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|Win32.Build.0 = Debug|Win32
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|x64.ActiveCfg = Debug|x64
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Debug|x64.Build.0 = Debug|x64
+ {CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|Any CPU.ActiveCfg = Release|x64
+ {CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|Any CPU.Build.0 = Release|x64
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|ARM.ActiveCfg = Release|ARM
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|ARM64.ActiveCfg = Release|ARM64
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|Win32.ActiveCfg = Release|Win32
{CC642427-64D7-44D9-8543-8CBBF981FAE7}.Release|x64.ActiveCfg = Release|x64
+ {1C497821-BD63-4F02-9094-32B185B62F23}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {1C497821-BD63-4F02-9094-32B185B62F23}.Debug|Any CPU.Build.0 = Debug|x64
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|ARM.ActiveCfg = Debug|ARM
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|ARM64.ActiveCfg = Debug|ARM64
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|Win32.ActiveCfg = Debug|Win32
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|Win32.Build.0 = Debug|Win32
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|x64.ActiveCfg = Debug|x64
{1C497821-BD63-4F02-9094-32B185B62F23}.Debug|x64.Build.0 = Debug|x64
+ {1C497821-BD63-4F02-9094-32B185B62F23}.Release|Any CPU.ActiveCfg = Release|x64
+ {1C497821-BD63-4F02-9094-32B185B62F23}.Release|Any CPU.Build.0 = Release|x64
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|ARM.ActiveCfg = Release|ARM
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|ARM64.ActiveCfg = Release|ARM64
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|Win32.ActiveCfg = Release|Win32
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|Win32.Build.0 = Release|Win32
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|x64.ActiveCfg = Release|x64
{1C497821-BD63-4F02-9094-32B185B62F23}.Release|x64.Build.0 = Release|x64
+ {F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|Any CPU.Build.0 = Debug|x64
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|ARM.ActiveCfg = Debug|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|ARM64.ActiveCfg = Debug|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|Win32.ActiveCfg = Debug|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|Win32.Build.0 = Debug|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|x64.ActiveCfg = Debug|x64
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Debug|x64.Build.0 = Debug|x64
+ {F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|Any CPU.ActiveCfg = Release|x64
+ {F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|Any CPU.Build.0 = Release|x64
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|ARM.ActiveCfg = Release|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|ARM64.ActiveCfg = Release|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|Win32.ActiveCfg = Release|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|Win32.Build.0 = Release|Win32
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|x64.ActiveCfg = Release|x64
{F53F3E9C-CC4D-4D1D-9C2E-719FE60A7E6B}.Release|x64.Build.0 = Release|x64
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Debug|ARM.ActiveCfg = Debug|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Debug|ARM64.ActiveCfg = Debug|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Debug|Win32.ActiveCfg = Debug|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Debug|Win32.Build.0 = Debug|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Debug|x64.ActiveCfg = Debug|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Release|ARM.ActiveCfg = Release|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Release|ARM64.ActiveCfg = Release|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Release|Win32.ActiveCfg = Release|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Release|Win32.Build.0 = Release|Win32
- {39F6E3F5-8F0B-4023-BC40-A66AE6C37095}.Release|x64.ActiveCfg = Release|Win32
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|ARM.Build.0 = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|Win32.Build.0 = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Debug|x64.Build.0 = Debug|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|ARM.ActiveCfg = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|ARM.Build.0 = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|ARM64.Build.0 = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|Win32.ActiveCfg = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|Win32.Build.0 = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|x64.ActiveCfg = Release|Any CPU
+ {2D028423-929D-4555-A61E-245BD9EC7383}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/xmake.lua b/xmake.lua
index 3a61b7fe1..32d4c398c 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -51,9 +51,24 @@ if is_arch("x64") or is_arch("x86") then
includes("RimeWithWeasel", "WeaselIPCServer", "WeaselServer", "WeaselDeployer")
end
-if is_arch("x86") then
- includes("WeaselSetup")
-end
+target("Weasel.Setup")
+ on_build(function(target)
+ import("package.tools.msbuild")
+
+ local sln = "weasel.sln"
+ local target_name = string.gsub(target:name(), "%.", "_")
+ local build_type = is_mode("debug") and "Debug" or "Release"
+ local platform = "Any CPU"
+ local configs = {
+ sln,
+ "/t:" .. target_name,
+ "-restore",
+ "/p:Configuration=" .. build_type,
+ "/p:Platform=" .. platform
+ }
+
+ msbuild.build(target, configs)
+ end)
if is_mode("debug") then
includes("test/TestWeaselIPC")