Created
January 7, 2026 02:02
-
-
Save daemondevin/67b24b4df804f34b4b2d404db07215b0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ;=# | |
| ; | |
| ; 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