Поиск папки пользователя приложения "true" для Windows?

У меня есть приложение Delphi 6, которое, как и большинство приложений Windows, считывает/записывает данные в папку "данные локальных приложений" пользователя. Я использую код ниже, чтобы определить эту папку. До сих пор этот код работал для большинства моих пользователей. Я столкнулся с пользователем, чьи локальные данные приложения не находятся в ожидаемой папке:

C:\Users\Bob\AppData\Roaming\

Обычно локальная папка данных приложения разрешает:

C:\Documents and Settings\Bob\Application Data\

Что странно в этой конкретной ситуации, так это то, что несколько ключей реестра, обычно встречающихся в HKEY_LOCAL_MACHINE, фактически находятся в HKEY_CURRENT_USER. Они работают в Windows 7.

Из-за отсутствия лучшего слова, есть ли способ получить "истинные" данные приложения для пользователя, чтобы я мог лучше ориентироваться в этой ситуации? Если разумно выбирать между специальными папками CSIDL_APPDATA, CSIDL_COMMON_APPDATA и CSIDL_LOCAL_APPDATA, какова логика для этого? Как вы можете сказать, я ищу универсальную функцию, которая может искоренить правильную папку данных приложения независимо от версии Windows, на которой выполняется пользователь, или их конкретной конфигурации ПК.

Я нашел этот пост, который, кажется, имеет ответ, но он использует функцию из библиотеки .NET, и я использую Delphi 6. Если это решение отвечает на мой вопрос, может кто-то сказать мне быстрый способ его репликации Delphi:

Как я могу получить путь к "данным приложения" текущего пользователя? папка?

// Function to get the app data special folder.
function GetAppdataFolder: string;
begin
   Result := GetSpecialFolderLocation(CSIDL_APPDATA);
end;

Ответ 1

Код .net, на который вы ссылаетесь, использует Environment.SpecialFolder.ApplicationData, который точно совпадает с CSIDL_APPDATA. Таким образом, ваш код уже эквивалентен коду .net, к которому вы привязываетесь. И они оба относятся к тому же местоположению, что и FOLDERID_RoamingAppData.

Взгляните на документацию для FOLDERID_RoamingAppData. В нем говорится:

Default Path        %APPDATA% (%USERPROFILE%\AppData\Roaming)
Legacy Default Path %APPDATA% (%USERPROFILE%\Application Data) 

"Путь по умолчанию" - это то, что вы увидите в Vista или позже. "Legacy Path" - это то, что вы видите на XP.

Различное поведение, которое вы наблюдали, представляет собой не что иное, как ожидаемое различие между XP и Vista/7/8.

На моей машине с Windows,

Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)

оценивается как

C:\Users\heff\AppData\Roaming

Другими словами, ваш код уже поступает правильно. Вам вообще не нужно вносить какие-либо изменения в это. Продолжайте использовать GetSpecialFolderLocation(CSIDL_APPDATA).


Что странно в этой конкретной ситуации, так это то, что несколько ключей реестра, обычно встречающихся в HKEY_LOCAL_MACHINE, фактически находятся в HKEY_CURRENT_USER.

Это не редкость. Довольно часто приложения настраивают параметры по умолчанию в HKLM, а затем копируют их в HKCU, когда приложение запускается в первый раз. Не зная более подробных сведений о параметрах, о которых идет речь, сложно комментировать этот аспект вашего вопроса.

Ответ 2

Вы можете использовать это (обертка). Вам нужно добавить ShlApi в свой раздел uses. Передайте его CSIDL_APPDATA точно так же, как ваш пример выше. Список различных значений CSIDL_ см. В странице MSDN здесь

function GetShellFolder(CSIDLFolder : integer) : string;
begin
  SetLength(Result, MAX_PATH);
  SHGetSpecialFolderPath(0, PChar(Result), CSIDLFolder, false);
  SetLength(Result, StrLen(PChar(Result)));
  if (Result <> '') then
    Result  := IncludeTrailingBackslash(Result);
end;

Если вы поддерживаете более раннюю версию Windows (XP и ниже), которая отображается в вашем тексте, вы можете вместо этого использовать SHGetFolderPath:

function GetFolderPath(Wnd: HWnd; CSIDLFolder: Integer): string;
begin
  SetLength(Result, MAX_PATH);
  Result := SHGetFolderPath(Wnd, CSIDLFolder, nil, 0, PChar(Result);
  SetLength(Result, StrLen(PChar(Result)));
end;

Если вы только поддерживаете Vista и выше, вы должны использовать SHGetKnownFolderPath и передать ей KNOWNFOLDERID.

Что касается проблемы с реестром, Windows Vista и 7 гораздо более ограничивают места, в которые может писать пользователь, не являющийся администратором, и одно из мест, которое происходит в HKLM и HKCR. Многие элементы, которые раньше находились в этих ульях, теперь находятся в HKCU или там отражены.

Ответ 3

Если разумно выбирать между специальными папками CSIDL_APPDATA, CSIDL_COMMON_APPDATA и CSIDL_LOCAL_APPDATA, какова логика для этого?

Да, это всего лишь вопрос. Ваш код уже работает как ожидалось.

CSIDL_APPDATA (FOLDERID_RoamingAppData) - это данные, которые доступны для текущей учетной записи пользователя вызывающего потока (которая может быть выдана под себя) на нескольких машинах (данные "роуминга" "hense" ).

CSIDL_LOCAL_APPDATA (FOLDERID_LocalAppData) предназначен для данных, доступных для текущей учетной записи пользователя вызывающего потока только на локальной машине (hense "локальные" данные).

CSIDL_COMMON_APPDATA (FOLDERID_ProgramData) предназначен для данных, доступных для любой учетной записи пользователя только на локальном компьютере (не "роуминг" ).