问题
I have a Delphi 6 application that, like most Windows applications, reads/writes data to the user's "local application data" folder. I use the code below to determine that folder. Up until now, that code worked for most of my users. I have encountered a user whose local application data is not in the expected folder:
C:\Users\Bob\AppData\Roaming\
Usually the local app data folder resolves to:
C:\Documents and Settings\Bob\Application Data\
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. They are running on Windows 7.
For lack of a better word, is there a way to get the "true" application data for a user so I can navigate this situation better? 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? As you can tell I'm looking for an all-purpose function that can root out the correct application data folder regardless of the version of Windows the user is running or their specific PC configuration.
I found this Stack Overflow post that seems to have the answer but it is using function from the .NET library and I am using Delphi 6. If this solution answers my question, can someone tell me a quick way to replicate it in Delphi:
How can i get the path of the current user's "Application Data" folder?
// Function to get the app data special folder.
function GetAppdataFolder: string;
begin
Result := GetSpecialFolderLocation(CSIDL_APPDATA);
end;
回答1:
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.
回答2:
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.
回答3:
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).
来源:https://stackoverflow.com/questions/12771473/finding-a-windows-users-true-application-data-folder