Search code examples
windows-10nsis

NSIS: delete AppData folder of logged in user


I have an installer that have to be executed as an admin. During the installation process the user has the option to execute a clean install. Selected this option the data in the AppData directory should be deleted. When I am logged in as a non-admin user and the admin enters the credentials I only have access to the AppData directory of the admin. How can delete data in the AppData directory from the actual logged in user?


Solution

  • This is of course a consequence of the UAC design.

    The official Microsoft recommendation is to just leave behind the junk in %AppData%.

    As a workaround you can use GetUserShellFolderFromRegistry to get special folders of other users.

    If you only want to operate on the SID associated with the current RDP session you can get the SID like this:

    !include LogicLib.nsh
    Function GetThisSessionLogonSidFromLsa
    System::Store S
    StrCpy $9 ""
    System::Call KERNEL32::GetCurrentProcessId()i.s
    System::Call KERNEL32::ProcessIdToSessionId(is,*i0r1)i.r0
    ${If} $0 <> 0
        System::Call 'SECUR32::LsaEnumerateLogonSessions(*i0r2,*p0r3)i.r0'
        ${If} $0 = 0
            IntOp $2 $2 - 1
            ${For} $4 0 $2
                IntOp $5 $4 * 8
                System::Call '*$3(&i$5,l.r5)'
                System::Call 'SECUR32::LsaGetLogonSessionData(*lr5,*p0r5)i.r0'
                ${If} $0 = 0
                    System::Call '*$5(p,l,p,p,p,p,p,p,i,i.r6,p.r7)'
                    ${If} $6 = $1
                        System::Call 'ADVAPI32::ConvertSidToStringSid(pr7,*p0r8)'
                        System::Call '*$8(&t${NSIS_MAX_STRLEN}.r9)'
                        System::Call 'KERNEL32::LocalFree(pr8)'
                    ${EndIf}
                    System::Call SECUR32::LsaFreeReturnBuffer(pr5)
                ${EndIf}
            ${IfThen} $9 != "" ${|} ${Break} ${|}
            ${Next}
            System::Call SECUR32::LsaFreeReturnBuffer(pr3)
        ${EndIf}
    ${EndIf}
    Push $9
    System::Store L
    FunctionEnd
    
    ...
    
    Call GetThisSessionLogonSidFromLsa
    Pop $0
    DetailPrint $0 ; S-1-... (Compare this with the parameter received in the EnumUsersReg callback)
    

    For your specific situation, a uglier hack might work:

    !include LogicLib.nsh
    Function GetUsernameOfCurrentSessionFromWTS
    Push $0
    Push $1
    Push $2
    StrCpy $2 ""
    System::Call 'WTSAPI32::WTSQuerySessionInformation(p0,i-1,i5,*p0r1,*i)i.r0'
    ${If} $0 <> 0
        System::Call '*$1(&t${NSIS_MAX_STRLEN}.r2)'
        System::Call 'WTSAPI32::WTSFreeMemory(p$1)'
    ${EndIf}
    Exch 2
    Pop $1
    Pop $0
    Exch $2
    FunctionEnd
    
    Section
    Call GetUsernameOfCurrentSessionFromWTS
    Pop $0
    RMDir /R "$PROFILE\..\$0\AppData\Roaming\MyApplication"
    SectionEnd
    

    This assumes that all profiles are stored in $PROFILE\.. and hardcodes AppData\Roaming and neither of those things might be correct in all configurations.