I created an NSIS installer that requires elevation and also installs some files in the users AppData
directory. This works fine as long as the current user is an administrator. If the user is a limited user then they must run the installer as an administrator, passing the admin credentials to the installer. This all works great except for the fact that NSIS installs the files to the admin users AppData
directory in this case. This is because I have been using the $PROFILE
variable to determine where to install the files but $PROFILE
returns the path to the user that is running the installer and not the user who is logged onto the system.
So to be clear, if User A
is a limited account and User B
is the admin account, when I log into the computer with User A
and run the installer, passing the credentials for User B
, I can only ever get User B
's username from NSIS and therefore cannot install the necessary files for User A
.
I am currently using NSIS v2.49, which I haven't updated in over 2 years so I assume it is quite outdated. At the time of writing, the NSIS site on sourceforge has been down for a few days so it has been difficult to find a current copy of the software. I am open to upgrading to NSIS v3.x if anyone can point out how this may help solve this issue.
Here is some very simple code demonstrating my problem. If you build this installer and run it on a limited users desktop it will demonstrate 3 different attemps to get the currently logged in username, but all 3 will bring back the name of the user that is running the installer instead:
Outfile Test.exe
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
!include LogicLib.nsh
Function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
Quit
${EndIf}
MessageBox MB_OK $PROFILE
System::Call "advapi32::GetUserName(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2"
MessageBox MB_OK "User name Test 1: $0"
ReadEnvStr $0 "USERNAME"
MessageBox MB_OK "User name Test 2: $0"
FunctionEnd
Page instfiles
Section
SectionEnd
Is there no way that an NSIS installer can determine the logged in users username when running the installer as another user?
This is just how UAC works.
If you require elevation then you are doing a machine/all users install and you should only write to $ProgramFiles and HKLM. Add SetShellVarContext all
to your .onInit
function to prevent writes to user profiles. You should also disable the run checkbox on the finish page.
If your application needs data in $AppData then the application should copy the template data from $ProgramFiles, ProgramData (SetShellVarContext all + $AppData) or $COMMONFILES to $AppData the first time a user runs your application. This has been a requirement in the Windows logo program since at least Windows 2000!
You can find these recommendations in Certification requirements for Windows desktop apps or the older Windows Logo requirements documentation:
10.3 Your app data, which must be shared among users on the computer, should be stored within ProgramData
10.4 Your app’s data that is exclusive to a specific user and that is not to be shared with other users of the computer, must be stored in Users\%username%\AppData
10.6 Your app must write user data at first run and not during the installation in “per-machine” installations
When the app is installed, there is no correct user location in which to store data. Attempts by an app to modify default association behaviors at a machine level after installation will be unsuccessful. Instead, defaults must be claimed on a per-user level, which prevents multiple users from overwriting each other's defaults.