Hi,
I have a strange issue and I cannot find a solution. My installer calls an external installer (included in it), but sometimes this external installer reboots the PC. I want my installer to execute some actions before the reboot. How it can handle this?
Execute actions before reboot
64 posts
I don't see how you can, ExecWait does not return until the process has completed. Is this a MSI based external installer?
Yes, it is MSI based installer. I know that I cannot do anything inside my installer, but I'm thinking that I can register somewhere another small application (before to call this external installer) that will be called when Windows tries to reboot and it can do the job.But I don't really know how to do it...
MSI installers take the /norestart command line parameter, and return 3010 if install was successful but requires a reboot.
If the installer didn't take a command line parameter, I could think of some wacky hacks like adding a logoff script and then removing it once the script runs, or just removing it without running it if the installer didn't reboot.
If the installer didn't take a command line parameter, I could think of some wacky hacks like adding a logoff script and then removing it once the script runs, or just removing it without running it if the installer didn't reboot.
Thank you Nutzzz, but ca you explain a little bit the idea of the logoff script. I didn't get it...
Well, since the former method should work for you, that's by far the better solution.
The hacky method I mention is to replicate the method that the group policy editor uses to assign a shutdown script to the machine (or a logoff script for the current user) by using a .reg file with "regedit.exe /s regfile.reg" (and/or a group of WriteRegStr/WriteRegDWORD NSIS commands). See this page for an example .reg file for a startup script.
Your script would be a .bat/.cmd or .vbs file that runs your actions and then applies another .reg file that deletes the registry entries so the shutdown script doesn't run next time around. If you want/need to use NSIS for your actions, you could create another installer or even leverage your uninstaller by calling it with a command line parameter that uses an otherwise unused routine and then immediately quits before the regular uninstall routines begin.
If a reboot isn't performed and execution returns to your installer, then delete the registry entries so that they're never used.
FYI, to delete registry values using a .reg file, assign it an unquoted hyphen (-), e.g.:
The hacky method I mention is to replicate the method that the group policy editor uses to assign a shutdown script to the machine (or a logoff script for the current user) by using a .reg file with "regedit.exe /s regfile.reg" (and/or a group of WriteRegStr/WriteRegDWORD NSIS commands). See this page for an example .reg file for a startup script.
Your script would be a .bat/.cmd or .vbs file that runs your actions and then applies another .reg file that deletes the registry entries so the shutdown script doesn't run next time around. If you want/need to use NSIS for your actions, you could create another installer or even leverage your uninstaller by calling it with a command line parameter that uses an otherwise unused routine and then immediately quits before the regular uninstall routines begin.
If a reboot isn't performed and execution returns to your installer, then delete the registry entries so that they're never used.
FYI, to delete registry values using a .reg file, assign it an unquoted hyphen (-), e.g.:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown\0\0]
"Script"=-
"Parameters"=-
"ExecTime"=-
Hi again Nutzzz,
Thanks a lot. This is exactly what I'm searching for.
Thanks a lot. This is exactly what I'm searching for.
Hi again!
This doesn't work. When the PC is connected to the network, the group policy is changed by the network server. The problem still occurs. Isn't there another way to catch the reboot without hacks?
This doesn't work. When the PC is connected to the network, the group policy is changed by the network server. The problem still occurs. Isn't there another way to catch the reboot without hacks?
One additional question. I found somewhere in Internet that NSIS handles WM_QUERYENDSESSION message and returns FALSE, so it must prevent rebooting from another application. Is there any possibility to execute a function from the script when thos message is received?
Only if you write a custom plug-in. You don't know if Windows is really restarting at that point though.Originally Posted by TrifonovS View PostOne additional question. I found somewhere in Internet that NSIS handles WM_QUERYENDSESSION message and returns FALSE, so it must prevent rebooting from another application. Is there any possibility to execute a function from the script when thos message is received?
See also:
Thank you Anders. I want to avoid writing a plug-in, because my knowledge is not enough. However, is it possible to use code like this:
!include SysFunc.nsh
!include WinMessages.nsh
...
StrCpy $5 $hwndParent
; Create MSG struct
System::Call "*${stMSG} (_) p.R9"
; Get message
System::Call "${sysGetMessage} (R9, r5,_) .s"
!insertmacro SINGLE_CALLBACK 1 $R8 1 ShutDownRequest
...
Function ShutDownRequest
MessageBox MB_OK "NSIS: Shutdown request detected!!!"
FunctionEnd I tried this code, but the installer hangs-up somewhere in these lines. My problem is that this is ready code and I don't understand it completely. Maybe there is an error that I cannot see...You can't use the system plug-in to do this. I wrote a WndSubClass plug-in a long time ago, it might work.
Hi Anders,
I'm following you advice. I installed the WndSubclass plug-in and also executed the example that comes with it. It works fine, but I still can't understand how to use it for my needs. Sorry for that, but can you give me a next hint?
I'm following you advice. I installed the WndSubclass plug-in and also executed the example that comes with it. It works fine, but I still can't understand how to use it for my needs. Sorry for that, but can you give me a next hint?
One more question. I cannot understand from the example why the message is in $2:
${If} $2 = ${WM_NCHITTEST}
; ...
${ElseIf} $2 = ${WM_TIMER}
; ...
${ElseIf} $2 = ${WM_LBUTTONUP}
; ...
${EndIf} Where it comes the value in $2?The plug-in uses $1, $2, $3 and $4 so you don't have to pop IIRC.
Ok! I played a little bit with the example to reach what I want. Probably I still don't understand how the things must work, but here is a part of the modified code:
Function ShutDownEvent
${If} $2 = ${WM_QUERYENDSESSION}
MessageBox MB_OK "ShutDownEvent"
${EndIf}
FunctionEnd
Function licshow
FindWindow $0 "#32770" "" $HWNDPARENT
${WndSubclass_Subclass} $0 ShutDownEvent $8 $8
FunctionEnd I expect that when I try to reboot Windows I will see the message box "ShutDownEvent", but this never happens. What I'm doing wrong?Only top-level windows get this message, you must subclass $HWNDPARENT. Also, WM_QUERYENDSESSION is not the shutdown event, it is the maybe shutdown event. You get one more message if it actually happens.
Great! Thank you, Anders. Now it works. It is not a problem for me if this message doesn't guarantee that the Windows will shut down after it. It will be a problem if I don't understand when Windows will shut down.
I have a new problem now. I moved my code from the example to my official installer and now there is no way to see the message box when reboot is requested. Here is my modified code:
!define MUI_PAGE_CUSTOMFUNCTION_SHOW InstFilesShow
!insertmacro MUI_PAGE_INSTFILES
...
; Function that is called when the installation dialog is shown
Function InstFilesShow
MessageBox MB_OK "InstFilesShow"
; Set a function that will receive the messages to the parent window
${WndSubclass_Subclass} $HWNDPARENT GetWinMessage $8 $8
FunctionEnd
; Function that receives the messages to the parent window
Function GetWinMessage
MessageBox MB_OK "Got a message!"
${If} $2 = ${WM_QUERYENDSESSION}
MessageBox MB_OK "End session query!"
${EndIf}
FunctionEnd When I start the installer I see the message box with text "InstFilesShow", but the function GetWinMessage is never called (I don't see even the message box with text "Got a message!". I really don't have an idea why...Putting a MessageBox that is displayed for all messages is not a good idea, remember that MessageBox itself will end up sending a message to the window that owns it (changing active window etc.) and it quickly ends up with a stackoverflow or something else bad.
Yes, I know this. Initially I didn't have this message box, but because I cannot see anymore the other text "End session query!" I added it. I removed it again, but the text "End session query!" doesn't appear when the installer's install page is shown and I try to reboot Windows. This worked well in the example, based on WndSubclass.ini, but not inside of my installer...
Maybe you are using other plug-ins that also subclass? Impossible to tell without more information.
Use System::Call 'kernel32::OutputDebugString(ts)' "Hello World$\n" and DebugView from SysInternals instead of MessageBox for something like this.
Use System::Call 'kernel32::OutputDebugString(ts)' "Hello World$\n" and DebugView from SysInternals instead of MessageBox for something like this.
I made this. I see that the function InstFilesShow is called, but function GetWinMessage - never. The $HWNDPARENT contains value 592010 and the returned value in $8 is "NONE". Is this useful information?
I was thinking more information about which other plug-ins you are using. Try commenting out other code until it works.
The value of $HWNDPARENT is not important, it just needs to be <> 0. "None" in $8 does not seem right, it should probably be a number and <> 0. This plug-in is also ANSI only.
The value of $HWNDPARENT is not important, it just needs to be <> 0. "None" in $8 does not seem right, it should probably be a number and <> 0. This plug-in is also ANSI only.
This is the problem. Thanks for the idea. I cannot imagine that I couldn't guess myself. The example is using ANSI plug-ins, but my installer - Unicode. And I cannot change this. I remember that I have seen somewhere in Internet a macro that converts the parameters. I hope that this will help. I another case I'm on a dead end again.
There is a CallAnsiPlugin plug-in but it will not work here. I guess you could create a little helper ANSI app.
On a first look it sounds complex to me 🙂. But if there is no other way I should make it. By the way I found this (it's only a part of the code, cause it is integrated in a macro):
Error: resolving install function "$0" in function "InstFilesShow"
Exch $0
System::Call 'kernel32::WideCharToMultiByte(i0,i0,w"$0",i-1,t.s,i${NSIS_MAX_STRLEN},i0,i0)i'
Pop $0
Exch $0 But it can't convert the function's name. I thought that it will be converted like a string, but it doesn't work like this. The compiler show error message:Error: resolving install function "$0" in function "InstFilesShow"
You cannot convert on your own.
I found the following plug-in: http://nsis.sourceforge.net/CallAnsiPlugin_plug-in.
It looks that it already can do what I need. I will try it.
It looks that it already can do what I need. I will try it.
I read in Internet that this plug-in will not going to work by sub-classing? Is this so?