ITaskScheduler::Delete fails - scheduled-tasks

!define GUIDTaskScheduler "{148BD52A-A2AB-11CE-B11F-00AA00530503}"
!define GUIDITaskScheduler "{148BD527-A2AB-11CE-B11F-00AA00530503}"
Function DeleteTask
!define DeleteTask "!insertmacro _DeleteTask"
!macro _DeleteTask _TASK _RESULT
Push ${_Task}
Call DeleteTask
Pop ${_RESULT}
!macroend
Exch $0
Push $1
Push $2
StrCpy $0 false
System::Call "ole32::CoCreateInstance(g'${GUIDTaskScheduler}',i0,i11,g '${GUIDITaskScheduler}',*i.r1)i.r2"
IntCmp $2 0 0 +4
System::Call '$1->7(w r0)i.r2'
IntCmp $2 0 0 +2
StrCpy $0 true
Pop $2
Pop $1
END:
Exch $0
FunctionEnd
${DeleteTask} "TaskName" $0
Pop $0
$0 should set to true or 1 but it's false. The task isn't deleting.
What am I doing wrong here?

Printing out the HRESULT error would probably give you a clue.
It looks like you are overwriting the service name with StrCpy $0 false!
You should also release the interface in $1 after you are done with it.
!define GUIDTaskScheduler "{148BD52A-A2AB-11CE-B11F-00AA00530503}"
!define GUIDITaskScheduler "{148BD527-A2AB-11CE-B11F-00AA00530503}"
Function DeleteTask
!define DeleteTask "!insertmacro _DeleteTask"
!macro _DeleteTask _TASK _RESULT
Push ${_Task}
Call DeleteTask
Pop ${_RESULT}
!macroend
Exch $0
Push $1
Push $2
System::Call "ole32::CoCreateInstance(g'${GUIDTaskScheduler}',i0,i11,g '${GUIDITaskScheduler}',*i.r1)i.r2"
IntCmp $2 0 "" fail
System::Call '$1->7(w r0)i.r2' ; Delete($0)
System::Call '$1->2()' ; Release
fail:
StrCpy $0 $2 ; HRESULT
Pop $2
Pop $1
Exch $0
FunctionEnd
Section
${DeleteTask} "TaskName" $0
DetailPrint HRESULT=$0
SectionEnd

I've figured it out. This was bothering me to no end but I've managed to figure it out after rewriting it. Here's the working function.
Function DeleteTask
!define TaskGUID `{148BD52A-A2AB-11CE-B11F-00AA00530503}`
!define ITaskGUID `{148BD527-A2AB-11CE-B11F-00AA00530503}`
!define OLE `ole32::CoCreateInstance(g"${TaskGUID}",`
!define OLE32 `${OLE}i0,i11,g "${ITaskGUID}",*i.r1)i.r2`
!define DeleteTask "!insertmacro _DeleteTask"
!macro _DeleteTask _RESULT _TASK
Push ${_Task}
Call DeleteTask
Pop ${_RESULT}
!macroend
Exch $0
Push $0
Push $1
Push $2
Push $3
StrCpy $3 false
System::Call `${OLE32}`
IntCmp $2 0 0 +5
System::Call "$1->7(w r0)i.r2"
IntCmp $2 0 0 +3
System::Call "$1->2()"
StrCpy $3 true
Pop $2
Pop $1
Pop $0
Exch $3
FunctionEnd
;= $0 Should return either true on success or false on failure.
${DeleteTask} $0 "Task Name"
StrCmpS $0 true 0 +2
DetailPrint "${TASK} was successfully deleted."
DetailPrint "Failed to remove the task ${TASK}."
Thanks goes to Anders for his help.

Related

NSI script : calling one macro from another using insertmacro gives error

I am trying to call 1 macro from another in NSI script. Both the macros have MB_OKCANCEL . It gives following error when compiled:
**
[exec] Error: label "abort_inst:" already declared in function
**
!include "MUI2.nsh"
OutFile abc.exe
!macro installA
MessageBox MB_OKCANCEL "A?" IDOK lblinstall IDCANCEL abort_inst
abort_inst:
ABORT
GoTo lblinstall
lblinstall:
!macroend
!macro uninstallA
MessageBox MB_OKCANCEL "?" IDOK install_A IDCANCEL abort_uninstall
abort_uninstall:
ABORT
install_A:
!insertmacro installA
!macroend
Function .onInit
ReadRegStr $0 HKLM "x" "version"
${If} $0 == ""
!insertmacro installA
${Else}
!insertmacro uninstallA
${EndIf}
FunctionEnd
Section "required" main_section
SectionEnd
Please help
(Next time, please make sure your code doesn't do weird line breaks)
When inserting a macro, all the code between !macro and !macroend will substitute your !insertmacro. Therefor, you should't use static labels in macros–you could only insert the macro once (making a macro pointless!) You can use relative jumps (e.g. Goto +2) or make your labels dynamic by adding parameters to your labels, e.g.:
!macro myMacro param1
${param1}_loop:
MessageBox MB_YESNO "Loop this message?" IDYES ${param1}_loop
# some more code
${param1}_end:
!macroend
However, since you don't pass any parameters to your macros, why don't you simply use functions?
Function installA
# your code here
Function
Function uninstallA
# your code here
Call installA
FunctionEnd
This got resolved by converting one macro into Function, as suggested by #idelberg
Again, as long as you're working with macros, you cannot use static labels. A static label can only be used once in a function or section. Your use of macros would translate to the following:
Function .onInit
ReadRegStr $0 HKLM "x" "version"
${If} $0 == ""
MessageBox MB_OKCANCEL "A?" IDOK lblinstall IDCANCEL abort_inst
abort_inst:
Abort
Goto lblinstall
lblinstall: # FIRST TIME USE
${Else}
MessageBox MB_OKCANCEL "?" IDOK install_A IDCANCEL abort_uninstall
abort_uninstall:
Abort
install_A:
MessageBox MB_OKCANCEL "A?" IDOK lblinstall IDCANCEL abort_inst
abort_inst:
Abort
Goto lblinstall
lblinstall: # SECOND TIME USE
${EndIf}
FunctionEnd
So, that won't work because of the lblinstall label which is being used twice. Instead, you could do something like this:
Function installA
MessageBox MB_OKCANCEL "A?" IDOK lblinstall
Abort
lblinstall:
FunctionEnd
Function uninstallA
MessageBox MB_OKCANCEL "?" IDOK install_A
Abort
install_A:
Call installA
FunctionEnd
Function .onInit
ReadRegStr $0 HKLM "x" "version"
${If} $0 == ""
Call installA
${Else}
Call uninstallA
${EndIf}
FunctionEnd
(I also took the freedom to strip some unnecessary labels from your example)

Write string to Unicode File

I am using NSIS Unicode version and I am trying to append a string to an existing Unicode file(UTF-16LE).
My Problem: After I write the string to the file then open the file, the string I wrote is just jibberish. I have a feeling that its trying to write an ANSI string to a UTF-16LE file.
How can I write a string to a unicode file?
Function ${prefix}AppendFile
# Note: Will automatically create file if it doesn't exist
# $0 = fName
# $1 = strToWrite
Pop $1
Pop $0
ClearErrors
FileOpen $3 $0 a
FileSeek $3 0 END
FileWrite $3 "$\r$\n" # write a new line
FileWrite $3 "$1"
FileWrite $3 "$\r$\n" # write an extra line
FileClose $3 # close the file
IfErrors 0 +2
MessageBox MB_OK "Append Error: $1 $\r$\n$\r$\n$0"
FunctionEnd
If you're dealing with UTF-16LE file, you need to use FileWriteUTF16LE, which writes Unicode text, rather than FileWrite, which writes ANSI text.

Getting Error result in System::call for MsiEnumProducts and GetLastError in NSIS script

I have written the following script to Enumerate Installed Products from MSI.
!include LogicLib.nsh
!define MsiGetProductCodeFromName "!insertmacro MsiGetProductCodeFromName"
!macro MsiGetProductCodeFromName MSINSISPRODUCTNAME
Push ${MSINSISPRODUCTNAME}
Call MsiGetProductCodeFromName
!macroend
Function MsiGetProductCodeFromName
;================
; Stack : <ProductName>, ...
;================
Exch $0 ; ProductName
Push $1 ; For internal use as buffer for ProductCode
Push $2 ; For internal use as counter to MsiEnumProducts
Push $3 ; For internal use as result of MsiEnumProducts
; loop through all productcodes
Strcpy $2 0
System::Call "advapi32::GetUserName(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2"
DetailPrint "User name - $0"
DetailPrint "String length - $1"
DetailPrint "Return value - $2"
loop:
DetailPrint "Calling MsiEnumProducts ( $2, $1 )"
System::call "msi::MsiEnumProducts (i r2, t .r1) i.r3"
DetailPrint "Result = $3"
System::call "kernel32::GetLastError () i.r3"
DetailPrint "LastError = $3"
${If} $R1 == 0
DetailPrint $1
Intop $R0 $R0 + 1
goto loop
${Endif}
end:
Pop $3
Pop $2
Exch
Pop $0
;=========================
; Stack : <ProductCode>, ...
; ========================
FunctionEnd
When I use the above script in the following manner:
${MsiGetProductCodeFromName} ${PRODUCT_NAME}
I am getting the following output:
Calling MsiEnumProducts(0, )
Result = error
LastError = error
Completed
Please note that this is excluding the output of System call to GetUserName, which is working fine.
Any help would be appreciated.
The first call to MsiEnumProducts has to start at index 0 so add Strcpy $2 0 before the loop label.
I'm not sure if you can have a space between dll::function and (), when you just get "error" from the system plugin it usually means there is something wrong with the syntax.
You cannot call kernel32::GetLastError and get a valid result, it has to be part of the original call: System::Call 'dll::function() ?e' and then pop the error code off the stack...

How to save DetailPrint command message in file in NSIS Script?

Idea is to run a NSIS script in silent mode to a remote machine and once installation is successfully done than get the log file back to host machine.
I have lots of Detailprint command messages that show the progress of the script. Now question is how am i gonna save these messages to the log file. I was looking into this http://nsis.sourceforge.net/Dump_log_to_file but it says it wont work in silent mode.
I use this method to save logs. I don't know if this the best method but it works well for me.
!ifndef DEBUG_MODE
!define DEBUG_MODE true
!endif
!define LOGFILE "$TEMP\my_logfile.log"
!if ${DEBUG_MODE} == true
!define DetailPrint '!insertmacro _debugMsg'
!else
!define DetailPrint '!insertmacro _nodebugMsg'
!endif
!macro _debugMsg MSG
push $3
push $2
push $1
push $0
push $R0
strcpy $R0 "${MSG}"
ClearErrors
FileOpen $1 ${LOGFILE} a
FileSeek $1 0 END
IfErrors +8
${GetTime} "" "L" $0 $0 $0 $0 $0 $2 $3
FileWrite $1 "$0:$2:$3$\t"
FileWrite $1 "${__FUNCTION__}$\t"
FileWrite $1 "$R0"
FileWrite $1 "$\t(${__FILE__},${__FUNCTION__},${__LINE__})"
FileWrite $1 "$\n"
FileClose $1
pop $R0
pop $0
pop $1
pop $2
pop $3
!macroend
!macro _nodebugMsg _MSG
;you can put here nothing or you can put the regular DetailPrint
DetailPrint "${MSG}"
!macroend
After that you just have to find and replace DetailPrint with ${DetailPrint}
That is correct, the listview window does not exist in silent mode.
I guess you have two options, roll your own logging:
var hFileLog
!macro Log_Init logfile
FileOpen $hFileLog "${logfile}" w ;Or "a" for append
!macroend
!macro Log_String msg
DetailPrint "${msg}"
FileWrite $hFileLog "${msg}$\r$\n"
!macroend
!macro Log_Close
FileWrite $hFileLog 'Done.'
FileClose $hFileLog
!macroend
!macro File src
File "${src}"
FileWrite $hFileLog 'Extracting "${src}" to $outdir$\r$\n'
!macroend
Section "Main Program"
!insertmacro Log_Init "$instdir\install.log"
!insertmacro Log_String "Starting install..."
SetOutPath $instdir
!insertmacro File "${__FILE__}"
SectionEnd
Section "Optional Foo Component"
!insertmacro Log_String "Installing Foo"
SectionEnd
Section
!insertmacro Log_Close
SectionEnd
or add more hacks to make DumpLog work:
Function .onInit
IfSilent 0 +2
StrCpy $0 1 ;We need to know if we are in "real silent" mode
SetSilent normal ;Turn off real silent mode so we can fake it
FunctionEnd
Function .onGuiInit
StrCmp $0 1 0 +3
HideWindow
SetSilent silent ;The docs say this is only supported in .onInit but all we care about is the internal flag used by IfSilent
FunctionEnd
LicenseData "${__FILE__}"
page license skipifsilent ; All pages except instfiles needs this hack
page directory skipifsilent
page instfiles
Function skipifsilent
IfSilent 0 +3
HideWindow
Abort
FunctionEnd
Section
IfSilent 0 +2
HideWindow
SetOutPath $instdir
File "${__FILE__}"
Sleep 2222 ;So we can see that it is hidden and not just finishing quickly
DetailPrint "$(^Completed)" ; Fake the completed message show it shows in the log
Push "$EXEDIR\install.log"
Call DumpLog
IfSilent 0 +2
Quit
SetDetailsPrint textonly
SectionEnd
!define LVM_GETITEMCOUNT 0x1004
!define LVM_GETITEMTEXT 0x102D
Function DumpLog
Exch $5
Push $0
Push $1
Push $2
Push $3
Push $4
Push $6
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 1016
StrCmp $0 0 exit
FileOpen $5 $5 "w"
StrCmp $5 "" exit
SendMessage $0 ${LVM_GETITEMCOUNT} 0 0 $6
System::Alloc ${NSIS_MAX_STRLEN}
Pop $3
StrCpy $2 0
System::Call "*(i, i, i, i, i, i, i, i, i) i \
(0, 0, 0, 0, 0, r3, ${NSIS_MAX_STRLEN}) .r1"
loop: StrCmp $2 $6 done
System::Call "User32::SendMessageA(i, i, i, i) i \
($0, ${LVM_GETITEMTEXT}, $2, r1)"
System::Call "*$3(&t${NSIS_MAX_STRLEN} .r4)"
FileWrite $5 "$4$\r$\n"
IntOp $2 $2 + 1
Goto loop
done:
FileClose $5
System::Free $1
System::Free $3
exit:
Pop $6
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
Exch $5
FunctionEnd

Window Socket programming in NSIS

I am trying to create a socket in the .nsi file to check whether the socket will get created successfully or not to check for port availability. So any help with respect to windows socket programmin in nsis is highly appreciated.
thank u
!include LogicLib.nsh
!ifndef AF_INET
!define AF_INET 2
!define SOCK_STREAM 1
!define IPPROTO_TCP 6
!define INADDR_ANY 0
!define SOL_SOCKET 0xffff
!define /math SO_EXCLUSIVEADDRUSE 0xffffffff ^ 0x4
!endif
Function QueryCanBindToIP4TCPPort
Exch $0
Push $1
Push $2
Push $0 ;Push port
System::Alloc 16 ;WSADATA & sockaddr_in
System::Call 'Ws2_32::WSAStartup(i 2,isr2)i.r0'
${If} $0 = 0
System::Call 'Ws2_32::socket(i ${AF_INET},i ${SOCK_STREAM},i ${IPPROTO_TCP})i.r1 ?e'
Pop $0
${If} $1 <> -1
System::Call 'Ws2_32::setsockopt(ir1,i ${SOL_SOCKET},i ${SO_EXCLUSIVEADDRUSE},*i 1,i4)i.r0'
System::Call 'Ws2_32::htons(iss)i.s' ;Convert port (and leaves the original push on the stack)
;Skipping htonl on address since INADDR_ANY is 0
System::Call '*$2(&i2 ${AF_INET},&i2 s,&i4 ${INADDR_ANY},&i8 0)'
System::Call 'Ws2_32::bind(ir1,ir2,i16)i.r0'
${If} $0 = 0
System::Call 'Ws2_32::listen(ir1,i0)i.r0'
;Listening here but once we hit WSACleanup the port will be free again (This is fine if all you need to do is make sure no app is bound to the port at this moment)
; System::Call 'Ws2_32::closesocket(ir1)'
${EndIf}
${EndIf}
System::Call 'Ws2_32::WSACleanup()' ;Remove this call to leave the port open for the duration of the installer
${EndIf}
System::Free $2
Pop $2 ;Throw away port
Pop $2
Pop $1
Exch $0
FunctionEnd
Section
Push 666
call QueryCanBindToIP4TCPPort
Pop $0
${If} $0 = 0
DetailPrint "Bind OK"
${Else}
DetailPrint "Bind Failed!"
${EndIf}
SectionEnd
As NSIS doesn't have any network functionality either built-in or in any plugins that I could find, the simplest solution in my opinion is to make a very small program that tries to create a listening socket on the specified port (where the port can either be hard-coded or passed as an argument). This program can then be used in the install script to check if the port is available.