Detect if an instance is running with kernel32::CreateMutexA - nsis

I'm working on an NSIS installer, and trying to check if a certain application is running before uninstalling. So, I use kernel32::CreateMutexA call. Here is the chunk:
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "cmd.exe") i .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_USERICON "The application is already running."
Abort
I put it into un.onInit. Trouble is, the process (cmd.exe here) is never detected.
Did I miss something?
Tx.

I found a simple solution; using FindProcDLL plugin.
So:
FindProcDLL::FindProc "cmd.exe"
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_USERICON "The application is already running." IDOK
Abort
P.S. FindProcDLL.dll must be copied into /Plugins.

All you are doing is creating a mutex with the global name "cmd.exe". From the MSDN article for CreateMutex:
If lpName matches the name of an existing event, semaphore, waitable
timer, job, or file-mapping object, the function fails and the
GetLastError function returns ERROR_INVALID_HANDLE. This occurs
because these objects share the same name space.
So unless cmd.exe creates a handle to one of those types of objects with the name "cmd.exe", this call will simply create a new mutex with that name and return you the (non-erronous) handle.

You're probably using the wrong Win32 API function. Your CreateMutex tries to create a named mutex "something.exe". If there isn't one with that name it will succeed, so unless the process you're trying to check is creating a mutex with that name, you won't get the result you're after.
What you want is probably to enumerate all running processes and see if the one you're after is there. You can do this with ToolHelp32 from Win32 API - see sample here. I don't know how easy it will be to convert it to 'pure' NSIS so you might want to write a DLL plugin, or check if there's an existing solution floating around the NSIS community.

For this kind of thing, I've used either the KillProcess or Find-Close-Terminate plugins for NSIS - see here
Documentation is pretty straightforward, hopefully this does what you need - with a fairly minimal overhead.

Related

how to move files on reboot in nsis

i need to move files on reboot from source to destination. tried below 3 methods and all have failed
IfFileExists "$Temp\test.dll" 0 new_installation
StrCpy $ALREADY_INSTALLED 1
new_installation:
SetOverwrite on
!insertmacro InstallLib DLL $ALREADY_INSTALLED REBOOT_PROTECTED "$Temp\test.dll" "$WINDIR\test.dll" "$WINDIR"
System::Call "kernel32::MoveFileEx(t '$TEMP\test.dll', t '$WINDIR\test.dll', i 5)"
Rename /REBOOTOK '$TEMP\test.dll' `$WINDIR\test.dll`
We confirmed elsewhere that your direct call to MoveFileEx succeeds. This should mean that the rename operation has been recorded in the registry. You can confirm this with the PendMoves tool from SysInternals. That same page also provides a MoveFile tool you can try.
Assuming the operation has been recorded, you should investigate the reasons the file operation might fail during boot:
One or both files are read-only.
The security descriptor (ACL) blocks the move and/or delete operation.
The file you are trying to replace is a protected system file.
File is locked by the system or a driver.
According to this answer, Process Monitor boot logging is active before the rename operation begins so you could use it to see if it provides any clues as to why the operation fails...

NSIS - Run a function after the installer released the files

I'm using NSIS to create a simple installer, and then NsisXML plug-in by Joel to change some things in the config.xml file.
I've created a simple function that does this:
Function ChangeConfig
${nsisXML->OpenXML} "$INSTDIR\configHexd.xml"
${nsisXML->CreateElement} "/config/config_hexd/paramlist" "param" ""
${nsisXML->SetElementAttr} "/config/config_hexd/paramlist/param[10]" "name" "logPath"
${nsisXML->SetElementAttr} "/config[0]/config_hexd/paramlist/param[10]" "value" "$INSTDIR/logs"
${nsisXML->CloseXML}
FunctionEnd
and I'm calling it like this:
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\run.exe"
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\readme.txt"
!define MUI_PAGE_CUSTOMFUNCTION_PRE ChangeConfig
!insertmacro MUI_PAGE_FINISH
The script compiling goes well and I get my setup.exe. The only problem is, the setup crashes with an 'unknown runtime error' when it's copying the files. This only happens when I'm using this function, if I don't call it everything goes well. Also everything goes well if I just open and close the XML file without trying to write into it. It only crashes when I try writing into the file.
I think that's because the installer tries writing to the file before it's done copying it over, and I'm wondering how I can prevent that.
Thanks!
Ok, I solved it, and it was way easier than I thought.
First of all, my node was not called paramlist but paramList, that was the main problem.
Second, as #Anders said, I shouldn't have used MUI_PAGE_CUSTOMFUNCTION_PRE for my problem, a simple Call of the function after the installer was finished transffering the files was enough.
So, lesson 1: Check for typos before blaming the program
Lesson 2: Read the docs to fully understand what something does.
Lesson 3: In most cases, when you find the answer, you're gonna be baffled as to how easy it was to solve the problem.

Where can I find nsis error codes?

I'm building an nsis script and sometimes I print the error codes, but then I cant find anything on the web that maps my error id to a proper description on whats the error.
There isn't any error code listage for NSIS?
Thanks
If we take the example you posted on the NSIS forum: ExecWait "net start Apache 2.2" $0 then $0 will contain the exit code of the net.exe process and you cannot really know for sure what they mean other than that 0 usually means success. Some applications use the Win32 error codes and you can look them up here. Net.exe is documented here.
If you want to control a service then there are better ways of doing that in NSIS...

How to Create Custom View in Windows Event Viewer using API?

Is there an API to create a Custom View in Windows Event Viewer?
I would like to do that in the installation using NSIS,
But if I cant and there is any other way then I'll write a plugin for it.
The problem is not NSIS but the way the Event Log works, you need to install a message table DLL somewhere on the system and register it in the registry. This DLL has to stay on the system since it is used by the Event Log Viewer when you view the log and is not used when you log the message! This design is really problematic for a installer.
It is probably better to just log to a plain text file in $temp or $exedir.
If you really want to use the Event Log and you don't mind hacks you could:
Put the message table DLL in $pluginsdir and register/unregister when the installer starts/ends (If it crashes you leave junk in the registry!)
Use a Microsoft/Windows message table (They are not documented, could change from version to version (And service packs?) so you would have to test every Windows version and configuration you support!)
The NSIS code looks something like this:
section
System::Call 'advapi32::RegisterEventSource(i0,t "Software Installation")i.r0'
#TODO: Check if $0 == 0 and display error message
System::Call '*(i,&t1000 "Hello world")i.r1'
IntOp $2 $1 + 4
System::Call '*$1(ir2)'
System::Call 'advapi32::ReportEvent(ir0,i ${EVENTLOG_ERROR_TYPE},i0,i 101,i0,i1,i0,ir1,i0)i'
System::Free $1
System::Call 'advapi32::DeregisterEventSource(ir0)'
sectionend

NSIS callback function crashes installer when file is not present

I'm stuck on this.
I have this callback function at the bottom of a subscript (.nsh) file.
(As you can see I'm using LogicLib):
Function InstallFoo
MessageBox MB_OK "Within InstallFoo function"
${If} ${FileExists} "$EXEDIR\Modules\foo.zip"
MessageBox MB_OK "foo.zip found, do install it!"
nsisunz::Unzip "$EXEDIR\Modules\foo.zip" "$INSTDIR\Foo"
${Else}
MessageBox MB_OK "No foo.zip found. Do nothing"
${EndIf}
MessageBox MB_OK "End reached"
FunctionEnd
Everything works exactly as I want it to when the 'foo.zip' is present, but when it isn't, the installer crashes, and I really can't understand why.
I would expect it to also be able to handle when the 'foo.zip' is not found, that is by doing nothing. What happens now is that the installer crashes with
"setup.exe - Application Error, The instruction at "some-address" referenced memory at "some-other-address". The memory could not be "read".
I have the same type of code within sections in my scripts and it works fine there, whether zip-files present or not. It's just in this callback function it doesn't work, so I'm starting to think it has something to do with it being a callback function.
In the 'foo.zip file not present' - case I get the following MessageBoxes: "Within InstallFoo function" "No foo.zip found. Do nothing" "End reached"
And then the crash. I have a MessageBox printout within the section where the callback was called from also, that is, the first thing to happen after the return from the callback function, but it never gets there.
Note, as soon as the foo.zip is there everything works just fine!
Does anyone have any idea of what I might be doing wrong here?
It seems to me I get the crash whenever nothing's done within the callback function.
Is it that NSIS allocates some memory for the callback function, and if it's not used we get the crash..?? Weird.

Resources