# -*- Ruby -*- =begin WinShell helps you to manage the virtual file system on Win32 platforms. 09/2003 by Sascha Doerdelmann =end require 'win32ole' class WinShell def initialize @specialFolderIds = { "ALTSTARTUP" => 0x1d, "APPDATA" => 0x1a, "BITBUCKET" => 0xa, "COMMONALTSTARTUP" => 0x1e, "COMMONAPPDATA" => 0x23, "COMMONDESKTOPDIR" => 0x19, "COMMONFAVORITES" => 0x1f, "COMMONPROGRAMS" => 0x17, "COMMONSTARTMENU" => 0x16, "COMMONSTARTUP" => 0x18, "CONTROLS" => 0x3, "COOKIES" => 0x21, "DESKTOP" => 0x0, "DESKTOPDIRECTORY" => 0x10, "DRIVES" => 0x11, "FAVORITES" => 0x6, "FONTS" => 0x14, "HISTORY" => 0x22, "INTERNETCACHE" => 0x20, "LOCALAPPDATA" => 0x1c, "MYPICTURES" => 0x27, "NETHOOD" => 0x13, "NETWORK" => 0x12, "PERSONAL" => 0x5, "PRINTERS" => 0x4, "PRINTHOOD" => 0x1b, "PROFILE" => 0x28, "PROGRAMFILES" => 0x26, "PROGRAMS" => 0x2, "RECENT" => 0x8, "SENDTO" => 0x9, "STARTMENU" => 0xb, "STARTUP" => 0x7, "SYSTEM" => 0x25, "TEMPLATES" => 0x15, "WINDOWS" => 0x24 } end # Get the path separator used by common ruby classes such as Dir and File. def separator return File.const_get('SEPARATOR') end # My shell is the ole object "Shell" as described in the Windows SDK. def shell # lazy initialisation, don't use @shell directly! @shell = WIN32OLE.new('Shell.application') if !@shell return @shell end def specialFolder(name) id = @specialFolderIds[name] return shell.NameSpace(id) end def pathForSpecialFolder(name) return desktopDirectory if name=="DESKTOP" oleFolder = specialFolder(name) return "" if !oleFolder return pathForFolder(specialFolder(name)) end # Has filename the Extension ext (e. g. ".jpg") def hasExtension?(filename, ext) raise ArgumentError, "Extension missing" if ext.empty? pExt = (ext[0] == '.'[0] ? ext : '.' + ext).downcase dcFilename = filename.downcase return File.basename(dcFilename, pExt) != dcFilename end # Is filename a Windows shortcut? def link?(filename) return hasExtension?(filename, '.lnk') end # Get a path string usable by Windows systems. def winPath(pathString) return pathString.gsub(separator, "\\") end # Get a path string usable by common ruby classes such as Dir and File. def rubyPath(pathString) return pathString.gsub(/\\/, separator) end # Get Directory entries as ole-objects, not as strings! def getFolderItems(directoryPathString) folder = shell.NameSpace(winPath(directoryPathString)) raise "Folderermittlung fehlgeschlagen" if !folder items = folder.items return (0..items.Count-1).collect { |i| items.item(i) } end def removePath(filenameWithPath) return rubyPath(filenameWithPath).split(separator)[-1] end #Get the path stored in the Windows shortcut filename. PRE: link?(filename) def getLinkTarget(filename, pathString = Dir.getwd) raise(ArgumentError, "Kein Link") if !link?(filename) filenameOnly = removePath(filename) # avoid errors ignore performance ext = '.' + filenameOnly.split('.')[-1] linkdef = File.basename(filenameOnly, ext) raise ArgumentError, "Linkname leer" if linkdef.empty? item = getFolderItems(pathString).find { |fi| linkdef == (fi.Name.to_s) } return item ? rubyPath(item.GetLink.Path.to_s) : '' end def pictureDirectory return pathForSpecialFolder("MYPICTURES") end def desktopDirectory return pathForSpecialFolder("DESKTOPDIRECTORY") end def pathForFolder(folder) current = folder.ParentFolder path = folder.Title while current do title = current.Title if title == "Desktop" # winXP, nicht win2000 current = specialFolder("DESKTOPDIRECTORY").ParentFolder title = current.Title end if title =~ /(.):/ title.gsub!(/.*\((\w:)\)/, '\1') return title + separator + path end path = title + separator + path current = current.ParentFolder end return path end def specialFolderListing @specialFolderIds.keys.sort.each do |aFolder| printf "#{aFolder}: " puts "#{pathForSpecialFolder aFolder}" end end end # Example if $0 == __FILE__ aShell = WinShell.new h = "Example 1: WinShell.new.specialFolderListing" hLine = (1..h.size).collect { |c| '*'} puts "#{hLine}\n#{h}\n#{hLine}" aShell.specialFolderListing h = "Example 2. Links on Desktop" hLine = (1..h.size).collect { |c| '*'} puts "#{hLine}\n#{h}\n#{hLine}" desktopDir = aShell.desktopDirectory links = Dir[desktopDir + '/*'].select { |p| aShell.link?(File.basename(p)) } links.each do |l| lName = File.basename l puts "#{lName} => \"#{aShell.getLinkTarget lName, desktopDir}\"" end end