2016-05-30 5 views
-2

Ich habe ein Programm, das SHGetKnownFolderPath mit FOLDERID_RoamingAppData aufruft.SHGetKnownFolderPath schlägt mit E_ACCESSDENIED fehl

Wenn ich das Programm durch Doppelklick starte, funktioniert es einwandfrei.

Wenn das Programm von einem Windows-Dienst (im aktuellen Benutzerkontext) gestartet wird, schlägt die Funktion mit Fehlern E_ACCESSDENIED (-2147024891). Diese

ist, was mein Code wie folgt aussieht:

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; 
} 

Dies ist der Code, der den Prozess in der aktuellen Benutzerkontext beginnt: (ich habe den Fehler behoben haben, um Ausführlichkeit zu reduzieren Handling)

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); 

    ... 
} 

Weiß jemand, was das Problem sein könnte?

+0

Vielleicht Benutzerzugriffskontrolle, u.a. UAC? Wie auch immer, warum benutzt du das dumme '_T' Zeug? Das ist 16 Jahre überholt. –

+0

Nun, wenn ich es durch einen Doppelklick starte, wird die UAC nicht angezeigt. Also, ich glaube nicht, dass es damit zu tun hat. – conectionist

+0

Möglicherweise liegt das Problem in 'WStringToTCHAR'. Zeige seine Definition. –

Antwort

1

The documentation for SHGetKnownFolderPath sagt in der Diskussion des hToken Parameter:

Neben den hToken des Benutzers vorbei, die Registrierungsstruktur dieses bestimmten Benutzers befestigt werden müssen.

The documentation for CreateProcessAsUser sagt

CreateProcessAsUser nicht die angegebenen Benutzers nicht geladen Profil in die HKEY_USERS Registrierungsschlüssel.

Diese zwei Abschnitte zusammen erklären, warum Ihr Code nicht funktioniert. Glücklicherweise erklärt der nächste Satz in der Dokumentation zu CreateProcessAsUser, was Sie tun müssen:

daher die Informationen im Schlüssel HKEY_CURRENT_USER Registrierung zuzugreifen, müssen Sie die Profilinformationen in HKEY_USERS des Benutzers laden mit der LoadUserProfile Funktion vor dem Aufruf CreateProcessAsUser. Rufen Sie nach dem Beenden des neuen Prozesses unbedingt UnloadUserProfile auf.

+0

Ich denke nicht, dass dies das Problem mit dem geposteten Code sein kann - das Token stammt von einer Kopie von 'explorer.exe', daher sollte das Benutzerprofil bereits geladen sein. –

+0

@HarryJohnston Es gibt zwei Probleme. Das falsche Token wird nicht als hToken-Parameter an SHGetKnownFolderPath übergeben. Die andere (anscheinend hier nicht relevant, aber relevant im Allgemeinen) ist das Laden des Profils. –

+0

Standardmäßig wird das Token des aktuellen Benutzers verwendet, sodass auch dieses Teil in Ordnung sein sollte. Aber vielleicht sind die Sicherheitsberechtigungen für den Prozess nicht richtig, da es aus einem anderen Kontext erstellt wurde. Wenn also SHGetKnownFolderPath das aktuelle Token abruft, schlägt es fehl. Ein bisschen wie https://blogs.msdn.microsoft.com/oldnewthing/20160512-00/?p=93447? –