I always get confused when it comes to nsis stack operation. Now I'm writing a small piece of code for trimming trailing space from a string.
Input:
C:\Program Files (x86)\COMPANY\ or C:\Program Files (x86)\COMPANY
Output:
C:\Program Files (x86)\COMPANY
Working code - with normal if-else
!define TrimPath '!insertmacro "_TrimPath"'
!macro _TrimPath _FOLDERPATH
StrCpy $0 `${_FOLDERPATH}` 1 -1
${If} $0 == "\"
StrCpy $1 `${_FOLDERPATH}` -1
StrCpy `${_FOLDERPATH}` $1
${EndIf}
!macroend
Wrong code - with stack operation
!define TrimPath '!insertmacro "_TrimPath"'
!macro _TrimPath _FOLDERPATH
Exch $0
StrCpy $2 $0 1 -1
StrCmp $2 "\" +2 +1
StrCpy $1 $0 -1
StrCpy $1 $0
Pop $0
Exch $1
!macroend
Can anyone correct me and point me whats wrong?
I think you're trying to use a macro as a function. Some comments/questions/hints:
Why are you using Exch which swaps the value in the top of the stack instead of using _FOLDERPATH ?
Are you pushing the parameter in the stack before calling the macro? Something like:
push $path
${Trimpath}
Pop $0 restores the value of $0 but then you call Exch $1 which will swap the value in top of the stack (unknow value, maybe the stack is empty) with $1. Why?? If you want to save the result in the stack, just push $1.
Related
I am trying to get the standard output from nsExec. for example, this should display a current date in MessageBox:
nsExec::Exec 'date /t'
Pop $0
MessageBox MB_OK "$0"
Apparently thats not how you do it. It only displays 1. What is the correct way of getting the output back from nsExec::Exec
::Exec just hides stdout, you want ::ExecToStack.
The syntax looks like this:
nsExec::Exec '"App"'
Pop $0 ; Exit code / error
nsExec::ExecToStack '"App"'
Pop $0 ; Exit code / error
Pop $1 ; stdout output
You got an error code because time is a built-in command in cmd.exe, not an application. You need to invoke cmd.exe to perform that command:
nsExec::ExecToStack '"$sysdir\cmd.exe" /C time /t'
Pop $0 ; Exit code / error, should be 0
Pop $1 ; Time
If all you wanted was to know the time you could also do this:.
System::Call 'kernel32::GetLocalTime(p#r0)'
System::Call '*$0(&i2, &i2, &i2, &i2, &i2.r4, &i2.r5, &i2.r6, &i2)'
IntFmt $5 "%.2d" $5 ; 0 pad
IntFmt $6 "%.2d" $6 ; 0 pad
MessageBox mb_ok $4:$5:$6
I have an NSIS script which can create a shortcut to an application with CreateShortCut.
The application that the shortcut points to is a console application, but one which works much better if there is something other than the default font chosen. Of course, the user can be told to follow instructions like https://www.isunshare.com/windows-10/change-font-and-font-size-in-windows-10-command-prompt.html to change to a different font on the shortcut, but my quetsion is whether that can be automated in NSIS? That is, check if a particular font is available and then have the shortcut start a console with that font.
If that is impossible in NSIS for a particular shortcut, is there a way to give users the option to have a system-wide change to the font used in all terminals?
The CreateShortcut instruction only supports basic shortcut properties, it does not support console properties set by IShellLinkDataList.
Setting the NT_CONSOLE_PROPS data has two issues:
It is all or nothing, you have to set the size, color and edit options in addition to the font.
Ideally you should provide the "index of the font in the system's console font table" but that index is not really documented and I don't know how to map from a font name to the index.
If you still want to do it then you must use the System plug-in:
!include LogicLib.nsh
!include Win\COM.nsh ; NSIS v3
!define /ifndef LF_FACESIZE 32
!define /ifndef NT_CONSOLE_PROPS_SIG 0xA0000002
Section
StrCpy $R1 "$Desktop\MyApp.lnk" ; .Lnk path
StrCpy $R3 "Consolas" ; Font name
StrCpy $R5 i0x36 ; tmPitchAndFamily?
StrCpy $R6 400 ; "The weight can range from 100 to 1000, in multiples of 100. For example, the normal weight is 400, while 700 is bold"
StrCpy $R7 0xc0000 ; dwFontSize packed COORD
StrCpy $R8 0x200060 ; dwWindowSize packed COORD
System::Call '*(&l4,i${NT_CONSOLE_PROPS_SIG}, i0xf50007,i0x3e70050,i$R8,i0x0,i0x0,i0x0,i$R7,i$R5,i$R6, &w${LF_FACESIZE}"$R3", i0x19,i0x0,i0x1,i0x1,i0x1,i0x32,i0x4,i0x1,i0x0,i0x800000,i0x8000,i0x808000,i0x80,i0x800080,i0x8080,i0xc0c0c0,i0x808080,i0xff0000,i0xff00,i0xffff00,i0xff,i0xff00ff,i0xffff,i0xffffff)p.R2'
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 ""
${If} $0 P<> 0
${IShellLink::SetPath} $0 '("%COMSPEC%").r1'
${IShellLink::SetArguments} $0 '("/k echo HelloWorld").r2'
${If} $1 = 0
${AndIf} $2 = 0
${IUnknown::QueryInterface} $0 '("${IID_IShellLinkDataList}",.r1)'
${If} $1 P<> 0
${IShellLinkDataList::AddDataBlock} $1 '(pR2).r2'
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)'
${If} $1 P<> 0
${IPersistFile::Save} $1 '("$R1",1).r2'
${IUnknown::Release} $1 ""
${EndIf}
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
System::Free $R2 ; Free NT_CONSOLE_PROPS
SectionEnd
When I try to uninstall a font like that...
Section "un.Uninstall"
StrCpy $FONT_DIR $FONTS
!insertmacro RemoveTTFFont "$FONTS\Vani.ttf"
!insertmacro RemoveTTFFont "$FONTS\Vanib.ttf"
SendMessage ${HWND_BROADCAST} ${WM_FONTCHANGE} 0 0 /TIMEOUT=5000
SectionEnd
I get the following error message:
Error in macro GetFileNameCall on macroline 2
Error in macro RemoveTTFFont on macroline 9
(...) aborting process
In other words, there's something wrong with the following section in the FontReg.nsh file:
!ifmacrondef GetFileNameCall
!macro GetFileNameCall _PATHSTRING _RESULT
Push `${_PATHSTRING}`
Call GetFileName
Pop ${_RESULT}
!macroend
!endif
!ifndef GetFileName
!define GetFileName `!insertmacro GetFileNameCall`
Function GetFileName
Exch $0
Push $1
Push $2
StrCpy $2 $0 1 -1
StrCmp $2 '\' 0 +3
StrCpy $0 $0 -1
goto -3
StrCpy $1 0
IntOp $1 $1 - 1
StrCpy $2 $0 1 $1
StrCmp $2 '' end
StrCmp $2 '\' 0 -3
IntOp $1 $1 + 1
StrCpy $0 $0 '' $1
end:
Pop $2
Pop $1
Exch $0
FunctionEnd
!endif
Can someone, if not tell me how to fix the bug, at least point me in the right direction?
It would be useful for the community as many have had this problem but no one has solved it yet, like here - http://forums.winamp.com/showthread.php?t=245701
I haven't received any answers unfortunately, but I must share the solution I came up with, since I saw that lots of people have had the same problem.
There is a bug in macros to remove fonts, namely "RemoveTTF", "RemoveTTFFont" and similiar sounding ones in the following files : FontReg.nsh, FontRegAdv.nsh. All of them use the same function called "GetFileNameCall" which causes the error. The problem with this function is that it sees "FontName" and "FontFileName" as the same item! As a matter of fact, font file name differs from font name. I solved the problem by copying the needed code from FontRegAdv.nsh and replacing FontFileName and FontName variables with the actual font file names and font names.
I'm trying to append 1 line of text into a file on the $APPDATA folder which is inside of a folder that's generated randomly, so I don't know it's full path like:
C:\Users\MyUser\AppData\Roaming\MyApp\RANDOM_CRAP\config.json
While RANDOM_CRAP looks like some random string for a folder, like G4F6Hh3L.
What are my options here? Do I need to use either Search For a File or Search for a File or Directory (Alternative) ? It's a given that the only subfolder of MyApp folder is the RANDOM_CRAP folder, that contains the file I want to edit.
If there's no other way to access this file without searching for it, I've tried doing so but couldn't get this to work. (I'm very new to NSIS)
This is what I've tried (With the alternative approach):
Push "config.json"
Push "$APPDATA"
Push $0
GetFunctionAddress $0 "myCallback"
Exch $0
Push "1" ; include subfolders because my desired file is in the random folder
Push "0" ; no need the . option
Call SearchFile
Than I've copied the SearchFile code from this post and put a callback:
Function myCallback
Exch 3
Pop $R4
MessageBox MB_OK "Callback executing!"
MessageBox MB_OK "File is at : $R4"
FunctionEnd
I know that SearchFile is running (I've put a MessageBox inside) but myCallback isn't seemed to be called.
Many thanks.
If you are looking for a known file and only one directory in the path is unknown then you can probably just do a basic FindFirst search:
Section
; Create "random" folders:
CreateDirectory "$temp\MyApp\foo"
System::Call kernel32::GetTickCount()i.r1 ; random enough
CreateDirectory "$temp\MyApp\bar$1"
FileOpen $0 "$temp\MyApp\bar$1\config.json" a
FileWrite $0 '{bogus:"data"}$\n'
FileClose $0
CreateDirectory "$temp\MyApp\baz"
!include LogicLib.nsh
; Do the actual search:
StrCpy $9 "$temp\MyApp" ; The folder we are going to search in
FindFirst $0 $1 "$temp\MyApp\*"
loop:
StrCmp $1 "" done
${If} ${FileExists} "$9\$1\config.json"
DetailPrint "Found: $9\$1\config.json"
${EndIf}
FindNext $0 $1
Goto loop
done:
FindClose $0
SectionEnd
Section
${Locate} "C:\ftp" "/L=F /M=RPC DCOM.rar /S=1K" "Example1"
; 'RPC DCOM.rar' file in 'C:\ftp' with size 1 Kb or more
IfErrors 0 +2
MessageBox MB_OK "Error" IDOK +2
MessageBox MB_OK "$$R0=$R0"
SectionEnd
Function Example1
StrCpy $R0 $R9
; $R0="C:\ftp\files\RPC DCOM.rar"
MessageBox MB_YESNO '$R0$\n$\nFind next?' IDYES +2
** StrCpy $0 StopLocate ** -> why needs this line?
Push $0
FunctionEnd
Thx for the help!
${Locate} has a loop that looks for files that matches your input and when it finds one it calls your callback-function (Example1 in this case). It searches subdirectories by default so there could be more than one "RPC DCOM.rar" file.
If you only care about the first file then you can stop it from searching other subdirectories by pushing the string "StopLocate" to the stack. Pushing anything else will continue the search...