How can I resize a NSD_SetImageOLE image - nsis

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

Related

NSIS window disappear

I have tried a lot of possibility with this code but it doesn't work. I need to check a VPN connection. If I run the code firs time it works, but if I don't restart the program and push the button secondly then the program just disappear. Any idea how to fix it? thanks.
Function VpnLoginWindow
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 0
${If} $3 == 1
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 1
StrCmp $3 "0" 0 +2
${ExitDo}
${Loop}
${EndIf}
${If} $4 == 1
${ExitDo}
${EndIf}
${Loop}
FunctionEnd
!macro VpnLoginWindow
StrCpy $5 $1
System::Get "(i.r1) iss"
Pop $R0
System::Call "user32::EnumWindows(k R0, i) i.s"
${Do}
Pop $0
StrCmp $0 "1" 0 +2
${ExitDo}
System::Call "user32::GetWindowText(ir1,t.r2,i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
StrCpy $3 0
${If} $2 == "SSL VPN"
StrCpy $3 1
${ExitDo}
${EndIf}
Push 1 # callback's return value
System::Call "$R0"
${Loop}
System::Free $R0
StrCpy $1 $5
!macroend
Callbacks are tricky to get right and the plug-in is a little buggy. Also, you should not use relative jumps to jump over macros like ${ExitDo}!
!include LogicLib.nsh
!macro FindVpnLoginWindow
Push "" ; Result: Window not found
System::Store S
System::Get '(p.r1, p)ir0r0'
Pop $9
System::Call 'USER32::EnumWindows(k r9, p 0)'
${Do}
${IfThen} $0 != "callback1" ${|} ${ExitDo} ${|} ; <-- adjust the callback# if required
StrCpy $0 1 ; Set callback return value, continue search
System::Call "USER32::GetWindowText(pr1, t.r2, i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
${If} $2 == "SSL VPN"
Pop $2 ; Throw away old result
Push $1 ; Result: HWND
StrCpy $0 0 ; Set callback return value, stop enum with 0
${EndIf}
System::Call $9 ; Return from callback function
${Loop}
System::Free $9
System::Store L
!macroend
Function BackgroundFindWindow
!insertmacro FindVpnLoginWindow
Pop $0
DetailPrint "Result:$0"
FunctionEnd
...
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait

NSIS: How to validate IP address box

I have added a ${NSD_CreateIPaddress} box in my nsi script, but while validating each field of IP is come up with by default to 0. I am getting the IP address value by using ${GetText}.
Is there any way to remove default value 0 and validate each field of IP address. Please help me out.
I'm not really sure what you are asking about. If you want to tell the difference between a blank address and the valid "0.0.0.0" address you can use the IPM_ISBLANK message. There should be no need to validate the individual fields because they are already limited to 0..255 and you can even set a custom range with IPM_SETRANGE.
If you still feel the need to look at the individual fields you can
A) Manually parse the string you get from ${NSD_GetText} yourself.
or
B) Unpack the 32-bit address you get from IPM_GETADDRESS:
Page Custom myPage myPageLeave
Page InstFiles
!include nsDialogs.nsh
; Using custom version of http://nsis.sourceforge.net/NsDialogs_CreateIPaddress
!ifndef NSD_CreateIPaddress
!define NSD_CreateIPaddress "!insertmacro NSD_CreateIPaddress "
!include LogicLib.nsh
!include WinMessages.nsh
!ifndef ICC_INTERNET_CLASSES
!define ICC_INTERNET_CLASSES 0x00000800
!endif
Function CCInitIP
!insertmacro _LOGICLIB_TEMP
System::Call '*(i8,i${ICC_INTERNET_CLASSES})p.s' ; NSIS 2.50+
System::Call 'COMCTL32::InitCommonControlsEx(pss)'
Pop $_LOGICLIB_TEMP
System::Free $_LOGICLIB_TEMP
FunctionEnd
!macro NSD_CreateIPaddress x y w h t
!insertmacro _LOGICLIB_TEMP
Call CCInitIP
nsDialogs::CreateControl "SysIPAddress32" ${DEFAULT_STYLES}|${WS_TABSTOP} 0 ${x} ${y} ${w} ${h} "${t}"
Exch $0
CreateFont $_LOGICLIB_TEMP "$(^Font)" "$(^FontSize)"
SendMessage $0 ${WM_SETFONT} $_LOGICLIB_TEMP 1
Exch $0
!macroend
!define /math IPM_GETADDRESS ${WM_USER} + 102
!define /math IPM_ISBLANK ${WM_USER} + 105
!endif
Function OnIPNotify ; This function displays some information about the IP
Pop $0 ; Not used
${NSD_GetText} $1 $3
StrCpy $4 "NSD_GetText: $3"
SendMessage $1 ${IPM_ISBLANK} 0 0 $0
StrCpy $4 "$4$\nIPM_ISBLANK: $0"
System::Call 'USER32::SendMessage(pr1, i ${IPM_GETADDRESS}, p 0, *i0 r3)p.r0' ; NSIS 2.50+
IntFmt $5 "0x%.8x" $3
StrCpy $4 "$4$\nIPM_GETADDRESS: ValidFields=$0 PackedIP=$5"
IntOp $0 $3 >> 24
IntOp $0 $0 & 0xff
StrCpy $4 "$4$\n$\t Field1=$0"
IntOp $0 $3 >> 16
IntOp $0 $0 & 0xff
StrCpy $4 "$4$\n$\t Field2=$0"
IntOp $0 $3 >> 8
IntOp $0 $0 & 0xff
StrCpy $4 "$4$\n$\t Field3=$0"
IntOp $0 $3 & 0xff
StrCpy $4 "$4$\n$\t Field4=$0"
${NSD_SetText} $2 $4
FunctionEnd
Function myPage
nsDialogs::Create 1018
Pop $0
${NSD_CreateIPaddress} 1% 0 50% 12u ""
Pop $1
${NSD_CreateLabel} 1% 20u 98% -20u "Enter an IP address to see information here..."
Pop $2
${NSD_OnNotify} $1 OnIPNotify ; Display some information when the control is changed
nsDialogs::Show
FunctionEnd
Function myPageLeave
Push 0
Call OnIPNotify
MessageBox mb_ok $4
FunctionEnd

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

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

nsis refresh environment during setup

During my setup i install redistributables. After installing my program i want to execute the program during the setup. For that the redistributables are needed. But the environment is not refreshed during setup, so i can't start the program. Can i refresh the environtment during the setup, or reread the environment? Or read the system environment which has changed after the redistributable installation?
I need the environment changes made during the installation of the redistributables during the setup.
Thanks for your help.
I had your same issue, that I solved with a workaround, but I came across this solution and I would like to share with you.
This should reload environment variables during setup:
!include LogicLib.nsh
!include WinCore.nsh
!ifndef NSIS_CHAR_SIZE
!define NSIS_CHAR_SIZE 1
!define SYSTYP_PTR i
!else
!define SYSTYP_PTR p
!endif
!ifndef ERROR_MORE_DATA
!define ERROR_MORE_DATA 234
!endif
/*!ifndef KEY_READ
!define KEY_READ 0x20019
!endif*/
Function RegReadExpandStringAlloc
System::Store S
Pop $R2 ; reg value
Pop $R3 ; reg path
Pop $R4 ; reg hkey
System::Alloc 1 ; mem
StrCpy $3 0 ; size
loop:
System::Call 'SHLWAPI::SHGetValue(${SYSTYP_PTR}R4,tR3,tR2,i0,${SYSTYP_PTR}sr2,*ir3r3)i.r0' ; NOTE: Requires SHLWAPI 4.70 (IE 3.01+ / Win95OSR2+)
${If} $0 = 0
Push $2
Push $0
${Else}
System::Free $2
${If} $0 = ${ERROR_MORE_DATA}
IntOp $3 $3 + ${NSIS_CHAR_SIZE} ; Make sure there is room for SHGetValue to \0 terminate
System::Alloc $3
Goto loop
${Else}
Push $0
${EndIf}
${EndIf}
System::Store L
FunctionEnd
Function RefreshProcessEnvironmentPath
System::Store S
Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $1
Push ${HKEY_LOCAL_MACHINE}
Push "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Push "Path"
Call RegReadExpandStringAlloc
Pop $0
${IfThen} $0 <> 0 ${|} System::Call *(i0)${SYSTYP_PTR}.s ${|}
Pop $2
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r1)i.R1'
System::Call 'KERNEL32::lstrlen(t)(${SYSTYP_PTR}r2)i.R2'
System::Call '*(&t$R2 "",&t$R1 "",i)${SYSTYP_PTR}.r0' ; The i is 4 bytes, enough for a ';' separator and a '\0' terminator (Unicode)
StrCpy $3 ""
${If} $R1 <> 0
${AndIf} $R2 <> 0
StrCpy $3 ";"
${EndIf}
System::Call 'USER32::wsprintf(${SYSTYP_PTR}r0,t"%s%s%s",${SYSTYP_PTR}r2,tr3,${SYSTYP_PTR}r1)?c'
System::Free $1
System::Free $2
System::Call 'KERNEL32::SetEnvironmentVariable(t"PATH",${SYSTYP_PTR}r0)'
System::Free $0
System::Store L
FunctionEnd
Section
Call RefreshProcessEnvironmentPath
SectionEnd
Source: http://forums.winamp.com/showpost.php?p=3028153&postcount=12

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

Resources