Finding a Windows user's “true” application data folder?

牧云@^-^@ 提交于 2019-12-03 11:53:06

The .net code you link to uses Environment.SpecialFolder.ApplicationData which is exactly the same as CSIDL_APPDATA. So your code is already equivalent to the .net code to which you link. And these both refer to the same location as FOLDERID_RoamingAppData.

Take a look at the documentation for FOLDERID_RoamingAppData. It says:

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

The "Default Path" is what you will see on Vista or later. The "Legacy Path" is what you see on XP.

The different behaviour that you have observed is nothing more than the expected difference between XP and Vista/7/8.

On my Windows machine,

Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)

evaluates to

C:\Users\heff\AppData\Roaming

In other words, your code is already doing the right thing. You do not need to make any changes to it at all. Carry on using GetSpecialFolderLocation(CSIDL_APPDATA).


What is odd about this user's particular situation is that several registry keys normally found in HKEY_LOCAL_MACHINE are actually located in HKEY_CURRENT_USER.

That's not uncommon. Quite often applications configure default settings in HKLM and then copy them to HKCU when the application is first run. Without knowing more details of the settings in question it's hard to comment on that aspect of your question.

You can use this (a wrapper). You'll need to add ShlApi to your uses clause. Pass it CSIDL_APPDATA just like your sample above does. For a list of the various CSIDL_ values, see the MSDN page here

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;

If you're supporting earlier of Windows (XP and below), which your text appears is the case, you can use SHGetFolderPath instead:

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;

If you're only supporting Vista and higher, you should use SHGetKnownFolderPath instead, and pass it a KNOWNFOLDERID.

As far as the registry issue, Windows Vista and 7 are much more restrictive about the places a non-Admin user can write to, and one of the places that occurs is in HKLM and HKCR. Many of the items that used to be in those hives are now in HKCU, or are mirrored there.

If it's a matter of intelligently choosing between the CSIDL_APPDATA, CSIDL_COMMON_APPDATA and CSIDL_LOCAL_APPDATA special folders, what is the logic for doing so?

Yes, it is just a matter of that. Your code is already working as expected.

CSIDL_APPDATA (FOLDERID_RoamingAppData) is for data that is accessible to the calling thread's current user account (which can be impersonated) on multiple machines (hense "roaming" data).

CSIDL_LOCAL_APPDATA (FOLDERID_LocalAppData) is for data that is accessible to the calling thread's current user account on the local machine only (hense "local" data).

CSIDL_COMMON_APPDATA (FOLDERID_ProgramData) is for data that is accessible to any user account on the local machine only (not "roaming" data).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!