2016-05-30 1 views
-2

J'ai un programme qui appelle SHGetKnownFolderPath avec FOLDERID_RoamingAppData.SHGetKnownFolderPath échoue avec E_ACCESSDENIED

Si je démarre le programme en double-cliquant dessus, cela fonctionne bien.

Si le programme est démarré par un service Windows (dans le contexte utilisateur actuel), la fonction échoue avec l'erreur E_ACCESSDENIED (-2147024891).

C'est ce que mon code ressemble à:

Tstring EasyGetFolderPath(REFKNOWNFOLDERID folderid) 
{ 
    Tstring sPath = _T(""); 
    PWSTR pszPath = NULL; 

    HRESULT hr = SHGetKnownFolderPath(folderid, 0, NULL, &pszPath); 

    if (hr == S_OK && pszPath) 
    { 
     sPath = WStringToTCHAR(pszPath); 
     CoTaskMemFree(pszPath); 
     return sPath; 
    } 
    else 
    { 
     throw HResultException(hr, _T("SHGetKnownFolderPath failed")); 
    } 
} 

Tstring EasyGetUsrAppDataPath() 
{ 
    return EasyGetFolderPath(FOLDERID_RoamingAppData); 
} 

static TCHAR* WStringToTCHAR(const std::wstring &s) 
{ 
#ifdef UNICODE 
    TCHAR *sT = new TCHAR[s.length() + 1]; 
    _tcscpy_s(sT, s.length() + 1, s.c_str()); 
    return sT; 
#else 
    std::string str = WStringToString(s); 
    TCHAR *sT = new TCHAR[str.length()+1]; 
    _tcscpy_s(sT, str.length() + 1, str.c_str()); 
    return sT; 
#endif // UNICODE 
} 

static std::string WStringToString(const std::wstring& s, bool method = true) 
{ 
    std::string temp; 
    temp.assign(s.begin(), s.end()); 
    return temp; 
} 

Ceci est le code qui démarre le processus dans le contexte de l'utilisateur actuel: (je l'ai enlevé l'erreur de manipulation afin de réduire verbosité)

void StartProcessInCurrentUserContext(const Tstring &sExeName, const Tstringarr &lstParams, const Tstring &sWorkingDir) 
{ 
    ... 

    EnableDebugPrivilege(); 

    errCode = GetProcessByName(_T("explorer.exe"), hProcess); 

    if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken)) 
    { 
     ... 
    } 

    if (hProcess) 
     CloseHandle(hProcess); 

    Tstring sCmdLine = ...; 

    ... 

    // Create the child process. 
    bSuccess = CreateProcessAsUser(hToken, NULL, 
     (LPTSTR)sCmdLine.c_str(),   // command line 
     NULL,   // process security attributes 
     NULL,   // primary thread security attributes 
     TRUE,   // handles are inherited 
     0,    // creation flags 
     NULL,   // use parent's environment 
     sWorkingDir.length() > 0 ? (LPCTSTR)sWorkingDir.c_str() : NULL, 
     &siStartInfo, // STARTUPINFO pointer 
     &piProcInfo); // receives PROCESS_INFORMATION 

    CloseHandle(hToken); 

    ... 
} 

Est-ce que quelqu'un sait quel pourrait être le problème?

+0

Peut-être un contrôle d'accès utilisateur, par exemple un UAC? Quoi qu'il en soit, pourquoi utilisez-vous le stupide '_T' des trucs? C'est 16 ans obsolète. –

+0

Eh bien, quand je le lance en double-cliquant, l'UAC n'apparaît pas. Donc, je ne pense pas que cela ait quelque chose à voir avec ça. – conectionist

+0

Il est possible que le problème réside dans 'WStringToTCHAR'. Montrer sa définition –

Répondre

1

The documentation for SHGetKnownFolderPath dit dans la discussion du hToken paramètre:

En plus de passer hToken, la ruche de Registre de l'utilisateur spécifique de l'utilisateur doit être monté.

The documentation for CreateProcessAsUser dit

CreateProcessAsUser ne charge pas le profil de l'utilisateur spécifié dans la clé de Registre HKEY_USERS.

Ces deux paragraphes expliquent ensemble pourquoi votre code ne fonctionne pas. Heureusement, la phrase suivante dans la documentation CreateProcessAsUser explique ce que vous devez faire:

Par conséquent, pour accéder à l'information contenue dans la clé de Registre HKEY_CURRENT_USER, vous devez charger les informations de profil de l'utilisateur dans HKEY_USERS avec la fonction LoadUserProfile avant d'appeler CreateProcessAsUser. Veillez à appeler le UnloadUserProfile après la fermeture du nouveau processus.

+0

Je ne pense pas que cela puisse être le problème avec le code affiché - le jeton provient d'une copie de 'explorer.exe', donc le profil de l'utilisateur devrait déjà être chargé. –

+0

@HarryJohnston Il y a deux problèmes. On ne passe pas le mauvais jeton comme le paramètre hToken à SHGetKnownFolderPath. L'autre (apparemment non pertinent ici, mais pertinent en général) charge le profil. –

+0

La valeur par défaut est d'utiliser le jeton de l'utilisateur actuel, de sorte que cette partie devrait également être OK. Mais peut-être que les autorisations de sécurité sur le processus ne sont pas correctes, car il a été créé à partir d'un contexte différent, alors quand SHGetKnownFolderPath va chercher le jeton actuel, il échoue? Un peu comme https://blogs.msdn.microsoft.com/oldnewthing/20160512-00/?p=93447? –