Windows: How to canonicalize a file to the special folder?

五迷三道 提交于 2019-12-04 22:52:30

问题


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 a CSIDL 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

回答1:


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.




回答2:


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

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