Search code examples
nsis

Startup shortcut for other user


I'm doing a forced remote installation with PsExecute on some client machines. The problem I have is that I execute the installers as a local admin but I would like to add a startup shoortcut for a specific user. In nsis I can only choose between the users (the local admin) or All environment. How can I add a shortcut to User1's startup folder?


Solution

  • If you know the users password you can call LogonUser + SHGetFolderPath, but I assume that you don't know the password.

    Using some undocumented registry locations is the only alternative if you don't know the password and the user is not logged on:

    You need parts of the EnumUsersReg and GetUserShellFolderFromRegistry header files from the NSIS wiki and this helper macro:

    !macro EnumUsersRegX_LoadRegFromSID SID CallbackFunc Subkey
    System::Store S
    ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\${SID}" "ProfileImagePath"
    ExpandEnvStrings $R1 $R1
    GetFunctionAddress $0 ${CallbackFunc}
    !insertmacro _EnumUsersReg_Load "$R1\NTUSER.DAT" $0 "${Subkey}"
    System::Store L
    !macroend
    

    Then use it like this:

    Function MyEnumUsersRegCallback
    Pop $0
    !insertmacro GetUserShellFolderFromRegistry "Startup" $0 $1
    DetailPrint Startup=$1 ;$1 could be "" if there is some sort of error
    ;Call CreateShortcut here
    FunctionEnd
    
    Section
    !insertmacro _EnumUsersReg_AdjustTokens
    StrCpy $R1 "User1" ;username goes here
    System::Call 'ADVAPI32::LookupAccountName(i0,t "$R1",i0,*i0r1,i0,*i0r2,*i)'
    System::Call '*(&i$1,&t$2)i.r3'
    IntOp $4 $3 + $1
    ${If} $3 <> 0
        System::Call 'ADVAPI32::LookupAccountName(i0,t "$R1",i $3,*ir1,i $4,*ir2,*i)i.r0'
        ${If} $0 <> 0
            System::Call 'ADVAPI32::ConvertSidToStringSid(ir3,*i.r2)i.r0' ;Win2000+
            ${If} $0 <> 0
                System::Call '*$2(&t${NSIS_MAX_STRLEN}.r1)'
                System::Call 'kernel32::LocalFree(i $2)'
                !insertmacro EnumUsersRegX_LoadRegFromSID "$1" MyEnumUsersRegCallback temp.key
            ${EndIf}
        ${EndIf}
        System::Free $3
    ${EndIf}
    SectionEnd
    

    If you know the users SID, you can call the EnumUsersRegX_LoadRegFromSID macro directly without calling LookupAccountName first.

    Notes: This code uses undocumented things and could therefore break in the next version of windows! You also need to be administrator and >= Win2000.