NSIS File Associations change in Windows 7 - nsis

I have a requirement to (force) change the file association for a particular file type (extenison ".theext" ) to open with "myapp.exe" when installing an app using NSIS.
I've read a few suggestions of how to achieve this, so currentky this is what I have in my NSIS script:
DeleteRegKey HKCR ".theext"
DeleteRegKey HKLM ".theext"
DeleteRegKey HKCU ".theext"
WriteRegStr HKCR ".theext" "" "theextfile"
WriteRegStr HKCR "theextfile" "" "My App Document"
WriteRegStr HKCR "theextfile\DefaultIcon" "" "$INSTDIR\${EXENAME}.exe,0"
WriteRegStr HKCR "theextfile\shell\open\command" "" '"$INSTDIR\${EXENAME}.exe" "%1"'
WriteRegStr HKCR "theextfile\shell\print\command" "" '"$INSTDIR\${EXENAME}.exe" /p "%1"'
WriteRegStr HKLM "Software\RegisteredApplications" "${EXENAME}" "$INSTDIR\${EXENAME}.exe"
WriteRegStr HKCU "Software\RegisteredApplications" "${EXENAME}" "$INSTDIR\${EXENAME}.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.theext\OpenWithList" "a" "$INSTDIR\${EXENAME}.exe"
To test this, I set the file assoc using WIndows 7 Explorer to GVIM.exe.
Since doing this, every time I run the installer, Windows 7 still opens the file on double click using GVim, and not "MyApp.exe".
But when I check the file associatin as follows, all seems fine:
ftype | findstr /i theext
Gives:
theextfile="C:\Program File (x86)\My App\myapp.exe" "%1"

You are doing everything you are supposed to do and if the extension is not already registered by somebody else you will become the default. You are not really supposed to delete the old keys first though (it can screw up the system but it will never help you become default). Forcing something is evil, the user is supposed to be in control.
Because people forced this in the past Microsoft starting making it harder to change the default. The undocumented FileExts key stores the users chosen default in the UserChoice sub-keys. In newer versions of Windows (8+?) the default is verified with some secret hash so you cannot override it.
The IApplicationAssociationRegistration interface does not work in newer version of Windows but it might work in Windows 7:
!include Win\COM.nsh
!include WinCore.nsh
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationAssociationRegistration} ${IID_IApplicationAssociationRegistration} r0 ""
${If} $0 P<> 0
${IApplicationAssociationRegistration::SetAppAsDefault} $0 '("MyApp", ".myext", ${AT_FILEEXTENSION})'
${IUnknown::Release} $0 ""
${EndIf}
In Windows 8 all you can do is launch the generic UI:
!include Win\COM.nsh
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationAssociationRegistrationUI} ${IID_IApplicationAssociationRegistrationUI} r0 ""
${If} $0 P<> 0
${IApplicationAssociationRegistrationUI::LaunchAdvancedAssociationUI} $0 '("Wordpad")' ; Replace with your name from the RegisteredApplications key
${IUnknown::Release} $0 ""
${EndIf}
In Windows 10 even this is gone, it will just display a toast telling the user to change their settings if you call LaunchAdvancedAssociationUI.
ftype does not know the true default, the default is only known when Windows actually runs the association code in the shell. IApplicationAssociationRegistration::QueryCurrentDefault is better at guessing the default but even it can fail if the default is actually a COM shell extension that overrides the default.

Related

NSIS Invalid Command userInfo::GetAccountType

Total NSIS nube here. I had a script that was working, and now it isn't. It's based on an example provided on the nsis site:
# This installs two files, app.exe and logo.ico, creates a start menu shortcut, builds an uninstaller, and
# adds uninstall information to the registry for Add/Remove Programs
# To get started, put this script into a folder with the two files (app.exe, logo.ico, and license.rtf -
# You'll have to create these yourself) and run makensis on it
# If you change the names "app.exe", "logo.ico", or "license.rtf" you should do a search and replace - they
# show up in a few places.
# All the other settings can be tweaked by editing the !defines at the top of this script
!define COMPANYNAME "F_YEAH"
!define DESCRIPTION "I'm stoked!"
# These three must be integers
!define VERSIONMAJOR 1
!define VERSIONMINOR 1
!define VERSIONBUILD 1
# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
# It is possible to use "mailto:" links in here to open the email client
!define HELPURL "http://..." # "Support Information" link
!define UPDATEURL "http://..." # "Product Updates" link
!define ABOUTURL "http://..." # "Publisher" link
# This is the size (in kB) of all the files copied into "Program Files"
!define INSTALLSIZE 7233
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}"
# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n)
# This will be in the installer/uninstaller's title bar
Name "${COMPANYNAME} - ${APPNAME}"
Icon "C:\Users\dkim\Documents\dog.ico"
outFile "GetSomeGA.exe"
!include LogicLib.nsh
# Just three pages - license agreement, install location, and installation
page license
page directory
Page instfiles
!macro VerifyUserIsAdmin
UserInfo::GetName
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
messageBox mb_iconstop "Administrator rights required!"
setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
quit
${EndIf}
!macroend
function .onInit
setShellVarContext all
!insertmacro VerifyUserIsAdmin
functionEnd
section "install"
# Files for the install directory - to build the installer, these should be in the same directory as the install script (this file)
setOutPath $INSTDIR
# Files added here should be removed by the uninstaller (see section "uninstall")
File /r "C:\Users\dkim\Documents\Projects\MOR\Projects\GAintegration\GA_ConversionOnDemand_0.1"
File "C:\Users\dkim\Documents\dog.ico"
# Add any other files for the install directory (license files, app data, etc) here
# Uninstaller - See function un.onInit and section "uninstall" for configuration
writeUninstaller "$INSTDIR\uninstall.exe"
# Start Menu
createDirectory "$SMPROGRAMS\${COMPANYNAME}"
createShortCut "$DESKTOP\pleaseClickMe.lnk" "$INSTDIR\GA_ConversionOnDemand\GA_ConversionOnDemand_run.bat" "" "$INSTDIR\dog.ico" 0
# Registry information for add/remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} -
${APPNAME} - ${DESCRIPTION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR
\uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$
\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR
\logo.ico$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$
\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$
\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
# There is no option for modifying or repairing the install
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
sectionEnd
# Uninstaller
function un.onInit
SetShellVarContext all
#Verify the uninstaller - last chance to back out
MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next
Abort
next:
# !insertmacro VerifyUserIsAdmin
functionEnd
section "uninstall"
# Remove Start Menu launcher
RMDir /r "$PROGRAMFILES\${COMPANYNAME}"
delete "$DESKTOP\click_or_die.lnk"
# Always delete uninstaller as the last action
delete $INSTDIR\uninstall.exe
# Remove uninstaller information from the registry
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
sectionEnd
The error message I get is:
!insertmacro: VerifyUserIsAdmin
Invalid command: UserInfo::GetName
Error in macro VerifyUserIsAdmin on macroline 1
Error in script "C:\Users\dkim\Documents\test3.nsi" on line 55 -- aborting creation process
It won't let me use any command involving UserInfo. Any ideas on what might be going on?
The compiler cannot find the UserInfo plugin or the plugin is corrupted.
The file should be in %ProgramFiles%\NSIS\Plugins in NSIS v2 and in %ProgramFiles%\NSIS\Plugins\x86-ansi or %ProgramFiles%\NSIS\Plugins\x86-unicode in NSIS v3 (depending on your target type).
This plugin is part of the default NSIS install so I would recommend that you just reinstall NSIS on top of your existing install...

Reading registry key value in NSIS fails

I already posted a similar question (NSIS - check if registry key value exists) and the solution to that question worked perfectly:
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" "NUL:"
And then:
${If} ${Errors}
#and so on
I do the same thing three more times in the script, reading different registry values and all attempts but the last one are successful:
ReadRegStr $3 HKLM "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\SomePrinter" "Name"
This always returns an error.
What should I do differently?
The first time I read a registry value, I use $0 to store the return vale. Then the second time $1 and then $2 and $3.
As I said, all work except for the last one. Does it have to do with the type of registry key I'm reading or should I use a different variable for the return value? I have tried other variables, but so far nothing has worked.
Thanks for help and tips!
The problem with
ReadRegStr $3 HKLM "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers\SomePrinter" "Name"
is that you specify the root twice:
HKLM
HKEY_LOCAL_MACHINE\...
Remove the HKEY_LOCAL_MACHINE from the sub key name and it should be OK. If you need to access another part of the registry, change the 2 parameter of ReadRegStr as described in WriteRegExpandStr manual section:
HKCR for HKEY_CLASSES_ROOT
HKLM for HKEY_LOCAL_MACHINE
HKCU for HKEY_CURRENT_USER
HKU for HKEY_USERS
HKCC for HKEY_CURRENT_CONFIG
HKDD for HKEY_DYN_DATA
HKPD for HKEY_PERFORMANCE_DATA
SHCTX for SHELL_CONTEXT

Can i detect 32 or 64 bit OS without plugin somehow?

now i use the x64.nsh for this, but i can detect it without this plugin?
${If} ${RunningX64}
MessageBox MB_OK "running on 64 bit"
File /r ${64BIT_OPENVPN_INSTALL}
Execwait ${64BIT_OPENVPN_INSTALL}
${Else}
MessageBox MB_OK "running on 32 bit"
File /r ${32BIT_OPENVPN_INSTALL}
Execwait ${32BIT_OPENVPN_INSTALL}
${EndIf}
x64.nsh does not implies specific external plugin usage (apart the system plugin): it is just an included file that defines 3 macros based on kernel calls (i.e kernel32::GetCurrentProcess() and kernel32::IsWow64Process()) through the system plugin, that can be conveniently used with LogicLib.nsh
There are probably many ways to detect the native bitness by just looking at files and registry keys but there is always the risk that some 32-bit systems have somehow ended up with a SysWOW64 folder in %WinDir% etc.
The SetRegView test should be pretty safe but there is a small window where some other app could change the registry at just the wrong time giving you the wrong result.
The correct way to detect this is of course to call the IsWow64Process function and the x64.nsh header already does that for you.
!include LogicLib.nsh
Section
!if "${NSIS_PTR_SIZE}" > 4
DetailPrint "64-bit NSIS, this must be a 64-bit system"
!endif
${If} ${FileExists} "$WinDir\SysWOW64\kernel32.dll"
DetailPrint "Probably not a native 32-bit system"
${EndIf}
${If} ${FileExists} "$WinDir\SysNative\kernel32.dll"
DetailPrint "Probably a 32-bit app on a native 64-bit system (Vista+ only)"
${EndIf}
SetRegView 64
ReadRegStr $6 HKLM "Software\Microsoft\Windows\CurrentVersion" "ProgramFilesDir"
SetRegView lastused
SetRegView 32
ReadRegStr $3 HKLM "Software\Microsoft\Windows\CurrentVersion" "ProgramFilesDir"
SetRegView lastused
${If} $3 != $6
DetailPrint "Probably a 32-bit app on a native 64-bit system"
${EndIf}
; ReadEnvStr on ProgramW6432 or PROCESSOR_ARCHITEW6432 etc
SectionEnd

NSIS uninstaller doesn't delete files/folders

I'm writing a NSIS installer for one of the apps that the company I work for uses internally, the install process works fine, with no problems all the REG keys are created, and so are the files folders and services, that the App uses. For some reason I can't understand, the uninstall process doesn't work's.
The services created by the app are deleted and so are the Registry keys, the most simple part, the files themselves, I can't delete them through the uninstaller!
#Includes
!include "x64.nsh"
#Defines and Installer Properties
Outfile "ESTvnc Installer.exe"
Name ESTvnc
Icon "${NSISDIR}\contrib\graphics\icons\VNCON.ico"
#Detect OS Version
Function .onInit
StrCpy $instdir $PROGRAMFILES
${If} ${RunningX64}
StrCpy $instdir $PROGRAMFILES32
${EndIf}
FunctionEnd
section
SetShellVarContext all
CreateDirectory $instdir\EST\ESTvnc
setOutPath $instdir\EST\ESTvnc
File /r installfiles\*
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ESTvnc\" \
"DisplayName" "ESTvnc"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ESTvnc"\
"UninstallString" "$instdir\EST\ESTvnc\uninstaller.exe"
writeUninstaller $instdir\EST\ESTvnc\uninstaller.exe
ExecWait '"$instdir\EST\estvnc\estvnc.exe" -install'
sectionEnd
section "Uninstall"
SetShellVarContext all
SimpleSC::StopService "ESTVNC" 1 30
pop $0
SimpleSC::StopService "ESTVNCSR" 1 30
pop $0
SimpleSC::RemoveService "ESTVNC"
SimpleSC::RemoveService "ESTVNCSR"
RMDir /r "$instdir\EST\ESTvnc"
Delete $instdir\EST\ESTvnc\uninstaller.exe
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ESTvnc"
sectionEnd
In the uninstaller, $instdir is the directory the uninstaller is in!
Either place the uninstaller in $instdir and delete $instdir\EST\ESTvnc or if you want to keep it in $instdir\EST\ESTvnc, delete $instdir...

Checking for Registry in NSIS

I'm using NSIS to make an executable for a project I'm doing in Visual C++ 2012 Express. Because I'm a beginner, I started with the Setup Wizard in HM NIS Edit.
I'm trying to bundle the Visual C++ 2012 Redistributable with my program, but every time the installer is re-ran, the Redistributable pops up with a "Repair" & "Remove" option, and that looks annoying.
So I decided to write a little bit of NSIS script, and this is the start of the script so far:
Var STR
Section CheckForReg
ClearErrors
ReadRegDWORD $0 HKLM "SOFTWARE\Classes\Installer\Dependencies\{8e70e4e1-06d7-470b-9f74-a51bef21088e}" "Version"
ifErrors 0 Blank
StrCpy $STR "$INSTDIR\vcredist_x86.exe"
GoTo End
Blank:
StrCpy $STR ""
End:
SectionEnd
This piece is called at the very start of the script, and the global variable STR is applied to:
!define MUI_FINISHPAGE_RUN $STR
Shortly after.
Obviously this is a really silly way to do it, but I don't need too much out of it.
The issue is that the CheckForReg always thinks the registry doesn't exist, and doesn't move to the label Blank. As a note, I'm manually checking the registry every time, and the registry entry looks like:
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Dependencies\{8e70e4e1-06d7-470b-9f74-a51bef21088e}]
"Version"="11.0.51106.1"
"DisplayName"="Microsoft Visual C++ 2012 Redistributable (x86) - 11.0.51106"
So the question is: Where am I going wrong with this? It looks very simple, but obviously I have something backwards.
-- Removed Code Dump
You can only read DWORDs with ReadRegDWORD!
!include LogicLib.nsh ; So we don't have to use all these labels
StrCpy $STR ""
ReadRegStr $0 HKLM "SOFTWARE\Classes\Installer\Dependencies\{8e70e4e1-06d7-470b-9f74-a51bef21088e}" "Version"
${If} $0 == ""
StrCpy $STR "$INSTDIR\vcredist_x86.exe"
${EndIf}

Resources