From 25410eb00f4051eee0f35080a85322a1c585dc50 Mon Sep 17 00:00:00 2001 From: "S. Razi Alavizadeh" Date: Mon, 19 Nov 2012 21:59:04 +0330 Subject: [PATCH] NSIS script improved: -'FileAssociation.nsh' rewritten and renamed to 'AllAssociation.nsh' -Now supports new association method on Vista and newer version of Windows. -Support register as protocol handler and add option for set as default browser. -Backup old association even on Vista and newer version of Windows and restore them when uninstalling. -Support 'Default Programs' on Vista and newer version of Windows. --- windows/AllAssociation.nsh | 456 ++++++++++++++++++++++++++++++++++++ windows/AppAssocReg.dll | Bin 0 -> 4608 bytes windows/FileAssociation.nsh | 190 --------------- windows/installer.nsi | 70 +++++- windows/languages.nsh | 118 ++++++++++ windows/registry.dll | Bin 0 -> 25088 bytes 6 files changed, 636 insertions(+), 198 deletions(-) create mode 100644 windows/AllAssociation.nsh create mode 100644 windows/AppAssocReg.dll delete mode 100644 windows/FileAssociation.nsh create mode 100644 windows/registry.dll diff --git a/windows/AllAssociation.nsh b/windows/AllAssociation.nsh new file mode 100644 index 000000000..8d71ac186 --- /dev/null +++ b/windows/AllAssociation.nsh @@ -0,0 +1,456 @@ +/* +_____________________________________________________________________________ + + All Association +_____________________________________________________________________________ + + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; Date: 2012-Nov-18, S. Razi Alavizadeh, v0.1 ; + ; Some Codes are based on code taken from: ; + ; http://nsis.sourceforge.net/File_Association ; + ; Ability to register protocol and extention associations. ; + ; It supports old association method and new method by using ; + ; IApplicationAssociationRegistration APIs. ; + ; that needed 'AppAssocReg' plugins, downloadable from: ; + ; http://nsis.sourceforge.net/SetVistaDefaultApp_plug-in ; + ; and also add support for set as default backuped association when ; + ; uninstalling. This needed 'Registery' plugins. ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + Requirment: + 1. 'AppAssocReg' plugins. + 2. 'Registery' plugins. + + Usage in script: + 1. !include "AllAssociation.nsh" + 2. [Section|Function] + ${AllAssociationFunction} "Param1" "Param2" "..." $var + [SectionEnd|FunctionEnd] + + AllAssociationFunction=[RegisterAssociation|UnRegisterAssociation|CreateProgId|UpdateSystemIcons|SetAsDefaultNoAppName] + +_____________________________________________________________________________ + + ${RegisterAssociation} "[assoc_name]" "[executable]" "[prog_id]" "[description]" "[icon]" "[type]" + +"[assoc_name]" ; assoc_name, which is file format's extention if type is "file" and is protocol's name if type is "protocol". + ; +"[executable]" ; executable which opens the file format or is protocol handler. + ; +"[prog_id]" ; registery internal id for assoc_name, on Vista+ if type is "protocol" this is our registered ProgId for assoc_name and is used for comparison. + ; +"[description]" ; description for the assoc_name. This will be display in Windows Explorer. + ; +"[icon]" ; icon path for this association. + ; +"[type]" ; type of association. "file" for extention and "protocol" for protocol handler. + ; + + ${UnRegisterAssociation} "[assoc_name]" "[prog_id]" "[executable]" "[type]" + +"[assoc_name]" ; assoc_name, which is file format's extention if type is "file" and is protocol's name if type is "protocol". + ; +"[prog_id]" ; registery internal id for assoc_name + ; +"[executable]" ; executable which opens the file format or is protocol handler. + ; +"[type]" ; type of association. "file" for extention and "protocol" for protocol handler. + ; + + ${CreateProgId} "[prog_id]" "[executable]" "[description]" "[icon]" + +"[prog_id]" ; registery internal id for assoc_name + ; +"[executable]" ; executable which opens the file format or is protocol handler. + ; +"[description]" ; description for the assoc_name. This will be display in Windows Explorer. + ; +"[icon]" ; icon path for this prog_id. + ; + + + ${SetAsDefaultNoAppName} "[prog_id]" "[assoc_name]" "[type]" + +"[prog_id]" ; registery internal id for assoc_name + ; +"[assoc_name]" ; assoc_name, which is file format's extention if type is "file" and is protocol's name if type is "protocol". + ; +"[type]" ; type of association. "file" for extention and "protocol" for protocol handler. + ; + + ${UpdateSystemIcons} ; it has not arguments and just notifies OS for updating icons for new associations. + +_____________________________________________________________________________ + + Macros +_____________________________________________________________________________ + + Change log window verbosity (default: 3=no script) + + Example: + !include "AllAssociation.nsh" + !insertmacro RegisterAssociation + ${AllAssociation_VERBOSE} 4 # all verbosity + !insertmacro UnRegisterAssociation + ${AllAssociation_VERBOSE} 3 # no script +*/ + + +!ifndef AllAssociation_INCLUDED +!define AllAssociation_INCLUDED + +!include Util.nsh + +!verbose push +!verbose 3 +!ifndef _AllAssociation_VERBOSE + !define _AllAssociation_VERBOSE 3 +!endif +!verbose ${_AllAssociation_VERBOSE} +!define AllAssociation_VERBOSE `!insertmacro AllAssociation_VERBOSE` +!verbose pop + +!macro AllAssociation_VERBOSE _VERBOSE + !verbose push + !verbose 3 + !undef _AllAssociation_VERBOSE + !define _AllAssociation_VERBOSE ${_VERBOSE} + !verbose pop +!macroend + + +; define some registery macros +!define RegistryRead `!insertmacro RegistryRead` + +!macro RegistryRead _PATH _VALUE _STRING _TYPE + registry::_Read /NOUNLOAD `${_PATH}` `${_VALUE}` + Pop ${_STRING} + Pop ${_TYPE} +!macroend + + +!define RegistryWrite `!insertmacro RegistryWrite` + +!macro RegistryWrite _PATH _VALUE _STRING _TYPE _ERR + registry::_Write /NOUNLOAD `${_PATH}` `${_VALUE}` `${_STRING}` `${_TYPE}` + Pop ${_ERR} +!macroend + +!define RegistryFind `!insertmacro RegistryFind` + +!macro RegistryFind _HANDLE _PATH _VALUEORKEY _STRING _TYPE + registry::_Find /NOUNLOAD `${_HANDLE}` + Pop ${_PATH} + Pop ${_VALUEORKEY} + Pop ${_STRING} + Pop ${_TYPE} +!macroend + +!define RegistryOpen `!insertmacro RegistryOpen` + +!macro RegistryOpen _PATH _OPTIONS _HANDLE + registry::_Open /NOUNLOAD `${_PATH}` `${_OPTIONS}` + Pop ${_HANDLE} +!macroend + +!macro RegisterAssociationCall _EXTENSION _EXECUTABLE _PROG_ID _DESCRIPTION _ICON _TYPE + !verbose push + !verbose ${_AllAssociation_VERBOSE} + + Push `${_EXECUTABLE}` + Push `${_PROG_ID}` + Push `${_DESCRIPTION}` + Push `${_ICON}` + + ${CallArtificialFunction} CreateProgId_ + + + Push `${_PROG_ID}` + Push `${_EXECUTABLE}` + Push `${_EXTENSION}` + Push `${_TYPE}` + + ${CallArtificialFunction} RegisterAssociation_ + + !verbose pop +!macroend + +!macro UnRegisterAssociationCall _EXTENSION _PROG_ID _EXECUTABLE _TYPE + !verbose push + !verbose ${_AllAssociation_VERBOSE} + Push `${_EXTENSION}` + Push `${_EXECUTABLE}` + Push `${_PROG_ID}` + Push `${_TYPE}` + + ${CallArtificialFunction} UnRegisterAssociation_ + + ; backuped ProgId was pushed from UnRegisterAssociation_ + Push `${_EXTENSION}` + Push `${_TYPE}` ; type of association + ${CallArtificialFunction} SetAsDefaultNoAppName_ + + !verbose pop +!macroend + +!macro CreateProgIdCall _PROG_ID _EXECUTABLE _DESCRIPTION _ICON + !verbose push + !verbose ${_AllAssociation_VERBOSE} + Push `${_EXECUTABLE}` + Push `${_PROG_ID}` + Push `${_DESCRIPTION}` + Push `${_ICON}` + + ${CallArtificialFunction} CreateProgId_ + !verbose pop +!macroend + +!macro SetAsDefaultNoAppNameCall _PROG_ID _EXTENSION _TYPE + !verbose push + !verbose ${_AllAssociation_VERBOSE} + Push `${_PROG_ID}` + Push `${_EXTENSION}` + Push `${_TYPE}` + + ${CallArtificialFunction} SetAsDefaultNoAppName_ + !verbose pop +!macroend + +!define SetAsDefaultNoAppName `!insertmacro SetAsDefaultNoAppNameCall` +!define un.SetAsDefaultNoAppName `!insertmacro SetAsDefaultNoAppNameCall` + +!macro SetAsDefaultNoAppName +!macroend + +!macro un.SetAsDefaultNoAppName +!macroend + +!macro SetAsDefaultNoAppName_ + !verbose push + !verbose ${_AllAssociation_VERBOSE} + + Pop $R0 ; TYPE + Pop $R1 ; EXTENSION + Pop $R2 ; PROG_ID + + StrCmp $R2 "No ProgId Pushed" NoBackupedProgId 0 + StrCmp $R2 "" NoBackupedProgId 0 + StrCmp $R1 "" NoBackupedProgId 0 + ${RegistryOpen} "HKLM\SOFTWARE\RegisteredApplications" "/K=0 /V=1 /S=0 /B=1" $R8 ; R8 = first registery handle + StrCmp $R8 0 close 0 +loop1: + ${RegistryFind} "$R8" $1 $R3 $3 $4 ; $R3 = AppRegisteredName + StrCmp $1 "" close 0 + + ${RegistryOpen} "HKLM\$3" "/K=0 /V=1 /S=0 /B=1 /N='$R1'" $R9 ; R9 = second registery handle + StrCmp $R9 0 loop1 0 +loop2: + ${RegistryFind} "$R9" $1 $2 $R4 $4 ; $R4 = ProgId registered for EXTENSION + StrCmp $1 "" loop1 0 + StrCmp $R4 "$R2" 0 loop2 + + AppAssocReg::SetAppAsDefault "$R3" "$R1" "$R0" + +close: + registry::_Close /NOUNLOAD "$R9" + registry::_Close /NOUNLOAD "$R8" + registry::_Unload + +NoBackupedProgId: + !verbose pop +!macroend + +!define CreateProgId `!insertmacro CreateProgIdCall` +!define un.CreateProgId `!insertmacro CreateProgIdCall` + +!macro CreateProgId +!macroend + +!macro un.CreateProgId +!macroend + +!macro CreateProgId_ + !verbose push + !verbose ${_AllAssociation_VERBOSE} + + Pop $R0 ;ICON + Pop $R1 ;DESCRIPTION + Pop $R2 ;PROG_ID + Pop $R3 ;EXECUTABLE + + ReadRegStr $0 HKCR $R2 "" +; StrCmp $0 "" 0 JustSkip ; if icon and description are present then just skip + WriteRegStr HKCR "$R2" "" "$R1" + WriteRegStr HKCR "$R2\shell" "" "open" + WriteRegStr HKCR "$R2\DefaultIcon" "" "$R0" +;JustSkip: + WriteRegStr HKCR "$R2\shell\open\command" "" '"$R3" "%1"' + WriteRegStr HKCR "$R2\shell\edit" "" "Edit $R1" + WriteRegStr HKCR "$R2\shell\edit\command" "" '"$R3" "%1"' + + !verbose pop +!macroend + +!define RegisterAssociation `!insertmacro RegisterAssociationCall` +!define un.RegisterAssociation `!insertmacro RegisterAssociationCall` + +!macro RegisterAssociation +!macroend + +!macro un.RegisterAssociation +!macroend + +!macro RegisterAssociation_ + !verbose push + !verbose ${_AllAssociation_VERBOSE} + + Pop $R0 ;type = file or protocol + Pop $R1 ;association name = ext or protocol + Pop $R2 ;exe + Pop $R3 ;prog_id + + + StrCmp "$R0" "file" 0 NoFile +; file + ReadRegStr $1 HKCR $R1 "" ; read current file association + StrCmp "$1" "" NoFileExtBackup ; is it empty + StrCmp "$1" "$R3" NoFileExtBackup ; it is our own + WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value +NoFileExtBackup: + WriteRegStr HKCR $R1 "" "$R3" ; set our file association +Goto VistaPlus + +NoFile: +; Protocol + StrCmp "$R0" "protocol" 0 NotSupported + ; write protocol flag + WriteRegStr HKCR $R1 "URL Protocol" "" + ;get prog_id of current default + + AppAssocReg::QueryCurrentDefault $R1 "protocol" "user" + Pop $1 + StrCmp "$1" "method failed" NoProgID 0 + StrCmp "$1" "method not available" NoProgID 0 + StrCmp "$1" "$R3" NoProgID 0 ; it is our own + WriteRegStr HKCR "$R1\shell\open\command" "backup_progid" "$1" ; backup current progid +NoProgID: +; ReadRegStr $1 HKCR "$R1\shell\open\command" "" ; read current protocol association + ; some applications write this as REG_EXPAND_SZ and don't work with REG_SZ (e.g.: some version of Opera) + ${RegistryRead} "HKCR\$R1\shell\open\command" "" '$1' '$0' ; read current protocol association and its registery type + StrCmp "$1" "" NoProtocolBackup ; is it empty + StrCmp "$1" '"$R2" "%1"' NoProtocolBackup ; it is our own + ;WriteRegStr HKCR "$R1\shell\open\command" "backup_val" "$1" ; backup current value + ${RegistryWrite} "HKCR\$R1\shell\open\command" "backup_val" '$1' '$0' '$2'; backup current value and its registery type +NoProtocolBackup: + WriteRegStr HKCR "$R1\shell\open\command" "" '"$R2" "%1"' ; set our file association +;Goto VistaPlus + +; TODO: type = startMenu or type = mime + +VistaPlus: + ;Vista and newer need some more works + AppAssocReg::SetAppAsDefault "${PRODUCT_NAME}" "$R1" "$R0" + +NotSupported: + registry::_Unload + !verbose pop +!macroend + +!define UnRegisterAssociation `!insertmacro UnRegisterAssociationCall` +!define un.UnRegisterAssociation `!insertmacro UnRegisterAssociationCall` + +!macro UnRegisterAssociation +!macroend + +!macro un.UnRegisterAssociation +!macroend + +!macro UnRegisterAssociation_ + !verbose push + !verbose ${_AllAssociation_VERBOSE} + + Pop $R3 ;type = file or protocol + Pop $R2 ;prog_id + Pop $R1 ;exe + Pop $R0 ;association name = ext or protocol + + StrCmp "$R3" "file" 0 NoFile +; file + ReadRegStr $1 HKCR $R0 "" + StrCmp $1 $R2 0 NoOwn ; only do this if we own it + ReadRegStr $1 HKCR $R0 "backup_val" + StrCmp $1 "" 0 RestoreFile ; if backup="" then delete the whole key + Push `"No ProgId Pushed"` + DeleteRegKey HKCR $R0 + Goto NoOwn + + RestoreFile: + Push `$1` + WriteRegStr HKCR $R0 "" $1 + DeleteRegValue HKCR $R0 "backup_val" + DeleteRegKey HKCR $R2 ;Delete key with association name settings + Goto NoOwn + +NoFile: +; Protocol + StrCmp "$R3" "protocol" 0 NoOwn + ReadRegStr $1 HKCR "$R0\shell\open\command" "" + StrCmp $1 '"$R1" "%1"' 0 NoOwn ; only do this if we own it + ; ReadRegStr $1 HKCR "$R0\shell\open\command" "backup_val" + ${RegistryRead} "HKEY_CLASSES_ROOT\$R0\shell\open\command" "backup_val" '$1' '$0' ; read current protocol association and its registery type + StrCmp $1 "" 0 RestoreProtocol ; if backup="" then delete the whole key + Push `"No ProgId Pushed"` + DeleteRegKey HKCR "$R0\shell\open\command" + Goto NoOwn + + RestoreProtocol: + ReadRegStr $2 HKCR "$R0\shell\open\command" "backup_progid" + StrCmp $2 "" 0 HasProgId + Push `"No ProgId Pushed"` + Goto NoProgID + HasProgId: + Push `$2` + NoProgID: + ;WriteRegStr HKCR "$R0\shell\open\command" "" $1 + ${RegistryWrite} "HKEY_CLASSES_ROOT\$R0\shell\open\command" "" '$1' '$0' '$2' + DeleteRegValue HKCR "$R0\shell\open\command" "backup_val" + DeleteRegValue HKCR "$R0\shell\open\command" "backup_progid" + StrCmp $R2 "" NoOwn 0 ;Delete protocol association if it's present + DeleteRegKey HKCR $R2 ;Delete key with association name settings +;Goto NoOwn + +; TODO: type = startMenu or type = mime + + NoOwn: + registry::_Unload + !verbose pop +!macroend + +!define UpdateSystemIcons `!insertmacro UpdateSystemIcons_` +!define un.UpdateSystemIcons `!insertmacro UpdateSystemIcons_` + +!macro UpdateSystemIcons +!macroend + +!macro un.UpdateSystemIcons +!macroend + + ; !defines for use with SHChangeNotify +!ifdef SHCNE_ASSOCCHANGED + !undef SHCNE_ASSOCCHANGED +!endif +!define SHCNE_ASSOCCHANGED 0x08000000 + +!ifdef SHCNF_FLUSHNOWAIT + !undef SHCNF_FLUSHNOWAIT +!endif +!define SHCNF_FLUSHNOWAIT 0x3000 + +!macro UpdateSystemIcons_ +; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we +; can update the shell. + System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSHNOWAIT}, 0, 0)" +!macroend +!endif # !AllAssociation_INCLUDED diff --git a/windows/AppAssocReg.dll b/windows/AppAssocReg.dll new file mode 100644 index 0000000000000000000000000000000000000000..b0d7f23bc1f1946ee749f3d694f38af5adbd4f76 GIT binary patch literal 4608 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 z%0A_ne3L=y!r}g-W907Z40(Rlwd$=J{sPqHUZVW+Fs4?Re+7TxWc{)le~~WD#RnUU z7FoY+>_yVz4;pUw;Gez-*ptD1J%iEEm6j?$fG@3@xSJu}xfR#Kexl{e?TJG{l-RaP#kpPeBEEwmPb44mu`qY$vpbry77Q*Qz_nV?<{3=sX zxG~Hiv@fxq$an8`yhghO&?ls)$$NzUq{S6dqdV6sYH~qg$%1xxir7M!mQN_EqbNV- zQJ!AM05KF(gh-vT(9l&rj+3D)%~#Y_h68YPVTWF$CghPdbt@^VA_&evXVec;NTDxi zqRprmAX2e{J&8}VK=d7CZ_M${R+Dza8aZE%hl9927u!&HA%?NjIs z>O;ALwZ*Bg9O}aE#Mvx*;=Lw%rl7KohI7>sD&)s03V&FK6Xx&3N4e zDqS~`n;%kEVVUqCp14X~!q6!sEPisifF5{oSeQb)0Fe`#NN(?V{V%ytXl2Km&i1cu z>xcYLXhO<#jH=q&Sv_=22f$GOR=no?HJZAF%;*d@a!7EjeP9YsZdKu|Cr0*lOA}s}u2Wbn^ zLr6bFnvX<31M08b#aK6z7U=_|6G;1!o<;gTQY+H^NFJm`Nb`|$kk+HQeZCRKqcE(L^+8ER?4ojV2xc(QgGCjz2u2IwQ*h z&$9rlB@8{%e%}H;F%{2SAd!JG6<)(OvklM%YfwiN@_?tdwy~&m)O<7t608u3N|$qv zR`6#{q@7xR>yR31Gk_n(jLN{y=YA>6j!+)nj~Aqp2U)*Dk+C%D(HU$bFzW69cvx;upp4fg`) zF-^4T(hdFe*s~gU!&;%PbnUu2%v*6o-D)-C#x5Di6|vPsm9UBgp<(SEqEOcenk%m@ z1qbiPI_p+9-05|!7QAkDc2KSeYeJ>!XxMOPT_e%(3@t(XY*xH(Lt|a}>h){D)$R5r zi|h+cS+m!yTgTX6V=B;W^<8!IH*9gU2SGbB)}{D2C_!z<&=OD^Nj#RUyK8|^cMoHq zuu_Od6_0&O5vy|Amqcm*K4wYXUA*ht>+WSAQqI0!yayRO`uboU~~`37qJlKE5*8odwD0s2D@~18O9hF3p|Ko z082Zx1UTI(w#!)fR8qcrPTX0$lt6`DWv#>a+|6?e}aH z&|uJvv07TYthT&v%hC-_p`mC1bhBEtwr@etjjXm}!=^e;jDSdQw8~iRlDgF(Ku`M? zp(_0d)?8skUMdw5(f{Iy^GE;he?jJ7P5-Y- zz<>ZhcpquJ*El}Yl=-8qpJrKe=jVQ$`?uU+?&rA|axdk6og2>8m=nxNX1#f|d7SwM zbDH@k^Uda&<~e4Q*=)9&=b4MmW#-$>Z=3&UK5ag0{?Pof`ETZ+`E&CH^Ck1w&>but zwh6BB}dOhoS*4tU9vp&tzndY0krd_7nvo~b7WIvbvYWAPA zzsyd_xh3cJoE16u;mf*Ts+hby3N{ZeZu;J^?mCX)~~FI1)~cx z3g#6oDyT1LENCuhE!b6Xu;9Id4+{QPaH)XVbT)%6)s}9%#Wvrz*ygs~Wqa0k^W37j zOXh~=+U9MYw}0Nzc~|Day<)SVc$1AKMu%~w@nPdG<1dWb%sH7gnfGKqnE6cRtC`v? zDeH->ai$HXADG@TjmVyoU50V)$bKq2CdZnyE$8u^Q#neGA$LM7){s?O zFsfi&furE5f>#Sp6!aFv*>1E=x7}&mX#1IM!d&Ovt#f}c_vdqeH}}oCOXfW|@8|P= zGw(mW>G2OVv*l65tY&HJK_?+=~#;nY{GQSOJJ(BsO%oj5EXCBWym3c0+ zC-X{XYSy%@nykmOp3Hg%DAW1)U?XfU}`bRrZ-H-P4Ah4rt#T7&i*XBFvp$a z&Doyg&)J#tX3je~ALo3YGbeXZ?(?}X#fdMkM;JV@eSjf#{V?_&Dd?cV$@_# z$V|)J26;Z5xi|Bb%!8TdGlyl3%gW4hXMI15cKHigFJ&oNf6oeMjW?y4W||I|3bTKf z{ZjUc>~T3pNbt8gf6DnNXH0HQZd2~A+!u3ym)o5i%FQt^G&`}+T4_)J>-R4S{7VA= HrzP;;Ipv25 literal 0 HcmV?d00001