Path: blob/master/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java
66646 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt.shell;2627import java.awt.Image;28import java.awt.Toolkit;29import java.awt.image.AbstractMultiResolutionImage;30import java.awt.image.BufferedImage;31import java.awt.image.ImageObserver;32import java.awt.image.MultiResolutionImage;33import java.io.File;34import java.io.FileNotFoundException;35import java.io.IOException;36import java.io.Serial;37import java.util.ArrayList;38import java.util.Arrays;39import java.util.Collections;40import java.util.Comparator;41import java.util.HashMap;42import java.util.List;43import java.util.Map;44import java.util.concurrent.Callable;4546import javax.swing.SwingConstants;4748// NOTE: This class supersedes Win32ShellFolder, which was removed from49// distribution after version 1.4.2.5051/**52* Win32 Shell Folders53* <P>54* <BR>55* There are two fundamental types of shell folders : file system folders56* and non-file system folders. File system folders are relatively easy57* to deal with. Non-file system folders are items such as My Computer,58* Network Neighborhood, and the desktop. Some of these non-file system59* folders have special values and properties.60* <P>61* <BR>62* Win32 keeps two basic data structures for shell folders. The first63* of these is called an ITEMIDLIST. Usually a pointer, called an64* LPITEMIDLIST, or more frequently just "PIDL". This structure holds65* a series of identifiers and can be either relative to the desktop66* (an absolute PIDL), or relative to the shell folder that contains them.67* Some Win32 functions can take absolute or relative PIDL values, and68* others can only accept relative values.69* <BR>70* The second data structure is an IShellFolder COM interface. Using71* this interface, one can enumerate the relative PIDLs in a shell72* folder, get attributes, etc.73* <BR>74* All Win32ShellFolder2 objects which are folder types (even non-file75* system folders) contain an IShellFolder object. Files are named in76* directories via relative PIDLs.77*78* @author Michael Martak79* @author Leif Samuelsson80* @author Kenneth Russell81* @since 1.4 */82@SuppressWarnings("serial") // JDK-implementation class83final class Win32ShellFolder2 extends ShellFolder {8485static final int SMALL_ICON_SIZE = 16;86static final int LARGE_ICON_SIZE = 32;87static final int MIN_QUALITY_ICON = 16;88static final int MAX_QUALITY_ICON = 256;89private final static int[] ICON_RESOLUTIONS90= {16, 24, 32, 48, 64, 72, 96, 128, 256};9192static final int FILE_ICON_ID = 1;93static final int FOLDER_ICON_ID = 4;9495private static native void initIDs();9697static {98initIDs();99}100101// Win32 Shell Folder Constants102public static final int DESKTOP = 0x0000;103public static final int INTERNET = 0x0001;104public static final int PROGRAMS = 0x0002;105public static final int CONTROLS = 0x0003;106public static final int PRINTERS = 0x0004;107public static final int PERSONAL = 0x0005;108public static final int FAVORITES = 0x0006;109public static final int STARTUP = 0x0007;110public static final int RECENT = 0x0008;111public static final int SENDTO = 0x0009;112public static final int BITBUCKET = 0x000a;113public static final int STARTMENU = 0x000b;114public static final int DESKTOPDIRECTORY = 0x0010;115public static final int DRIVES = 0x0011;116public static final int NETWORK = 0x0012;117public static final int NETHOOD = 0x0013;118public static final int FONTS = 0x0014;119public static final int TEMPLATES = 0x0015;120public static final int COMMON_STARTMENU = 0x0016;121public static final int COMMON_PROGRAMS = 0X0017;122public static final int COMMON_STARTUP = 0x0018;123public static final int COMMON_DESKTOPDIRECTORY = 0x0019;124public static final int APPDATA = 0x001a;125public static final int PRINTHOOD = 0x001b;126public static final int ALTSTARTUP = 0x001d;127public static final int COMMON_ALTSTARTUP = 0x001e;128public static final int COMMON_FAVORITES = 0x001f;129public static final int INTERNET_CACHE = 0x0020;130public static final int COOKIES = 0x0021;131public static final int HISTORY = 0x0022;132133// Win32 shell folder attributes134public static final int ATTRIB_CANCOPY = 0x00000001;135public static final int ATTRIB_CANMOVE = 0x00000002;136public static final int ATTRIB_CANLINK = 0x00000004;137public static final int ATTRIB_CANRENAME = 0x00000010;138public static final int ATTRIB_CANDELETE = 0x00000020;139public static final int ATTRIB_HASPROPSHEET = 0x00000040;140public static final int ATTRIB_DROPTARGET = 0x00000100;141public static final int ATTRIB_LINK = 0x00010000;142public static final int ATTRIB_SHARE = 0x00020000;143public static final int ATTRIB_READONLY = 0x00040000;144public static final int ATTRIB_GHOSTED = 0x00080000;145public static final int ATTRIB_HIDDEN = 0x00080000;146public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;147public static final int ATTRIB_FOLDER = 0x20000000;148public static final int ATTRIB_FILESYSTEM = 0x40000000;149public static final int ATTRIB_HASSUBFOLDER = 0x80000000;150public static final int ATTRIB_VALIDATE = 0x01000000;151public static final int ATTRIB_REMOVABLE = 0x02000000;152public static final int ATTRIB_COMPRESSED = 0x04000000;153public static final int ATTRIB_BROWSABLE = 0x08000000;154public static final int ATTRIB_NONENUMERATED = 0x00100000;155public static final int ATTRIB_NEWCONTENT = 0x00200000;156157// IShellFolder::GetDisplayNameOf constants158public static final int SHGDN_NORMAL = 0;159public static final int SHGDN_INFOLDER = 1;160public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;161public static final int SHGDN_FORADDRESSBAR = 0x4000;162public static final int SHGDN_FORPARSING = 0x8000;163164/** The referent to be registered with the Disposer. */165private Object disposerReferent = new Object();166167// Values for system call LoadIcon()168public enum SystemIcon {169IDI_APPLICATION(32512),170IDI_HAND(32513),171IDI_ERROR(32513),172IDI_QUESTION(32514),173IDI_EXCLAMATION(32515),174IDI_WARNING(32515),175IDI_ASTERISK(32516),176IDI_INFORMATION(32516),177IDI_WINLOGO(32517);178179private final int iconID;180181SystemIcon(int iconID) {182this.iconID = iconID;183}184185public int getIconID() {186return iconID;187}188}189190// Known Folder data191static final class KnownFolderDefinition {192String guid;193int category;194String name;195String description;196String parent;197String relativePath;198String parsingName;199String tooltip;200String localizedName;201String icon;202String security;203long attributes;204int defenitionFlags;205String ftidType;206String path;207String saveLocation;208}209210static final class KnownLibraries {211static final List<KnownFolderDefinition> INSTANCE = getLibraries();212}213214static class FolderDisposer implements sun.java2d.DisposerRecord {215/*216* This is cached as a concession to getFolderType(), which needs217* an absolute PIDL.218*/219long absolutePIDL;220/*221* We keep track of shell folders through the IShellFolder222* interface of their parents plus their relative PIDL.223*/224long pIShellFolder;225long relativePIDL;226227boolean disposed;228public void dispose() {229if (disposed) return;230invoke(new Callable<Void>() {231public Void call() {232if (relativePIDL != 0) {233releasePIDL(relativePIDL);234}235if (absolutePIDL != 0) {236releasePIDL(absolutePIDL);237}238if (pIShellFolder != 0) {239releaseIShellFolder(pIShellFolder);240}241return null;242}243});244disposed = true;245}246}247FolderDisposer disposer = new FolderDisposer();248private void setIShellFolder(long pIShellFolder) {249disposer.pIShellFolder = pIShellFolder;250}251private void setRelativePIDL(long relativePIDL) {252disposer.relativePIDL = relativePIDL;253}254/*255* The following are for caching various shell folder properties.256*/257private long pIShellIcon = -1L;258private String folderType = null;259private String displayName = null;260private Image smallIcon = null;261private Image largeIcon = null;262private Boolean isDir = null;263private final boolean isLib;264private static final String FNAME = COLUMN_NAME;265private static final String FSIZE = COLUMN_SIZE;266private static final String FTYPE = "FileChooser.fileTypeHeaderText";267private static final String FDATE = COLUMN_DATE;268269/*270* The following is to identify the My Documents folder as being special271*/272private boolean isPersonal;273274private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {275String path = getFileSystemPath(csidl);276return path == null277? ("ShellFolder: 0x" + Integer.toHexString(csidl))278: path;279}280281/**282* Create a system special shell folder, such as the283* desktop or Network Neighborhood.284*/285Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {286// Desktop is parent of DRIVES and NETWORK, not necessarily287// other special shell folders.288super(null, composePathForCsidl(csidl));289isLib = false;290291invoke(new Callable<Void>() {292public Void call() throws InterruptedException {293if (csidl == DESKTOP) {294initDesktop();295} else {296initSpecial(getDesktop().getIShellFolder(), csidl);297// At this point, the native method initSpecial() has set our relativePIDL298// relative to the Desktop, which may not be our immediate parent. We need299// to traverse this ID list and break it into a chain of shell folders from300// the top, with each one having an immediate parent and a relativePIDL301// relative to that parent.302long pIDL = disposer.relativePIDL;303parent = getDesktop();304while (pIDL != 0) {305// Get a child pidl relative to 'parent'306long childPIDL = copyFirstPIDLEntry(pIDL);307if (childPIDL != 0) {308// Get a handle to the rest of the ID list309// i,e, parent's grandchilren and down310pIDL = getNextPIDLEntry(pIDL);311if (pIDL != 0) {312// Now we know that parent isn't immediate to 'this' because it313// has a continued ID list. Create a shell folder for this child314// pidl and make it the new 'parent'.315parent = createShellFolder((Win32ShellFolder2) parent, childPIDL);316} else {317// No grandchildren means we have arrived at the parent of 'this',318// and childPIDL is directly relative to parent.319disposer.relativePIDL = childPIDL;320}321} else {322break;323}324}325}326return null;327}328}, InterruptedException.class);329330sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);331}332333334/**335* Create a system shell folder336*/337Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path, boolean isLib) {338super(parent, (path != null) ? path : "ShellFolder: ");339this.isLib = isLib;340this.disposer.pIShellFolder = pIShellFolder;341this.disposer.relativePIDL = relativePIDL;342sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);343}344345346/**347* Creates a shell folder with a parent and relative PIDL348*/349static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, long pIDL)350throws InterruptedException {351String path = invoke(new Callable<String>() {352public String call() {353return getFileSystemPath(parent.getIShellFolder(), pIDL);354}355}, RuntimeException.class);356String libPath = resolveLibrary(path);357if (libPath == null) {358return new Win32ShellFolder2(parent, 0, pIDL, path, false);359} else {360return new Win32ShellFolder2(parent, 0, pIDL, libPath, true);361}362}363364// Initializes the desktop shell folder365// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details366private native void initDesktop();367368// Initializes a special, non-file system shell folder369// from one of the above constants370// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details371private native void initSpecial(long desktopIShellFolder, int csidl);372373/** Marks this folder as being the My Documents (Personal) folder */374public void setIsPersonal() {375isPersonal = true;376}377378/**379* This method is implemented to make sure that no instances380* of {@code ShellFolder} are ever serialized. If {@code isFileSystem()} returns381* {@code true}, then the object is representable with an instance of382* {@code java.io.File} instead. If not, then the object depends383* on native PIDL state and should not be serialized.384*385* @return a {@code java.io.File} replacement object. If the folder386* is a not a normal directory, then returns the first non-removable387* drive (normally "C:\").388*/389@Serial390protected Object writeReplace() throws java.io.ObjectStreamException {391return invoke(new Callable<File>() {392public File call() {393if (isFileSystem()) {394return new File(getPath());395} else {396Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();397if (drives != null) {398File[] driveRoots = drives.listFiles();399if (driveRoots != null) {400for (int i = 0; i < driveRoots.length; i++) {401if (driveRoots[i] instanceof Win32ShellFolder2) {402Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];403if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {404return new File(sf.getPath());405}406}407}408}409}410// Ouch, we have no hard drives. Return something "valid" anyway.411return new File("C:\\");412}413}414});415}416417418/**419* Finalizer to clean up any COM objects or PIDLs used by this object.420*/421protected void dispose() {422disposer.dispose();423}424425426// Given a (possibly multi-level) relative PIDL (with respect to427// the desktop, at least in all of the usage cases in this code),428// return a pointer to the next entry. Does not mutate the PIDL in429// any way. Returns 0 if the null terminator is reached.430// Needs to be accessible to Win32ShellFolderManager2431static native long getNextPIDLEntry(long pIDL);432433// Given a (possibly multi-level) relative PIDL (with respect to434// the desktop, at least in all of the usage cases in this code),435// copy the first entry into a newly-allocated PIDL. Returns 0 if436// the PIDL is at the end of the list.437// Needs to be accessible to Win32ShellFolderManager2438static native long copyFirstPIDLEntry(long pIDL);439440// Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL441private static native long combinePIDLs(long ppIDL, long pIDL);442443// Release a PIDL object444// Needs to be accessible to Win32ShellFolderManager2445static native void releasePIDL(long pIDL);446447// Release an IShellFolder object448// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details449private static native void releaseIShellFolder(long pIShellFolder);450451/**452* Accessor for IShellFolder453*/454private long getIShellFolder() {455if (disposer.pIShellFolder == 0) {456try {457disposer.pIShellFolder = invoke(new Callable<Long>() {458public Long call() {459assert(isDirectory());460assert(parent != null);461long parentIShellFolder = getParentIShellFolder();462if (parentIShellFolder == 0) {463throw new InternalError("Parent IShellFolder was null for "464+ getAbsolutePath());465}466// We are a directory with a parent and a relative PIDL.467// We want to bind to the parent so we get an468// IShellFolder instance associated with us.469long pIShellFolder = bindToObject(parentIShellFolder,470disposer.relativePIDL);471if (pIShellFolder == 0) {472throw new InternalError("Unable to bind "473+ getAbsolutePath() + " to parent");474}475return pIShellFolder;476}477}, RuntimeException.class);478} catch (InterruptedException e) {479// Ignore error480}481}482return disposer.pIShellFolder;483}484485/**486* Get the parent ShellFolder's IShellFolder interface487*/488public long getParentIShellFolder() {489Win32ShellFolder2 parent = (Win32ShellFolder2)getParentFile();490if (parent == null) {491// Parent should only be null if this is the desktop, whose492// relativePIDL is relative to its own IShellFolder.493return getIShellFolder();494}495return parent.getIShellFolder();496}497498/**499* Accessor for relative PIDL500*/501public long getRelativePIDL() {502if (disposer.relativePIDL == 0) {503throw new InternalError("Should always have a relative PIDL");504}505return disposer.relativePIDL;506}507508private long getAbsolutePIDL() {509if (parent == null) {510// This is the desktop511return getRelativePIDL();512} else {513if (disposer.absolutePIDL == 0) {514disposer.absolutePIDL = combinePIDLs(((Win32ShellFolder2)parent).getAbsolutePIDL(), getRelativePIDL());515}516517return disposer.absolutePIDL;518}519}520521/**522* Helper function to return the desktop523*/524public Win32ShellFolder2 getDesktop() {525return Win32ShellFolderManager2.getDesktop();526}527528/**529* Helper function to return the desktop IShellFolder interface530*/531public long getDesktopIShellFolder() {532return getDesktop().getIShellFolder();533}534535private static boolean pathsEqual(String path1, String path2) {536// Same effective implementation as Win32FileSystem537return path1.equalsIgnoreCase(path2);538}539540/**541* Check to see if two ShellFolder objects are the same542*/543public boolean equals(Object o) {544if (o == null || !(o instanceof Win32ShellFolder2)) {545// Short-circuit circuitous delegation path546if (!(o instanceof File)) {547return super.equals(o);548}549return pathsEqual(getPath(), ((File) o).getPath());550}551Win32ShellFolder2 rhs = (Win32ShellFolder2) o;552if ((parent == null && rhs.parent != null) ||553(parent != null && rhs.parent == null)) {554return false;555}556557if (isFileSystem() && rhs.isFileSystem()) {558// Only folders with identical parents can be equal559return (pathsEqual(getPath(), rhs.getPath()) &&560(parent == rhs.parent || parent.equals(rhs.parent)));561}562563if (parent == rhs.parent || parent.equals(rhs.parent)) {564try {565return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);566} catch (InterruptedException e) {567return false;568}569}570571return false;572}573574private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2)575throws InterruptedException {576return invoke(new Callable<Boolean>() {577public Boolean call() {578return compareIDs(pIShellFolder, pidl1, pidl2) == 0;579}580}, RuntimeException.class);581}582583// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details584private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);585586private volatile Boolean cachedIsFileSystem;587588/**589* @return Whether this is a file system shell folder590*/591public boolean isFileSystem() {592if (cachedIsFileSystem == null) {593cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);594}595596return cachedIsFileSystem;597}598599/**600* Return whether the given attribute flag is set for this object601*/602public boolean hasAttribute(final int attribute) {603Boolean result = invoke(new Callable<Boolean>() {604public Boolean call() {605// Caching at this point doesn't seem to be cost efficient606return (getAttributes0(getParentIShellFolder(),607getRelativePIDL(), attribute)608& attribute) != 0;609}610});611612return result != null && result;613}614615/**616* Returns the queried attributes specified in attrsMask.617*618* Could plausibly be used for attribute caching but have to be619* very careful not to touch network drives and file system roots620* with a full attrsMask621* NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details622*/623624private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);625626// Return the path to the underlying file system object627// Should be called from the COM thread628private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {629int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;630if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&631getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {632633String s =634getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),635getLinkLocation(parentIShellFolder, relativePIDL, false));636if (s != null && s.startsWith("\\\\")) {637return s;638}639}640String path = getDisplayNameOf(parentIShellFolder, relativePIDL,641SHGDN_FORPARSING);642return path;643}644645private static String resolveLibrary(String path) {646// if this is a library its default save location is taken as a path647// this is a temp fix until java.io starts support Libraries648if( path != null && path.startsWith("::{") &&649path.toLowerCase().endsWith(".library-ms")) {650for (KnownFolderDefinition kf : KnownLibraries.INSTANCE) {651if (path.toLowerCase().endsWith(652"\\" + kf.relativePath.toLowerCase()) &&653path.toUpperCase().startsWith(654kf.parsingName.substring(0, 40).toUpperCase())) {655return kf.saveLocation;656}657}658}659return null;660}661662// Needs to be accessible to Win32ShellFolderManager2663static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {664String path = invoke(new Callable<String>() {665public String call() throws IOException {666return getFileSystemPath0(csidl);667}668}, IOException.class);669if (path != null) {670@SuppressWarnings("removal")671SecurityManager security = System.getSecurityManager();672if (security != null) {673security.checkRead(path);674}675}676return path;677}678679// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details680private static native String getFileSystemPath0(int csidl) throws IOException;681682// Return whether the path is a network root.683// Path is assumed to be non-null684private static boolean isNetworkRoot(String path) {685return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));686}687688/**689* @return The parent shell folder of this shell folder, null if690* there is no parent691*/692public File getParentFile() {693return parent;694}695696public boolean isDirectory() {697if (isDir == null) {698// Folders with SFGAO_BROWSABLE have "shell extension" handlers and are699// not traversable in JFileChooser.700if (hasAttribute(ATTRIB_FOLDER) && !hasAttribute(ATTRIB_BROWSABLE)) {701isDir = Boolean.TRUE;702} else if (isLink()) {703ShellFolder linkLocation = getLinkLocation(false);704isDir = Boolean.valueOf(linkLocation != null && linkLocation.isDirectory());705} else {706isDir = Boolean.FALSE;707}708}709return isDir.booleanValue();710}711712/*713* Functions for enumerating an IShellFolder's children714*/715// Returns an IEnumIDList interface for an IShellFolder. The value716// returned must be released using releaseEnumObjects().717private long getEnumObjects(final boolean includeHiddenFiles) throws InterruptedException {718return invoke(new Callable<Long>() {719public Long call() {720boolean isDesktop = disposer.pIShellFolder == getDesktopIShellFolder();721722return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);723}724}, RuntimeException.class);725}726727// Returns an IEnumIDList interface for an IShellFolder. The value728// returned must be released using releaseEnumObjects().729// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details730private native long getEnumObjects(long pIShellFolder, boolean isDesktop,731boolean includeHiddenFiles);732// Returns the next sequential child as a relative PIDL733// from an IEnumIDList interface. The value returned must734// be released using releasePIDL().735// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details736private native long getNextChild(long pEnumObjects);737// Releases the IEnumIDList interface738// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details739private native void releaseEnumObjects(long pEnumObjects);740741// Returns the IShellFolder of a child from a parent IShellFolder742// and a relative PIDL. The value returned must be released743// using releaseIShellFolder().744// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details745private static native long bindToObject(long parentIShellFolder, long pIDL);746747/**748* @return An array of shell folders that are children of this shell folder749* object. The array will be empty if the folder is empty. Returns750* {@code null} if this shellfolder does not denote a directory.751*/752public File[] listFiles(final boolean includeHiddenFiles) {753@SuppressWarnings("removal")754SecurityManager security = System.getSecurityManager();755if (security != null) {756security.checkRead(getPath());757}758759try {760File[] files = invoke(new Callable<File[]>() {761public File[] call() throws InterruptedException {762if (!isDirectory()) {763return null;764}765// Links to directories are not directories and cannot be parents.766// This does not apply to folders in My Network Places (NetHood)767// because they are both links and real directories!768if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {769return new File[0];770}771772Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();773Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();774775// If we are a directory, we have a parent and (at least) a776// relative PIDL. We must first ensure we are bound to the777// parent so we have an IShellFolder to query.778long pIShellFolder = getIShellFolder();779// Now we can enumerate the objects in this folder.780ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();781long pEnumObjects = getEnumObjects(includeHiddenFiles);782if (pEnumObjects != 0) {783try {784long childPIDL;785int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;786do {787childPIDL = getNextChild(pEnumObjects);788boolean releasePIDL = true;789if (childPIDL != 0 &&790(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {791Win32ShellFolder2 childFolder;792if (Win32ShellFolder2.this.equals(desktop)793&& personal != null794&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {795childFolder = personal;796} else {797childFolder = createShellFolder(Win32ShellFolder2.this, childPIDL);798releasePIDL = false;799}800list.add(childFolder);801}802if (releasePIDL) {803releasePIDL(childPIDL);804}805} while (childPIDL != 0 && !Thread.currentThread().isInterrupted());806} finally {807releaseEnumObjects(pEnumObjects);808}809}810return Thread.currentThread().isInterrupted()811? new File[0]812: list.toArray(new ShellFolder[list.size()]);813}814}, InterruptedException.class);815816return Win32ShellFolderManager2.checkFiles(files);817} catch (InterruptedException e) {818return new File[0];819}820}821822823/**824* Look for (possibly special) child folder by it's path825*826* @return The child shellfolder, or null if not found.827*/828Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {829return invoke(new Callable<Win32ShellFolder2>() {830public Win32ShellFolder2 call() throws InterruptedException {831long pIShellFolder = getIShellFolder();832long pEnumObjects = getEnumObjects(true);833Win32ShellFolder2 child = null;834long childPIDL;835836while ((childPIDL = getNextChild(pEnumObjects)) != 0) {837if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {838String path = getFileSystemPath(pIShellFolder, childPIDL);839if(isLib) path = resolveLibrary( path );840if (path != null && path.equalsIgnoreCase(filePath)) {841long childIShellFolder = bindToObject(pIShellFolder, childPIDL);842child = new Win32ShellFolder2(Win32ShellFolder2.this,843childIShellFolder, childPIDL, path, isLib);844break;845}846}847releasePIDL(childPIDL);848}849releaseEnumObjects(pEnumObjects);850return child;851}852}, InterruptedException.class);853}854855private volatile Boolean cachedIsLink;856857/**858* @return Whether this shell folder is a link859*/860public boolean isLink() {861if (cachedIsLink == null) {862cachedIsLink = hasAttribute(ATTRIB_LINK);863}864865return cachedIsLink;866}867868/**869* @return Whether this shell folder is marked as hidden870*/871public boolean isHidden() {872return hasAttribute(ATTRIB_HIDDEN);873}874875876// Return the link location of a shell folder877// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details878private static native long getLinkLocation(long parentIShellFolder,879long relativePIDL, boolean resolve);880881/**882* @return The shell folder linked to by this shell folder, or null883* if this shell folder is not a link or is a broken or invalid link884*/885public ShellFolder getLinkLocation() {886return getLinkLocation(true);887}888889private Win32ShellFolder2 getLinkLocation(final boolean resolve) {890return invoke(new Callable<Win32ShellFolder2>() {891public Win32ShellFolder2 call() {892if (!isLink()) {893return null;894}895896Win32ShellFolder2 location = null;897long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),898getRelativePIDL(), resolve);899if (linkLocationPIDL != 0) {900try {901location =902Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),903linkLocationPIDL);904} catch (InterruptedException e) {905// Return null906} catch (InternalError e) {907// Could be a link to a non-bindable object, such as a network connection908// TODO: getIShellFolder() should throw FileNotFoundException instead909}910}911return location;912}913});914}915916// Parse a display name into a PIDL relative to the current IShellFolder.917long parseDisplayName(final String name) throws IOException, InterruptedException {918return invoke(new Callable<Long>() {919public Long call() throws IOException {920return parseDisplayName0(getIShellFolder(), name);921}922}, IOException.class);923}924925// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details926private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;927928// Return the display name of a shell folder929// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details930private static native String getDisplayNameOf(long parentIShellFolder,931long relativePIDL,932int attrs);933934// Returns data of all Known Folders registered in the system935private static native KnownFolderDefinition[] loadKnownFolders();936937/**938* @return The name used to display this shell folder939*/940public String getDisplayName() {941if (displayName == null) {942displayName =943invoke(new Callable<String>() {944public String call() {945return getDisplayNameOf(getParentIShellFolder(),946getRelativePIDL(), SHGDN_NORMAL);947}948});949}950return displayName;951}952953// Return the folder type of a shell folder954// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details955private static native String getFolderType(long pIDL);956957/**958* @return The type of shell folder as a string959*/960public String getFolderType() {961if (folderType == null) {962final long absolutePIDL = getAbsolutePIDL();963folderType =964invoke(new Callable<String>() {965public String call() {966return getFolderType(absolutePIDL);967}968});969}970return folderType;971}972973// Return the executable type of a file system shell folder974private native String getExecutableType(String path);975976/**977* @return The executable type as a string978*/979public String getExecutableType() {980if (!isFileSystem()) {981return null;982}983return getExecutableType(getAbsolutePath());984}985986987988// Icons989990private static Map<Integer, Image> smallSystemImages = new HashMap<>();991private static Map<Integer, Image> largeSystemImages = new HashMap<>();992private static Map<Integer, Image> smallLinkedSystemImages = new HashMap<>();993private static Map<Integer, Image> largeLinkedSystemImages = new HashMap<>();994995// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details996private static native long getIShellIcon(long pIShellFolder);997998// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details999private static native int getIconIndex(long parentIShellIcon, long relativePIDL);10001001// Return the icon of a file system shell folder in the form of an HICON1002private static native long getIcon(String absolutePath, boolean getLargeIcon);10031004// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1005private static native long extractIcon(long parentIShellFolder, long relativePIDL,1006int size, boolean getDefaultIcon);10071008// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1009private static native boolean hiResIconAvailable(long parentIShellFolder, long relativePIDL);10101011// Returns an icon from the Windows system icon list in the form of an HICON1012private static native long getSystemIcon(int iconID);1013private static native long getIconResource(String libName, int iconID,1014int cxDesired, int cyDesired);10151016// Return the bits from an HICON. This has a side effect of setting1017// the imageHash variable for efficient caching / comparing.1018private static native int[] getIconBits(long hIcon);1019// Dispose the HICON1020private static native void disposeIcon(long hIcon);10211022// Get buttons from native toolbar implementation.1023static native int[] getStandardViewButton0(int iconIndex, boolean small);10241025// Should be called from the COM thread1026private long getIShellIcon() {1027if (pIShellIcon == -1L) {1028pIShellIcon = getIShellIcon(getIShellFolder());1029}10301031return pIShellIcon;1032}10331034private static Image makeIcon(long hIcon) {1035if (hIcon != 0L && hIcon != -1L) {1036// Get the bits. This has the side effect of setting the imageHash value for this object.1037final int[] iconBits = getIconBits(hIcon);1038if (iconBits != null) {1039// icons are always square1040final int iconSize = (int) Math.sqrt(iconBits.length);1041final BufferedImage img =1042new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB);1043img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize);1044return img;1045}1046}1047return null;1048}104910501051/**1052* @return The icon image used to display this shell folder1053*/1054public Image getIcon(final boolean getLargeIcon) {1055Image icon = getLargeIcon ? largeIcon : smallIcon;1056int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;1057if (icon == null) {1058icon =1059invoke(new Callable<Image>() {1060public Image call() {1061Image newIcon = null;1062Image newIcon2 = null;1063if (isLink()) {1064Win32ShellFolder2 folder = getLinkLocation(false);1065if (folder != null && folder.isLibrary()) {1066return folder.getIcon(getLargeIcon);1067}1068}1069if (isFileSystem() || isLibrary()) {1070long parentIShellIcon = (parent != null)1071? ((Win32ShellFolder2) parent).getIShellIcon()1072: 0L;1073long relativePIDL = getRelativePIDL();10741075// These are cached per type (using the index in the system image list)1076int index = getIconIndex(parentIShellIcon, relativePIDL);1077if (index > 0) {1078Map<Integer, Image> imageCache;1079if (isLink()) {1080imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;1081} else {1082imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;1083}1084newIcon = imageCache.get(Integer.valueOf(index));1085if (newIcon == null) {1086long hIcon = getIcon(getAbsolutePath(), getLargeIcon);1087newIcon = makeIcon(hIcon);1088disposeIcon(hIcon);1089if (newIcon != null) {1090imageCache.put(Integer.valueOf(index), newIcon);1091}1092}1093if (newIcon != null) {1094if (isLink()) {1095imageCache = getLargeIcon ? smallLinkedSystemImages1096: largeLinkedSystemImages;1097} else {1098imageCache = getLargeIcon ? smallSystemImages : largeSystemImages;1099}1100newIcon2 = imageCache.get(index);1101if (newIcon2 == null) {1102long hIcon = getIcon(getAbsolutePath(), !getLargeIcon);1103newIcon2 = makeIcon(hIcon);1104disposeIcon(hIcon);1105}1106}11071108if (newIcon2 != null) {1109Map<Integer, Image> bothIcons = new HashMap<>(2);1110bothIcons.put(getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon);1111bothIcons.put(getLargeIcon ? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2);1112newIcon = new MultiResolutionIconImage(getLargeIcon ? LARGE_ICON_SIZE1113: SMALL_ICON_SIZE, bothIcons);1114}1115}1116}11171118if (hiResIconAvailable(getParentIShellFolder(), getRelativePIDL()) || newIcon == null) {1119int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;1120newIcon = getIcon(size, size);1121}11221123if (newIcon == null) {1124newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);1125}1126return newIcon;1127}1128});1129}1130return icon;1131}11321133/**1134* @return The icon image of specified size used to display this shell folder1135*/1136public Image getIcon(int width, int height) {1137int size = Math.max(width, height);1138return invoke(() -> {1139Image newIcon = null;1140if (isLink()) {1141Win32ShellFolder2 folder = getLinkLocation(false);1142if (folder != null && folder.isLibrary()) {1143return folder.getIcon(size, size);1144}1145}1146Map<Integer, Image> multiResolutionIcon = new HashMap<>();1147int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0;1148int increment = size > MAX_QUALITY_ICON ? -1 : 1;1149int end = size > MAX_QUALITY_ICON ? -1 : ICON_RESOLUTIONS.length;1150for (int i = start; i != end; i += increment) {1151int s = ICON_RESOLUTIONS[i];1152if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON1153|| (s >= size && s <= size*2)) {1154long hIcon = extractIcon(getParentIShellFolder(),1155getRelativePIDL(), s, false);11561157// E_PENDING: loading can take time so get the default1158if (hIcon <= 0) {1159hIcon = extractIcon(getParentIShellFolder(),1160getRelativePIDL(), s, true);1161if (hIcon <= 0) {1162if (isDirectory()) {1163newIcon = getShell32Icon(FOLDER_ICON_ID, size);1164} else {1165newIcon = getShell32Icon(FILE_ICON_ID, size);1166}1167if (newIcon == null) {1168return null;1169}1170if (!(newIcon instanceof MultiResolutionImage)) {1171newIcon = new MultiResolutionIconImage(size, newIcon);1172}1173return newIcon;1174}1175}1176newIcon = makeIcon(hIcon);1177disposeIcon(hIcon);11781179multiResolutionIcon.put(s, newIcon);1180if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON) {1181break;1182}1183}1184}1185return new MultiResolutionIconImage(size, multiResolutionIcon);1186});1187}11881189/**1190* Gets an icon from the Windows system icon list as an {@code Image}1191*/1192static Image getSystemIcon(SystemIcon iconType) {1193long hIcon = getSystemIcon(iconType.getIconID());1194Image icon = makeIcon(hIcon);1195if (LARGE_ICON_SIZE != icon.getWidth(null)) {1196icon = new MultiResolutionIconImage(LARGE_ICON_SIZE, icon);1197}1198disposeIcon(hIcon);1199return icon;1200}12011202/**1203* Gets an icon from the Windows system icon list as an {@code Image}1204*/1205static Image getShell32Icon(int iconID, int size) {1206long hIcon = getIconResource("shell32.dll", iconID, size, size);1207if (hIcon != 0) {1208Image icon = makeIcon(hIcon);1209if (size != icon.getWidth(null)) {1210icon = new MultiResolutionIconImage(size, icon);1211}1212disposeIcon(hIcon);1213return icon;1214}1215return null;1216}12171218/**1219* Returns the canonical form of this abstract pathname. Equivalent to1220* <code>new Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.1221*1222* @see java.io.File#getCanonicalFile1223*/1224public File getCanonicalFile() throws IOException {1225return this;1226}12271228/*1229* Indicates whether this is a special folder (includes My Documents)1230*/1231public boolean isSpecial() {1232return isPersonal || !isFileSystem() || (this == getDesktop());1233}12341235/**1236* Compares this object with the specified object for order.1237*1238* @see sun.awt.shell.ShellFolder#compareTo(File)1239*/1240public int compareTo(File file2) {1241if (!(file2 instanceof Win32ShellFolder2)) {1242if (isFileSystem() && !isSpecial()) {1243return super.compareTo(file2);1244} else {1245return -1; // Non-file shellfolders sort before files1246}1247}1248return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);1249}12501251// native constants from commctrl.h1252private static final int LVCFMT_LEFT = 0;1253private static final int LVCFMT_RIGHT = 1;1254private static final int LVCFMT_CENTER = 2;12551256public ShellFolderColumnInfo[] getFolderColumns() {1257ShellFolder library = resolveLibrary();1258if (library != null) return library.getFolderColumns();1259return invoke(new Callable<ShellFolderColumnInfo[]>() {1260public ShellFolderColumnInfo[] call() {1261ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());12621263if (columns != null) {1264List<ShellFolderColumnInfo> notNullColumns =1265new ArrayList<ShellFolderColumnInfo>();1266for (int i = 0; i < columns.length; i++) {1267ShellFolderColumnInfo column = columns[i];1268if (column != null) {1269column.setAlignment(column.getAlignment() == LVCFMT_RIGHT1270? SwingConstants.RIGHT1271: column.getAlignment() == LVCFMT_CENTER1272? SwingConstants.CENTER1273: SwingConstants.LEADING);12741275column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));12761277notNullColumns.add(column);1278}1279}1280columns = new ShellFolderColumnInfo[notNullColumns.size()];1281notNullColumns.toArray(columns);1282}1283return columns;1284}1285});1286}12871288public Object getFolderColumnValue(final int column) {1289if(!isLibrary()) {1290ShellFolder library = resolveLibrary();1291if (library != null) return library.getFolderColumnValue(column);1292}1293return invoke(new Callable<Object>() {1294public Object call() {1295return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);1296}1297});1298}12991300boolean isLibrary() {1301return isLib;1302}13031304private ShellFolder resolveLibrary() {1305for (ShellFolder f = this; f != null; f = f.parent) {1306if (!f.isFileSystem()) {1307if (f instanceof Win32ShellFolder2 &&1308((Win32ShellFolder2)f).isLibrary()) {1309try {1310return getShellFolder(new File(getPath()));1311} catch (FileNotFoundException e) {1312}1313}1314break;1315}1316}1317return null;1318}13191320// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1321private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);13221323// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1324private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);13251326// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1327private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);132813291330public void sortChildren(final List<? extends File> files) {1331// To avoid loads of synchronizations with Invoker and improve performance we1332// synchronize the whole code of the sort method once1333invoke(new Callable<Void>() {1334public Void call() {1335Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));13361337return null;1338}1339});1340}13411342private static class ColumnComparator implements Comparator<File> {1343private final Win32ShellFolder2 shellFolder;13441345private final int columnIdx;13461347public ColumnComparator(Win32ShellFolder2 shellFolder, int columnIdx) {1348this.shellFolder = shellFolder;1349this.columnIdx = columnIdx;1350}13511352// compares 2 objects within this folder by the specified column1353public int compare(final File o, final File o1) {1354Integer result = invoke(new Callable<Integer>() {1355public Integer call() {1356if (o instanceof Win32ShellFolder21357&& o1 instanceof Win32ShellFolder2) {1358// delegates comparison to native method1359return compareIDsByColumn(shellFolder.getIShellFolder(),1360((Win32ShellFolder2) o).getRelativePIDL(),1361((Win32ShellFolder2) o1).getRelativePIDL(),1362columnIdx);1363}1364return 0;1365}1366});13671368return result == null ? 0 : result;1369}1370}13711372// Extracts libraries and their default save locations from Known Folders list1373private static List<KnownFolderDefinition> getLibraries() {1374return invoke(new Callable<List<KnownFolderDefinition>>() {1375@Override1376public List<KnownFolderDefinition> call() throws Exception {1377KnownFolderDefinition[] all = loadKnownFolders();1378List<KnownFolderDefinition> folders = new ArrayList<>();1379if (all != null) {1380for (KnownFolderDefinition kf : all) {1381if (kf.relativePath == null || kf.parsingName == null ||1382kf.saveLocation == null) {1383continue;1384}1385folders.add(kf);1386}1387}1388return folders;1389}1390});1391}13921393static class MultiResolutionIconImage extends AbstractMultiResolutionImage {1394final int baseSize;1395final Map<Integer, Image> resolutionVariants = new HashMap<>();13961397public MultiResolutionIconImage(int baseSize, Map<Integer, Image> resolutionVariants) {1398this.baseSize = baseSize;1399this.resolutionVariants.putAll(resolutionVariants);1400}14011402public MultiResolutionIconImage(int baseSize, Image image) {1403this.baseSize = baseSize;1404this.resolutionVariants.put(baseSize, image);1405}14061407@Override1408public int getWidth(ImageObserver observer) {1409return baseSize;1410}14111412@Override1413public int getHeight(ImageObserver observer) {1414return baseSize;1415}14161417@Override1418protected Image getBaseImage() {1419return getResolutionVariant(baseSize, baseSize);1420}14211422@Override1423public Image getResolutionVariant(double width, double height) {1424int dist = 0;1425Image retVal = null;1426// We only care about width since we don't support non-rectangular icons1427int w = (int) width;1428int retindex = 0;1429for (Integer i : resolutionVariants.keySet()) {1430if (retVal == null || dist > Math.abs(i - w)1431|| (dist == Math.abs(i - w) && i > retindex)) {1432retindex = i;1433dist = Math.abs(i - w);1434retVal = resolutionVariants.get(i);1435if (i == w) {1436break;1437}1438}1439}1440return retVal;1441}14421443@Override1444public List<Image> getResolutionVariants() {1445return Collections.unmodifiableList(1446new ArrayList<Image>(resolutionVariants.values()));1447}1448}1449}145014511452