NSIS: How to add custom button to left bottom corner and handle it's click? - nsis

I tried the ButtonEvent plugin, but when I run compiled example, it fails with memory access error. Maybe it is able to do with System plugin via Windows API or something else? Can anyone show how it can be done?
UPD: Error was appeared because I tried to use non-unicode ButtonEvent on Unicode NSIS. Now example compiles and executes OK, but when I click on TryMe button, callback function is not called and nothing happens. How to determine what is the problem? Can anyone compile ButtonEventMUI.nsi and click on TryMe button? I downloaded latest ButtonEvent version. Using NSIS 2.46 Unicode

The system plugin cannot do this because it cannot subclass windows.
The ButtonEvent plugin works fine for me (NSIS 2.46):
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
Function .onGuiInit
; You are supposed to use ChangeUI (or MUI_UI) and a modified ui file to add new buttons but this example adds the button at run-time...
GetDlgItem $0 $hwndparent 2 ; Find cancel button
System::Call *(i,i,i,i)i.r1
System::Call 'USER32::GetWindowRect(ir0,ir1)'
System::Call *$1(i.r2,i.r3,i.r4,i.r5)
IntOp $5 $5 - $3 ;height
IntOp $4 $4 - $2 ;width
System::Call 'USER32::ScreenToClient(i$hwndparent,ir1)'
System::Call *$1(i.r2,i.r3)
System::Free $1
IntOp $2 $2 + $4 ;x
IntOp $2 $2 + 8 ;x+padding
System::Call 'USER32::CreateWindowEx(i0,t "Button",t "Click Me",i${WS_CHILD}|${WS_VISIBLE}|${WS_TABSTOP},ir2,ir3,ir4,ir5,i $hwndparent,i 0x666,i0,i0)i.r0'
SendMessage $hwndparent ${WM_GETFONT} 0 0 $1
SendMessage $0 ${WM_SETFONT} $1 1
GetFunctionAddress $0 onmybtnclick
ButtonEvent::AddEventHandler 0x666 $0
FunctionEnd
Function onmybtnclick
MessageBox mb_ok "You clicked me!"
FunctionEnd
Page Directory
Page Instfiles
Section
SectionEnd

Related

How to use System::Call and MessageBox when calling the C++ dll method from the NSIS script

My requirement is I need to test "If the device is present before we try to disable the Native Power" from the system.
For that I need to call the below function that is there in testutil.dll
BOOL IsTherePower()
Below is the NSIS script to call this function:
Name "PowerTest"
OutFile "PowerTest.exe"
InstallDir $PROGRAMFILES\PowerTest
Section "PowerTest(required)"
SectionIn RO
DetailPrint "PowerTest"
; Set output path to the installation directory. Here is the path C:\Program Files\PowerTest
SetOutPath $INSTDIR
; Give the dll path
File E:\Code\Source\Validatepower.exe
File E:\Code\Source\testutil.dll
File E:\Code\Source\ntutil.dll
File E:\Code\Source\dlgdll.dll
System::Call "$INSTDIR\testutil.dll::IsTherePower() i.r0"
Pop $0
MessageBox MB_OK "Return value = $R0, lasterr = $0"
IntCmp $R0 1 OkToInstall CancelInstall
CancelInstall:
Abort "Not allowed to install"
OkToInstall:
Do the install
With the above code when i run the application i am getting "Return value=, lasterr = error". I am not sure why i am getting the "Return value" blank (null). Did I miss anything here?
I have written "System::Call" and "MessageBox" but not sure what they are doing.
Here I want to know what is "i.r0" from System::Call
And also what is "Pop $0"?
You are using the wrong register. r0 in System syntax is $0, not $R0 (R0 and r10 is $R0). System::Call "$INSTDIR\drvutil.dll::IsUPSPresent() i.r0" puts the INT32 return value in $0 and then you overwrite $0 with the Pop and your stack happened to be empty.
If you need to call GetLastError() then you must append the ?e option:
System::Call "$INSTDIR\drvutil.dll::IsUPSPresent() i.r0 ?e" ; Pushes error code on top of the stack
Pop $1 ; Get error code
DetailPrint "Return=$0 LastError=$1"
?e pushes the last error on the stack and Pop extracts the top item on the stack.
I can confirm that my code works, I tested in on a dummy .DLL. If it does not work for you then System::Call is unable to load the .DLL or find the exported function. The most likely issue is that you have not exported the function correctly in your .DLL.
Inspect your .DLL with Dependency Walker, it is supposed to look like this:
not
You can also try do verify it manually in NSIS:
!include LogicLib.nsh
Section
SetOutPath $InstDir
File drvutil.dll
System::Call 'KERNEL32::LoadLibrary(t "$InstDir\drvutil.dll")p.r8 ?e'
Pop $7
${If} $8 P<> 0
MessageBox MB_OK 'Successfully loaded "$InstDir\drvutil.dll" # $8'
System::Call 'KERNEL32::GetProcAddress(pr8, m "IsUPSPresent")p.r9 ?e'
Pop $7
${If} $9 P<> 0
MessageBox MB_OK 'Successfully found "IsUPSPresent" # $9'
${Else}
MessageBox MB_ICONSTOP 'Unable to find "IsUPSPresent", error $7'
${EndIf}
System::Call 'KERNEL32::FreeLibrary(pr8)'
${Else}
MessageBox MB_ICONSTOP 'Unable to load "$InstDir\drvutil.dll", error $7'
${EndIf}

Creating shortcut to console application in NSIS with particular font

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

Deregistering a font with NSIS

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.

why need in Locate plugin find section the following line?

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

NSIS - Conditionally Display Components

I want the installer/uninstaller to check if any of the possible components are installed, and display only relevan components (installer should display only components not already installed and uninstaller should display only already-installed components)
I'm using MUI.
The component section of my .nsi looks something like this:
; Section descriptions
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description"
!insertmacro MUI_DESCRIPTION_TEXT ${Component2} "Component2 Description."
!insertmacro MUI_DESCRIPTION_TEXT ${Component3} "Component3 Description."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
I've tried a couple of different ways:
First, I tried using this macro for checking if a registry exists:
!insertmacro IfKeyExists HKEY_LOCAL_MACHINE SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall "Component1"
Pop $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work.
Then I tried using the Registry Plug-In:
${registry::KeyExists} "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Component1" $R0
${If} $R0 == 0 #Not installed yet. Display.
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1"
${EndIf}
This didn't work either.
So then I thought maybe there was some other more basic problem, and I tried this:
StrCpy $0 "0"
${If} $0 == "1"
!insertmacro MUI_DESCRIPTION_TEXT ${Component1} "Component1 Description."
${EndIf}
But even then I still saw "Component1" as a possible component during installation!
What am I doing wrong and how can I go about achieving my goal?
Thanks in advance!
To select a section (= a component) at runtime you can use SectionGetFlag / SectionSetFlag and some bit manipulation :
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 | ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
To deselect it, just toogle its selected bit, with
SectionGetFlags ${test_section_id} $0
IntOp $0 $0 ^ ${SF_SELECTED}
SectionSetFlags ${test_section_id} $0
These are simpler if you include the Sections.nsh header and use the SelectSection and UnselectSection macros.
If you want to hide a section, you just need to set its text to "" (empty string)
SectionSetText ${test_section_id} ""
You are modifying the wrong text. MUI_DESCRIPTION_TEXT is the long description that is typically displayed when hovering the mouse over a component. But the visibility of a section depends on its name to be empty or not empty.
Therefore you have to use this instead to hide your component1:
SectionSetText ${Component1} ""

Resources