Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding a Windows user's "true" application data folder?

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;
like image 309
Robert Oschler Avatar asked Oct 07 '12 18:10

Robert Oschler


2 Answers

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.

like image 151
David Heffernan Avatar answered Sep 22 '22 14:09

David Heffernan


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.

like image 29
Ken White Avatar answered Sep 19 '22 14:09

Ken White