Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NSIS installer always show "XYZ cannot be closed. Please close it ..." when there is other process name contains "XYZ" #6865

Open
athenakia opened this issue May 14, 2022 · 42 comments

Comments

@athenakia
Copy link

  • Electron-Builder Version: 23.0.3
  • Node Version: 16.3.1
  • Electron Version: 16.2.5
  • Electron Type (current, beta, nightly):current
  • Target: Windows 11

In my case, productName of my project is XYZ, it allows user to install a service named XYZ Helper on Windows, which runs when the operating system starts up. When I use electron-builder(23.0.3) to package(target: NSIS), the installer always show XYZ cannot be closed. Please close it manually and click retry to continue. even after I'm sure to quit XYZ completely. Only after I also stop the service named XYZ Helper, this step can be processed. But XYZ Helper is not part of XYZ, which is a separated process and should not be stopped. electron-builder(22.14.13) doesn't have this problem.

@athenakia
Copy link
Author

This problem does not exist on version 22.14.13 or previous.

@qingniao927
Copy link

23.1.0 version , also not working

@chemistryx
Copy link

same here, version with 23.0.3

@nickfujita
Copy link

nickfujita commented Jul 25, 2022

Getting the same error when trying to change the install location to Program Files. When using the default install location, or install to any directory outside of Program Files, am not getting this error (eg. C:\MyApp)

Originally using 23.0.3, but tried upgrading to 23.3.1 & 23.3.2 (pre-release), but both also had this issue.

Interestingly if right clicking on the .exe installer and selecting Run as administrator this issue does not come up. So not sure if this is a signing issue? I do not currently have any signing implemented for windows, but will try this.

Also tried downgrading to 22.14.13, but got a different error: Error opening file for writing: C:\Program Files\MyApp\Uninstall MyApp.exe

Using the custom path described in docs: https://www.electron.build/configuration/nsis.html#common-questions

EDIT: main goal to install to Program Files was resolved by simply setting perMachine to true rather than setting manually with custom .nsh script. This issue had the same error message, but ultimately a different issue.

@jdunkerley
Copy link

Saw this in our project too - still a problem in v23.3.3.

From a bit of looking around and experimenting it seems that the addition of args.push("-INPUTCHARSET", "UTF8") to packages/app-builder-lib/src/targets/nsis/NsisTarget.ts is causing the issue (but no idea why).

With that line removed the produced installer works fine.

mwu-tow added a commit to enso-org/enso that referenced this issue Aug 24, 2022
* Downgrading the electron-builder to avoid electron-userland/electron-builder#6865
* Removed "git clean" workaround from CI.
mwu-tow added a commit to enso-org/enso that referenced this issue Sep 21, 2022
This PR reverts two version bumps from #3712:
* `yargs` needs to be downgraded, because a never version can trigger evanw/esbuild#2441
* `electron-builder` needs to be downgraded, because our workaround for electron-userland/electron-builder#6865 proved insufficient.

As the upstream issues are resolved, we should bump these dependencies again.
@sergeushenecz
Copy link

Hi. Any solutiuon exist or need to downgrade?

@x6pnda
Copy link

x6pnda commented Dec 21, 2022

I'm fairly impressed that this issue isn't high priority right now... Currently downgrading as this is a breaking bug for our Windows users. A solid 5% of our users got hit with this bug which can't happen in a production build ofcourse

@x6pnda
Copy link

x6pnda commented Dec 23, 2022

For the developer picking this up, somehow FIND_PROCESS inside packages/app-builder-lib/temapltes/nsis/include/allowOnlyOneInstallerInstance.nsh says that a process is running while if you execute the same command in the command line, it reports nothing is running. It gets stuck because it reports there is a process active but it can't kill any of the processes as there are non active which causes you to be stuck in an infinite loop.

EDIT 2: Alright so the issue is the following: the exit code of FIND_PROCESS is 0. This is probably because there is a parent process active which doesn't fall under the same name. It's on admin level so the user can't close it as it doesn't know the name, we can't close it as it's on admin level and it keeps looping. I think the solution to this problem is to show a list of running processes and display the list of processes blocking the installer instead of a simple retry.

Edit: Seems I have found it. When I have the installer "test-app-installer.exe" open for "Test App.exe" for an one click installation for an user installation, the allowOnlyOneInstallerInstance.nsh calls FIND_PROCESS which execs the following command: cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq Test App.exe" | %SYSTEMROOT%\System32\find.exe "Test App.exe". While having the installer open, I check this command inside my cmd and it returns 0 as exit code (so blocking installation) but if I look at the result, there are no processes active (clear result)

@sandeep1995
Copy link
Contributor

Anyone have a solution ?

@x6pnda
Copy link

x6pnda commented Jan 4, 2023

Definitely not a solution, but you can include a custom script with a macro so app-builder-lib uses this macro instead of the default one.

In add a path to a custom .nsh script file in your package.json under build:
"nsis": { "include": "installer/installer.nsh" }

Inside the installer.nsh just add:

!macro customCheckAppRunning

!macroend

This will bypass the WHOLE check. You can add the code from allowOnlyOneInstallerInstance.nsh and edit it so it works for you. I don't have enough knowledge to propose permanent a solution but this is a step for people to make their own and create a PR to fix it! To iterate it again: THIS WILL BYPASS THE WHOLE RUNNING CHECK SO THIS MIGHT CORRUPT FILES IF THE PROGRAM IS ALREADY OPEN!

@mmaietta
Copy link
Collaborator

mmaietta commented Jan 6, 2023

From a bit of looking around and experimenting it seems that the addition of args.push("-INPUTCHARSET", "UTF8") to packages/app-builder-lib/src/targets/nsis/NsisTarget.ts is causing the issue (but no idea why).
With that line removed the produced installer works fine.

Quick note. This was added to resolve these (based off the commit message):
#4898 If the path contains Chinese characters, packaging will fail.
#6232 shortcutName in chinese
#6259 Random code After packing

NSIS installer always show "XYZ cannot be closed. Please close it ..." when there is other process name contains "XYZ"

I'm wondering if there's a way for us to identify it other than "contains" and instead look for an exact match? Not sure if that's where the bug is originating though.

At a base level, my apologies for this issue being stagnant. I've been the only maintainer on this project for some time now and am largely dependent upon community contributions, especially when relating to windows and linux (I'm predominantly a mac user).

@x6pnda
Copy link

x6pnda commented Jan 8, 2023

I'm wondering if there's a way for us to identify it other than "contains" and instead look for an exact match? Not sure if that's where the bug is originating though.

The weird thing is, even on brand new systems with nothing on it this bug will happen. I have verified the results and every user who reported this error has a clear prompt of the exact tasklist command while it still returns 0 inside the nsis installer. At this point i'm guessing it's find.exe or the ns exec plugin for nsis doing something weird rather than the tasklist command. The tasklist command always returns 0 as result so we can't exclude find.exe without a replacement. I'll take a look myself next week to research all of the nsis file changes but it's really hard to test as I haven't been able to replicate it. (I'm already on my 3rd VPS and still no luck)

@mmaietta
Copy link
Collaborator

mmaietta commented Feb 3, 2023

Soooo circling back, what do we do here? Completely rewrite the "Application still running" logic? Or just revert the implementation that was introduced in electron-builder 23.x.x?
I'm really out of my element here so I personally am unable to do the rewrite approach. I'd love to unblock you guys such that you can update electron-builder to latest.

@mmaietta
Copy link
Collaborator

mmaietta commented Mar 2, 2023

Curious. Would anyone mind trying out this?
Replace the final portion of:

nsExec::Exec `cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" | %SYSTEMROOT%\System32\find.exe "${_FILE}"`

With:

| findstr /B /C:"INFO: " > nul

I think that'll provide the return value info we're looking for?
Ref: https://stackoverflow.com/questions/56569414/process-names-over-21-characters-not-detected-supported-by-tasklist/56577365#56577365

Curious if this would fix the issue, but I have no manner with which to test

@SergiiMarkin
Copy link

In my case, If I set perMachine: false, then It will throw the same error when installing .exe file.
All of my version is same as @athenakia .

@mmaietta Is there any solution to prevent this error?

@personalizedrefrigerator
Copy link

personalizedrefrigerator commented Jul 11, 2023

I'm going to try explaining what I think the relevant portions of the NSIS script are doing and what a probable issue is. (I'm not certain about this).

My understanding of the relevant parts of the NSIS script

An explanation of relevant parts of the NSIS script

The cmd /c tasklist /FI ... line

If I open up cmd.exe and run

cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq cmd.exe"

I get something similar to this:

Image Name                  PID Session Name
====================== ======== =============
cmd.exe                    1234 Console
cmd.exe                    5678 Console
cmd.exe                    7654 Console

The first filter, /FI "USERNAME eq %USERNAME%" should return only processes running under the current user (the user with %USERNAME%).1

The second filter, /FI "IMAGENAME eq cmd.exe" should return only processes with cmd.exe in the "Image Name" column.

Thus, if we then run

cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq cmd.exe" | find "cmd.exe"

only lines that contain "cmd.exe" should be printed out:

cmd.exe                    1234 Console
cmd.exe                    5678 Console
cmd.exe                    7654 Console

Similarly, as can be seen by printing %errorlevel%, find has exit status 0 when matches are found and non-zero exit status when no matches are found.2

What the nsExec::Exec ... Pop ${_ERR} part is (probably) doing

According to the NSIS documentation3, NSIS maintains a stack where

Push "something here"

pushes values onto the stack and

Pop ${foo}

pops values off the top of the stack and stores them in the variable foo. Presumably,

    nsExec::Exec `cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" | find.exe "${_FILE}"`
    Pop ${_ERR}

executes the command

cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" | find.exe "${_FILE}"

and nsExec::Exec stores its exit status on the stack. Hence, Pop ${_ERR} moves the exit status from the stack into the variable _ERR.

Observe that FIND_PROCESS is being used like this:

!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
${if} $R0 == 0
${if} ${isUpdated}
# allow app to exit without explicit kill
Sleep 1000
Goto doStopProcess
${endIf}
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "$(appRunning)" /SD IDOK IDOK doStopProcess
Quit

Thus, if | find.exe "${_FILE}" exits with status 0, we assume that the app executable is still running.

A possible issue

Notice that running app processes are killed like this:

      doStopProcess:

      DetailPrint `Closing running "${PRODUCT_NAME}"...`

      # https://github.com/electron-userland/electron-builder/issues/2516#issuecomment-372009092
      !ifdef INSTALL_MODE_PER_ALL_USERS
        nsExec::Exec `taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid"`
      !else
        # ↓ This line! ↓
        nsExec::Exec `cmd /c taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
        #                                                                  ↑
        #                                            Notice the "PID ne $pid"
      !endif

where /fi "PID ne $pid" causes the command to kill only processes that don't have the same PID as this one (where $pid is defined at the beginning of _CHECK_APP_RUNNING to be the pid of the current process).

Thus, we should (probably) also be filtering the current process' PID out of the list in FIND_PROCESS.

A patch

I'm currently using this patch:

diff --git a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
index a1fd1875d852ff69c087a3103eff827c20d37ca5..5222614ddad3276876857e7a9dde4017a6b9fc85 100644
--- a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
+++ b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
@@ -42,7 +42,7 @@
     ${nsProcess::FindProcess} "${_FILE}" ${_ERR}
   !else
     # find process owned by current user
-    nsExec::Exec `cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
+    nsExec::Exec `cmd /c tasklist /FI "USERNAME eq %USERNAME%" /FI "PID ne $pid" /FI "IMAGENAME eq ${_FILE}" | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
     Pop ${_ERR}
   !endif
 !macroend

It seems to work, but it's based on the assumption that, without this change, the tasklist includes the installer, which might not be true.

Edit: Given this comment:

I'm wondering if there's a way for us to identify it other than "contains" and instead look for an exact match? Not sure if that's where the bug is originating though.

The weird thing is, even on brand new systems with nothing on it this bug will happen. I have verified the results and every user who reported this error has a clear prompt of the exact tasklist command while it still returns 0 inside the nsis installer. At this point i'm guessing it's find.exe or the ns exec plugin for nsis doing something weird rather than the tasklist command. The tasklist command always returns 0 as result so we can't exclude find.exe without a replacement. I'll take a look myself next week to research all of the nsis file changes but it's really hard to test as I haven't been able to replicate it. (I'm already on my 3rd VPS and still no luck)

My guess is that the above patch does not work.

Footnotes

  1. tasklist documentation: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/tasklist#parameters

  2. find documentation: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/find#exit-codes

  3. https://nsis.sourceforge.io/Docs/Chapter4.html#plugindlls

@AviVahl
Copy link
Contributor

AviVahl commented Oct 4, 2023

We've (https://www.codux.com/) been hit quite hard with this one.
Several of our users have reported failures to install our new version over the old one with the same error dialog.
The uninstaller, which gets launched by the installer, fails 5 times.
Launching the uninstaller manually from "Apps/Installed-Apps/Add-Remove" works.

Our investigation found several possible changes that caused this:
#6551
#5902

We've forcibly ran the uninstaller in non-silent mode from installer (e.g. removed the /S) and saw it failing to remove the application directory.

Tried several workarounds. We're currently trying to define customRemoveFiles to RMDir /r $INSTDIR and see if it helps.

EDIT: we're adding the following .nsh script using electronBuilderConfig->nsis->include:

!macro customRemoveFiles
    DetailPrint "Removing files..."
    RMDir /r $INSTDIR
 !macroend

@AviVahl
Copy link
Contributor

AviVahl commented Oct 4, 2023

Alright, more info. Our installation contained a file with a really long file path (247 characters long). This caused the rename operation to fail on some machines (depending on the user name, where the temp dir is, etc).
It probably reached the 256 limit when trying to rename it and failed.

It seems that when the uninstaller fails, the installer assumes it's because the app is still running, giving a wrong error message?

Not sure about the above statement ^, but it wouldn't surprise me if that's the culprit of many failures that were reported here.

@cylon3035
Copy link

cylon3035 commented Oct 4, 2023

It is possible to get same error during first clean installation if install path + file path in the installation is greater than 256.
First installer unzips files into temp directory then copies to install location. If it fails then error XYZ cannot be closed. Please close it manually and click retry to continue. will be displayed even thought XYZ was never installed before.

If an installation package contains really long file paths then install path can be the last drop to push file path length over 256 limit when copying files to install location.
Different users have different user names or longer default install locations. It explains magic nature of reproducing this bug.

@AviVahl
Copy link
Contributor

AviVahl commented Oct 4, 2023

Yes, the error message is incorrect. In our case it was too long file paths.

@sergeushenecz
Copy link

Any workarounds exists? Now i downgrade to 22.14.13 and it works.

@JumpinHack
Copy link

The 'customRemoveFiles' solution as suggested by @AviVahl did the trick for us, but I think this is not much different from downgrade to version 22.14. Using customRemoveFiles completely bypass the atomic renaming stuff (and I think it is a good thing to have... when it works).
We don't have long names, nor strange charsets (like chinese), and we hit the problem ONLY on Windows 11, on Windows 10 it' going as expected (is it possible that win11 has more restriction on path length than win10???)

@nitsir
Copy link

nitsir commented Nov 13, 2023

Same issue.
Fixed with CRCCheck off
#6409

mergify bot pushed a commit to enso-org/enso that referenced this issue Nov 16, 2023
This PR avoids #8119 by selectively bumping the `electron-builder` on macOS CI runners. We do this only on macOS, as we do not want to trigger electron-userland/electron-builder#6865 on Windows.
@flying19880517
Copy link

StrCpy and RM and other cmd not support long file path, un.atomicRMDir always failed.
I'm also use "include" to use custom .nsh, but customRemoveFiles seems not run for my project, customInit works fine.
Only delete Uninstall.exe can skip copy program files, and install success.

!macro customInit
  Delete "$INSTDIR\Uninstall*.exe"
!macroend

@mirkin
Copy link

mirkin commented Jan 23, 2024

I'm having this issue also, tried most of the fixes suggested but the only thing that worked was reverting to "electron-builder": "22.14.13"

@sergeushenecz
Copy link

@mirkin The same. Only reverted to this version works as excepted.

@VigneshSonaiya
Copy link

I think this chunk of code was changed in this commit. I don't have knowledge on nsh script. maybe someone can check this.
https://github.com/electron-userland/electron-builder/pull/6771/files

@maoryadin
Copy link

maoryadin commented May 13, 2024

Weird, renaming the file to a really short name, make it work.
No changes needed

@felippenardi
Copy link

I'm also having this problem, we have had no updates yet?

@mmaietta
Copy link
Collaborator

mmaietta commented Jul 5, 2024

I need help recreating this issue via a minimum reproducible repo, then I can debug into this
Ref: #6865 (comment)

@Ali-Albaker
Copy link

This is still an issue

@ObadaZay
Copy link

ObadaZay commented Aug 26, 2024

StrCpy and RM and other cmd not support long file path, un.atomicRMDir always failed. I'm also use "include" to use custom .nsh, but customRemoveFiles seems not run for my project, customInit works fine. Only delete Uninstall.exe can skip copy program files, and install success.

!macro customInit
  Delete "$INSTDIR\Uninstall*.exe"
!macroend

tested this solution (workaround) on windows 11 electron builder 25.0.4 and it is working, the cause of the issue was long user name on windows account

@finnlaymfarmor
Copy link

Also having these issues with 25.0.5, seriously breaking.

@finnlaymfarmor
Copy link

StrCpy and RM and other cmd not support long file path, un.atomicRMDir always failed. I'm also use "include" to use custom .nsh, but customRemoveFiles seems not run for my project, customInit works fine. Only delete Uninstall.exe can skip copy program files, and install success.

!macro customInit
  Delete "$INSTDIR\Uninstall*.exe"
!macroend

tested this solution (workaround) on windows 11 electron builder 25.0.4 and it is working, the cause of the issue was long user name on windows account

Will report back on how this goes

@ssmethurst
Copy link

I had the same problem. The installer would work as expected on the first install, but on the 2nd install would produce this error.

StrCpy and RM and other cmd not support long file path, un.atomicRMDir always failed. I'm also use "include" to use custom .nsh, but customRemoveFiles seems not run for my project, customInit works fine. Only delete Uninstall.exe can skip copy program files, and install success.

!macro customInit
  Delete "$INSTDIR\Uninstall*.exe"
!macroend

This workaround worked for me.

Tested with "electron": "^32.2.0", "electron-builder": "^25.1.7". On Windows 8, 10, 11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests