 {
 ****************************************************************************
 CRW32 project
 Copyright (c) by Kuryakin Alexey, Sarov, Russia, 2020, <kouriakine@mail.ru>
 System special folders.
 Modifications:
 20200914 - derived from GetSpecialFolderPath utility
 ****************************************************************************
 }

unit _SPCFLD; {Special Folders}

{$I _sysdef}

interface

uses sysutils,windows,_alloc,_str;

const
 // registry entries for special paths are kept in
 REGSTR_PATH_SPECIAL_FOLDERS   = 'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders';
 CSIDL_DESKTOP                 = 0;
 CSIDL_INTERNET                = 1;
 CSIDL_PROGRAMS                = 2;
 CSIDL_CONTROLS                = 3;
 CSIDL_PRINTERS                = 4;
 CSIDL_PERSONAL                = 5;
 CSIDL_FAVORITES               = 6;
 CSIDL_STARTUP                 = 7;
 CSIDL_RECENT                  = 8;
 CSIDL_SENDTO                  = 9;
 CSIDL_BITBUCKET               = 10;
 CSIDL_STARTMENU               = 11;
 CSIDL_MYDOCUMENTS             = 12;
 CSIDL_MYMUSIC                 = 13;
 CSIDL_MYVIDEO                 = 14;
 CSIDL_DESKTOPDIRECTORY        = 16;
 CSIDL_DRIVES                  = 17;
 CSIDL_NETWORK                 = 18;
 CSIDL_NETHOOD                 = 19;
 CSIDL_FONTS                   = 20;
 CSIDL_TEMPLATES               = 21;
 CSIDL_COMMON_STARTMENU        = 22;
 CSIDL_COMMON_PROGRAMS         = 23;
 CSIDL_COMMON_STARTUP          = 24;
 CSIDL_COMMON_DESKTOPDIRECTORY = 25;
 CSIDL_APPDATA                 = 26;
 CSIDL_PRINTHOOD               = 27;
 CSIDL_LOCAL_APPDATA           = 28;
 CSIDL_ALTSTARTUP              = 29;
 CSIDL_COMMON_ALTSTARTUP       = 30;
 CSIDL_COMMON_FAVORITES        = 31;
 CSIDL_INTERNET_CACHE          = 32;
 CSIDL_COOKIES                 = 33;
 CSIDL_HISTORY                 = 34;
 CSIDL_COMMON_APPDATA          = 35;
 CSIDL_WINDOWS                 = 36;
 CSIDL_SYSTEM                  = 37;
 CSIDL_PROGRAM_FILES           = 38;
 CSIDL_MYPICTURES              = 39;
 CSIDL_PROFILE                 = 40;
 CSIDL_SYSTEMX86               = 41;
 CSIDL_PROGRAM_FILESX86        = 42;
 CSIDL_PROGRAM_FILES_COMMON    = 43;
 CSIDL_PROGRAM_FILES_COMMONX86 = 44;
 CSIDL_COMMON_TEMPLATES        = 45;
 CSIDL_COMMON_DOCUMENTS        = 46;
 CSIDL_COMMON_ADMINTOOLS       = 47;
 CSIDL_ADMINTOOLS              = 48;
 CSIDL_CONNECTIONS             = 49;
 CSIDL_COMMON_MUSIC            = 53;
 CSIDL_COMMON_PICTURES         = 54;
 CSIDL_COMMON_VIDEO            = 55;
 CSIDL_RESOURCES               = 56;
 CSIDL_RESOURCES_LOCALIZED     = 57;
 CSIDL_COMMON_OEM_LINKS        = 58;
 CSIDL_CDBURN_AREA             = 59;
 CSIDL_COMPUTERSNEARME         = 61;
 CSIDL_FIRST                   = CSIDL_DESKTOP;
 CSIDL_LAST                    = CSIDL_COMPUTERSNEARME;
 
function GetSpecialShellFolderPath(CSIDL:Integer):LongString;

function CSIDL_GetName(CSIDL:Integer):LongString;
function CSIDL_FindByName(const Name:LongString):Integer;
function CSIDL_FolderByName(const Name:LongString; Charset:Integer=DEFAULT_CHARSET; const Delim:LongString=''):LongString;
function CSIDL_ListAllAsText(Charset:Integer=DEFAULT_CHARSET; NoPrefix:Boolean=False; SkipEmpty:Boolean=False):LongString;

function StrToOem(const AnsiStr:LongString):LongString;
function StrToAnsi(const OemStr:LongString):LongString;

implementation

function CSIDL_GetName(CSIDL:Integer):LongString;
begin
 Result:='';
 case CSIDL of
  CSIDL_DESKTOP                 : Result:='CSIDL_DESKTOP';
  CSIDL_INTERNET                : Result:='CSIDL_INTERNET';
  CSIDL_PROGRAMS                : Result:='CSIDL_PROGRAMS';
  CSIDL_CONTROLS                : Result:='CSIDL_CONTROLS';
  CSIDL_PRINTERS                : Result:='CSIDL_PRINTERS';
  CSIDL_PERSONAL                : Result:='CSIDL_PERSONAL';
  CSIDL_FAVORITES               : Result:='CSIDL_FAVORITES';
  CSIDL_STARTUP                 : Result:='CSIDL_STARTUP';
  CSIDL_RECENT                  : Result:='CSIDL_RECENT';
  CSIDL_SENDTO                  : Result:='CSIDL_SENDTO';
  CSIDL_BITBUCKET               : Result:='CSIDL_BITBUCKET';
  CSIDL_STARTMENU               : Result:='CSIDL_STARTMENU';
  CSIDL_MYDOCUMENTS             : Result:='CSIDL_MYDOCUMENTS';
  CSIDL_MYMUSIC                 : Result:='CSIDL_MYMUSIC';
  CSIDL_MYVIDEO                 : Result:='CSIDL_MYVIDEO';
  CSIDL_DESKTOPDIRECTORY        : Result:='CSIDL_DESKTOPDIRECTORY';
  CSIDL_DRIVES                  : Result:='CSIDL_DRIVES';
  CSIDL_NETWORK                 : Result:='CSIDL_NETWORK';
  CSIDL_NETHOOD                 : Result:='CSIDL_NETHOOD';
  CSIDL_FONTS                   : Result:='CSIDL_FONTS';
  CSIDL_TEMPLATES               : Result:='CSIDL_TEMPLATES';
  CSIDL_COMMON_STARTMENU        : Result:='CSIDL_COMMON_STARTMENU';
  CSIDL_COMMON_PROGRAMS         : Result:='CSIDL_COMMON_PROGRAMS';
  CSIDL_COMMON_STARTUP          : Result:='CSIDL_COMMON_STARTUP';
  CSIDL_COMMON_DESKTOPDIRECTORY : Result:='CSIDL_COMMON_DESKTOPDIRECTORY';
  CSIDL_APPDATA                 : Result:='CSIDL_APPDATA';
  CSIDL_PRINTHOOD               : Result:='CSIDL_PRINTHOOD';
  CSIDL_LOCAL_APPDATA           : Result:='CSIDL_LOCAL_APPDATA';
  CSIDL_ALTSTARTUP              : Result:='CSIDL_ALTSTARTUP';
  CSIDL_COMMON_ALTSTARTUP       : Result:='CSIDL_COMMON_ALTSTARTUP';
  CSIDL_COMMON_FAVORITES        : Result:='CSIDL_COMMON_FAVORITES';
  CSIDL_INTERNET_CACHE          : Result:='CSIDL_INTERNET_CACHE';
  CSIDL_COOKIES                 : Result:='CSIDL_COOKIES';
  CSIDL_HISTORY                 : Result:='CSIDL_HISTORY';
  CSIDL_COMMON_APPDATA          : Result:='CSIDL_COMMON_APPDATA';
  CSIDL_WINDOWS                 : Result:='CSIDL_WINDOWS';
  CSIDL_SYSTEM                  : Result:='CSIDL_SYSTEM';
  CSIDL_PROGRAM_FILES           : Result:='CSIDL_PROGRAM_FILES';
  CSIDL_MYPICTURES              : Result:='CSIDL_MYPICTURES';
  CSIDL_PROFILE                 : Result:='CSIDL_PROFILE';
  CSIDL_SYSTEMX86               : Result:='CSIDL_SYSTEMX86';
  CSIDL_PROGRAM_FILESX86        : Result:='CSIDL_PROGRAM_FILESX86';
  CSIDL_PROGRAM_FILES_COMMON    : Result:='CSIDL_PROGRAM_FILES_COMMON';
  CSIDL_PROGRAM_FILES_COMMONX86 : Result:='CSIDL_PROGRAM_FILES_COMMONX86';
  CSIDL_COMMON_TEMPLATES        : Result:='CSIDL_COMMON_TEMPLATES';
  CSIDL_COMMON_DOCUMENTS        : Result:='CSIDL_COMMON_DOCUMENTS';
  CSIDL_COMMON_ADMINTOOLS       : Result:='CSIDL_COMMON_ADMINTOOLS';
  CSIDL_ADMINTOOLS              : Result:='CSIDL_ADMINTOOLS';
  CSIDL_CONNECTIONS             : Result:='CSIDL_CONNECTIONS';
  CSIDL_COMMON_MUSIC            : Result:='CSIDL_COMMON_MUSIC';
  CSIDL_COMMON_PICTURES         : Result:='CSIDL_COMMON_PICTURES';
  CSIDL_COMMON_VIDEO            : Result:='CSIDL_COMMON_VIDEO';
  CSIDL_RESOURCES               : Result:='CSIDL_RESOURCES';
  CSIDL_RESOURCES_LOCALIZED     : Result:='CSIDL_RESOURCES_LOCALIZED';
  CSIDL_COMMON_OEM_LINKS        : Result:='CSIDL_COMMON_OEM_LINKS';
  CSIDL_CDBURN_AREA             : Result:='CSIDL_CDBURN_AREA';
  CSIDL_COMPUTERSNEARME         : Result:='CSIDL_COMPUTERSNEARME';
 end;
end;

function CSIDL_FindByName(const Name:LongString):Integer;
var CSIDL:Integer; Item:LongString;
begin
 Result:=-1;
 if Length(Name)>0 then begin
  CSIDL:=StrToIntDef(Name,-1);
  if Length(CSIDL_GetName(CSIDL))>0 then Result:=CSIDL else
  for CSIDL:=CSIDL_FIRST to CSIDL_LAST do begin
   Item:=CSIDL_GetName(CSIDL); if Length(Item)=0 then Continue;
   if AnsiSameText(Name,Item) then begin Result:=CSIDL; Break; end;
   Delete(Item,1,Length('CSIDL_'));
   if AnsiSameText(Name,Item) then begin Result:=CSIDL; Break; end;
  end;
 end;
end;

{$DEFINE useSHGetFolderPath}

{$IFDEF useSHGetFolderPath}

function Succeeded(Res:HResult):Boolean;
begin
 Result:=((Res and $80000000) = 0);
end;

function SHGetFolderPath(hwndOwner: HWND; nFolder: Integer; hToken: THANDLE; dwFlags: DWORD;
         pszPath: PChar):HRESULT; stdcall;  external 'shell32.dll' name 'SHGetFolderPathA';

function GetSpecialShellFolderPath(CSIDL:Integer):LongString;
var Buff:array[0..MAX_PATH] of Char;
begin
 if Succeeded(SHGetFolderPath(0,CSIDL,0,0,Buff)) then Result:=Buff else Result:='';
end;

{$ELSE}

function SHGetSpecialFolderPath(hwndOwner: HWND; lpszPath: PChar;  nFolder: Integer;
         fCreate: BOOL): BOOL; stdcall;  external 'shell32.dll' name 'SHGetSpecialFolderPathA';

function GetSpecialShellFolderPath(CSIDL:Integer):LongString;
var Buff:array[0..MAX_PATH] of Char;
begin
 if SHGetSpecialFolderPath(0,Buff,CSIDL,True) then Result:=Buff else Result:='';
end;

{$ENDIF}

function StrToOem(const AnsiStr:LongString):LongString;
begin
 SetLength(Result,Length(AnsiStr));
 if Length(Result)>0 then CharToOem(PChar(AnsiStr),PChar(Result));
end;

function StrToAnsi(const OemStr:LongString):LongString;
begin
 SetLength(Result,Length(OemStr));
 if Length(Result)>0 then OemToChar(PChar(OemStr),PChar(Result));
end;

function CSIDL_ListAllAsText(Charset:Integer=DEFAULT_CHARSET; NoPrefix:Boolean=False; SkipEmpty:Boolean=False):LongString;
var CSIDL:Integer; Item,Folder:LongString;
begin
 Result:='';
 for CSIDL:=CSIDL_FIRST to CSIDL_LAST do begin
  Item:=CSIDL_GetName(CSIDL); if Length(Item)=0 then Continue;
  if NoPrefix then Delete(Item,1,Length('CSIDL_'));
  Folder:=GetSpecialShellFolderPath(CSIDL);
  if Length(Folder)=0 then if SkipEmpty then Continue;
  Result:=Result+Item+'='+Folder+LineEnding;
 end;
 if (Charset=OEM_CHARSET) and (Length(Result)>0) then Result:=StrToOem(Result);
 if (Charset=ANSI_CHARSET) and (Length(Result)>0) then Result:=StrToAnsi(Result);
end;

function CSIDL_FolderByName(const Name:LongString; Charset:Integer=DEFAULT_CHARSET; const Delim:LongString=''):LongString;
var CSIDL:Integer;
begin
 Result:='';
 if Name<>'' then begin
  CSIDL:=CSIDL_FindByName(Name);
  if CSIDL<>-1 then Result:=GetSpecialShellFolderPath(CSIDL);
  if Length(Result)>0 then Result:=Result+Delim;
 end;
 if (Charset=OEM_CHARSET) and (Length(Result)>0) then Result:=StrToOem(Result);
 if (Charset=ANSI_CHARSET) and (Length(Result)>0) then Result:=StrToAnsi(Result);
end;
 
end.
