fix(installer): unregister TSF IME before replacing OpenLessIme.dll on upgrade (#321) (#369)

Auto-update was failing on Windows because OpenLessIme.dll is a TSF input-method
DLL: once registered, Windows maps it into every text-input client process
(browsers, IDEs, chat apps...). The installer's PREINSTALL hook tried to
overwrite the file directly while it was still mapped, hitting a sharing
violation and aborting the update.

NSIS hook (openless-ime-hooks.nsh):
- PREINSTALL now calls OPENLESS_IME_UNREGISTER_X86/X64 first to release the
  COM/TSF registration. On a fresh install this is a harmless no-op (existing
  unregister macros log the exit code without aborting).
- DLL bytes are written to OpenLessIme.dll.new and atomically renamed into
  place. If the old DLL is still locked by a TSF client process, Delete fails
  and we fall back to Rename /REBOOTOK + SetRebootFlag — Windows queues the
  replacement via PendingFileRenameOperations and the new bytes activate after
  the next reboot. Same TSF registration path is preserved, so re-registration
  in POSTINSTALL stays valid across the reboot.

WiX (openless-ime.wxs):
- UnregisterOpenLessImeX86/X64 now also fire Before InstallFiles when a
  previous version is installed (Installed AND NOT REMOVE). Original condition
  (REMOVE="ALL", Before RemoveFiles) only covered the dedicated uninstall
  sequence; auto-update / minor-upgrade skipped it entirely and ran straight
  into the same sharing violation as NSIS.

Bumps to 1.2.24-5 (Beta-5).

Co-authored-by: baiqing <lbx12309@icloud.com>
This commit is contained in:
TRIPandbaiqing authored and GitHub committed 2026-05-08 19:58:05 +08:00
1 parent d51f6ee59e
commit b44cb998af
7 files changed
+40 -13

No files matched your search

+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "openless-app",
"version": "1.2.24-4",
"version": "1.2.24-5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openless-app",
"version": "1.2.24-4",
"version": "1.2.24-5",
"dependencies": {
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-autostart": "^2.5.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "openless-app",
"private": true,
"version": "1.2.24-4",
"version": "1.2.24-5",
"type": "module",
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -3751,7 +3751,7 @@ dependencies = [
[[package]]
name = "openless"
version = "1.2.24-4"
version = "1.2.24-5"
dependencies = [
"anyhow",
"arboard",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "openless"
version = "1.2.24-4"
version = "1.2.24-5"
description = "OpenLess — local voice input that types where your cursor is"
authors = ["OpenLess"]
edition = "2021"
@@ -56,12 +56,34 @@
!insertmacro OPENLESS_IME_ABORT_IF_FAILED $0 "x86 registration"
!macroend
!macro NSIS_HOOK_PREINSTALL
SetOutPath "$INSTDIR\windows-ime\x64"
File /oname=OpenLessIme.dll "$%OPENLESS_IME_DLL_X64%"
!macro OPENLESS_IME_STAGE_AND_REPLACE PLATFORM_DIR ENV_VAR
; Write new bytes to OpenLessIme.dll.new, then atomically replace the old file.
; If the old DLL is still mapped into TSF client processes (browsers, IDEs,
; chat apps, ...) and Delete fails, queue the rename for next reboot via
; PendingFileRenameOperations. Issue #321.
SetOutPath "$INSTDIR\windows-ime\${PLATFORM_DIR}"
File /oname=OpenLessIme.dll.new "$%${ENV_VAR}%"
ClearErrors
Delete "$INSTDIR\windows-ime\${PLATFORM_DIR}\OpenLessIme.dll"
${If} ${Errors}
DetailPrint "OpenLessIme.dll (${PLATFORM_DIR}) is in use; queuing replacement at next reboot"
Rename /REBOOTOK "$INSTDIR\windows-ime\${PLATFORM_DIR}\OpenLessIme.dll.new" "$INSTDIR\windows-ime\${PLATFORM_DIR}\OpenLessIme.dll"
SetRebootFlag true
${Else}
Rename "$INSTDIR\windows-ime\${PLATFORM_DIR}\OpenLessIme.dll.new" "$INSTDIR\windows-ime\${PLATFORM_DIR}\OpenLessIme.dll"
${EndIf}
!macroend
SetOutPath "$INSTDIR\windows-ime\x86"
File /oname=OpenLessIme.dll "$%OPENLESS_IME_DLL_X86%"
!macro NSIS_HOOK_PREINSTALL
; Upgrade path: release the COM/TSF registration so new client processes won't
; bind to the old DLL while we replace it. Fresh install: regsvr32 /u against
; a missing DLL is harmless — the existing UNREGISTER macros log the exit code
; and continue without aborting.
!insertmacro OPENLESS_IME_UNREGISTER_X86
!insertmacro OPENLESS_IME_UNREGISTER_X64
!insertmacro OPENLESS_IME_STAGE_AND_REPLACE "x64" "OPENLESS_IME_DLL_X64"
!insertmacro OPENLESS_IME_STAGE_AND_REPLACE "x86" "OPENLESS_IME_DLL_X86"
SetOutPath "$INSTDIR"
!macroend
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "OpenLess",
"version": "1.2.24-4",
"version": "1.2.24-5",
"identifier": "com.openless.app",
"build": {
"beforeDevCommand": "npm run dev",
@@ -45,8 +45,13 @@
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="UnregisterOpenLessImeX86" Before="RemoveFiles"><![CDATA[REMOVE="ALL"]]></Custom>
<Custom Action="UnregisterOpenLessImeX64" Before="UnregisterOpenLessImeX86"><![CDATA[REMOVE="ALL"]]></Custom>
<!-- Issue #321: unregister the old DLL before InstallFiles whenever a
previous version is installed, so Windows Installer can overwrite
OpenLessIme.dll instead of failing on a sharing violation. The
previous condition (REMOVE="ALL") only fired during dedicated
uninstall and skipped the auto-update / minor-upgrade path. -->
<Custom Action="UnregisterOpenLessImeX86" Before="InstallFiles"><![CDATA[Installed AND NOT REMOVE]]></Custom>
<Custom Action="UnregisterOpenLessImeX64" Before="UnregisterOpenLessImeX86"><![CDATA[Installed AND NOT REMOVE]]></Custom>
<Custom Action="RegisterOpenLessImeX64" After="InstallFiles"><![CDATA[NOT REMOVE]]></Custom>
<Custom Action="RegisterOpenLessImeX86" After="RegisterOpenLessImeX64"><![CDATA[NOT REMOVE]]></Custom>
</InstallExecuteSequence>