Skip to content

Instantly share code, notes, and snippets.

@daemondevin
Created January 7, 2026 02:02
Show Gist options
  • Select an option

  • Save daemondevin/67b24b4df804f34b4b2d404db07215b0 to your computer and use it in GitHub Desktop.

Select an option

Save daemondevin/67b24b4df804f34b4b2d404db07215b0 to your computer and use it in GitHub Desktop.
;=#
;
; NSIS Associations Handler
; Provides functions for managing file types, protocols, and context menus
; by daemon.devin (daemon.devin@gmail.com)
;
; AVAILABLE FILETYPE MACROS
; ${GetFileTypeProgID}
; ${CheckFileTypeExists}
; ${GetFileTypeInfo}
; ${BackupFileType}
; ${RestoreFileType}
; ${RegisterFileType}
; ${UnregisterFileType}
;
; AVAILABLE PROTOCOL MACROS
; ${GetProtocolHandler}
; ${CheckProtocolExists}
; ${GetProtocolInfo}
; ${BackupProtocol}
; ${RestoreProtocol}
; ${RegisterProtocol}
; ${UnregisterProtocol}
;
; AVAILABLE CONTEXT MENU MACROS
; ${AddContextMenu}
; ${RemoveContextMenu}
; ${AddGlobalContextMenu}
; ${RemoveGlobalContextMenu}
;
; AVAILABLE MISC. MACROS
; ${CheckProgIDExists}
; ${GetProgIDDescription}
; ${GetProgIDIcon}
; ${GetProgIDOpenCommand}
; ${SetDefaultProgram}
; ${RegisterAppCapabilities}
; ${UnegisterAppCapabilities}
; ${CleanAllBackups}
;
; EXAMPLE USAGE IN INSTALLER
; ============================================================================
/*
; Define your product name for backup key
!define PRODUCT_NAME "MyApp"
Section "Install"
SetOutPath "$INSTDIR"
File "MyApp.exe"
; Example 1: Check and display current file type owner
${GetFileTypeProgID} ".txt"
${If} $R9 != ""
DetailPrint ".txt is currently registered to: $R9"
; Get full info about the file type
${GetFileTypeInfo} ".txt"
DetailPrint " ProgID: $R9"
DetailPrint " Description: $R8"
DetailPrint " Icon: $R7"
DetailPrint " Command: $R6"
${Else}
DetailPrint ".txt is not registered"
${EndIf}
; Example 2: Check protocol before registering
${GetProtocolHandler} "http"
${If} $R9 != ""
DetailPrint "HTTP protocol handler: $R9"
${EndIf}
; Example 3: Get complete protocol info
${GetProtocolInfo} "mailto"
DetailPrint "MailTo Protocol:"
DetailPrint " Description: $R9"
DetailPrint " Icon: $R8"
DetailPrint " Command: $R7"
; Example 4: Check if .myapp is already registered
${CheckFileTypeExists} ".myapp"
${If} $R9 == "1"
${GetFileTypeProgID} ".myapp"
${If} $R9 == "MyApp.Document"
DetailPrint ".myapp is already registered to MyApp"
${Else}
MessageBox MB_YESNO ".myapp is registered to $R9. Override it?" IDYES override
Goto skipfileassoc
override:
${EndIf}
${EndIf}
; Register .myapp file extension (automatically backs up existing)
${RegisterFileType} ".myapp" "MyApp.Document" "MyApp Document File" "$INSTDIR\MyApp.exe,0" '"$INSTDIR\MyApp.exe" "%1"'
skipfileassoc:
; Example 5: Check ProgID details
${CheckProgIDExists} "txtfile"
${If} $R9 == "1"
${GetProgIDDescription} "txtfile"
DetailPrint "txtfile description: $R9"
${GetProgIDIcon} "txtfile"
DetailPrint "txtfile icon: $R9"
${GetProgIDOpenCommand} "txtfile"
DetailPrint "txtfile command: $R9"
${EndIf}
; Check if protocol exists
${CheckProtocolExists} "myapp"
${If} $R9 == "1"
${GetProtocolHandler} "myapp"
DetailPrint "Protocol myapp:// already exists, handler: $R9"
${EndIf}
; Register custom protocol (automatically backs up existing)
${RegisterProtocol} "myapp" "MyApp Protocol" "$INSTDIR\MyApp.exe,0" '"$INSTDIR\MyApp.exe" --url "%1"'
; Add context menu to .myapp files
${AddContextMenu} "MyApp.Document" "edit" "Edit with MyApp" '"$INSTDIR\MyApp.exe" --edit "%1"' "$INSTDIR\MyApp.exe,1"
${AddContextMenu} "MyApp.Document" "print" "Print with MyApp" '"$INSTDIR\MyApp.exe" --print "%1"' ""
; Add global context menu for all files
${AddGlobalContextMenu} "*" "OpenWithMyApp" "Open with MyApp" '"$INSTDIR\MyApp.exe" "%1"' "$INSTDIR\MyApp.exe,0"
; Add context menu to folder background
${AddGlobalContextMenu} "Directory\Background" "NewMyAppDoc" "New MyApp Document" '"$INSTDIR\MyApp.exe" --new' "$INSTDIR\MyApp.exe,0"
SectionEnd
Section "Uninstall"
; Ask if user wants to restore previous associations
MessageBox MB_YESNO "Do you want to restore previous file associations?" IDYES restore IDNO remove
restore:
; Remove file associations and restore backups
${UnregisterFileType} ".myapp" "MyApp.Document"
${UnregisterProtocol} "myapp"
Goto contextmenus
remove:
; Just remove without restoring
DeleteRegKey ${ASSOC_ROOT} ".myapp"
DeleteRegKey ${ASSOC_ROOT} "MyApp.Document"
DeleteRegKey ${ASSOC_ROOT} "myapp"
${CleanAllBackups}
contextmenus:
; Remove context menus
${RemoveContextMenu} "MyApp.Document" "edit"
${RemoveContextMenu} "MyApp.Document" "print"
${RemoveGlobalContextMenu} "*" "OpenWithMyApp"
${RemoveGlobalContextMenu} "Directory\Background" "NewMyAppDoc"
Delete "$INSTDIR\MyApp.exe"
RMDir "$INSTDIR"
SectionEnd
*/
!ifndef __ASSOCIATIONS_NSH__
!define __ASSOCIATIONS_NSH__
!ifdef ASSOC_SCOPE_MACHINE
!define ASSOC_ROOT HKLM
!define ASSOC_CLASSES "Software\Classes"
!define BACKUP_ROOT HKLM
!else
!define ASSOC_ROOT HKCU
!define ASSOC_CLASSES "Software\Classes"
!define BACKUP_ROOT HKCU
!endif
; Required includes
!ifndef LOGICLIB
!include LogicLib.nsh
!endif
!ifndef WINMESSAGES_INCLUDED
!include "WinMessages.nsh"
!endif
; Backup registry location
!define ASSOC_BACKUP_KEY "Software\${PRODUCT_NAME}\AssocBackup"
; SHELL NOTIFY
; Refreshes icon cache of Windows Explorer
!define NotifyShell "!insertmacro _NotifyShell"
!macro _NotifyShell
System::Call 'shell32.dll::SHChangeNotify(i,i,i,i)v(${SHCNE_ASSOCCHANGED},0,0,0)'
!macroend
; GET FILE TYPE PROGID
; Gets the ProgID associated with a file extension
;
; Usage: ${GetFileTypeProgID} ".ext"
; Returns: $R9 = ProgID string or empty string if not found
!define GetFileTypeProgID "!insertmacro _GetFileTypeProgID"
!macro _GetFileTypeProgID _EXT
Push "${_EXT}"
Call _GetFileTypeProgIDFunc
Pop $R9
!macroend
Function _GetFileTypeProgIDFunc
Pop $R0
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" ""
${If} ${Errors}
Push ""
${Else}
Push "$R1"
${EndIf}
FunctionEnd
; CHECK IF FILE TYPE EXISTS
; Checks if a file extension is already registered
;
; Usage: ${CheckFileTypeExists} ".ext"
; Returns: $R9 = "1" if exists, "0" if not
!define CheckFileTypeExists "!insertmacro _CheckFileTypeExists"
!macro _CheckFileTypeExists _EXT
Push "${_EXT}"
Call _CheckFileTypeExistsFunc
Pop $R9
!macroend
Function _CheckFileTypeExistsFunc
Pop $R0 ; Extension
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0" ""
${If} ${Errors}
Push "0"
${Else}
Push "1"
${EndIf}
FunctionEnd
; GET PROGID DESCRIPTION
; Gets the friendly description of a ProgID
;
; Usage: ${GetProgIDDescription} "MyApp.Document"
; Returns: $R9 = description string or empty string if not found
!define GetProgIDDescription "!insertmacro _GetProgIDDescription"
!macro _GetProgIDDescription _PROGID
Push "${_PROGID}"
Call _GetProgIDDescriptionFunc
Pop $R9
!macroend
Function _GetProgIDDescriptionFunc
Pop $R0 ; ProgID
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0" ""
${If} ${Errors}
Push ""
${Else}
Push "$R1"
${EndIf}
FunctionEnd
; GET PROGID ICON
; Gets the icon path associated with a ProgID
;
; Usage: ${GetProgIDIcon} "MyApp.Document"
; Returns: $R9 = icon path or empty string if not found
!define GetProgIDIcon "!insertmacro _GetProgIDIcon"
!macro _GetProgIDIcon _PROGID
Push "${_PROGID}"
Call _GetProgIDIconFunc
Pop $R9
!macroend
Function _GetProgIDIconFunc
Pop $R0 ; ProgID
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0\DefaultIcon" ""
${If} ${Errors}
Push ""
${Else}
Push "$R1"
${EndIf}
FunctionEnd
; GET PROGID OPEN COMMAND
; Gets the open command associated with a ProgID
;
; Usage: ${GetProgIDOpenCommand} "MyApp.Document"
; Returns: $R9 = command string or empty string if not found
!define GetProgIDOpenCommand "!insertmacro _GetProgIDOpenCommand"
!macro _GetProgIDOpenCommand _PROGID
Push "${_PROGID}"
Call _GetProgIDOpenCommandFunc
Pop $R9
!macroend
Function _GetProgIDOpenCommandFunc
Pop $R0 ; ProgID
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0\shell\open\command" ""
${If} ${Errors}
Push ""
${Else}
Push "$R1"
${EndIf}
FunctionEnd
; CHECK IF PROGID EXISTS
; Checks if a ProgID is registered
;
; Usage: ${CheckProgIDExists} "MyApp.Document"
; Returns: $R9 = "1" if exists, "0" if not
!define CheckProgIDExists "!insertmacro _CheckProgIDExists"
!macro _CheckProgIDExists _PROGID
Push "${_PROGID}"
Call _CheckProgIDExistsFunc
Pop $R9
!macroend
Function _CheckProgIDExistsFunc
Pop $R0
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" ""
${If} ${Errors}
Push "0"
${Else}
Push "1"
${EndIf}
FunctionEnd
; GET FILE TYPE INFO
; Gets complete information about a file type
;
; Usage: ${GetFileTypeInfo} ".ext"
; Returns: $R9 = ProgID, $R8 = Description, $R7 = Icon, $R6 = Open Command
; All empty strings if not found
!define GetFileTypeInfo "!insertmacro _GetFileTypeInfo"
!macro _GetFileTypeInfo _EXT
Push "${_EXT}"
Call _GetFileTypeInfoFunc
Pop $R9
Pop $R8
Pop $R7
Pop $R6
!macroend
Function _GetFileTypeInfoFunc
Pop $R0 ; Extension
; Get ProgID
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0" ""
${If} ${Errors}
Push ""
Push ""
Push ""
Push ""
Return
${EndIf}
; Get Description
ClearErrors
ReadRegStr $R2 ${ASSOC_ROOT} "$R1" ""
${If} ${Errors}
StrCpy $R2 ""
${EndIf}
; Get Icon
ClearErrors
ReadRegStr $R3 ${ASSOC_ROOT} "$R1\DefaultIcon" ""
${If} ${Errors}
StrCpy $R3 ""
${EndIf}
; Get Open Command
ClearErrors
ReadRegStr $R4 ${ASSOC_ROOT} "$R1\shell\open\command" ""
${If} ${Errors}
StrCpy $R4 ""
${EndIf}
Push "$R4" ; Open Command
Push "$R3" ; Icon
Push "$R2" ; Description
Push "$R1" ; ProgID
FunctionEnd
; GET PROTOCOL INFO
; Gets complete information about a protocol
;
; Usage: ${GetProtocolInfo} "myapp"
; Returns: $R9 = Description, $R8 = Icon, $R7 = Command
; All empty strings if not found
!define GetProtocolInfo "!insertmacro _GetProtocolInfo"
!macro _GetProtocolInfo _PROTOCOL
Push "${_PROTOCOL}"
Call _GetProtocolInfoFunc
Pop $R9
Pop $R8
Pop $R7
!macroend
Function _GetProtocolInfoFunc
Pop $R0 ; Protocol
; Check if protocol exists
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0" "URL Protocol"
${If} ${Errors}
Push ""
Push ""
Push ""
Return
${EndIf}
; Get Description
ClearErrors
ReadRegStr $R2 ${ASSOC_ROOT} "$R0" ""
${If} ${Errors}
StrCpy $R2 ""
${EndIf}
; Get Icon
ClearErrors
ReadRegStr $R3 ${ASSOC_ROOT} "$R0\DefaultIcon" ""
${If} ${Errors}
StrCpy $R3 ""
${EndIf}
; Get Command
ClearErrors
ReadRegStr $R4 ${ASSOC_ROOT} "$R0\shell\open\command" ""
${If} ${Errors}
StrCpy $R4 ""
${EndIf}
Push "$R4" ; Command
Push "$R3" ; Icon
Push "$R2" ; Description
FunctionEnd
; BACKUP FILE TYPE
; Backs up existing file type registration before overwriting
;
; Usage: ${BackupFileType} ".ext"
!define BackupFileType "!insertmacro _BackupFileType"
!macro _BackupFileType _EXT
Push "${_EXT}"
Call _BackupFileTypeFunc
!macroend
Function _BackupFileTypeFunc
Pop $R0
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" ""
${If} ${Errors}
Return
${EndIf}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\FileTypes\$R0" "ProgID" "$R1"
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1" "" "$R2"
${EndIf}
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\DefaultIcon" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1\DefaultIcon" "" "$R2"
${EndIf}
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\shell\open\command" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1\shell\open\command" "" "$R2"
${EndIf}
FunctionEnd
; RESTORE FILE TYPE
; Restores a previously backed up file type registration
;
; Usage: ${RestoreFileType} ".ext"
!define RestoreFileType "!insertmacro _RestoreFileType"
!macro _RestoreFileType _EXT
Push "${_EXT}"
Call _RestoreFileTypeFunc
!macroend
Function _RestoreFileTypeFunc
Pop $R0
ReadRegStr $R1 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\FileTypes\$R0" "ProgID"
${If} ${Errors}
Return
${EndIf}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "" "$R1"
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1" "" "$R2"
${EndIf}
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1\DefaultIcon" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\DefaultIcon" "" "$R2"
${EndIf}
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1\shell\open\command" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\shell\open\command" "" "$R2"
${EndIf}
DeleteRegKey ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\FileTypes\$R0"
DeleteRegKey ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\ProgIDs\$R1"
${NotifyShell}
FunctionEnd
; REGISTER FILE TYPE
; Registers a file extension with Windows (with automatic backup)
;
; Usage: ${RegisterFileType} ".ext" "AppName.FileType" "Description" "Icon" "OpenCommand"
; Example: ${RegisterFileType} ".myapp" "MyApp.Document" "MyApp Document" "$INSTDIR\app.exe,0" '"$INSTDIR\app.exe" "%1"'
!define RegisterFileType "!insertmacro _RegisterFileType"
!macro _RegisterFileType _EXT _PROGID _DESC _ICON _CMD
Push "${_EXT}"
Push "${_PROGID}"
Push "${_DESC}"
Push "${_ICON}"
Push "${_CMD}"
Call _RegisterFileTypeFunc
!macroend
Function _RegisterFileTypeFunc
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Pop $R0
Push "$R0"
Call _BackupFileTypeFunc
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "" "$R1"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1" "" "$R2"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\DefaultIcon" "" "$R3"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R1\shell\open\command" "" "$R4"
${NotifyShell}
FunctionEnd
; UNREGISTER FILE TYPE
; Removes a file extension registration (with automatic restore)
;
; Usage: ${UnregisterFileType} ".ext" "AppName.FileType"
!define UnregisterFileType "!insertmacro _UnregisterFileType"
!macro _UnregisterFileType _EXT _PROGID
Push "${_EXT}"
Push "${_PROGID}"
Call _UnregisterFileTypeFunc
!macroend
Function _UnregisterFileTypeFunc
Pop $R1 ; ProgID
Pop $R0 ; Extension
; Check if this extension belongs to our ProgID
ReadRegStr $R2 ${ASSOC_ROOT} "$R0" ""
${If} $R2 == "$R1"
; This is our registration, restore backup or remove
Push "$R0"
Call RestoreFileTypeFunc
${EndIf}
; Remove our ProgID
DeleteRegKey ${ASSOC_ROOT} "$R1"
; Notify shell
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
FunctionEnd
; GET PROTOCOL HANDLER
; Gets the command handler associated with a protocol
;
; Usage: ${GetProtocolHandler} "myapp"
; Returns: $R9 = command string or empty string if not found
!define GetProtocolHandler "!insertmacro _GetProtocolHandler"
!macro _GetProtocolHandler _PROTOCOL
Push "${_PROTOCOL}"
Call _GetProtocolHandlerFunc
Pop $R9
!macroend
Function _GetProtocolHandlerFunc
Pop $R0 ; Protocol
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0\shell\open\command" ""
${If} ${Errors}
Push ""
${Else}
Push "$R1"
${EndIf}
FunctionEnd
; CHECK IF PROTOCOL EXISTS
; Checks if a protocol is already registered
;
; Usage: ${CheckProtocolExists} "myapp"
; Returns: $R9 = "1" if exists, "0" if not
!define CheckProtocolExists "!insertmacro _CheckProtocolExists"
!macro _CheckProtocolExists _PROTOCOL
Push "${_PROTOCOL}"
Call _CheckProtocolExistsFunc
Pop $R9
!macroend
Function _CheckProtocolExistsFunc
Pop $R0 ; Protocol
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "$R0" "URL Protocol"
${If} ${Errors}
Push "0"
${Else}
Push "1"
${EndIf}
FunctionEnd
; BACKUP PROTOCOL
; Backs up existing protocol registration before overwriting
;
; Usage: ${BackupProtocol} "myapp"
!define BackupProtocol "!insertmacro _BackupProtocol"
!macro _BackupProtocol _PROTOCOL
Push "${_PROTOCOL}"
Call _BackupProtocolFunc
!macroend
Function _BackupProtocolFunc
Pop $R0
ClearErrors
ReadRegStr $R1 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "URL Protocol"
${If} ${Errors}
Return
${EndIf}
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0" "" "$R2"
${EndIf}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0" "URL Protocol" ""
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\DefaultIcon" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0\DefaultIcon" "" "$R2"
${EndIf}
ReadRegStr $R2 ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\open\command" ""
${IfNot} ${Errors}
WriteRegStr ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0\shell\open\command" "" "$R2"
${EndIf}
FunctionEnd
; RESTORE PROTOCOL
; Restores a previously backed up protocol registration
;
; Usage: ${RestoreProtocol} "myapp"
!define RestoreProtocol "!insertmacro _RestoreProtocol"
!macro _RestoreProtocol _PROTOCOL
Push "${_PROTOCOL}"
Call _RestoreProtocolFunc
!macroend
Function _RestoreProtocolFunc
Pop $R0
ReadRegStr $R1 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0" "URL Protocol"
${If} ${Errors}
DeleteRegKey ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0"
${NotifyShell}
Return
${EndIf}
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "" "$R2"
${EndIf}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "URL Protocol" ""
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0\DefaultIcon" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\DefaultIcon" "" "$R2"
${EndIf}
ReadRegStr $R2 ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0\shell\open\command" ""
${IfNot} ${Errors}
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\open\command" "" "$R2"
${EndIf}
DeleteRegKey ${BACKUP_ROOT} "${ASSOC_BACKUP_KEY}\Protocols\$R0"
${NotifyShell}
FunctionEnd
; REGISTER PROTOCOL
; Registers a custom URL protocol (e.g., myapp://) with automatic backup
;
; Usage: ${RegisterProtocol} "myapp" "MyApp Protocol" "Icon" "Command"
; Example: ${RegisterProtocol} "myapp" "MyApp Protocol" "$INSTDIR\app.exe,0" '"$INSTDIR\app.exe" "%1"'
!define RegisterProtocol "!insertmacro _RegisterProtocol"
!macro _RegisterProtocol _PROTOCOL _DESC _ICON _CMD
Push "${_PROTOCOL}"
Push "${_DESC}"
Push "${_ICON}"
Push "${_CMD}"
Call _RegisterProtocolFunc
!macroend
Function _RegisterProtocolFunc
Pop $R3
Pop $R2
Pop $R1
Pop $R0
Push "$R0"
Call _BackupProtocolFunc
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "" "$R1"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0" "URL Protocol" ""
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\DefaultIcon" "" "$R2"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\open\command" "" "$R3"
${NotifyShell}
FunctionEnd
; UNREGISTER PROTOCOL
; Removes a custom URL protocol (with automatic restore)
;
; Usage: ${UnregisterProtocol} "myapp"
!define UnregisterProtocol "!insertmacro _UnregisterProtocol"
!macro _UnregisterProtocol _PROTOCOL
Push "${_PROTOCOL}"
Call _UnregisterProtocolFunc
!macroend
Function _UnregisterProtocolFunc
Pop $R0
Push "$R0"
Call _RestoreProtocolFunc
FunctionEnd
; ADD CONTEXT MENU
; Adds a context menu entry for a file type or ProgID
;
; Usage: ${AddContextMenu} "ProgID" "MenuKey" "MenuText" "Command" "Icon"
; Example: ${AddContextMenu} "MyApp.Document" "edit" "Edit with MyApp" '"$INSTDIR\app.exe" --edit "%1"' "$INSTDIR\app.exe,1"
!define AddContextMenu "!insertmacro _AddContextMenu"
!macro _AddContextMenu _PROGID _KEY _TEXT _CMD _ICON
Push "${_PROGID}"
Push "${_KEY}"
Push "${_TEXT}"
Push "${_CMD}"
Push "${_ICON}"
Call _AddContextMenuFunc
!macroend
Function _AddContextMenuFunc
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Pop $R0
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1" "" "$R2"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1\command" "" "$R3"
${If} $R4 != ""
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1" "Icon" "$R4"
${EndIf}
${NotifyShell}
FunctionEnd
; REMOVE CONTEXT MENU
; Removes a context menu entry
;
; Usage: ${RemoveContextMenu} "ProgID" "MenuKey"
!define RemoveContextMenu "!insertmacro _RemoveContextMenu"
!macro _RemoveContextMenu _PROGID _KEY
Push "${_PROGID}"
Push "${_KEY}"
Call _RemoveContextMenuFunc
!macroend
Function _RemoveContextMenuFunc
Pop $R1
Pop $R0
DeleteRegKey ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1"
${NotifyShell}
FunctionEnd
; ADD GLOBAL CONTEXT MENU
; Adds a context menu entry for all files or directories
;
; Usage: ${AddGlobalContextMenu} "Target" "MenuKey" "MenuText" "Command" "Icon"
; Target can be: "*" (all files), "Directory" (folders), "Directory\Background" (folder background)
; Example: ${AddGlobalContextMenu} "*" "myapp" "Open with MyApp" '"$INSTDIR\app.exe" "%1"' "$INSTDIR\app.exe,0"
!define AddGlobalContextMenu "!insertmacro _AddGlobalContextMenu"
!macro _AddGlobalContextMenu _TARGET _KEY _TEXT _CMD _ICON
Push "${_TARGET}"
Push "${_KEY}"
Push "${_TEXT}"
Push "${_CMD}"
Push "${_ICON}"
Call _AddGlobalContextMenuFunc
!macroend
Function _AddGlobalContextMenuFunc
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Pop $R0
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1" "" "$R2"
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1\command" "" "$R3"
${If} $R4 != ""
WriteRegStr ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1" "Icon" "$R4"
${EndIf}
${NotifyShell}
FunctionEnd
; REMOVE GLOBAL CONTEXT MENU
; Removes a global context menu entry
;
; Usage: ${RemoveGlobalContextMenu} "Target" "MenuKey"
!define RemoveGlobalContextMenu "!insertmacro _RemoveGlobalContextMenu"
!macro _RemoveGlobalContextMenu _TARGET _KEY
Push "${_TARGET}"
Push "${_KEY}"
Call _RemoveGlobalContextMenuFunc
!macroend
Function _RemoveGlobalContextMenuFunc
Pop $R1
Pop $R0
DeleteRegKey ${ASSOC_ROOT} "${ASSOC_CLASSES}\$R0\shell\$R1"
${NotifyShell}
FunctionEnd
; SET DEFAULT PROGRAM
; Sets the default program for a file type
;
; Usage: ${SetDefaultProgram} ".ext" "AppName.exe"
!macro SetDefaultProgram EXT APPNAME
Push "${EXT}"
Push "${APPNAME}"
Call SetDefaultProgramFunc
!macroend
!define SetDefaultProgram "!insertmacro SetDefaultProgram"
Function SetDefaultProgramFunc
Pop $R1 ; AppName
Pop $R0 ; Extension
; Register with Windows Default Programs
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$R0\UserChoice" "Progid" "$R1"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
FunctionEnd
; REGISTER APP CAPAABILITIES
; Windows 11–compliant capabilities
;
; Usage: ${RegisterAppCapabilities}
!define RegisterAppCapabilities "!insertmacro _RegisterAppCapabilities"
!macro _RegisterAppCapabilities _APPID _FRIENDLY _EXE
WriteRegStr HKCU "Software\RegisteredApplications" "${_APPID}" "Software\${_APPID}\Capabilities"
WriteRegStr HKCU "Software\${_APPID}\Capabilities" "ApplicationName" "${_FRIENDLY}"
WriteRegStr HKCU "Software\${_APPID}\Capabilities" "ApplicationDescription" "${_FRIENDLY}"
WriteRegStr HKCU "Software\${_APPID}\Capabilities" "ApplicationIcon" "$INSTDIR\${_EXE},0"
!macroend
; UNREGISTER APP CAPAABILITIES
; Windows 11–compliant capabilities
;
; Usage: ${UnegisterAppCapabilities}
!define UnregisterAppCapabilities "!insertmacro _UnregisterAppCapabilities"
!macro _UnregisterAppCapabilities _APPID
DeleteRegValue HKCU "Software\RegisteredApplications" "${_APPID}"
DeleteRegKey HKCU "Software\${_APPID}\Capabilities"
!macroend
; CLEAN ALL BACKUPS
; Cleans up all backup data (useful if user chooses to keep associations)
;
; Usage: ${CleanAllBackups}
!macro CleanAllBackups
Call CleanAllBackupsFunc
!macroend
!define CleanAllBackups "!insertmacro CleanAllBackups"
Function CleanAllBackupsFunc
DeleteRegKey HKLM "${ASSOC_BACKUP_KEY}"
DetailPrint "Cleaned up all backup data"
FunctionEnd
!endif ; __ASSOCIATIONS_NSH__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment