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

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

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...

NSIS: reading DWORD from the registry?

I'm trying to update an existing installer to read a registry value (if it exists) and change the install settings to match existing installation settings when doing upgrade installations.
But I'm getting hung up on not being able to sucessfully read the registry value.
I write the key like this in the installer:
WriteRegDWORD HKLM "Software\${PRODUCT}" "IniPath" 0x00000026
This part works just fine, the key is created or modified, and is of type DWORD, all is well.
But then if I try to read that value back, I seem to be getting an empty value every time.
ReadRegDWORD $IniPath HKCU "Software\${PRODUCT}" "IniPath"
MessageBox MB_OK "$$IniPath=$IniPath"
Results in printing $IniPath=
I also tried doing ClearErrors before reading and IfErrors after, and it does appear to be getting into IfErrors...but I don't understand why it would be having errors and not seeing the key. I am running 32 bit windows, so I doubt this is 64bit "wrong section of the registry" error.
The line to write the DWORD is working just fine. If I delete the registry key, and examine the registry using regedit immediately after, the key is there. But regardless of whether the key existed.
So what could be going wrong to cause it to fail to read the value, or how to debug this?
You are writing to HKLM and reading from HKCU...
To debug registry issues you can use Process Monitor.

Detect if an instance is running with kernel32::CreateMutexA

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.

Resources