Path: blob/master/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java
41159 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.io.File;33import java.io.FileNotFoundException;34import java.io.IOException;35import java.io.Serial;36import java.util.ArrayList;37import java.util.Arrays;38import java.util.Collections;39import java.util.Comparator;40import java.util.HashMap;41import java.util.List;42import java.util.Map;43import java.util.concurrent.Callable;4445import javax.swing.SwingConstants;4647// NOTE: This class supersedes Win32ShellFolder, which was removed from48// distribution after version 1.4.2.4950/**51* Win32 Shell Folders52* <P>53* <BR>54* There are two fundamental types of shell folders : file system folders55* and non-file system folders. File system folders are relatively easy56* to deal with. Non-file system folders are items such as My Computer,57* Network Neighborhood, and the desktop. Some of these non-file system58* folders have special values and properties.59* <P>60* <BR>61* Win32 keeps two basic data structures for shell folders. The first62* of these is called an ITEMIDLIST. Usually a pointer, called an63* LPITEMIDLIST, or more frequently just "PIDL". This structure holds64* a series of identifiers and can be either relative to the desktop65* (an absolute PIDL), or relative to the shell folder that contains them.66* Some Win32 functions can take absolute or relative PIDL values, and67* others can only accept relative values.68* <BR>69* The second data structure is an IShellFolder COM interface. Using70* this interface, one can enumerate the relative PIDLs in a shell71* folder, get attributes, etc.72* <BR>73* All Win32ShellFolder2 objects which are folder types (even non-file74* system folders) contain an IShellFolder object. Files are named in75* directories via relative PIDLs.76*77* @author Michael Martak78* @author Leif Samuelsson79* @author Kenneth Russell80* @since 1.4 */81@SuppressWarnings("serial") // JDK-implementation class82final class Win32ShellFolder2 extends ShellFolder {8384static final int SMALL_ICON_SIZE = 16;85static final int LARGE_ICON_SIZE = 32;86static final int MIN_QUALITY_ICON = 16;87static final int MAX_QUALITY_ICON = 256;88private final static int[] ICON_RESOLUTIONS89= {16, 24, 32, 48, 64, 72, 96, 128, 256};9091static final int FILE_ICON_ID = 1;92static final int FOLDER_ICON_ID = 4;9394private static native void initIDs();9596static {97initIDs();98}99100// Win32 Shell Folder Constants101public static final int DESKTOP = 0x0000;102public static final int INTERNET = 0x0001;103public static final int PROGRAMS = 0x0002;104public static final int CONTROLS = 0x0003;105public static final int PRINTERS = 0x0004;106public static final int PERSONAL = 0x0005;107public static final int FAVORITES = 0x0006;108public static final int STARTUP = 0x0007;109public static final int RECENT = 0x0008;110public static final int SENDTO = 0x0009;111public static final int BITBUCKET = 0x000a;112public static final int STARTMENU = 0x000b;113public static final int DESKTOPDIRECTORY = 0x0010;114public static final int DRIVES = 0x0011;115public static final int NETWORK = 0x0012;116public static final int NETHOOD = 0x0013;117public static final int FONTS = 0x0014;118public static final int TEMPLATES = 0x0015;119public static final int COMMON_STARTMENU = 0x0016;120public static final int COMMON_PROGRAMS = 0X0017;121public static final int COMMON_STARTUP = 0x0018;122public static final int COMMON_DESKTOPDIRECTORY = 0x0019;123public static final int APPDATA = 0x001a;124public static final int PRINTHOOD = 0x001b;125public static final int ALTSTARTUP = 0x001d;126public static final int COMMON_ALTSTARTUP = 0x001e;127public static final int COMMON_FAVORITES = 0x001f;128public static final int INTERNET_CACHE = 0x0020;129public static final int COOKIES = 0x0021;130public static final int HISTORY = 0x0022;131132// Win32 shell folder attributes133public static final int ATTRIB_CANCOPY = 0x00000001;134public static final int ATTRIB_CANMOVE = 0x00000002;135public static final int ATTRIB_CANLINK = 0x00000004;136public static final int ATTRIB_CANRENAME = 0x00000010;137public static final int ATTRIB_CANDELETE = 0x00000020;138public static final int ATTRIB_HASPROPSHEET = 0x00000040;139public static final int ATTRIB_DROPTARGET = 0x00000100;140public static final int ATTRIB_LINK = 0x00010000;141public static final int ATTRIB_SHARE = 0x00020000;142public static final int ATTRIB_READONLY = 0x00040000;143public static final int ATTRIB_GHOSTED = 0x00080000;144public static final int ATTRIB_HIDDEN = 0x00080000;145public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;146public static final int ATTRIB_FOLDER = 0x20000000;147public static final int ATTRIB_FILESYSTEM = 0x40000000;148public static final int ATTRIB_HASSUBFOLDER = 0x80000000;149public static final int ATTRIB_VALIDATE = 0x01000000;150public static final int ATTRIB_REMOVABLE = 0x02000000;151public static final int ATTRIB_COMPRESSED = 0x04000000;152public static final int ATTRIB_BROWSABLE = 0x08000000;153public static final int ATTRIB_NONENUMERATED = 0x00100000;154public static final int ATTRIB_NEWCONTENT = 0x00200000;155156// IShellFolder::GetDisplayNameOf constants157public static final int SHGDN_NORMAL = 0;158public static final int SHGDN_INFOLDER = 1;159public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;160public static final int SHGDN_FORADDRESSBAR = 0x4000;161public static final int SHGDN_FORPARSING = 0x8000;162163/** The referent to be registered with the Disposer. */164private Object disposerReferent = new Object();165166// Values for system call LoadIcon()167public enum SystemIcon {168IDI_APPLICATION(32512),169IDI_HAND(32513),170IDI_ERROR(32513),171IDI_QUESTION(32514),172IDI_EXCLAMATION(32515),173IDI_WARNING(32515),174IDI_ASTERISK(32516),175IDI_INFORMATION(32516),176IDI_WINLOGO(32517);177178private final int iconID;179180SystemIcon(int iconID) {181this.iconID = iconID;182}183184public int getIconID() {185return iconID;186}187}188189// Known Folder data190static final class KnownFolderDefinition {191String guid;192int category;193String name;194String description;195String parent;196String relativePath;197String parsingName;198String tooltip;199String localizedName;200String icon;201String security;202long attributes;203int defenitionFlags;204String ftidType;205String path;206String saveLocation;207}208209static final class KnownLibraries {210static final List<KnownFolderDefinition> INSTANCE = getLibraries();211}212213static class FolderDisposer implements sun.java2d.DisposerRecord {214/*215* This is cached as a concession to getFolderType(), which needs216* an absolute PIDL.217*/218long absolutePIDL;219/*220* We keep track of shell folders through the IShellFolder221* interface of their parents plus their relative PIDL.222*/223long pIShellFolder;224long relativePIDL;225226boolean disposed;227public void dispose() {228if (disposed) return;229invoke(new Callable<Void>() {230public Void call() {231if (relativePIDL != 0) {232releasePIDL(relativePIDL);233}234if (absolutePIDL != 0) {235releasePIDL(absolutePIDL);236}237if (pIShellFolder != 0) {238releaseIShellFolder(pIShellFolder);239}240return null;241}242});243disposed = true;244}245}246FolderDisposer disposer = new FolderDisposer();247private void setIShellFolder(long pIShellFolder) {248disposer.pIShellFolder = pIShellFolder;249}250private void setRelativePIDL(long relativePIDL) {251disposer.relativePIDL = relativePIDL;252}253/*254* The following are for caching various shell folder properties.255*/256private long pIShellIcon = -1L;257private String folderType = null;258private String displayName = null;259private Image smallIcon = null;260private Image largeIcon = null;261private Boolean isDir = null;262private final boolean isLib;263private static final String FNAME = COLUMN_NAME;264private static final String FSIZE = COLUMN_SIZE;265private static final String FTYPE = "FileChooser.fileTypeHeaderText";266private static final String FDATE = COLUMN_DATE;267268/*269* The following is to identify the My Documents folder as being special270*/271private boolean isPersonal;272273private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {274String path = getFileSystemPath(csidl);275return path == null276? ("ShellFolder: 0x" + Integer.toHexString(csidl))277: path;278}279280/**281* Create a system special shell folder, such as the282* desktop or Network Neighborhood.283*/284Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {285// Desktop is parent of DRIVES and NETWORK, not necessarily286// other special shell folders.287super(null, composePathForCsidl(csidl));288isLib = false;289290invoke(new Callable<Void>() {291public Void call() throws InterruptedException {292if (csidl == DESKTOP) {293initDesktop();294} else {295initSpecial(getDesktop().getIShellFolder(), csidl);296// At this point, the native method initSpecial() has set our relativePIDL297// relative to the Desktop, which may not be our immediate parent. We need298// to traverse this ID list and break it into a chain of shell folders from299// the top, with each one having an immediate parent and a relativePIDL300// relative to that parent.301long pIDL = disposer.relativePIDL;302parent = getDesktop();303while (pIDL != 0) {304// Get a child pidl relative to 'parent'305long childPIDL = copyFirstPIDLEntry(pIDL);306if (childPIDL != 0) {307// Get a handle to the rest of the ID list308// i,e, parent's grandchilren and down309pIDL = getNextPIDLEntry(pIDL);310if (pIDL != 0) {311// Now we know that parent isn't immediate to 'this' because it312// has a continued ID list. Create a shell folder for this child313// pidl and make it the new 'parent'.314parent = createShellFolder((Win32ShellFolder2) parent, childPIDL);315} else {316// No grandchildren means we have arrived at the parent of 'this',317// and childPIDL is directly relative to parent.318disposer.relativePIDL = childPIDL;319}320} else {321break;322}323}324}325return null;326}327}, InterruptedException.class);328329sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);330}331332333/**334* Create a system shell folder335*/336Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path, boolean isLib) {337super(parent, (path != null) ? path : "ShellFolder: ");338this.isLib = isLib;339this.disposer.pIShellFolder = pIShellFolder;340this.disposer.relativePIDL = relativePIDL;341sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);342}343344345/**346* Creates a shell folder with a parent and relative PIDL347*/348static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, long pIDL)349throws InterruptedException {350String path = invoke(new Callable<String>() {351public String call() {352return getFileSystemPath(parent.getIShellFolder(), pIDL);353}354}, RuntimeException.class);355String libPath = resolveLibrary(path);356if (libPath == null) {357return new Win32ShellFolder2(parent, 0, pIDL, path, false);358} else {359return new Win32ShellFolder2(parent, 0, pIDL, libPath, true);360}361}362363// Initializes the desktop shell folder364// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details365private native void initDesktop();366367// Initializes a special, non-file system shell folder368// from one of the above constants369// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details370private native void initSpecial(long desktopIShellFolder, int csidl);371372/** Marks this folder as being the My Documents (Personal) folder */373public void setIsPersonal() {374isPersonal = true;375}376377/**378* This method is implemented to make sure that no instances379* of {@code ShellFolder} are ever serialized. If {@code isFileSystem()} returns380* {@code true}, then the object is representable with an instance of381* {@code java.io.File} instead. If not, then the object depends382* on native PIDL state and should not be serialized.383*384* @return a {@code java.io.File} replacement object. If the folder385* is a not a normal directory, then returns the first non-removable386* drive (normally "C:\").387*/388@Serial389protected Object writeReplace() throws java.io.ObjectStreamException {390return invoke(new Callable<File>() {391public File call() {392if (isFileSystem()) {393return new File(getPath());394} else {395Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();396if (drives != null) {397File[] driveRoots = drives.listFiles();398if (driveRoots != null) {399for (int i = 0; i < driveRoots.length; i++) {400if (driveRoots[i] instanceof Win32ShellFolder2) {401Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];402if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {403return new File(sf.getPath());404}405}406}407}408}409// Ouch, we have no hard drives. Return something "valid" anyway.410return new File("C:\\");411}412}413});414}415416417/**418* Finalizer to clean up any COM objects or PIDLs used by this object.419*/420protected void dispose() {421disposer.dispose();422}423424425// Given a (possibly multi-level) relative PIDL (with respect to426// the desktop, at least in all of the usage cases in this code),427// return a pointer to the next entry. Does not mutate the PIDL in428// any way. Returns 0 if the null terminator is reached.429// Needs to be accessible to Win32ShellFolderManager2430static native long getNextPIDLEntry(long pIDL);431432// Given a (possibly multi-level) relative PIDL (with respect to433// the desktop, at least in all of the usage cases in this code),434// copy the first entry into a newly-allocated PIDL. Returns 0 if435// the PIDL is at the end of the list.436// Needs to be accessible to Win32ShellFolderManager2437static native long copyFirstPIDLEntry(long pIDL);438439// Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL440private static native long combinePIDLs(long ppIDL, long pIDL);441442// Release a PIDL object443// Needs to be accessible to Win32ShellFolderManager2444static native void releasePIDL(long pIDL);445446// Release an IShellFolder object447// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details448private static native void releaseIShellFolder(long pIShellFolder);449450/**451* Accessor for IShellFolder452*/453private long getIShellFolder() {454if (disposer.pIShellFolder == 0) {455try {456disposer.pIShellFolder = invoke(new Callable<Long>() {457public Long call() {458assert(isDirectory());459assert(parent != null);460long parentIShellFolder = getParentIShellFolder();461if (parentIShellFolder == 0) {462throw new InternalError("Parent IShellFolder was null for "463+ getAbsolutePath());464}465// We are a directory with a parent and a relative PIDL.466// We want to bind to the parent so we get an467// IShellFolder instance associated with us.468long pIShellFolder = bindToObject(parentIShellFolder,469disposer.relativePIDL);470if (pIShellFolder == 0) {471throw new InternalError("Unable to bind "472+ getAbsolutePath() + " to parent");473}474return pIShellFolder;475}476}, RuntimeException.class);477} catch (InterruptedException e) {478// Ignore error479}480}481return disposer.pIShellFolder;482}483484/**485* Get the parent ShellFolder's IShellFolder interface486*/487public long getParentIShellFolder() {488Win32ShellFolder2 parent = (Win32ShellFolder2)getParentFile();489if (parent == null) {490// Parent should only be null if this is the desktop, whose491// relativePIDL is relative to its own IShellFolder.492return getIShellFolder();493}494return parent.getIShellFolder();495}496497/**498* Accessor for relative PIDL499*/500public long getRelativePIDL() {501if (disposer.relativePIDL == 0) {502throw new InternalError("Should always have a relative PIDL");503}504return disposer.relativePIDL;505}506507private long getAbsolutePIDL() {508if (parent == null) {509// This is the desktop510return getRelativePIDL();511} else {512if (disposer.absolutePIDL == 0) {513disposer.absolutePIDL = combinePIDLs(((Win32ShellFolder2)parent).getAbsolutePIDL(), getRelativePIDL());514}515516return disposer.absolutePIDL;517}518}519520/**521* Helper function to return the desktop522*/523public Win32ShellFolder2 getDesktop() {524return Win32ShellFolderManager2.getDesktop();525}526527/**528* Helper function to return the desktop IShellFolder interface529*/530public long getDesktopIShellFolder() {531return getDesktop().getIShellFolder();532}533534private static boolean pathsEqual(String path1, String path2) {535// Same effective implementation as Win32FileSystem536return path1.equalsIgnoreCase(path2);537}538539/**540* Check to see if two ShellFolder objects are the same541*/542public boolean equals(Object o) {543if (o == null || !(o instanceof Win32ShellFolder2)) {544// Short-circuit circuitous delegation path545if (!(o instanceof File)) {546return super.equals(o);547}548return pathsEqual(getPath(), ((File) o).getPath());549}550Win32ShellFolder2 rhs = (Win32ShellFolder2) o;551if ((parent == null && rhs.parent != null) ||552(parent != null && rhs.parent == null)) {553return false;554}555556if (isFileSystem() && rhs.isFileSystem()) {557// Only folders with identical parents can be equal558return (pathsEqual(getPath(), rhs.getPath()) &&559(parent == rhs.parent || parent.equals(rhs.parent)));560}561562if (parent == rhs.parent || parent.equals(rhs.parent)) {563try {564return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);565} catch (InterruptedException e) {566return false;567}568}569570return false;571}572573private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2)574throws InterruptedException {575return invoke(new Callable<Boolean>() {576public Boolean call() {577return compareIDs(pIShellFolder, pidl1, pidl2) == 0;578}579}, RuntimeException.class);580}581582// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details583private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);584585private volatile Boolean cachedIsFileSystem;586587/**588* @return Whether this is a file system shell folder589*/590public boolean isFileSystem() {591if (cachedIsFileSystem == null) {592cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);593}594595return cachedIsFileSystem;596}597598/**599* Return whether the given attribute flag is set for this object600*/601public boolean hasAttribute(final int attribute) {602Boolean result = invoke(new Callable<Boolean>() {603public Boolean call() {604// Caching at this point doesn't seem to be cost efficient605return (getAttributes0(getParentIShellFolder(),606getRelativePIDL(), attribute)607& attribute) != 0;608}609});610611return result != null && result;612}613614/**615* Returns the queried attributes specified in attrsMask.616*617* Could plausibly be used for attribute caching but have to be618* very careful not to touch network drives and file system roots619* with a full attrsMask620* NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details621*/622623private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);624625// Return the path to the underlying file system object626// Should be called from the COM thread627private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {628int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;629if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&630getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {631632String s =633getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),634getLinkLocation(parentIShellFolder, relativePIDL, false));635if (s != null && s.startsWith("\\\\")) {636return s;637}638}639String path = getDisplayNameOf(parentIShellFolder, relativePIDL,640SHGDN_FORPARSING);641return path;642}643644private static String resolveLibrary(String path) {645// if this is a library its default save location is taken as a path646// this is a temp fix until java.io starts support Libraries647if( path != null && path.startsWith("::{") &&648path.toLowerCase().endsWith(".library-ms")) {649for (KnownFolderDefinition kf : KnownLibraries.INSTANCE) {650if (path.toLowerCase().endsWith(651"\\" + kf.relativePath.toLowerCase()) &&652path.toUpperCase().startsWith(653kf.parsingName.substring(0, 40).toUpperCase())) {654return kf.saveLocation;655}656}657}658return null;659}660661// Needs to be accessible to Win32ShellFolderManager2662static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {663String path = invoke(new Callable<String>() {664public String call() throws IOException {665return getFileSystemPath0(csidl);666}667}, IOException.class);668if (path != null) {669@SuppressWarnings("removal")670SecurityManager security = System.getSecurityManager();671if (security != null) {672security.checkRead(path);673}674}675return path;676}677678// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details679private static native String getFileSystemPath0(int csidl) throws IOException;680681// Return whether the path is a network root.682// Path is assumed to be non-null683private static boolean isNetworkRoot(String path) {684return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));685}686687/**688* @return The parent shell folder of this shell folder, null if689* there is no parent690*/691public File getParentFile() {692return parent;693}694695public boolean isDirectory() {696if (isDir == null) {697// Folders with SFGAO_BROWSABLE have "shell extension" handlers and are698// not traversable in JFileChooser.699if (hasAttribute(ATTRIB_FOLDER) && !hasAttribute(ATTRIB_BROWSABLE)) {700isDir = Boolean.TRUE;701} else if (isLink()) {702ShellFolder linkLocation = getLinkLocation(false);703isDir = Boolean.valueOf(linkLocation != null && linkLocation.isDirectory());704} else {705isDir = Boolean.FALSE;706}707}708return isDir.booleanValue();709}710711/*712* Functions for enumerating an IShellFolder's children713*/714// Returns an IEnumIDList interface for an IShellFolder. The value715// returned must be released using releaseEnumObjects().716private long getEnumObjects(final boolean includeHiddenFiles) throws InterruptedException {717return invoke(new Callable<Long>() {718public Long call() {719boolean isDesktop = disposer.pIShellFolder == getDesktopIShellFolder();720721return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);722}723}, RuntimeException.class);724}725726// Returns an IEnumIDList interface for an IShellFolder. The value727// returned must be released using releaseEnumObjects().728// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details729private native long getEnumObjects(long pIShellFolder, boolean isDesktop,730boolean includeHiddenFiles);731// Returns the next sequential child as a relative PIDL732// from an IEnumIDList interface. The value returned must733// be released using releasePIDL().734// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details735private native long getNextChild(long pEnumObjects);736// Releases the IEnumIDList interface737// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details738private native void releaseEnumObjects(long pEnumObjects);739740// Returns the IShellFolder of a child from a parent IShellFolder741// and a relative PIDL. The value returned must be released742// using releaseIShellFolder().743// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details744private static native long bindToObject(long parentIShellFolder, long pIDL);745746/**747* @return An array of shell folders that are children of this shell folder748* object. The array will be empty if the folder is empty. Returns749* {@code null} if this shellfolder does not denote a directory.750*/751public File[] listFiles(final boolean includeHiddenFiles) {752@SuppressWarnings("removal")753SecurityManager security = System.getSecurityManager();754if (security != null) {755security.checkRead(getPath());756}757758try {759File[] files = invoke(new Callable<File[]>() {760public File[] call() throws InterruptedException {761if (!isDirectory()) {762return null;763}764// Links to directories are not directories and cannot be parents.765// This does not apply to folders in My Network Places (NetHood)766// because they are both links and real directories!767if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {768return new File[0];769}770771Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();772Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();773774// If we are a directory, we have a parent and (at least) a775// relative PIDL. We must first ensure we are bound to the776// parent so we have an IShellFolder to query.777long pIShellFolder = getIShellFolder();778// Now we can enumerate the objects in this folder.779ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();780long pEnumObjects = getEnumObjects(includeHiddenFiles);781if (pEnumObjects != 0) {782try {783long childPIDL;784int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;785do {786childPIDL = getNextChild(pEnumObjects);787boolean releasePIDL = true;788if (childPIDL != 0 &&789(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {790Win32ShellFolder2 childFolder;791if (Win32ShellFolder2.this.equals(desktop)792&& personal != null793&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {794childFolder = personal;795} else {796childFolder = createShellFolder(Win32ShellFolder2.this, childPIDL);797releasePIDL = false;798}799list.add(childFolder);800}801if (releasePIDL) {802releasePIDL(childPIDL);803}804} while (childPIDL != 0 && !Thread.currentThread().isInterrupted());805} finally {806releaseEnumObjects(pEnumObjects);807}808}809return Thread.currentThread().isInterrupted()810? new File[0]811: list.toArray(new ShellFolder[list.size()]);812}813}, InterruptedException.class);814815return Win32ShellFolderManager2.checkFiles(files);816} catch (InterruptedException e) {817return new File[0];818}819}820821822/**823* Look for (possibly special) child folder by it's path824*825* @return The child shellfolder, or null if not found.826*/827Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {828return invoke(new Callable<Win32ShellFolder2>() {829public Win32ShellFolder2 call() throws InterruptedException {830long pIShellFolder = getIShellFolder();831long pEnumObjects = getEnumObjects(true);832Win32ShellFolder2 child = null;833long childPIDL;834835while ((childPIDL = getNextChild(pEnumObjects)) != 0) {836if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {837String path = getFileSystemPath(pIShellFolder, childPIDL);838if(isLib) path = resolveLibrary( path );839if (path != null && path.equalsIgnoreCase(filePath)) {840long childIShellFolder = bindToObject(pIShellFolder, childPIDL);841child = new Win32ShellFolder2(Win32ShellFolder2.this,842childIShellFolder, childPIDL, path, isLib);843break;844}845}846releasePIDL(childPIDL);847}848releaseEnumObjects(pEnumObjects);849return child;850}851}, InterruptedException.class);852}853854private volatile Boolean cachedIsLink;855856/**857* @return Whether this shell folder is a link858*/859public boolean isLink() {860if (cachedIsLink == null) {861cachedIsLink = hasAttribute(ATTRIB_LINK);862}863864return cachedIsLink;865}866867/**868* @return Whether this shell folder is marked as hidden869*/870public boolean isHidden() {871return hasAttribute(ATTRIB_HIDDEN);872}873874875// Return the link location of a shell folder876// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details877private static native long getLinkLocation(long parentIShellFolder,878long relativePIDL, boolean resolve);879880/**881* @return The shell folder linked to by this shell folder, or null882* if this shell folder is not a link or is a broken or invalid link883*/884public ShellFolder getLinkLocation() {885return getLinkLocation(true);886}887888private Win32ShellFolder2 getLinkLocation(final boolean resolve) {889return invoke(new Callable<Win32ShellFolder2>() {890public Win32ShellFolder2 call() {891if (!isLink()) {892return null;893}894895Win32ShellFolder2 location = null;896long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),897getRelativePIDL(), resolve);898if (linkLocationPIDL != 0) {899try {900location =901Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),902linkLocationPIDL);903} catch (InterruptedException e) {904// Return null905} catch (InternalError e) {906// Could be a link to a non-bindable object, such as a network connection907// TODO: getIShellFolder() should throw FileNotFoundException instead908}909}910return location;911}912});913}914915// Parse a display name into a PIDL relative to the current IShellFolder.916long parseDisplayName(final String name) throws IOException, InterruptedException {917return invoke(new Callable<Long>() {918public Long call() throws IOException {919return parseDisplayName0(getIShellFolder(), name);920}921}, IOException.class);922}923924// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details925private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;926927// Return the display name of a shell folder928// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details929private static native String getDisplayNameOf(long parentIShellFolder,930long relativePIDL,931int attrs);932933// Returns data of all Known Folders registered in the system934private static native KnownFolderDefinition[] loadKnownFolders();935936/**937* @return The name used to display this shell folder938*/939public String getDisplayName() {940if (displayName == null) {941displayName =942invoke(new Callable<String>() {943public String call() {944return getDisplayNameOf(getParentIShellFolder(),945getRelativePIDL(), SHGDN_NORMAL);946}947});948}949return displayName;950}951952// Return the folder type of a shell folder953// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details954private static native String getFolderType(long pIDL);955956/**957* @return The type of shell folder as a string958*/959public String getFolderType() {960if (folderType == null) {961final long absolutePIDL = getAbsolutePIDL();962folderType =963invoke(new Callable<String>() {964public String call() {965return getFolderType(absolutePIDL);966}967});968}969return folderType;970}971972// Return the executable type of a file system shell folder973private native String getExecutableType(String path);974975/**976* @return The executable type as a string977*/978public String getExecutableType() {979if (!isFileSystem()) {980return null;981}982return getExecutableType(getAbsolutePath());983}984985986987// Icons988989private static Map<Integer, Image> smallSystemImages = new HashMap<>();990private static Map<Integer, Image> largeSystemImages = new HashMap<>();991private static Map<Integer, Image> smallLinkedSystemImages = new HashMap<>();992private static Map<Integer, Image> largeLinkedSystemImages = new HashMap<>();993994// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details995private static native long getIShellIcon(long pIShellFolder);996997// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details998private static native int getIconIndex(long parentIShellIcon, long relativePIDL);9991000// Return the icon of a file system shell folder in the form of an HICON1001private static native long getIcon(String absolutePath, boolean getLargeIcon);10021003// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1004private static native long extractIcon(long parentIShellFolder, long relativePIDL,1005int size, boolean getDefaultIcon);10061007// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1008private static native boolean hiResIconAvailable(long parentIShellFolder, long relativePIDL);10091010// Returns an icon from the Windows system icon list in the form of an HICON1011private static native long getSystemIcon(int iconID);1012private static native long getIconResource(String libName, int iconID,1013int cxDesired, int cyDesired);10141015// Return the bits from an HICON. This has a side effect of setting1016// the imageHash variable for efficient caching / comparing.1017private static native int[] getIconBits(long hIcon);1018// Dispose the HICON1019private static native void disposeIcon(long hIcon);10201021// Get buttons from native toolbar implementation.1022static native int[] getStandardViewButton0(int iconIndex, boolean small);10231024// Should be called from the COM thread1025private long getIShellIcon() {1026if (pIShellIcon == -1L) {1027pIShellIcon = getIShellIcon(getIShellFolder());1028}10291030return pIShellIcon;1031}10321033private static Image makeIcon(long hIcon) {1034if (hIcon != 0L && hIcon != -1L) {1035// Get the bits. This has the side effect of setting the imageHash value for this object.1036final int[] iconBits = getIconBits(hIcon);1037if (iconBits != null) {1038// icons are always square1039final int iconSize = (int) Math.sqrt(iconBits.length);1040final BufferedImage img =1041new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB);1042img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize);1043return img;1044}1045}1046return null;1047}104810491050/**1051* @return The icon image used to display this shell folder1052*/1053public Image getIcon(final boolean getLargeIcon) {1054Image icon = getLargeIcon ? largeIcon : smallIcon;1055int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;1056if (icon == null) {1057icon =1058invoke(new Callable<Image>() {1059public Image call() {1060Image newIcon = null;1061Image newIcon2 = null;1062if (isLink()) {1063Win32ShellFolder2 folder = getLinkLocation(false);1064if (folder != null && folder.isLibrary()) {1065return folder.getIcon(getLargeIcon);1066}1067}1068if (isFileSystem() || isLibrary()) {1069long parentIShellIcon = (parent != null)1070? ((Win32ShellFolder2) parent).getIShellIcon()1071: 0L;1072long relativePIDL = getRelativePIDL();10731074// These are cached per type (using the index in the system image list)1075int index = getIconIndex(parentIShellIcon, relativePIDL);1076if (index > 0) {1077Map<Integer, Image> imageCache;1078if (isLink()) {1079imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;1080} else {1081imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;1082}1083newIcon = imageCache.get(Integer.valueOf(index));1084if (newIcon == null) {1085long hIcon = getIcon(getAbsolutePath(), getLargeIcon);1086newIcon = makeIcon(hIcon);1087disposeIcon(hIcon);1088if (newIcon != null) {1089imageCache.put(Integer.valueOf(index), newIcon);1090}1091}1092if (newIcon != null) {1093if (isLink()) {1094imageCache = getLargeIcon ? smallLinkedSystemImages1095: largeLinkedSystemImages;1096} else {1097imageCache = getLargeIcon ? smallSystemImages : largeSystemImages;1098}1099newIcon2 = imageCache.get(index);1100if (newIcon2 == null) {1101long hIcon = getIcon(getAbsolutePath(), !getLargeIcon);1102newIcon2 = makeIcon(hIcon);1103disposeIcon(hIcon);1104}1105}11061107if (newIcon2 != null) {1108Map<Integer, Image> bothIcons = new HashMap<>(2);1109bothIcons.put(getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon);1110bothIcons.put(getLargeIcon ? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2);1111newIcon = new MultiResolutionIconImage(getLargeIcon ? LARGE_ICON_SIZE1112: SMALL_ICON_SIZE, bothIcons);1113}1114}1115}11161117if (hiResIconAvailable(getParentIShellFolder(), getRelativePIDL()) || newIcon == null) {1118int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;1119newIcon = getIcon(size, size);1120}11211122if (newIcon == null) {1123newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);1124}1125return newIcon;1126}1127});1128}1129return icon;1130}11311132/**1133* @return The icon image of specified size used to display this shell folder1134*/1135public Image getIcon(int width, int height) {1136int size = Math.max(width, height);1137return invoke(() -> {1138Image newIcon = null;1139if (isLink()) {1140Win32ShellFolder2 folder = getLinkLocation(false);1141if (folder != null && folder.isLibrary()) {1142return folder.getIcon(size, size);1143}1144}1145Map<Integer, Image> multiResolutionIcon = new HashMap<>();1146int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0;1147int increment = size > MAX_QUALITY_ICON ? -1 : 1;1148int end = size > MAX_QUALITY_ICON ? -1 : ICON_RESOLUTIONS.length;1149for (int i = start; i != end; i += increment) {1150int s = ICON_RESOLUTIONS[i];1151if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON1152|| (s >= size && s <= size*2)) {1153long hIcon = extractIcon(getParentIShellFolder(),1154getRelativePIDL(), s, false);11551156// E_PENDING: loading can take time so get the default1157if (hIcon <= 0) {1158hIcon = extractIcon(getParentIShellFolder(),1159getRelativePIDL(), s, true);1160if (hIcon <= 0) {1161if (isDirectory()) {1162return getShell32Icon(FOLDER_ICON_ID, size);1163} else {1164return getShell32Icon(FILE_ICON_ID, size);1165}1166}1167}1168newIcon = makeIcon(hIcon);1169disposeIcon(hIcon);11701171multiResolutionIcon.put(s, newIcon);1172if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON) {1173break;1174}1175}1176}1177return new MultiResolutionIconImage(size, multiResolutionIcon);1178});1179}11801181/**1182* Gets an icon from the Windows system icon list as an {@code Image}1183*/1184static Image getSystemIcon(SystemIcon iconType) {1185long hIcon = getSystemIcon(iconType.getIconID());1186Image icon = makeIcon(hIcon);1187if (LARGE_ICON_SIZE != icon.getWidth(null)) {1188icon = new MultiResolutionIconImage(LARGE_ICON_SIZE, icon);1189}1190disposeIcon(hIcon);1191return icon;1192}11931194/**1195* Gets an icon from the Windows system icon list as an {@code Image}1196*/1197static Image getShell32Icon(int iconID, int size) {1198long hIcon = getIconResource("shell32.dll", iconID, size, size);1199if (hIcon != 0) {1200Image icon = makeIcon(hIcon);1201if (size != icon.getWidth(null)) {1202icon = new MultiResolutionIconImage(size, icon);1203}1204disposeIcon(hIcon);1205return icon;1206}1207return null;1208}12091210/**1211* Returns the canonical form of this abstract pathname. Equivalent to1212* <code>new Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.1213*1214* @see java.io.File#getCanonicalFile1215*/1216public File getCanonicalFile() throws IOException {1217return this;1218}12191220/*1221* Indicates whether this is a special folder (includes My Documents)1222*/1223public boolean isSpecial() {1224return isPersonal || !isFileSystem() || (this == getDesktop());1225}12261227/**1228* Compares this object with the specified object for order.1229*1230* @see sun.awt.shell.ShellFolder#compareTo(File)1231*/1232public int compareTo(File file2) {1233if (!(file2 instanceof Win32ShellFolder2)) {1234if (isFileSystem() && !isSpecial()) {1235return super.compareTo(file2);1236} else {1237return -1; // Non-file shellfolders sort before files1238}1239}1240return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);1241}12421243// native constants from commctrl.h1244private static final int LVCFMT_LEFT = 0;1245private static final int LVCFMT_RIGHT = 1;1246private static final int LVCFMT_CENTER = 2;12471248public ShellFolderColumnInfo[] getFolderColumns() {1249ShellFolder library = resolveLibrary();1250if (library != null) return library.getFolderColumns();1251return invoke(new Callable<ShellFolderColumnInfo[]>() {1252public ShellFolderColumnInfo[] call() {1253ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());12541255if (columns != null) {1256List<ShellFolderColumnInfo> notNullColumns =1257new ArrayList<ShellFolderColumnInfo>();1258for (int i = 0; i < columns.length; i++) {1259ShellFolderColumnInfo column = columns[i];1260if (column != null) {1261column.setAlignment(column.getAlignment() == LVCFMT_RIGHT1262? SwingConstants.RIGHT1263: column.getAlignment() == LVCFMT_CENTER1264? SwingConstants.CENTER1265: SwingConstants.LEADING);12661267column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));12681269notNullColumns.add(column);1270}1271}1272columns = new ShellFolderColumnInfo[notNullColumns.size()];1273notNullColumns.toArray(columns);1274}1275return columns;1276}1277});1278}12791280public Object getFolderColumnValue(final int column) {1281if(!isLibrary()) {1282ShellFolder library = resolveLibrary();1283if (library != null) return library.getFolderColumnValue(column);1284}1285return invoke(new Callable<Object>() {1286public Object call() {1287return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);1288}1289});1290}12911292boolean isLibrary() {1293return isLib;1294}12951296private ShellFolder resolveLibrary() {1297for (ShellFolder f = this; f != null; f = f.parent) {1298if (!f.isFileSystem()) {1299if (f instanceof Win32ShellFolder2 &&1300((Win32ShellFolder2)f).isLibrary()) {1301try {1302return getShellFolder(new File(getPath()));1303} catch (FileNotFoundException e) {1304}1305}1306break;1307}1308}1309return null;1310}13111312// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1313private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);13141315// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1316private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);13171318// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1319private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);132013211322public void sortChildren(final List<? extends File> files) {1323// To avoid loads of synchronizations with Invoker and improve performance we1324// synchronize the whole code of the sort method once1325invoke(new Callable<Void>() {1326public Void call() {1327Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));13281329return null;1330}1331});1332}13331334private static class ColumnComparator implements Comparator<File> {1335private final Win32ShellFolder2 shellFolder;13361337private final int columnIdx;13381339public ColumnComparator(Win32ShellFolder2 shellFolder, int columnIdx) {1340this.shellFolder = shellFolder;1341this.columnIdx = columnIdx;1342}13431344// compares 2 objects within this folder by the specified column1345public int compare(final File o, final File o1) {1346Integer result = invoke(new Callable<Integer>() {1347public Integer call() {1348if (o instanceof Win32ShellFolder21349&& o1 instanceof Win32ShellFolder2) {1350// delegates comparison to native method1351return compareIDsByColumn(shellFolder.getIShellFolder(),1352((Win32ShellFolder2) o).getRelativePIDL(),1353((Win32ShellFolder2) o1).getRelativePIDL(),1354columnIdx);1355}1356return 0;1357}1358});13591360return result == null ? 0 : result;1361}1362}13631364// Extracts libraries and their default save locations from Known Folders list1365private static List<KnownFolderDefinition> getLibraries() {1366return invoke(new Callable<List<KnownFolderDefinition>>() {1367@Override1368public List<KnownFolderDefinition> call() throws Exception {1369KnownFolderDefinition[] all = loadKnownFolders();1370List<KnownFolderDefinition> folders = new ArrayList<>();1371if (all != null) {1372for (KnownFolderDefinition kf : all) {1373if (kf.relativePath == null || kf.parsingName == null ||1374kf.saveLocation == null) {1375continue;1376}1377folders.add(kf);1378}1379}1380return folders;1381}1382});1383}13841385static class MultiResolutionIconImage extends AbstractMultiResolutionImage {1386final int baseSize;1387final Map<Integer, Image> resolutionVariants = new HashMap<>();13881389public MultiResolutionIconImage(int baseSize, Map<Integer, Image> resolutionVariants) {1390this.baseSize = baseSize;1391this.resolutionVariants.putAll(resolutionVariants);1392}13931394public MultiResolutionIconImage(int baseSize, Image image) {1395this.baseSize = baseSize;1396this.resolutionVariants.put(baseSize, image);1397}13981399@Override1400public int getWidth(ImageObserver observer) {1401return baseSize;1402}14031404@Override1405public int getHeight(ImageObserver observer) {1406return baseSize;1407}14081409@Override1410protected Image getBaseImage() {1411return getResolutionVariant(baseSize, baseSize);1412}14131414@Override1415public Image getResolutionVariant(double width, double height) {1416int dist = 0;1417Image retVal = null;1418// We only care about width since we don't support non-rectangular icons1419int w = (int) width;1420int retindex = 0;1421for (Integer i : resolutionVariants.keySet()) {1422if (retVal == null || dist > Math.abs(i - w)1423|| (dist == Math.abs(i - w) && i > retindex)) {1424retindex = i;1425dist = Math.abs(i - w);1426retVal = resolutionVariants.get(i);1427if (i == w) {1428break;1429}1430}1431}1432return retVal;1433}14341435@Override1436public List<Image> getResolutionVariants() {1437return Collections.unmodifiableList(1438new ArrayList<Image>(resolutionVariants.values()));1439}1440}1441}144214431444