How to retain data on custom page when back/next button clicked when the data is taken from a file? - nsis

I have written a custom component page which is having a rich text box and that text box shows the information which is read from the "component.rtf" file. When I go first time on my custom page it shows me rich text box filled with data, but when I click next or back button and come back to custom page again at that time it shows me the rich text box as blank. It shows nothing.
I have written following code for my custom page-
;-------Custom page variables---------
Var Dialog
Var CustomHeaderText
Var CustomSubText
Var path
Var temp1
Var CONTROL
;-------------------------------------
Page custom nsDialogsPage
;------Custom page function----------
Function nsDialogsPage
StrCpy $CustomHeaderText "Components of My Installer"
StrCpy $CustomSubText "Detail list of components are"
!insertmacro MUI_HEADER_TEXT $CustomHeaderText $CustomSubText
!define SF_RTF 2
!define EM_STREAMIN 1097
nsDialogs::Create /NOUNLOAD 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
nsDialogs::CreateControl /NOUNLOAD "RichEdit20A" ${ES_READONLY}|${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} ${WS_EX_STATICEDGE} 0 10u 100% 110u ''
Pop $CONTROL
FileOpen $4 "$path\components.rtf" r
StrCpy $0 $CONTROL
SendMessage $CONTROL ${EM_EXLIMITTEXT} 0 0x7fffffff
; set EM_AUTOURLDETECT to detect URL automatically
SendMessage $CONTROL 1115 1 0
System::Get /NoUnload "(i, i .R0, i .R1, i .R2) iss"
Pop $2
System::Call /NoUnload "*(i 0, i 0, k r2) i .r3"
System::Call /NoUnload "user32::SendMessage(i r0, i ${EM_STREAMIN}, i ${SF_RTF}, i r3) i.s"
loop:
Pop $0
StrCmp $0 "callback1" 0 done
System::Call /NoUnload "kernel32::ReadFile(i $4, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call /NoUnload "$2"
goto loop
done:
System::Free $2
System::Free $3
FileClose $4
nsDialogs::Show
FunctionEnd
;--------Custom page function end------------
In above code it reads the file "components.rtf" and displays it. Can someone tell me how to write the code which will retain this data when I will click Back/Next button on component page.?

It works fine for me. Perhaps you are overwriting $path on another page? Add MessageBox MB_OK $4 after FileOpen to make sure you are able to open the file.
!include MUI2.nsh
Var Dialog
Var CustomHeaderText
Var CustomSubText
Var path
#Var temp1
Var CONTROL
;-------------------------------------
Page custom nsDialogsPage
;------Custom page function----------
Function nsDialogsPage
StrCpy $CustomHeaderText "Components of My Installer"
StrCpy $CustomSubText "Detail list of components are"
!insertmacro MUI_HEADER_TEXT $CustomHeaderText $CustomSubText
!define SF_RTF 2
!define EM_STREAMIN 1097
nsDialogs::Create /NOUNLOAD 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
nsDialogs::CreateControl /NOUNLOAD "RichEdit20A" ${ES_READONLY}|${WS_VISIBLE}|${WS_CHILD}|${WS_TABSTOP}|${WS_VSCROLL}|${ES_MULTILINE}|${ES_WANTRETURN} ${WS_EX_STATICEDGE} 0 10u 100% 110u ''
Pop $CONTROL
FileOpen $4 "$path\components.rtf" r
StrCpy $0 $CONTROL
SendMessage $CONTROL ${EM_EXLIMITTEXT} 0 0x7fffffff
; set EM_AUTOURLDETECT to detect URL automatically
SendMessage $CONTROL 1115 1 0
System::Get /NoUnload "(i, i .R0, i .R1, i .R2) iss"
Pop $2
System::Call /NoUnload "*(i 0, i 0, k r2) i .r3"
System::Call /NoUnload "user32::SendMessage(i r0, i ${EM_STREAMIN}, i ${SF_RTF}, i r3) i.s"
loop:
Pop $0
StrCmp $0 "callback1" 0 done
System::Call /NoUnload "kernel32::ReadFile(i $4, i $R0, i $R1, i $R2, i 0)"
Push 0 # callback's return value
System::Call /NoUnload "$2"
goto loop
done:
System::Free $2
System::Free $3
FileClose $4
nsDialogs::Show
FunctionEnd
Function .onInit
; Create a RTF file
InitPluginsDir
StrCpy $path "$PluginsDir" ; Set $path used by the custom page
FileOpen $0 "$path\components.rtf" w
FileWrite $0 '{\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard{Show {\b Me}}\par{http://example.com/#Funny}\par{Goodbye}}'
FileClose $0
FunctionEnd
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Section
SectionEnd

Related

Set run-time cursor position in directory page dialog box - NSIS

I want to add at the end of the path the installation folder name of my App. I do it successfully after clicking the Browse button but, after many tentatives, I couldn't do it if I modify the text directly in the textbox.
Function .onVerifyInstDir
Push $0
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 0x3FB
;in alternative for MUI >2.0 I could use directly $mui.DirectoryPage.Directory. Is it right?
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
StrCmp $R0 "\${APP_FOLDER_NAME}" +2
;add "\MyApp" after browse button clicking (OK)
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
;add "\MyApp" after typing directly into the textbox but the cursor position reset to the first character. Tried to solve saving the current cursor position and then reapply it (NOK)
SendMessage $0 ${EM_GETSEL} null $1
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
FunctionEnd
I don't succeed of mantain the cursor where I'm modifying the path string and it reset always to the first char.
In a previous phase of install process I initialise the Install path as follow
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
NSIS is supposed to do this for you already.
From the docs:
... the part of this string following the last \ will be used if the user selects 'browse', and may be appended back on to the string at install time (to disable this, end the directory with a \ (which will require the entire parameter to be enclosed with quotes). If this doesn't make any sense, play around with the browse button a bit.
Meaning,
InstallDir "$ProgramFiles\MyApp"
is not the same as
InstallDir "$ProgramFiles\MyApp\"
Setting the text in .onVerifyInstDir is not officially supported but this code somewhat works:
!define APP_FOLDER_NAME MyApp
Page Directory
Page InstFiles
!include WinMessages.nsh
!include LogicLib.nsh
Function .onInit
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
FunctionEnd
Var InOnVerifyInstDir
Var SeenModal
Function .onVerifyInstDir
${IfThen} $InOnVerifyInstDir <> 0 ${|} Return ${|} ; Don't recurse into .onVerifyInstDir
!if ${MUI_SYSVERSION} >= 2.0
StrCpy $0 $mui.DirectoryPage.Directory
!else
FindWindow $0 "#32770" "" $hWndParent
GetDlgItem $0 $0 0x3FB
!endif
System::Call 'USER32::GetActiveWindow()p.r2'
System::Call 'USER32::GetFocus()p.r1'
${If} $1 P<> $0
${If} $hWndParent P<> $2
StrCpy $SeenModal 1
Return
${EndIf}
${If} $SeenModal = 0
Return
${EndIf}
${EndIf}
StrCpy $SeenModal ""
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
${If} $R0 != "\${APP_FOLDER_NAME}"
StrCpy $InOnVerifyInstDir 1
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
SendMessage $0 ${EM_GETSEL} "" "" $1
IntOp $1 $1 >> 16 ; shift hiword
IntOp $1 $1 & 0xffff ; mask possible sign bit
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
StrCpy $InOnVerifyInstDir 0
${EndIf}
FunctionEnd
but I would still recommend just using InstallDir.

NSIS script + Skip button alignment issue

I am trying NSIS script to add "SKIP" button.
I have tested below code and it works well but when I actually integrate with the main code which is having a screen of option "back" "Next" and "Cancel" button. this skip button is not visible at all, it is in extreme right of cancel button and hardly visible.
How can move this "Skip" button towards left side.
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
!define MUI_CUSTOMFUNCTION_GUIINIT SKIP
Function SKIP
; 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
And also this button appears on all screen How can I display only in one screen.
Change $2 and $3 before the call to CreateWindowEx to change the button position. You can save the handle to the button in another variable after CreateWindowEx but I just used the Id in this example (0x666).
Name BtnTest
Outfile test.exe
Installdir "$temp"
RequestExecutionLevel user
BrandingText " " ;Button covers this text
!include nsDialogs.nsh ;For WS_*
!define MUI_CUSTOMFUNCTION_GUIINIT SKIP
/**/ !include MUI2.nsh #*/
!ifndef MUI_INCLUDED
Function .onGuiInit
Call SKIP
FunctionEnd
!endif
Function SKIP
; 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
!ifdef MUI_INCLUDED
StrCpy $2 15 ; Whatever you feel looks good
!else
IntOp $2 $2 + $4 ;x
IntOp $2 $2 + 8 ;x+padding
!endif
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
Function HideIt
GetDlgItem $0 $hwndparent 0x666
ShowWindow $0 0
FunctionEnd
Function ShowIt
GetDlgItem $0 $hwndparent 0x666
ShowWindow $0 1
FunctionEnd
!ifdef MUI_INCLUDED
!define MUI_PAGE_CUSTOMFUNCTION_SHOW HideIt
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_SHOW ShowIt
!insertmacro MUI_PAGE_COMPONENTS
!define MUI_PAGE_CUSTOMFUNCTION_SHOW HideIt
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE English
!else
Page Components "" HideIt
Page Components "" ShowIt
Page Components "" HideIt
Page Instfiles
!endif
Section
SectionEnd

SelectFolderDialog with only drives, no folder selection inside drives

In nsis SelectFolderDialog for folder selection window. But i want to appear or user to select only drives.There should not be selection of folders inside a drive.
So user should get drives like C: D: E: etc
Is there any option to do it?
This only allows you to select paths shorter than 4 characters inside My Computer, if you want to hide the folders in the dialog you need to write a plugin and implement IFolderFilter (WinXP+ only)
#BIF_RETURNONLYFSDIRS 0x00000001
#BIF_NEWDIALOGSTYLE 0x00000040
#BIF_NONEWFOLDERBUTTON 0x00000200
!include LogicLib.nsh
!include WinMessages.nsh
!define /math BFFM_ENABLEOK ${WM_USER} + 101
!define BFFM_SELCHANGED 2
System::Call 'SHELL32::SHGetSpecialFolderLocation(i0,i0x11,*i.r2)i.r0'
System::Get "(i.R0,i.R1,i.R2,i)iR9R9"
Pop $3
System::Call '*(i$hwndparent,i$2,i,t "Hello",i0x241,kr3,i0,i0)i.r1'
System::Call 'SHELL32::SHBrowseForFolder(ir1)i.r0'
Sys_BFFCALLBACK:
${If} $R9 == "callback1"
${If} ${BFFM_SELCHANGED} = $R1
System::Call 'SHELL32::SHGetPathFromIDList(i$R2,t "" R9)'
StrLen $R9 $R9
${IfThen} $R9 > 3 ${|} StrCpy $R9 0 ${|}
SendMessage $R0 ${BFFM_ENABLEOK} 0 $R9
${EndIf}
StrCpy $R9 0 ; return value
System::Call $3
Goto Sys_BFFCALLBACK
${EndIf}
System::Free $3 ; system callback
System::Call 'OLE32::CoTaskMemFree(ir2)' ; BROWSEINFO.pidlRoot
System::Free $1 ; BROWSEINFO
${If} $0 <> 0
System::Call 'SHELL32::SHGetPathFromIDList(i$0,t "" R9)'
MessageBox mb_ok SHBrowseForFolder=$R9
${EndIf}
System::Call 'OLE32::CoTaskMemFree(ir0)' ; pidl result

How to use REMOVE or REPAIR feature in NSIS script?

I have used nsis script for creating installer.When i run my installer second time with same name,REPAIR and REMOVE should be check and do the corresponding operation.I have find out my application already installed or not using following codes,
Function checkinstall
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\My app" "UninstallString"
IfFileExists $R0 +1 NotInstalled
call nsDialogpage
NotInstalled:
FunctionEnd
Function nsDialogpage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $hwnd
${NSD_AddStyle} $hwnd ${WS_GROUP}
${NSD_OnClick} $hwnd ???
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $hwnd
${NSD_OnClick} $hwnd ???
nsDialogs::Show
If the user select repair button it should overwrites existing installation path else uninstall existing installed and continue with new one.what am i need do to replace the (???) of the above code
page custom checkinstall
!insertmacro MUI_PAGE_DIRECTORY
My next page is Directory selection.so i need to call this page? How to achieve this?
1.How can i call un installer function if the user selects remove button?
Function un.Init, section /o -un.Main UNSEC000,section -un.post UNSE001
these are the un installer funtions.How can i call these functions? i have tried call method but it did not work.
You need to specify a callback function, like in the nsDialogs documentation, look for the nsDialogsPageLeave function in this example:
!include nsDialogs.nsh
!include LogicLib.nsh
Name nsDialogs
OutFile nsDialogs.exe
XPStyle on
Var Dialog
Var Label
Var Text
Page custom nsDialogsPage nsDialogsPageLeave
Page instfiles
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0 0 100% 12u "Hello, welcome to nsDialogs!"
Pop $Label
${NSD_CreateText} 0 13u 100% -13u "Type something here..."
Pop $Text
${NSD_OnChange} $Text nsDialogsPageTextChange
nsDialogs::Show
FunctionEnd
Function nsDialogsPageLeave
${NSD_GetText} $Text $0
MessageBox MB_OK "You typed:$\n$\n$0"
FunctionEnd
Function nsDialogsPageTextChange
Pop $1 # $1 == $ Text
${NSD_GetText} $Text $0
${If} $0 == "hello"
MessageBox MB_OK "right back at ya!"
${EndIf}
FunctionEnd
Section
DetailPrint "hello world"
SectionEnd

How can I resize a NSD_SetImageOLE image

I'm using the plugin macro NSD_SetImageOLE from http://nsis.sourceforge.net/NsDialogs_SetImageOLE - And I would like add another macro NSD_SetStretchedImageOLE the same way nsDialog.nsh works.
But I'm not sure is it's even possible, I've found that the resizing of an IPicture can be done by getting the "HBITMAP, BITMAP and BITMAPINFO" and resizing it (quoted from http://www.mofeel.net/958-microsoft-public-vc-mfc/12516.aspx). Anyway, I'm kind of lost trying to covert these methods to NSIS's System::Call style.
I have updated http://nsis.sourceforge.net/mediawiki/images/6/65/NsDialogs_setImageOle.zip with a ${NSD_SetStretchedImageOLE} which encapsulates Anders' code for reusability. I've also changed it to use the control dimensions rather than you having to specify the size yourself.
!ifndef IID_IPicture
!define IID_IPicture {7BF80980-BF32-101A-8BBB-00AA00300CAB}
!endif
!define SRCCOPY 0xCC0020
!include nsDialogs.nsh
!define IMAGEPATH "$sysdir\migwiz\PostMigRes\Web\base_images\Documents.gif" ;"C:\Windows\Web\Wallpaper\Windows\img0.jpg"
!define NEWSIZEW 200
!define NEWSIZEH 100
Page Custom mypagestretchcreate_GDI ; GDI resize
Page Custom mypagestretchcreate_CTL ; Simple control resize
Function mypagestretchcreate_GDI
nsDialogs::Create 1018
Pop $0
System::Call 'oleaut32::OleLoadPicturePath(w "${IMAGEPATH}",i0r2,i0,i0,g"${IID_IPicture}",*i.r9)i.r1'
${If} $1 = 0
System::Call 'user32::GetDC(i0)i.s'
System::Call 'gdi32::CreateCompatibleDC(iss)i.r1'
System::Call 'gdi32::CreateCompatibleBitmap(iss,i${NEWSIZEW},i${NEWSIZEH})i.r2'
System::Call 'user32::ReleaseDC(i0,is)'
System::Call $9->3(*i.r3)i.r4 ; IPicture->get_Handle
${If} $4 = 0
System::Call 'gdi32::SetStretchBltMode(ir1,i4)'
System::Call '*(&i40,&i1024)i.r4' ; BITMAP / BITMAPINFO
System::Call 'gdi32::GetObject(ir3,i24,ir4)'
System::Call 'gdi32::SelectObject(ir1,ir2)i.s'
System::Call '*$4(i40,i.r6,i.r7,i0,i,i.s)' ; Grab size and bits-ptr AND init as BITMAPINFOHEADER
System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFOHEADER
System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFO
System::Call 'gdi32::StretchDIBits(ir1,i0,i0,i${NEWSIZEW},i${NEWSIZEH},i0,i0,ir6,ir7,is,ir4,i0,i${SRCCOPY})'
System::Call 'gdi32::SelectObject(ir1,is)'
System::Free $4
${EndIf}
System::Call 'gdi32::DeleteDC(ir1)'
System::Call $9->2() ; IPicture->release()
${EndIf}
${NSD_CreateBitmap} 1u 1u ${NEWSIZEW} ${NEWSIZEH} ""
Pop $9
;Not required when the control size matches: ${NSD_AddStyle} $9 ${SS_CENTERIMAGE}
SendMessage $9 ${STM_SETIMAGE} ${IMAGE_BITMAP} $2
nsDialogs::Show
System::Call 'gdi32::DeleteObject(ir2)'
FunctionEnd
Function mypagestretchcreate_CTL
nsDialogs::Create 1018
Pop $2
${NSD_CreateBitmap} 0 1u 70% 50% ""
Pop $3
${NSD_AddStyle} $3 ${SS_REALSIZECONTROL}
File "/oname=$PLUGINSDIR\image.bmp" "${NSISDIR}\Contrib\Graphics\Header\win.bmp"
${NSD_SetImage} $3 "$PLUGINSDIR\image.bmp" $1
nsDialogs::Show
${NSD_FreeImage} $1
FunctionEnd

Resources