i want to to persist some filenames for the user (e.g. recent files).
Let's use six example files:
c:\Documents & Settings\Ian\My Documents\Budget.xls
c:\Documents & Settings\Ian\My Documents\My Pictures\Daughter's Winning Goal.jpg
c:\Documents & Settings\Ian\Application Data\uTorrent
c:\Documents & Settings\All Users\Application Data\Consonto\SpellcheckDictionary.dat
c:\Develop\readme.txt
c:\Program Files\Adobe\Reader\WhatsNew.txt
i'm now hard-coding path to special folders. If the user redirects their folders, roams to another computer, or upgrades their operating system, the paths will be broken:
i want to be a good developer, and convert these hard-coded absolute paths to relative paths from the appropriate special folders:
%CSIDL_Personal%\Budget.xls
%CSIDL_MyPictures%\Daughter's Winning Goal.jpg
%CSIDL_AppData%\uTorrent
%CSIDL_Common_AppData%\Consonto\SpellcheckDictionary.dat
c:\Develop\readme.txt
%CSIDL_Program_Files%\Adobe\Reader\WhatsNew.txt
The difficulty comes with the fact that there can be multiple representations for the same file, e.g.:
c:\Documents & Settings\Ian\My Documents\My Pictures\Daughter's Winning Goal.jpg
%CSIDL_Profile%\My Documents\My Pictures\Daughter's Winning Goal.jpg
%CSIDL_Personal%\My Pictures\Daughter's Winning Goal.jpg
%CSIDL_MyPictures%\Daughter's Winning Goal.jpg
Note also that in Windows XP My Pictures are stored in My Documents
:
%CSIDL_Profile%\My Documents
%CSIDL_Profile%\My Documents\My Pictures
But on Vista/7 they are separate:
%CSIDL_Profile%\Documents
%CSIDL_Profile%\Pictures
Note: i realize the syntax
%CSIDL_xxx%\filename.ext
is not valid; that Windows will not expand those keywords like they are environment strings. i'm only using it as a way to ask this question. Internally i would obviously store the items some other way, perhaps as aCSIDL
parent and the tail of the path, e.g.:CSIDL_Personal \Budget.xls CSIDL_MyPictures \Daughter's Winning Goal.jpg CSIDL_AppData \uTorrent CSIDL_Common_AppData \Consonto\SpellcheckDictionary.dat -1 c:\Develop\readme.txt (-1, since 0 is a valid csidl) CSIDL_Program_Files \Adobe\Reader\WhatsNew.txt
The question becomes, how to use, as much as possible, paths relative to canonical special folders?
I'm thinking:
void CanonicalizeSpecialPath(String path, ref CSLID cslid, ref String relativePath)
{
return "todo";
}
See also
- MSDN: CSIDL Enumeration
- New Old Thing: Beware of roaming user profiles
- New Old Thing: Beware of redirected folders, too
- MSDN: PathCanonicalize Function
I suppose you could find out how the CSIDL map to paths (using something like SHGetKnownFolderPath), build a reverse dictionary of them, then check whether the beginning of the path you want to store matches any of the keys in the dictionary and then remove the beginning and store the CSIDL that matched.
Not overtly elegant, but it should get the work done.
function CanonicalizeSpecialPath(const path: string): string;
var
s: string;
BestPrefix: string;
BestCSIDL: Integer;
i: Integer;
begin
BestPrefix := ''; //Start with no csidl being the one
BestCSIDL := 0;
//Iterate over the csidls i know about today for Windows XP.
for i := Low(csidls) to High(csidls) do
begin
//Get the path of this csidl. If the OS doesn't understand it, it returns blank
s := GetSpecialFolderPath(0, i, False);
if s = '' then
Continue;
//Don't do a string search unless this candidate is larger than what we have
if (BestPrefix='') or (Length(s) > Length(BestPrefix)) then
begin
//The special path must be at the start of our string
if Pos(s, Path) = 1 then //1=start
begin
//This is the best csidl we have so far
BestPrefix := s;
BestCSIDL := i;
end;
end;
end;
//If we found nothing useful, then return the original string
if BestPrefix = '' then
begin
Result := Path;
Exit;
end;
{
Return the canonicalized path as pseudo-environment string, e.g.:
%CSIDL_PERSONAL%\4th quarter.xls
}
Result := '%'+CsidlToStr(BestCSIDL)+'%'+Copy(Path, Length(BestPrefix)+1, MaxInt);
end;
And then there's a function that "expands" the special environment keywords:
function ExpandSpecialPath(const path: string): string;
begin
...
end;
which expands:
%CSIDL_PERSONAL%\4th quarter.xls
into
\\RoamingProfileServ\Users\ian\My Documents\4th quarter.xls
It does it by looking for %xx% at the start of the string, and expanding it.
来源:https://stackoverflow.com/questions/3181120/windows-how-to-canonicalize-a-file-to-the-special-folder