Obtain Current Users %APPDATA% Path and not Admins - windows

I am looking to get the path to the current users %APPDATA% folder.
Note: I am aware of the variable $APPDATA BUT if you run your installer with RequestExecutionLevel admin then $APPDATA will point to the admins roaming folder and NOT the current user's app data folder.
I need to find out the current users %APPDATA% path so I can write files to their roaming directory. Does anyone know how I can find this out?
RequestExecutionLevel admin
Section "Main"
MessageBox MB_OK "AppData is: $APPDATA" # knowtice that its the path to the admins folder not the current user's
SectionEnd

The term "Current User" is ambiguous, do you mean:
The user you get from WTSQueryUserToken()? (WinLogon)
The user that the shell's taskbar is running as? (GetShellWindow())
The user (parent process) that started your setup process?
All of those can be different users if you are having fun with runas!
The comment from Harry Johnston is spot on and once you start mixing %ProgramFiles% and %AppData% and/or HKLM and HKCU your setup is broken in multi-user scenarios. What happens when a different user starts the application? They are not going to have your files in their %AppData%.
If the addin is installed/registered in a global location you can install the AppData "template" files in %ProgramFiles%, %CommonProgramFiles% or %ALLUSERSPROFILE% and when your addin runs as a specific user for the first time you copy the files to %AppData%.
Active Setup could be used as a alternative but it will probably require a log-off/log-on cycle.
If you cannot implement the delayed copy/install for some reason you are left with hacks like the UAC plugin which gives you some access to the user that started your installer...

Ok, thanks for the advice but I found a nice plugin that tells me the location of all User directories. I still cant figure out which user is currently logged in but I can figure out all non-admin users which is very useful.
!include "NTProfiles.nsi"
!macro HandleUserProfiles
!define NTProfilePaths::IgnoreLocal
!ifndef __UNINSTALL__
${EnumProfilePaths} HandleUserProfile
!else
${EnumProfilePaths} un.HandleUserProfile
!endif
!macroend
!macro HandleUserProfile prefix
Function ${prefix}HandleUserProfile
Pop $R9
!ifndef __UNINSTALL__
# Copy files to user dir
SetOutPath "$R9\AppData\Roaming\Autodesk\Revit\Addins\2013" # $APPDATA = C:\ProgramData
FILE /r "${INSTALLFILEDIR}\Addins\Revit_2013\myAddin.addin"
!else
Delete "$R9\AppData\Roaming\Autodesk\Revit\Addins\2013\myAddin.addin"
!endif
# Continue Enumeration
Continue:
Push ""
Return
# Stop Enumeration
Stop:
Push "~" # Any value other than an empty string will abort the enumeration
FunctionEnd
!macroend
!insertmacro HandleUserProfile ""
!insertmacro HandleUserProfile "un."

Related

How to get network path of a directory using command line

Say I have a directory called Temp in windows.
When I right click on it and open properties, and go to Sharing tab, I see this:
The field Network Path is Not Shared.
Now I click Share and select to share with and click ok.
The field Network Path now has value:
Is there a way to get the value of Network Path from command line.
Ideally I am looking for a command which takes directory path as input e.g. C:\Temp and output \\S5XXXXXXN\Temp if the directory is shared or Not Shared if the directory is not shared.
Background: I have a NSIS script to create an installer. The end of installation process is supposed to spit out a list of properties. One of the properties required for output is the Network Path of a directory. Inside NSIS what I have available is C:\Temp which I want to convert to \\S5XXXXXXN\Temp (if available) before spitting it out.
I tried to use this https://nsis.sourceforge.io/Get_Universal_Name but its not working.
DetailPrint "My directory is:"
# value of MYDIR is C:\Temp
DetailPrint $MYDIR
Push $MYDIR
Call get_universal_name
Pop $MYDIRNET
DetailPrint "My dir after call is: "
DetailPrint $MYDIRNET
# above also prints C:\Temp even though its shared and has value \\S5XXXXXXN\Temp
Update:
The answer below by #Anders works like a charm.
The list returned has an entry with blank $3 though. Just to be aware of and to add a necessary check if required.
get_universal_name calls WNetGetUniversalName and this function only supports mapped drive letters (net.exe use style). WNetGetConnection seems to have the same limitation. Even if this limitation did not exist it would still not work because a process elevated with UAC does not share the net connections with non-elevated processes.
Going the other way does seem to work, getting a list of shares and their local paths:
!include LogicLib.nsh
Section "Check shares"
System::Call 'NETAPI32::NetShareEnum(p0,i2,*p.r1,i-1,*i0r5,*i0,p0)i.r0'
${If} $0 = 0
Push $1
${DoWhile} $5 <> 0
System::Call '*$1(p.r2,i,p,i,i,i,p.r3,p,&l.r4)'
IntPtrOp $1 $1 + $4
System::Call '*$2(&w${NSIS_MAX_STRLEN}.r2)'
System::Call '*$3(&w${NSIS_MAX_STRLEN}.r3)'
System::Call 'NETAPI32::NetServerGetInfo(p0,i100,*p.r4)' ; Get computer name
System::Call '*$4(i,w.s)'
System::Call 'NETAPI32::NetApiBufferFree(p$4)'
Pop $4
DetailPrint "$3=\\$4\$2"
; ${If} $3 == "$MYDIR" ...TODO... ${EndIf}
IntOp $5 $5 - 1
${Loop}
System::Call 'NETAPI32::NetApiBufferFree(ps)'
${EndIf}
SectionEnd

How to bundle exe file inside another exe file using nsis script

I have a installer file called sample.exe. This exe file will have some components that use have to be defined such as port number, installation directory and etc. I have to bundle this sample.exe file inside another installer called test.exe. So when i try to install the test.exe, it should also install the sample.exe. i could see there are options to achieve this in nsis, but how to provide options for the use to enter the port, directory path of sample.exe while installing the test.exe ? i am beginner to nsis and any reference are example script will help me lot. Thanks in advance.
You need to create custom (installer) page where these values are entered.
Use nsDialogs for this (recommended): http://nsis.sourceforge.net/Docs/nsDialogs/Readme.html
There is no exact solution for this as your specification is really vague, rather check the examples and use provided code snippets from them.
You generally use the directory page to let the user choose the installation directory. Non-standard user input can be recorded on a custom page using the nsDialogs plug-in:
Name "Foo"
OutFile "TestSetup.exe"
RequestExecutionLevel admin
InstallDir "$ProgramFiles\Test"
!define DEFAULTPORT 666
!include nsDialogs.nsh
Var MyPort
Var PortEdit
Function .onInit
StrCpy $MyPort "${DEFAULTPORT}"
FunctionEnd
Page Directory
Page Custom MyPageCreate MyPageLeave
Page InstFiles
Function MyPageCreate
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0u 10% 20u 12u "Port: "
Pop $0
${NSD_CreateNumber} 20u 10% 40u 12u "$MyPort"
Pop $PortEdit
nsDialogs::Show
FunctionEnd
Function MyPageLeave
${NSD_GetText} $PortEdit $MyPort
FunctionEnd
Section
SetOutPath $InstDir
File "Sample.exe"
; Write the chosen port to a config file:
WriteIniStr "$InstDir\Config.ini" "Network" "Port" "$MyPort"
SectionEnd

How to set the ALL USER Directory as an output directory for a installer created with NSIS

I am trying to install a file to ALL USER DOCUMENTS Directory(windows 7) using NSIS.
In my code i am setting "SetShellVarContext all" but still the files are getting installed at current user directory
Please help
Here is my code
# define installer name
OutFile "installer.exe"
# set desktop as install directory
InstallDir $DOCUMENTS
# default section start
Section
# define output path
SetShellVarContext all
SetOutPath $INSTDIR
# specify file to go in output path
File test.txt
# define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe
#-------
# default section end
SectionEnd
# create a section to define what the uninstaller does.
# the section will always be named "Uninstall"
Section "Uninstall"
# Always delete uninstaller first
Delete $INSTDIR\uninstaller.exe
# now delete installed file
Delete $INSTDIR\test.txt
SectionEnd
SetShellVarContext does not affect the InstallDir attribute, you must manually set $InstDir:
Function .onInit
SetShellVarContext all
StrCpy $InstDir $Documents
FunctionEnd

How to access InstallDirRegKey in NSIS

I can specify installation directory and registry value in NSIS like this:
InstallDir "$PROGRAMFILES\CTVI"
InstallDirRegKey HKLM "Software\CTVI" "Install_Dir"
Now I can access InstallDir by using the notation $INSTDIR throughout the script. How do I similarly access InstallDirRegKey? Is there a similar notation as there exists for InstallDir? I tried $INSTDIRREGKEY but that is not. Now what I do is that I type HKLM "Software\CTVI" "Install_Dir" everytime I require it. But that is not so DRY :)
When the InstallDirRegKey attribute is used, NSIS will look-up the specified registry entry at startup and if it finds a path there it will place that value in $Instdir before any of your code runs...

Create more than 1 uninstaller in a NSIS Section

Can a NSIS Section create more than 1 uninstaller?
My installer can install plugins for 3 different versions of an Application - therefore theres 3 different directories where the installer will install the files.
In each of those directories I want to add an uninstaller file that will remove only the files in that directory.
Each of the 3 uninstall files are created within the same Section area, is this invalid? How can I get my script to create 3 uninstallers(if possible)?
The following Section only creates one uninstaller, the last one(Version 10 uninstaller):
Section "Install Plugin Files" MainSetup
CheckInstallVers8:
IntCmp $installVers8 1 InstallVersion8 CheckInstallVers9 InstallVersion8
CheckInstallVers9:
IntCmp $installVers9 1 InstallVersion9 CheckInstallVers10 InstallVersion9
CheckInstallVers10:
IntCmp $installVers10 1 InstallVersion10 MainInstallation InstallVersion10
InstallVersion8:
# install plugins...
SetOutPath $VERS8DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers9
InstallVersion9:
SetOutPath $VERS9DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers10
InstallVersion10:
SetOutPath $VERS10DIR
writeUninstaller "${APPNAME} Uninstall.exe"
SectionEnd
You can call WriteUninstaller as many times as you want but you should use the full path name (writeUninstaller "$VERSxDIR\${APPNAME} Uninstall.exe")
You did not post a full script so it is hard to tell what is wrong with the logic (You might want to use LogicLib.nsh so you can do {IF}s) but you should be able to "MessageBox debug" your way to the solution.
One thing you did not talk about that might be relevant is the uninstaller logic. If the 3 uninstallers all do the exact same task then this is not an issue but I'd expect at least a difference in the uninstaller registry registration.
There are two ways to deal with this:
Tag data to the end of the uninstaller (or a .ini in the same directory)
Use !system to call makensis.exe and generate uninstallers at compile time that you include as normal Files
A different solution that might be relevant for plugins in sub-directories is to use a component page in the uninstaller and only delete the uninstaller when all 3 plugins have been removed...

Resources