Installer correctly associates torrents and magnet links with qBittorrent under non-admin accounts. Closes #291 #527 #579 #614 #964.

This commit is contained in:
sledgehammer999 2014-07-28 19:29:51 +03:00
parent 25c923c69e
commit d0e0b25c9e
7 changed files with 476 additions and 25 deletions

View file

@ -22,7 +22,11 @@ I tested with NSIS 3.0a0.
"!define PROG_VERSION "3.0.3"" to the version of qbittorrent you just built.
2. Extract the plugins found in the folder "nsis plugins" into your
NSIS's unicode Plugin directory(usually C:\Program Files\NSIS\Plugins\x86-unicode).
Only the *.dll files are needed.
Only the *.dll files are needed. Don't use the .dll from UAC.zip, use the one from "UAC Unicode.zip".
NOTE: To build the unicode version of UAC with MSVC2008 you need:
a) the sources from UAC.zip
b) apply the util.cpp.diff from "UAC Unicode.zip" to util.cpp
c) in a msvc command prompt issue: cl.exe /O1s /GS- /GR- /EHs-c- /Zl /LD /DUNICODE RunAs.cpp uac.cpp util.cpp /link kernel32.lib user32.lib shell32.lib advapi32.lib ole32.lib /DLL /MANIFEST:NO /OUT:uac.dll
3. The script you need to compile is "qbittorrent.nsi". It includes all other necessary scripts.
4. The script expects the following file tree:
@ -59,5 +63,5 @@ If you add any new LangString variable to the scripts you NEED to provide
"translations" of it to all the .nsi files inside "installer-translations.
You can always leave the english string but you have to use all the LANG_<lang name>
for the given variable. Otherwise, if the user chooses a language that you
haven't provide a LANG_<lang name> for your variable then your string will be empty.
haven't provided a LANG_<lang name> for your variable then your string will be empty.
Don't worry though, NSIS throws warnings for this when compiling the scripts.

297
src/windows/UAC.nsh Normal file
View file

@ -0,0 +1,297 @@
/*** UAC Plug-in ***
Interactive User (MediumIL) Admin user (HighIL)
***[Setup.exe]************* ***[Setup.exe]**************
* * * *
* +++[.OnInit]+++++++++++ * * +++[.OnInit]++++++++++++ *
* + UAC_RunElevated >---+-+----> * + + *
* + NSIS.Quit + * * + + *
* +++++++++++++++++++++++ * * ++++++++++++++++++++++++ *
* * * *
* * * *
* +++[Section]+++++++++++ * * +++[Section]++++++++++++ *
* + + * /--+-+-<UAC_AsUser_ExecShell+ *
* +++++++++++++++++++++++ * | * ++++++++++++++++++++++++ *
* * | * *
* Win32.ShellExecute <---+--/ * *
* * * *
*************************** ****************************
*/
!ifndef UAC_HDR__INC
!verbose push
!verbose 3
!ifndef UAC_VERBOSE
!define UAC_VERBOSE 3
!endif
!verbose ${UAC_VERBOSE}
!define UAC_HDR__INC 0x00020400 ;MMmmbbrr
!include LogicLib.nsh
/* UAC_RunElevated
**
** Starts the elevation operation.
**
** Return values:
**
** $0: Win32 error code (0 on success, 1223 if user aborted elevation dialog, anything else should be treated as a fatal error)
** $1: If $0==0:
** 0 UAC is not supported by the OS
** 1 Started a elevated child process, the current process should act like a wrapper (Call Quit without any further processing)
** 2 The process is already running @ HighIL (Member of admin group)
** 3 You should call RunElevated again (This can happen if a user without admin priv. is used in the runas dialog)
** $2: If $0==0 && $1==1: ExitCode of the elevated fork process (The NSIS errlvl is also set)
** $3: If $0==0: 1 if the user is a member of the admin group or 0 otherwise
**/
!macro UAC_RunElevated
UAC::_ 0
!macroend
!macro UAC_PageElevation_RunElevated
UAC::_ 0
!macroend
/*!macro UAC_OnInitElevation_RunElevated
UAC::_ 0
!macroend
!macro UAC_OnInitElevation_OnGuiInit
!macroend*/
/* UAC_GetIntegrityLevel <NSISVar:Output | "s">
**
** Get integrity level of current process
**
**/
!macro UAC_GetIntegrityLevel outvar
UAC::_ 6
!if "${outvar}" != "s"
Pop ${outvar}
!endif
!macroend
/* UAC_IsAdmin
**
** Is the current process running with administrator privileges? Result in $0
**
** ${If} ${UAC_IsAdmin} ...
**
**/
!macro UAC_IsAdmin
UAC::_ 2
!macroend
!define UAC_IsAdmin `"" UAC_IsAdmin ""`
!macro _UAC_IsAdmin _a _b _t _f
!insertmacro _UAC_MakeLL_Cmp _!= 0 2s
!macroend
/* UAC_IsInnerInstance
**
** Does the current process have a NSIS/UAC parent process that is part of the elevation operation?
**
** ${If} ${UAC_IsInnerInstance} ...
**
**/
!macro UAC_IsInnerInstance
UAC::_ 3
!macroend
!define UAC_IsInnerInstance `"" UAC_IsInnerInstance ""`
!macro _UAC_IsInnerInstance _a _b _t _f
!insertmacro _UAC_MakeLL_Cmp _!= 0 3s
!macroend
/* UAC_PageElevation_OnInit, UAC_PageElevation_OnGuiInit,
**
** Helper macros for elevation on a custom elevation page, see the DualMode example for more information.
**
**/
!macro UAC_Notify_OnGuiInit
UAC::_ 4
!macroend
!macro UAC_PageElevation_OnGuiInit
!insertmacro UAC_Notify_OnGuiInit
!macroend
!macro UAC_PageElevation_OnInit
UAC::_ 5
${IfThen} ${Errors} ${|} Quit ${|}
!macroend
/* UAC_AsUser_Call <Function|Label> <NSISAddressName> <UAC_* flags>
**
** Calls a function or label in the user process instance.
** All the UAC_AsUser_* macros use this helper macro.
**
**/
!define UAC_SYNCREGISTERS 0x1
;define UAC_SYNCSTACK 0x2
!define UAC_SYNCOUTDIR 0x4
!define UAC_SYNCINSTDIR 0x8
;define UAC_CLEARERRFLAG 0x10
!macro UAC_AsUser_Call type name flags
push $0
Get${type}Address $0 ${name}
!verbose push
!verbose ${UAC_VERBOSE}
!insertmacro _UAC_ParseDefineFlagsToInt _UAC_AsUser_Call__flags ${flags}
!verbose pop
StrCpy $0 "1$0:${_UAC_AsUser_Call__flags}"
!undef _UAC_AsUser_Call__flags
Exch $0
UAC::_
!macroend
/*
** UAC_AsUser_GetSection <Flags|InstTypes|Size|Text> <SectionIndex> <NSISVar:Output>
*/
!macro UAC_AsUser_GetSection secprop secidx outvar
!insertmacro _UAC_AsUser_GenOp ${outvar} SectionGet${secprop} ${secidx} ""
!macroend
/*
** UAC_AsUser_GetGlobalVar <NSISVar:SourceAndOutput>
** UAC_AsUser_GetGlobal <NSISVar:Output> <NSISVar:Source>
*/
!macro UAC_AsUser_GetGlobalVar var
!insertmacro _UAC_AsUser_GenOp ${var} StrCpy "" ${var}
!macroend
!macro UAC_AsUser_GetGlobal outvar srcvar
!insertmacro _UAC_AsUser_GenOp ${outvar} StrCpy "" ${srcvar}
!macroend
/*
** UAC_AsUser_ExecShell <Verb> <ApplicationOrFile> <Parameters> <Working Directory> <SW_*>
**
** Call ExecShell in the user process instance.
**
*/
!macro UAC_AsUser_ExecShell verb command params workdir show
!insertmacro _UAC_IncL
goto _UAC_L_E_${__UAC_L}
_UAC_L_F_${__UAC_L}:
ExecShell "${verb}" "${command}" '${params}' ${show}
return
_UAC_L_E_${__UAC_L}:
!if "${workdir}" != ""
push $outdir
SetOutPath "${workdir}"
!endif
!insertmacro UAC_AsUser_Call Label _UAC_L_F_${__UAC_L} ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR} #|${UAC_CLEARERRFLAG}
!if "${workdir}" != ""
pop $outdir
SetOutPath $outdir
!endif
!macroend
!macro _UAC_MakeLL_Cmp cmpop cmp pluginparams
!insertmacro _LOGICLIB_TEMP
UAC::_ ${pluginparams}
pop $_LOGICLIB_TEMP
!insertmacro ${cmpop} $_LOGICLIB_TEMP ${cmp} `${_t}` `${_f}`
!macroend
!macro _UAC_definemath def val1 op val2
!define /math _UAC_definemath "${val1}" ${op} ${val2}
!ifdef ${def}
!undef ${def}
!endif
!define ${def} "${_UAC_definemath}"
!undef _UAC_definemath
!macroend
!macro _UAC_ParseDefineFlags_orin parse outflags
!searchparse /noerrors ${${parse}} "" _UAC_ParseDefineFlags_orin_f1 "|" _UAC_ParseDefineFlags_orin_f2
!define _UAC_ParseDefineFlags_orin_this ${_UAC_ParseDefineFlags_orin_f1}
!undef ${parse}
!define ${parse} ${_UAC_ParseDefineFlags_orin_f2}
!define _UAC_ParseDefineFlags_orin_saveout ${${outflags}}
!undef ${outflags}
!define /math ${outflags} "${_UAC_ParseDefineFlags_orin_saveout}" | "${_UAC_ParseDefineFlags_orin_this}"
!undef _UAC_ParseDefineFlags_orin_saveout
!undef _UAC_ParseDefineFlags_orin_this
!ifdef _UAC_ParseDefineFlags_orin_f1
!undef _UAC_ParseDefineFlags_orin_f1
!undef _UAC_ParseDefineFlags_orin_f2
!endif
!macroend
!macro _UAC_ParseDefineFlags_Begin _outdef _in
!define _UAC_PDF${_outdef}_parse "${_in}"
!define _UAC_PDF${_outdef}_flags ""
!define _UAC_PDF${_outdef}_r 0
!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x1
!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x2
!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x4
!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x8
!insertmacro _UAC_ParseDefineFlags_orin _UAC_PDF${_outdef}_parse _UAC_PDF${_outdef}_flags ;0x10
!macroend
!macro _UAC_ParseDefineFlags_End _outdef
!define ${_outdef} ${_UAC_PDF${_outdef}_r}
!undef _UAC_PDF${_outdef}_r
!undef _UAC_PDF${_outdef}_flags
!undef _UAC_PDF${_outdef}_parse
!macroend
!macro _UAC_ParseDefineFlags_IncludeFlag _outdef flag
!if ${_UAC_PDF${_outdef}_flags} & ${flag}
!insertmacro _UAC_definemath _UAC_PDF${_outdef}_r ${_UAC_PDF${_outdef}_r} | ${flag}
!endif
!macroend
!macro _UAC_ParseDefineFlagsToInt _outdef _in
!insertmacro _UAC_ParseDefineFlags_Begin _UAC_ParseDefineFlagsToInt_tmp "${_in}"
!define ${_outdef} ${_UAC_PDF_UAC_ParseDefineFlagsToInt_tmp_flags}
!insertmacro _UAC_ParseDefineFlags_End _UAC_ParseDefineFlagsToInt_tmp
!undef _UAC_ParseDefineFlagsToInt_tmp
!macroend
!macro _UAC_IncL
!insertmacro _UAC_definemath __UAC_L "${__UAC_L}" + 1
!macroend
!macro _UAC_AsUser_GenOp outvar op opparam1 opparam2
!define _UAC_AUGOGR_ID _UAC_AUGOGR_OP${outvar}${op}${opparam1}${opparam2}
!ifndef ${_UAC_AUGOGR_ID} ;Has this exact action been done before?
!if ${outvar} == $0
!define ${_UAC_AUGOGR_ID} $1
!else
!define ${_UAC_AUGOGR_ID} $0
!endif
!if "${opparam1}" == ""
!define _UAC_AUGOGR_OPP1 ${${_UAC_AUGOGR_ID}}
!define _UAC_AUGOGR_OPP2 ${opparam2}
!else
!define _UAC_AUGOGR_OPP1 ${opparam1}
!define _UAC_AUGOGR_OPP2 ${${_UAC_AUGOGR_ID}}
!endif
goto ${_UAC_AUGOGR_ID}_C
${_UAC_AUGOGR_ID}_F:
${op} ${_UAC_AUGOGR_OPP1} ${_UAC_AUGOGR_OPP2}
return
${_UAC_AUGOGR_ID}_C:
!undef _UAC_AUGOGR_OPP1
!undef _UAC_AUGOGR_OPP2
!endif
push ${${_UAC_AUGOGR_ID}}
!insertmacro UAC_AsUser_Call Label ${_UAC_AUGOGR_ID}_F ${UAC_SYNCREGISTERS}
StrCpy ${outvar} ${${_UAC_AUGOGR_ID}}
pop ${${_UAC_AUGOGR_ID}}
!undef _UAC_AUGOGR_ID
!macroend
!verbose pop
!endif /* UAC_HDR__INC */

View file

@ -100,6 +100,72 @@ Section $(inst_startmenu) ;"Create Start Menu Shortcut"
SectionEnd
Section $(inst_torrent) ;"Open .torrent files with qBittorrent"
ReadRegStr $0 HKLM "Software\Classes\.torrent" ""
StrCmp $0 "qBittorrent" clear_errors 0
;Check if empty string
StrCmp $0 "" clear_errors 0
;Write old value to OpenWithProgIds
WriteRegStr HKLM "Software\Classes\.torrent\OpenWithProgIds" $0 ""
clear_errors:
ClearErrors
WriteRegStr HKLM "Software\Classes\.torrent" "" "qBittorrent"
WriteRegStr HKLM "Software\Classes\.torrent" "Content Type" "application/x-bittorrent"
!insertmacro UAC_AsUser_Call Function inst_torrent_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
SectionEnd
Function inst_torrent_user
ReadRegStr $0 HKCU "Software\Classes\.torrent" ""
StrCmp $0 "qBittorrent" clear_errors 0
;Check if empty string
StrCmp $0 "" clear_errors 0
;Write old value to OpenWithProgIds
WriteRegStr HKCU "Software\Classes\.torrent\OpenWithProgIds" $0 ""
clear_errors:
ClearErrors
WriteRegStr HKCU "Software\Classes\.torrent" "" "qBittorrent"
WriteRegStr HKCU "Software\Classes\.torrent" "Content Type" "application/x-bittorrent"
FunctionEnd
Section $(inst_magnet) ;"Open magnet links with qBittorrent"
WriteRegStr HKLM "Software\Classes\magnet" "" "URL:Magnet link"
WriteRegStr HKLM "Software\Classes\magnet" "Content Type" "application/x-magnet"
WriteRegStr HKLM "Software\Classes\magnet" "URL Protocol" ""
WriteRegStr HKLM "Software\Classes\magnet\DefaultIcon" "" '"$INSTDIR\qbittorrent.exe",1'
WriteRegStr HKLM "Software\Classes\magnet\shell" "" "open"
WriteRegStr HKLM "Software\Classes\magnet\shell\open\command" "" '"$INSTDIR\qbittorrent.exe" "%1"'
!insertmacro UAC_AsUser_Call Function inst_magnet_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
SectionEnd
Function inst_magnet_user
WriteRegStr HKCU "Software\Classes\magnet" "" "URL:Magnet link"
WriteRegStr HKCU "Software\Classes\magnet" "Content Type" "application/x-magnet"
WriteRegStr HKCU "Software\Classes\magnet" "URL Protocol" ""
WriteRegStr HKCU "Software\Classes\magnet\DefaultIcon" "" '"$INSTDIR\qbittorrent.exe",1'
WriteRegStr HKCU "Software\Classes\magnet\shell" "" "open"
WriteRegStr HKCU "Software\Classes\magnet\shell\open\command" "" '"$INSTDIR\qbittorrent.exe" "%1"'
FunctionEnd
Section $(inst_firewall)
DetailPrint $(inst_firewallinfo)
@ -109,22 +175,29 @@ SectionEnd
;--------------------------------
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
Function .onInit
!insertmacro Init "installer"
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
Function check_instance
check:
FindProcDLL::FindProc "qbittorrent.exe"
StrCmp $R0 "1" 0 notfound
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(inst_warning) IDRETRY check IDCANCEL done
check:
FindProcDLL::FindProc "qbittorrent.exe"
StrCmp $R0 "1" 0 notfound
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(inst_warning) IDRETRY check IDCANCEL done
done:
Abort
notfound:
FunctionEnd
Function PageFinishRun
!insertmacro UAC_AsUser_ExecShell "" "$INSTDIR\qbittorrent.exe" "" "" ""
done:
Abort
notfound:
FunctionEnd

Binary file not shown.

Binary file not shown.

View file

@ -7,7 +7,8 @@ SetCompressor /SOLID LZMA
SetCompressorDictSize 64
XPStyle on
!include "MUI.nsh"
!include "MUI.nsh"
!include "UAC.nsh"
!include "FileFunc.nsh"
;For the file association
@ -19,7 +20,8 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
!define PROG_VERSION "3.2.0"
!define MUI_FINISHPAGE_RUN "$INSTDIR\qbittorrent.exe"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
; The name of the installer
@ -31,7 +33,7 @@ OutFile "qbittorrent_${PROG_VERSION}_setup.exe"
;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2013 The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2014 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}"
@ -45,7 +47,7 @@ InstallDir $PROGRAMFILES\qBittorrent
InstallDirRegKey HKLM Software\qbittorrent InstallLocation
; Request application privileges for Windows Vista
RequestExecutionLevel admin
RequestExecutionLevel user
;--------------------------------
;General Settings
@ -81,3 +83,29 @@ RequestExecutionLevel admin
!insertmacro MUI_RESERVEFILE_LANGDLL
ReserveFile "${NSISDIR}\Plugins\x86-unicode\FindProcDLL.dll"
ReserveFile "${NSISDIR}\Plugins\x86-unicode\UAC.dll"
!macro Init thing
uac_tryagain:
!insertmacro UAC_RunElevated
${Switch} $0
${Case} 0
${IfThen} $1 = 1 ${|} Quit ${|} ;we are the outer process, the inner process has done its work, we are done
${IfThen} $3 <> 0 ${|} ${Break} ${|} ;we are admin, let the show go on
${If} $1 = 3 ;RunAs completed successfully, but with a non-admin user
MessageBox mb_YesNo|mb_IconExclamation|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, try again" /SD IDNO IDYES uac_tryagain IDNO 0
${EndIf}
;fall-through and die
${Case} 1223
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, aborting!"
Quit
${Case} 1062
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Logon service not running, aborting!"
Quit
${Default}
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate , error $0"
Quit
${EndSwitch}
SetShellVarContext all
!macroend

View file

@ -49,6 +49,42 @@ Section "un.$(remove_shortcuts)" ;"un.Remove shortcuts"
Delete "$DESKTOP\qBittorrent.lnk"
SectionEnd
Section "un.$(remove_associations)" ;"un.Remove file associations"
SectionIn RO
ReadRegStr $0 HKLM "Software\Classes\.torrent" ""
StrCmp $0 "qBittorrent" 0 torrent_end
DetailPrint "$(uninst_tor_warn) $0"
DeleteRegValue HKLM "Software\Classes\.torrent" ""
DeleteRegKey /ifempty HKLM "Software\Classes\.torrent"
torrent_end:
ReadRegStr $0 HKLM "Software\Classes\magnet\shell\open\command" ""
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
DetailPrint "$(uninst_mag_warn) $0"
DeleteRegKey HKLM "Software\Classes\magnet"
magnet_end:
!insertmacro UAC_AsUser_Call Function un.remove_associations_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)'
SectionEnd
Function un.remove_associations_user
ReadRegStr $0 HKCU "Software\Classes\.torrent" ""
StrCmp $0 "qBittorrent" 0 torrent_end
DetailPrint "$(uninst_tor_warn) $0"
DeleteRegValue HKCU "Software\Classes\.torrent" ""
DeleteRegKey /ifempty HKCU "Software\Classes\.torrent"
torrent_end:
ReadRegStr $0 HKCU "Software\Classes\magnet\shell\open\command" ""
StrCmp $0 '"$INSTDIR\qbittorrent.exe" "%1"' 0 magnet_end
DetailPrint "$(uninst_mag_warn) $0"
DeleteRegKey HKCU "Software\Classes\magnet"
magnet_end:
FunctionEnd
Section "un.$(remove_registry)" ;"un.Remove registry keys"
SectionIn RO
; Remove registry keys
@ -68,23 +104,36 @@ SectionEnd
Section /o "un.$(remove_conf)" ;"un.Remove configuration files"
System::Call 'shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r1, i ${CSIDL_APPDATA}, i0)i.r0'
RMDir /r "$1\qBittorrent"
!insertmacro UAC_AsUser_Call Function un.remove_conf_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
SectionEnd
Section /o "un.$(remove_cache)"
Function un.remove_conf_user
System::Call 'shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r1, i ${CSIDL_APPDATA}, i0)i.r0'
RMDir /r "$1\qBittorrent"
FunctionEnd
Section /o "un.$(remove_cache)"
!insertmacro UAC_AsUser_Call Function un.remove_cache_user ${UAC_SYNCREGISTERS}|${UAC_SYNCOUTDIR}|${UAC_SYNCINSTDIR}
SectionEnd
Function un.remove_cache_user
System::Call 'shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r1, i ${CSIDL_LOCALAPPDATA}, i0)i.r0'
RMDir /r "$1\qBittorrent\"
SectionEnd
FunctionEnd
;--------------------------------
;Uninstaller Functions
Function un.onInit
!insertmacro Init "uninstaller"
!insertmacro MUI_UNGETLANGUAGE
FunctionEnd