UPDATE

This snippet was completely rewritten after comments made by crazyinsomniac, Corion and Tye in either the chatterbox or in direct reply. The crappy code that they justifiably jeered at has been removed and replaced by this.

END UPDATE

Contains two Win32::API wrappers for finding the windows path as well as the paths of other "special folders". (And yes I am aware that _normally_ $ENV{windir} holds the former value.)

GetWindowsDirectory takes no parameters and returns either "" or the location of the windows directory.

GetSpecialFolderPath takes a list CSIDL names (without the leading CISDL_) and returns


If no CSIDL names are passed then ALL CSIDL names will be looked up and the apropriate results returned depending on context.

Some of the CSIDL names have duplicates, with a short and long version (this currently applies to the names ending in DIRECTORY, with short form DIR being provided as well.)

Ive only tested the behavior of these subs on a W2k box running AS629 so far.

Comment/criticism/bugfixes welcome!

Yves/Demerphq

use Win32::API; use strict; use warnings; BEGIN { # for static vars. my %CSIDL_Constants = ( # Types are: VF - Virtual Folder| W - From Wine, no MS docs to che +ck against| NT - Avail on NT(+?) only, # Shell32.DLL versions : 4.71 - All IE4.0 | 4.72 - All IE4.01/98 | + 5.0 - W2k/Me+ # Look into ShFolder.dll if you have trouble. # http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/r +eference/functions/shgetspecialfolderpath.asp # Name Value Type Comment/Example +Return # ------------------------------------------------------------- +---------------------------------------------------- DESKTOP => 0x0000, # VF -- Root of namespac +e INTERNET => 0x0001, # VF -- The internet PROGRAMS => 0x0002, # -- C:\Documents and + Settings\username\Start Menu\Programs CONTROLS => 0x0003, # VF -- Icons for contro +l panel PRINTERS => 0x0004, # VF -- Installed Printe +rs PERSONAL => 0x0005, # -- C:\Documents and + Settings\username\My Documents FAVORITES => 0x0006, # -- C:\Documents and + Settings\username\Favorites STARTUP => 0x0007, # -- C:\Documents and + Settings\username\Start Menu\Programs\Startup RECENT => 0x0008, # -- C:\Documents and + Settings\username\Recent SENDTO => 0x0009, # -- C:\Documents and + Settings\username\SendTo BITBUCKET => 0x000a, # VF -- Recycling bin STARTMENU => 0x000b, # -- C:\Documents and + Settings\username\Start Menu MYDOCUMENTS => 0x000c, # W -- ?? MYMUSIC => 0x000d, # -- C:\My Music MYVIDEO => 0x000e, # W -- ?? DESKTOPDIRECTORY => 0x0010, # -- C:\Documents and + Settings\username\Desktop DESKTOPDIR => 0x0010, # -- C:\Documents and + Settings\username\Desktop DRIVES => 0x0011, # VF -- My Computer NETWORK => 0x0012, # VF -- Root of network +namespace hierarchy NETHOOD => 0x0013, # -- C:\Documents and + Settings\username\NetHood FONTS => 0x0014, # VF -- C:\WINNT\Fonts TEMPLATES => 0x0015, # -- Document templat +es COMMON_STARTMENU => 0x0016, # NT -- C:\Documents and + Settings\All Users\Start Menu, COMMON_PROGRAMS => 0x0017, # NT -- C:\Documents and + Settings\All Users\Start Menu\Programs COMMON_STARTUP => 0x0018, # NT -- C:\Documents and + Settings\All Users\Start Menu\Programs\Startup COMMON_DESKTOPDIR => 0x0019, # -- C:\Documents and + Settings\All Users\Desktop COMMON_DESKTOPDIRECTORY => 0x0019, # -- C:\Documents and + Settings\All Users\Desktop APPDATA => 0x001a, # 4.71 -- C:\Documents and + Settings\username\Application Data PRINTHOOD => 0x001b, # -- C:\Documents and + Settings\username\PrintHood LOCAL_APPDATA => 0x001c, # 5.0 -- C:\Documents and + Settings\username\Local Settings\Application Data ALTSTARTUP => 0x001d, # -- nonlocalized sta +rtup group COMMON_ALTSTARTUP => 0x001e, # NT -- All users nonloc +alized startup COMMON_FAVORITES => 0x001f, # NT -- Favorite items INTERNET_CACHE => 0x0020, # 4.72 -- C:\Documents and + Settings\username\Temporary Internet Files COOKIES => 0x0021, # -- C:\Documents and + Settings\username\Cookies HISTORY => 0x0022, # -- Internet History COMMON_APPDATA => 0x0023, # 5.0 -- C:\Documents and + Settings\All Users\Application Data WINDOWS => 0x0024, # 5.0 -- %windir% == %SYS +TEMROOT% == C:\WINNT SYSTEM => 0x0025, # 5.0 -- C:\WINNT\SYSTEM3 +2 PROGRAM_FILES => 0x0026, # 5.0 -- C:\Program Files MYPICTURES => 0x0027, # 5.0 -- C:\Documents and + Settings\username\My Documents\My Pictures PROFILE => 0x0028, # 5.0 -- Users profile fo +lder SYSTEMX86 => 0x0029, # W -- ?? PROGRAM_FILESX86 => 0x002a, # W -- ?? PROGRAM_FILES_COMMON => 0x002b, # 5.0/NT+ -- C:\Program Fi +les\Common PROGRAM_FILES_COMMONX86 => 0x002c, # W -- ?? COMMON_TEMPLATES => 0x002d, # W -- ?? COMMON_DOCUMENTS => 0x002e, # -- C:\Documents and + Settings\All Users\Documents COMMON_ADMINTOOLS => 0x002f, # 5.0 -- MMC Admin tools +dir for all users. ADMINTOOLS => 0x0030, # 5.0 -- MMC Admin tools +directory for user CONNECTIONS => 0x0031, # W -- ?? COMMON_MUSIC => 0x0035, # W -- ?? COMMON_PICTURES => 0x0036, # W -- ?? COMMON_VIDEO => 0x0037, # W -- ?? RESOURCES => 0x0038, # W -- ?? RESOURCES_LOCALIZED => 0x0039, # W -- ?? COMMON_OEM_LINKS => 0x003a, # W -- ?? CDBURN_AREA => 0x003b, # W -- ?? COMPUTERSNEARME => 0x003d, # W -- ?? # FLAG_CREATE 0x8000 # FLAG_DONT_VERIFY 0x4000 # FLAG_MASK 0xff00 # FLAG_NO_ALIAS 0x1000 # FLAG_PER_USER_INIT 0x0800 # FOLDER_MASK 0x00ff ); my $GetWindowsDirectory = Win32::API->new( "KERNEL32", "GetWindowsDirectoryA", [ "P", "N +" ], "N" ); # Returns the windows directory, equiv to %windir% but slightly sa +fer sub GetWindowsDirectory { my $buffer = "\0" x 255; $GetWindowsDirectory->Call( $buffer, length $buffer ); $buffer =~ tr/\0//d; return $buffer; }; my $SHGetSpecialFolderPath = Win32::API->new( "SHELL32", "SHGetSpecialFolderPath", [ "P", " +P", "I", "I" ], "I" ); # Returns the paths of special windows folders, sub GetSpecialFolderPath { my @csidl = @_; @csidl = keys %CSIDL_Constants unless @csidl; my %ret; foreach (@csidl) { my $buffer = "\0" x 255; # Preinit the buffer my $hWnd = ""; # this isnt used (afaict) #0 for no create my $r = $SHGetSpecialFolderPath->Call( $hWnd, $buffer +, $CSIDL_Constants{$_}, 0 ); if ($r) { $buffer =~ tr/\0//d; # remove the \0 chars $ret{$_} = $buffer; # save the result } else { $ret{$_} = undef; } } return %ret if wantarray; return ( @csidl > 1 ) ? \%ret : $ret{ $csidl[0] }; } } # Demo usage # Print out the windir and the special folders... $\ = "\n"; print GetWindowsDirectory(); use Data::Dumper; my %hash = GetSpecialFolderPath(); print Dumper( \%hash );

Replies are listed 'Best First'.
Re: Determine Standard Windows Paths
by Corion (Patriarch) on Feb 11, 2002 at 18:45 UTC

    I prefer to ask Windows about where the stuff is instead of guessing myself, but on the other side, knowledge about these functions is well-buried in the MSDN (and in the dark recesses of my mind, back from the days when I programmed a low level language).

    The Windows System directory (C:/WINNT and/or C:/WINDOWS or wherever your admin chose to install it. Note that you can have C:/WINDOWS on an NT machine even when not changing the defaults if you upgrade from Win9x or Win311 to Windows NT...). The code as posted isn't completely correct though, it won't work (or I think that it won't) under Win32s, as there is no kernel32.exe there. So some checks should be added before loading Win32::API.

    use Win32::API; my $GetWindowsDirectory = new Win32::API( "KERNEL32","GetWindowsDirectoryA","PN","N" ); sub GetWindowsDirectory { my $buffer = "\0" x 255; $GetWindowsDirectory->Call($buffer,length $buffer); $buffer =~ tr/\0//d; return $buffer; }; print GetWindowsDirectory();

    I'm still searching for the information on how to get at the directories for the Desktop, the Program Files and other stuff, but I guess this will be found within the next hour and then posted as an update to this node. These paths are stored in the registry somewhere, but taking them straight out of there is considered bad manners - you are supposed to obtain them via some Windows API call.

    Update: The call in question is SHGetSpecialFolderLocation - I'll try out the prototype and post it soon.

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
      Hey Corion. Spurred on by your comments I implemented the update I posted above.

      I had to change your Win32::API calls a bit though :-)

      Anyway, I wasn't able to get anything working with SHGetSpecialFolderLocation as I couldnt figure out how to pass/manipulate ppidl or ITEMIDLIST objects properly. (I believe they would have to passed on to SHGetPathFromIDList which has pointer/memory management issues that frankly I didnt want to research into...)

      So instead I used SHGetSpecialFolderPath and to use that I spent a bit of time looking into the CSIDL enumeration (which was a bit of a pain) so if you get SHGetSpecialFolderLocation happening you should be able to use my CSIDL data.

      Anyway, hope my new version meets with your approval.

      :-)

      Yves / DeMerphq
      --
      When to use Prototypes?