提交 26b463b3 编写于 作者: R rupashka

6491795: COM should be initialized for Shell API calls in ShellFolder2.cpp

Reviewed-by: peterz, loneid
上级 5f55e0e8
...@@ -27,6 +27,7 @@ package javax.swing.plaf.basic; ...@@ -27,6 +27,7 @@ package javax.swing.plaf.basic;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.*; import javax.swing.filechooser.*;
import javax.swing.event.*; import javax.swing.event.*;
...@@ -223,113 +224,115 @@ public class BasicDirectoryModel extends AbstractListModel implements PropertyCh ...@@ -223,113 +224,115 @@ public class BasicDirectoryModel extends AbstractListModel implements PropertyCh
this.fid = fid; this.fid = fid;
} }
private void invokeLater(DoChangeContents runnable) {
runnables.addElement(runnable);
SwingUtilities.invokeLater(runnable);
}
public void run() { public void run() {
run0(); run0();
setBusy(false, fid); setBusy(false, fid);
} }
public void run0() { public void run0() {
FileSystemView fileSystem = filechooser.getFileSystemView(); DoChangeContents doChangeContents = ShellFolder.getInvoker().invoke(new Callable<DoChangeContents>() {
public DoChangeContents call() throws Exception {
FileSystemView fileSystem = filechooser.getFileSystemView();
File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled()); File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
Vector<File> acceptsList = new Vector<File>(); Vector<File> acceptsList = new Vector<File>();
if (isInterrupted()) { if (isInterrupted()) {
return; return null;
} }
// run through the file list, add directories and selectable files to fileCache // run through the file list, add directories and selectable files to fileCache
for (File file : list) { for (File file : list) {
if (filechooser.accept(file)) { if (filechooser.accept(file)) {
acceptsList.addElement(file); acceptsList.addElement(file);
} }
} }
if (isInterrupted()) { if (isInterrupted()) {
return; return null;
} }
// First sort alphabetically by filename // First sort alphabetically by filename
sort(acceptsList); sort(acceptsList);
Vector<File> newDirectories = new Vector<File>(50); Vector<File> newDirectories = new Vector<File>(50);
Vector<File> newFiles = new Vector<File>(); Vector<File> newFiles = new Vector<File>();
// run through list grabbing directories in chunks of ten // run through list grabbing directories in chunks of ten
for(int i = 0; i < acceptsList.size(); i++) { for (int i = 0; i < acceptsList.size(); i++) {
File f = acceptsList.elementAt(i); File f = acceptsList.elementAt(i);
boolean isTraversable = filechooser.isTraversable(f); boolean isTraversable = filechooser.isTraversable(f);
if (isTraversable) { if (isTraversable) {
newDirectories.addElement(f); newDirectories.addElement(f);
} else if (!isTraversable && filechooser.isFileSelectionEnabled()) { } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
newFiles.addElement(f); newFiles.addElement(f);
} }
if(isInterrupted()) { if (isInterrupted()) {
return; return null;
} }
} }
Vector<File> newFileCache = new Vector<File>(newDirectories); Vector<File> newFileCache = new Vector<File>(newDirectories);
newFileCache.addAll(newFiles); newFileCache.addAll(newFiles);
int newSize = newFileCache.size(); int newSize = newFileCache.size();
int oldSize = fileCache.size(); int oldSize = fileCache.size();
if (newSize > oldSize) { if (newSize > oldSize) {
//see if interval is added //see if interval is added
int start = oldSize; int start = oldSize;
int end = newSize; int end = newSize;
for (int i = 0; i < oldSize; i++) { for (int i = 0; i < oldSize; i++) {
if (!newFileCache.get(i).equals(fileCache.get(i))) { if (!newFileCache.get(i).equals(fileCache.get(i))) {
start = i; start = i;
for (int j = i; j < newSize; j++) { for (int j = i; j < newSize; j++) {
if (newFileCache.get(j).equals(fileCache.get(i))) { if (newFileCache.get(j).equals(fileCache.get(i))) {
end = j; end = j;
break;
}
}
break; break;
} }
} }
break; if (start >= 0 && end > start
} && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
} if (isInterrupted()) {
if (start >= 0 && end > start return null;
&& newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) { }
if(isInterrupted()) { return new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid);
return; }
} } else if (newSize < oldSize) {
invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid)); //see if interval is removed
newFileCache = null; int start = -1;
} int end = -1;
} else if (newSize < oldSize) { for (int i = 0; i < newSize; i++) {
//see if interval is removed if (!newFileCache.get(i).equals(fileCache.get(i))) {
int start = -1; start = i;
int end = -1; end = i + oldSize - newSize;
for (int i = 0; i < newSize; i++) { break;
if (!newFileCache.get(i).equals(fileCache.get(i))) { }
start = i; }
end = i + oldSize - newSize; if (start >= 0 && end > start
break; && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
if (isInterrupted()) {
return null;
}
return new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)), start, fid);
}
} }
} if (!fileCache.equals(newFileCache)) {
if (start >= 0 && end > start if (isInterrupted()) {
&& fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) { cancelRunnables(runnables);
if(isInterrupted()) { }
return; return new DoChangeContents(newFileCache, 0, fileCache, 0, fid);
} }
invokeLater(new DoChangeContents(null, 0, new Vector<File>(fileCache.subList(start, end)), return null;
start, fid));
newFileCache = null;
}
}
if (newFileCache != null && !fileCache.equals(newFileCache)) {
if (isInterrupted()) {
cancelRunnables(runnables);
} }
invokeLater(new DoChangeContents(newFileCache, 0, fileCache, 0, fid)); });
if (doChangeContents != null) {
runnables.addElement(doChangeContents);
SwingUtilities.invokeLater(doChangeContents);
} }
} }
......
...@@ -31,6 +31,7 @@ import java.awt.Toolkit; ...@@ -31,6 +31,7 @@ import java.awt.Toolkit;
import java.io.*; import java.io.*;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
/** /**
* @author Michael Martak * @author Michael Martak
...@@ -461,6 +462,35 @@ public abstract class ShellFolder extends File { ...@@ -461,6 +462,35 @@ public abstract class ShellFolder extends File {
return null; return null;
} }
private static Invoker invoker;
/**
* Provides the single access point to the {@link Invoker}. It is guaranteed that the value
* returned by this method will be always the same.
*
* @return the singleton instance of {@link Invoker}
*/
public static Invoker getInvoker() {
if (invoker == null) {
invoker = shellFolderManager.createInvoker();
}
return invoker;
}
/**
* Interface allowing to invoke tasks in different environments on different platforms.
*/
public static interface Invoker {
/**
* Invokes a callable task. If the {@code task} throws a checked exception,
* it will be wrapped into a {@link RuntimeException}
*
* @param task a task to invoke
* @return the result of {@code task}'s invokation
*/
<T> T invoke(Callable<T> task);
}
/** /**
* Provides a default comparator for the default column set * Provides a default comparator for the default column set
*/ */
......
...@@ -27,6 +27,7 @@ package sun.awt.shell; ...@@ -27,6 +27,7 @@ package sun.awt.shell;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.concurrent.Callable;
/** /**
* @author Michael Martak * @author Michael Martak
...@@ -96,9 +97,23 @@ class ShellFolderManager { ...@@ -96,9 +97,23 @@ class ShellFolderManager {
} }
public boolean isFileSystemRoot(File dir) { public boolean isFileSystemRoot(File dir) {
if (dir instanceof ShellFolder && !((ShellFolder)dir).isFileSystem()) { if (dir instanceof ShellFolder && !((ShellFolder) dir).isFileSystem()) {
return false; return false;
} }
return (dir.getParentFile() == null); return (dir.getParentFile() == null);
} }
protected ShellFolder.Invoker createInvoker() {
return new DirectInvoker();
}
private static class DirectInvoker implements ShellFolder.Invoker {
public <T> T invoke(Callable<T> task) {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
} }
...@@ -34,6 +34,7 @@ import java.text.DateFormat; ...@@ -34,6 +34,7 @@ import java.text.DateFormat;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
...@@ -900,6 +901,16 @@ public class FilePane extends JPanel implements PropertyChangeListener { ...@@ -900,6 +901,16 @@ public class FilePane extends JPanel implements PropertyChangeListener {
} }
} }
@Override
public void sort() {
ShellFolder.getInvoker().invoke(new Callable<Void>() {
public Void call() throws Exception {
DetailsTableRowSorter.super.sort();
return null;
}
});
}
public void modelStructureChanged() { public void modelStructureChanged() {
super.modelStructureChanged(); super.modelStructureChanged();
updateComparators(detailsTableModel.getColumns()); updateComparators(detailsTableModel.getColumns());
......
...@@ -32,6 +32,7 @@ import java.io.File; ...@@ -32,6 +32,7 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.*;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
// NOTE: This class supersedes Win32ShellFolder, which was removed from // NOTE: This class supersedes Win32ShellFolder, which was removed from
...@@ -184,15 +185,20 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -184,15 +185,20 @@ final class Win32ShellFolder2 extends ShellFolder {
boolean disposed; boolean disposed;
public void dispose() { public void dispose() {
if (disposed) return; if (disposed) return;
if (relativePIDL != 0) { ShellFolder.getInvoker().invoke(new Callable<Void>() {
releasePIDL(relativePIDL); public Void call() throws Exception {
} if (relativePIDL != 0) {
if (absolutePIDL != 0) { releasePIDL(relativePIDL);
releasePIDL(absolutePIDL); }
} if (absolutePIDL != 0) {
if (pIShellFolder != 0) { releasePIDL(absolutePIDL);
releaseIShellFolder(pIShellFolder); }
} if (pIShellFolder != 0) {
releaseIShellFolder(pIShellFolder);
}
return null;
}
});
disposed = true; disposed = true;
} }
} }
...@@ -218,50 +224,59 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -218,50 +224,59 @@ final class Win32ShellFolder2 extends ShellFolder {
*/ */
private boolean isPersonal; private boolean isPersonal;
private static String composePathForCsidl(int csidl) throws IOException {
String path = getFileSystemPath(csidl);
return path == null
? ("ShellFolder: 0x" + Integer.toHexString(csidl))
: path;
}
/** /**
* Create a system special shell folder, such as the * Create a system special shell folder, such as the
* desktop or Network Neighborhood. * desktop or Network Neighborhood.
*/ */
Win32ShellFolder2(int csidl) throws IOException { Win32ShellFolder2(final int csidl) throws IOException {
// Desktop is parent of DRIVES and NETWORK, not necessarily // Desktop is parent of DRIVES and NETWORK, not necessarily
// other special shell folders. // other special shell folders.
super(null, super(null, composePathForCsidl(csidl));
(getFileSystemPath(csidl) == null) ShellFolder.getInvoker().invoke(new Callable<Void>() {
? ("ShellFolder: 0x"+Integer.toHexString(csidl)) : getFileSystemPath(csidl)); public Void call() throws Exception {
if (csidl == DESKTOP) { if (csidl == DESKTOP) {
initDesktop(); initDesktop();
} else {
initSpecial(getDesktop().getIShellFolder(), csidl);
// At this point, the native method initSpecial() has set our relativePIDL
// relative to the Desktop, which may not be our immediate parent. We need
// to traverse this ID list and break it into a chain of shell folders from
// the top, with each one having an immediate parent and a relativePIDL
// relative to that parent.
long pIDL = disposer.relativePIDL;
parent = getDesktop();
while (pIDL != 0) {
// Get a child pidl relative to 'parent'
long childPIDL = copyFirstPIDLEntry(pIDL);
if (childPIDL != 0) {
// Get a handle to the the rest of the ID list
// i,e, parent's grandchilren and down
pIDL = getNextPIDLEntry(pIDL);
if (pIDL != 0) {
// Now we know that parent isn't immediate to 'this' because it
// has a continued ID list. Create a shell folder for this child
// pidl and make it the new 'parent'.
parent = new Win32ShellFolder2((Win32ShellFolder2)parent, childPIDL);
} else {
// No grandchildren means we have arrived at the parent of 'this',
// and childPIDL is directly relative to parent.
disposer.relativePIDL = childPIDL;
}
} else { } else {
break; initSpecial(getDesktop().getIShellFolder(), csidl);
// At this point, the native method initSpecial() has set our relativePIDL
// relative to the Desktop, which may not be our immediate parent. We need
// to traverse this ID list and break it into a chain of shell folders from
// the top, with each one having an immediate parent and a relativePIDL
// relative to that parent.
long pIDL = disposer.relativePIDL;
parent = getDesktop();
while (pIDL != 0) {
// Get a child pidl relative to 'parent'
long childPIDL = copyFirstPIDLEntry(pIDL);
if (childPIDL != 0) {
// Get a handle to the the rest of the ID list
// i,e, parent's grandchilren and down
pIDL = getNextPIDLEntry(pIDL);
if (pIDL != 0) {
// Now we know that parent isn't immediate to 'this' because it
// has a continued ID list. Create a shell folder for this child
// pidl and make it the new 'parent'.
parent = new Win32ShellFolder2((Win32ShellFolder2) parent, childPIDL);
} else {
// No grandchildren means we have arrived at the parent of 'this',
// and childPIDL is directly relative to parent.
disposer.relativePIDL = childPIDL;
}
} else {
break;
}
}
} }
return null;
} }
} });
sun.java2d.Disposer.addRecord(this, disposer); sun.java2d.Disposer.addRecord(this, disposer);
} }
...@@ -281,17 +296,26 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -281,17 +296,26 @@ final class Win32ShellFolder2 extends ShellFolder {
/** /**
* Creates a shell folder with a parent and relative PIDL * Creates a shell folder with a parent and relative PIDL
*/ */
Win32ShellFolder2(Win32ShellFolder2 parent, long relativePIDL) { Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) {
super(parent, getFileSystemPath(parent.getIShellFolder(), relativePIDL)); super(parent,
ShellFolder.getInvoker().invoke(new Callable<String>() {
public String call() throws Exception {
return getFileSystemPath(parent.getIShellFolder(), relativePIDL);
}
})
);
this.disposer.relativePIDL = relativePIDL; this.disposer.relativePIDL = relativePIDL;
getAbsolutePath(); getAbsolutePath();
sun.java2d.Disposer.addRecord(this, disposer); sun.java2d.Disposer.addRecord(this, disposer);
} }
// Initializes the desktop shell folder // Initializes the desktop shell folder
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native void initDesktop(); private native void initDesktop();
// Initializes a special, non-file system shell folder // Initializes a special, non-file system shell folder
// from one of the above constants // from one of the above constants
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native void initSpecial(long desktopIShellFolder, int csidl); private native void initSpecial(long desktopIShellFolder, int csidl);
/** Marks this folder as being the My Documents (Personal) folder */ /** Marks this folder as being the My Documents (Personal) folder */
...@@ -311,26 +335,30 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -311,26 +335,30 @@ final class Win32ShellFolder2 extends ShellFolder {
* drive (normally "C:\"). * drive (normally "C:\").
*/ */
protected Object writeReplace() throws java.io.ObjectStreamException { protected Object writeReplace() throws java.io.ObjectStreamException {
if (isFileSystem()) { return ShellFolder.getInvoker().invoke(new Callable<File>() {
return new File(getPath()); public File call() throws Exception {
} else { if (isFileSystem()) {
Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives(); return new File(getPath());
if (drives != null) { } else {
File[] driveRoots = drives.listFiles(); Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
if (driveRoots != null) { if (drives != null) {
for (int i = 0; i < driveRoots.length; i++) { File[] driveRoots = drives.listFiles();
if (driveRoots[i] instanceof Win32ShellFolder2) { if (driveRoots != null) {
Win32ShellFolder2 sf = (Win32ShellFolder2)driveRoots[i]; for (int i = 0; i < driveRoots.length; i++) {
if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) { if (driveRoots[i] instanceof Win32ShellFolder2) {
return new File(sf.getPath()); Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
return new File(sf.getPath());
}
}
} }
} }
} }
// Ouch, we have no hard drives. Return something "valid" anyway.
return new File("C:\\");
} }
} }
// Ouch, we have no hard drives. Return something "valid" anyway. });
return new File("C:\\");
}
} }
...@@ -364,6 +392,7 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -364,6 +392,7 @@ final class Win32ShellFolder2 extends ShellFolder {
static native void releasePIDL(long pIDL); static native void releasePIDL(long pIDL);
// Release an IShellFolder object // Release an IShellFolder object
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native void releaseIShellFolder(long pIShellFolder); private static native void releaseIShellFolder(long pIShellFolder);
/** /**
...@@ -371,18 +400,28 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -371,18 +400,28 @@ final class Win32ShellFolder2 extends ShellFolder {
*/ */
public long getIShellFolder() { public long getIShellFolder() {
if (disposer.pIShellFolder == 0) { if (disposer.pIShellFolder == 0) {
assert(isDirectory()); disposer.pIShellFolder =
assert(parent != null); ShellFolder.getInvoker().invoke(new Callable<Long>() {
long parentIShellFolder = getParentIShellFolder(); public Long call() throws Exception {
if (parentIShellFolder == 0) { assert(isDirectory());
throw new InternalError("Parent IShellFolder was null for " + getAbsolutePath()); assert(parent != null);
} long parentIShellFolder = getParentIShellFolder();
// We are a directory with a parent and a relative PIDL. if (parentIShellFolder == 0) {
// We want to bind to the parent so we get an IShellFolder instance associated with us. throw new InternalError("Parent IShellFolder was null for "
disposer.pIShellFolder = bindToObject(parentIShellFolder, disposer.relativePIDL); + getAbsolutePath());
if (disposer.pIShellFolder == 0) { }
throw new InternalError("Unable to bind " + getAbsolutePath() + " to parent"); // We are a directory with a parent and a relative PIDL.
} // We want to bind to the parent so we get an
// IShellFolder instance associated with us.
long pIShellFolder = bindToObject(parentIShellFolder,
disposer.relativePIDL);
if (pIShellFolder == 0) {
throw new InternalError("Unable to bind "
+ getAbsolutePath() + " to parent");
}
return pIShellFolder;
}
});
} }
return disposer.pIShellFolder; return disposer.pIShellFolder;
} }
...@@ -472,24 +511,42 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -472,24 +511,42 @@ final class Win32ShellFolder2 extends ShellFolder {
return false; return false;
} }
private static boolean pidlsEqual(long pIShellFolder, long pidl1, long pidl2) { private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2) {
return (compareIDs(pIShellFolder, pidl1, pidl2) == 0); return ShellFolder.getInvoker().invoke(new Callable<Boolean>() {
public Boolean call() throws Exception {
return (compareIDs(pIShellFolder, pidl1, pidl2) == 0);
}
});
} }
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2); private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);
private Boolean cachedIsFileSystem;
/** /**
* @return Whether this is a file system shell folder * @return Whether this is a file system shell folder
*/ */
public boolean isFileSystem() { public synchronized boolean isFileSystem() {
return hasAttribute(ATTRIB_FILESYSTEM); if (cachedIsFileSystem == null) {
cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
}
return cachedIsFileSystem;
} }
/** /**
* Return whether the given attribute flag is set for this object * Return whether the given attribute flag is set for this object
*/ */
public boolean hasAttribute(int attribute) { public boolean hasAttribute(final int attribute) {
// Caching at this point doesn't seem to be cost efficient return ShellFolder.getInvoker().invoke(new Callable<Boolean>() {
return (getAttributes0(getParentIShellFolder(), getRelativePIDL(), attribute) & attribute) != 0; public Boolean call() throws Exception {
// Caching at this point doesn't seem to be cost efficient
return (getAttributes0(getParentIShellFolder(),
getRelativePIDL(), attribute)
& attribute) != 0;
}
});
} }
/** /**
...@@ -498,26 +555,42 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -498,26 +555,42 @@ final class Win32ShellFolder2 extends ShellFolder {
* Could plausibly be used for attribute caching but have to be * Could plausibly be used for attribute caching but have to be
* very careful not to touch network drives and file system roots * very careful not to touch network drives and file system roots
* with a full attrsMask * with a full attrsMask
* NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
*/ */
private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask); private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
// Return the path to the underlying file system object // Return the path to the underlying file system object
private static String getFileSystemPath(long parentIShellFolder, long relativePIDL) { private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER; return ShellFolder.getInvoker().invoke(new Callable<String>() {
if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() && public String call() throws Exception {
getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) { int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
String s = getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
getLinkLocation(parentIShellFolder, relativePIDL, false)); String s =
if (s != null && s.startsWith("\\\\")) { getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
return s; getLinkLocation(parentIShellFolder, relativePIDL, false));
if (s != null && s.startsWith("\\\\")) {
return s;
}
}
return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);
} }
} });
return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_NORMAL | SHGDN_FORPARSING);
} }
// Needs to be accessible to Win32ShellFolderManager2 // Needs to be accessible to Win32ShellFolderManager2
static native String getFileSystemPath(int csidl) throws IOException; static String getFileSystemPath(final int csidl) throws IOException {
return ShellFolder.getInvoker().invoke(new Callable<String>() {
public String call() throws Exception {
return getFileSystemPath0(csidl);
}
});
}
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native String getFileSystemPath0(int csidl) throws IOException;
// Return whether the path is a network root. // Return whether the path is a network root.
// Path is assumed to be non-null // Path is assumed to be non-null
...@@ -557,24 +630,33 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -557,24 +630,33 @@ final class Win32ShellFolder2 extends ShellFolder {
*/ */
// Returns an IEnumIDList interface for an IShellFolder. The value // Returns an IEnumIDList interface for an IShellFolder. The value
// returned must be released using releaseEnumObjects(). // returned must be released using releaseEnumObjects().
private long getEnumObjects(long pIShellFolder, boolean includeHiddenFiles) { private long getEnumObjects(long pIShellFolder, final boolean includeHiddenFiles) {
boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder()); final boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles); return ShellFolder.getInvoker().invoke(new Callable<Long>() {
public Long call() throws Exception {
return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
}
});
} }
// Returns an IEnumIDList interface for an IShellFolder. The value // Returns an IEnumIDList interface for an IShellFolder. The value
// returned must be released using releaseEnumObjects(). // returned must be released using releaseEnumObjects().
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native long getEnumObjects(long pIShellFolder, boolean isDesktop, private native long getEnumObjects(long pIShellFolder, boolean isDesktop,
boolean includeHiddenFiles); boolean includeHiddenFiles);
// Returns the next sequential child as a relative PIDL // Returns the next sequential child as a relative PIDL
// from an IEnumIDList interface. The value returned must // from an IEnumIDList interface. The value returned must
// be released using releasePIDL(). // be released using releasePIDL().
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native long getNextChild(long pEnumObjects); private native long getNextChild(long pEnumObjects);
// Releases the IEnumIDList interface // Releases the IEnumIDList interface
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native void releaseEnumObjects(long pEnumObjects); private native void releaseEnumObjects(long pEnumObjects);
// Returns the IShellFolder of a child from a parent IShellFolder // Returns the IShellFolder of a child from a parent IShellFolder
// and a relative PIDL. The value returned must be released // and a relative PIDL. The value returned must be released
// using releaseIShellFolder(). // using releaseIShellFolder().
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native long bindToObject(long parentIShellFolder, long pIDL); private static native long bindToObject(long parentIShellFolder, long pIDL);
/** /**
...@@ -582,60 +664,64 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -582,60 +664,64 @@ final class Win32ShellFolder2 extends ShellFolder {
* object. The array will be empty if the folder is empty. Returns * object. The array will be empty if the folder is empty. Returns
* <code>null</code> if this shellfolder does not denote a directory. * <code>null</code> if this shellfolder does not denote a directory.
*/ */
public File[] listFiles(boolean includeHiddenFiles) { public File[] listFiles(final boolean includeHiddenFiles) {
SecurityManager security = System.getSecurityManager(); SecurityManager security = System.getSecurityManager();
if (security != null) { if (security != null) {
security.checkRead(getPath()); security.checkRead(getPath());
} }
if (!isDirectory()) {
return null;
}
// Links to directories are not directories and cannot be parents.
// This does not apply to folders in My Network Places (NetHood)
// because they are both links and real directories!
if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
return new File[0];
}
Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop(); return ShellFolder.getInvoker().invoke(new Callable<File[]>() {
Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal(); public File[] call() throws Exception {
if (!isDirectory()) {
// If we are a directory, we have a parent and (at least) a return null;
// relative PIDL. We must first ensure we are bound to the
// parent so we have an IShellFolder to query.
long pIShellFolder = getIShellFolder();
// Now we can enumerate the objects in this folder.
ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
long pEnumObjects = getEnumObjects(pIShellFolder, includeHiddenFiles);
if (pEnumObjects != 0) {
long childPIDL;
int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
do {
if (Thread.currentThread().isInterrupted()) {
return new File[0];
} }
childPIDL = getNextChild(pEnumObjects); // Links to directories are not directories and cannot be parents.
boolean releasePIDL = true; // This does not apply to folders in My Network Places (NetHood)
if (childPIDL != 0 && // because they are both links and real directories!
(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) { if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
Win32ShellFolder2 childFolder = null; return new File[0];
if (this.equals(desktop)
&& personal != null
&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
childFolder = personal;
} else {
childFolder = new Win32ShellFolder2(this, childPIDL);
releasePIDL = false;
}
list.add(childFolder);
} }
if (releasePIDL) {
releasePIDL(childPIDL); Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();
Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();
// If we are a directory, we have a parent and (at least) a
// relative PIDL. We must first ensure we are bound to the
// parent so we have an IShellFolder to query.
long pIShellFolder = getIShellFolder();
// Now we can enumerate the objects in this folder.
ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
long pEnumObjects = getEnumObjects(pIShellFolder, includeHiddenFiles);
if (pEnumObjects != 0) {
long childPIDL;
int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
do {
childPIDL = getNextChild(pEnumObjects);
boolean releasePIDL = true;
if (childPIDL != 0 &&
(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
Win32ShellFolder2 childFolder;
if (Win32ShellFolder2.this.equals(desktop)
&& personal != null
&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
childFolder = personal;
} else {
childFolder = new Win32ShellFolder2(Win32ShellFolder2.this, childPIDL);
releasePIDL = false;
}
list.add(childFolder);
}
if (releasePIDL) {
releasePIDL(childPIDL);
}
} while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
releaseEnumObjects(pEnumObjects);
} }
} while (childPIDL != 0); return Thread.currentThread().isInterrupted()
releaseEnumObjects(pEnumObjects); ? new File[0]
} : list.toArray(new ShellFolder[list.size()]);
return list.toArray(new ShellFolder[list.size()]); }
});
} }
...@@ -644,33 +730,43 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -644,33 +730,43 @@ final class Win32ShellFolder2 extends ShellFolder {
* *
* @return The child shellfolder, or null if not found. * @return The child shellfolder, or null if not found.
*/ */
Win32ShellFolder2 getChildByPath(String filePath) { Win32ShellFolder2 getChildByPath(final String filePath) {
long pIShellFolder = getIShellFolder(); return ShellFolder.getInvoker().invoke(new Callable<Win32ShellFolder2>() {
long pEnumObjects = getEnumObjects(pIShellFolder, true); public Win32ShellFolder2 call() throws Exception {
Win32ShellFolder2 child = null; long pIShellFolder = getIShellFolder();
long childPIDL; long pEnumObjects = getEnumObjects(pIShellFolder, true);
Win32ShellFolder2 child = null;
while ((childPIDL = getNextChild(pEnumObjects)) != 0) { long childPIDL = 0;
if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
String path = getFileSystemPath(pIShellFolder, childPIDL); while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
if (path != null && path.equalsIgnoreCase(filePath)) { if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
long childIShellFolder = bindToObject(pIShellFolder, childPIDL); String path = getFileSystemPath(pIShellFolder, childPIDL);
child = new Win32ShellFolder2(this, childIShellFolder, childPIDL, path); if (path != null && path.equalsIgnoreCase(filePath)) {
break; long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
child = new Win32ShellFolder2(Win32ShellFolder2.this,
childIShellFolder, childPIDL, path);
break;
}
}
releasePIDL(childPIDL);
} }
releaseEnumObjects(pEnumObjects);
return child;
} }
releasePIDL(childPIDL); });
}
releaseEnumObjects(pEnumObjects);
return child;
} }
private Boolean cachedIsLink;
/** /**
* @return Whether this shell folder is a link * @return Whether this shell folder is a link
*/ */
public boolean isLink() { public synchronized boolean isLink() {
return hasAttribute(ATTRIB_LINK); if (cachedIsLink == null) {
cachedIsLink = hasAttribute(ATTRIB_LINK);
}
return cachedIsLink;
} }
/** /**
...@@ -682,6 +778,7 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -682,6 +778,7 @@ final class Win32ShellFolder2 extends ShellFolder {
// Return the link location of a shell folder // Return the link location of a shell folder
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native long getLinkLocation(long parentIShellFolder, private static native long getLinkLocation(long parentIShellFolder,
long relativePIDL, boolean resolve); long relativePIDL, boolean resolve);
...@@ -693,38 +790,52 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -693,38 +790,52 @@ final class Win32ShellFolder2 extends ShellFolder {
return getLinkLocation(true); return getLinkLocation(true);
} }
private ShellFolder getLinkLocation(boolean resolve) { private ShellFolder getLinkLocation(final boolean resolve) {
if (!isLink()) { return ShellFolder.getInvoker().invoke(new Callable<ShellFolder>() {
return null; public ShellFolder call() throws Exception {
} if (!isLink()) {
return null;
}
ShellFolder location = null; ShellFolder location = null;
long linkLocationPIDL = getLinkLocation(getParentIShellFolder(), long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
getRelativePIDL(), resolve); getRelativePIDL(), resolve);
if (linkLocationPIDL != 0) { if (linkLocationPIDL != 0) {
try { try {
location = location =
Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(), Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
linkLocationPIDL); linkLocationPIDL);
} catch (InternalError e) { } catch (InternalError e) {
// Could be a link to a non-bindable object, such as a network connection // Could be a link to a non-bindable object, such as a network connection
// TODO: getIShellFolder() should throw FileNotFoundException instead // TODO: getIShellFolder() should throw FileNotFoundException instead
}
}
return location;
} }
} });
return location;
} }
// Parse a display name into a PIDL relative to the current IShellFolder. // Parse a display name into a PIDL relative to the current IShellFolder.
long parseDisplayName(String name) throws FileNotFoundException { long parseDisplayName(final String name) throws FileNotFoundException {
try { try {
return parseDisplayName0(getIShellFolder(), name); return ShellFolder.getInvoker().invoke(new Callable<Long>() {
} catch (IOException e) { public Long call() throws Exception {
throw new FileNotFoundException("Could not find file " + name); return parseDisplayName0(getIShellFolder(), name);
}
});
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw new FileNotFoundException("Could not find file " + name);
}
throw e;
} }
} }
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException; private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;
// Return the display name of a shell folder // Return the display name of a shell folder
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native String getDisplayNameOf(long parentIShellFolder, private static native String getDisplayNameOf(long parentIShellFolder,
long relativePIDL, long relativePIDL,
int attrs); int attrs);
...@@ -734,12 +845,19 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -734,12 +845,19 @@ final class Win32ShellFolder2 extends ShellFolder {
*/ */
public String getDisplayName() { public String getDisplayName() {
if (displayName == null) { if (displayName == null) {
displayName = getDisplayNameOf(getParentIShellFolder(), getRelativePIDL(), SHGDN_NORMAL); displayName =
ShellFolder.getInvoker().invoke(new Callable<String>() {
public String call() throws Exception {
return getDisplayNameOf(getParentIShellFolder(),
getRelativePIDL(), SHGDN_NORMAL);
}
});
} }
return displayName; return displayName;
} }
// Return the folder type of a shell folder // Return the folder type of a shell folder
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native String getFolderType(long pIDL); private static native String getFolderType(long pIDL);
/** /**
...@@ -747,7 +865,13 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -747,7 +865,13 @@ final class Win32ShellFolder2 extends ShellFolder {
*/ */
public String getFolderType() { public String getFolderType() {
if (folderType == null) { if (folderType == null) {
folderType = getFolderType(getAbsolutePIDL()); final long absolutePIDL = getAbsolutePIDL();
folderType =
ShellFolder.getInvoker().invoke(new Callable<String>() {
public String call() throws Exception {
return getFolderType(absolutePIDL);
}
});
} }
return folderType; return folderType;
} }
...@@ -774,11 +898,16 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -774,11 +898,16 @@ final class Win32ShellFolder2 extends ShellFolder {
private static Map smallLinkedSystemImages = new HashMap(); private static Map smallLinkedSystemImages = new HashMap();
private static Map largeLinkedSystemImages = new HashMap(); private static Map largeLinkedSystemImages = new HashMap();
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native long getIShellIcon(long pIShellFolder); private static native long getIShellIcon(long pIShellFolder);
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native int getIconIndex(long parentIShellIcon, long relativePIDL); private static native int getIconIndex(long parentIShellIcon, long relativePIDL);
// Return the icon of a file system shell folder in the form of an HICON // Return the icon of a file system shell folder in the form of an HICON
private static native long getIcon(String absolutePath, boolean getLargeIcon); private static native long getIcon(String absolutePath, boolean getLargeIcon);
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native long extractIcon(long parentIShellFolder, long relativePIDL, private static native long extractIcon(long parentIShellFolder, long relativePIDL,
boolean getLargeIcon); boolean getLargeIcon);
...@@ -799,7 +928,12 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -799,7 +928,12 @@ final class Win32ShellFolder2 extends ShellFolder {
private long getIShellIcon() { private long getIShellIcon() {
if (pIShellIcon == -1L) { if (pIShellIcon == -1L) {
pIShellIcon = getIShellIcon(getIShellFolder()); pIShellIcon =
ShellFolder.getInvoker().invoke(new Callable<Long>() {
public Long call() throws Exception {
return getIShellIcon(getIShellFolder());
}
});
} }
return pIShellIcon; return pIShellIcon;
} }
...@@ -850,50 +984,60 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -850,50 +984,60 @@ final class Win32ShellFolder2 extends ShellFolder {
/** /**
* @return The icon image used to display this shell folder * @return The icon image used to display this shell folder
*/ */
public Image getIcon(boolean getLargeIcon) { public Image getIcon(final boolean getLargeIcon) {
Image icon = getLargeIcon ? largeIcon : smallIcon; Image icon = getLargeIcon ? largeIcon : smallIcon;
if (icon == null) { if (icon == null) {
long parentIShellIcon = (parent != null) ? ((Win32ShellFolder2)parent).getIShellIcon() : 0L; icon =
long relativePIDL = getRelativePIDL(); ShellFolder.getInvoker().invoke(new Callable<Image>() {
public Image call() throws Exception {
if (isFileSystem()) { Image newIcon = null;
// These are cached per type (using the index in the system image list) if (isFileSystem()) {
int index = getIconIndex(parentIShellIcon, relativePIDL); long parentIShellIcon = (parent != null)
if (index > 0) { ? ((Win32ShellFolder2) parent).getIShellIcon()
Map imageCache; : 0L;
if (isLink()) { long relativePIDL = getRelativePIDL();
imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
} else { // These are cached per type (using the index in the system image list)
imageCache = getLargeIcon ? largeSystemImages : smallSystemImages; int index = getIconIndex(parentIShellIcon, relativePIDL);
} if (index > 0) {
icon = (Image)imageCache.get(Integer.valueOf(index)); Map imageCache;
if (icon == null) { if (isLink()) {
long hIcon = getIcon(getAbsolutePath(), getLargeIcon); imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
icon = makeIcon(hIcon, getLargeIcon); } else {
disposeIcon(hIcon); imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
if (icon != null) { }
imageCache.put(Integer.valueOf(index), icon); newIcon = (Image) imageCache.get(Integer.valueOf(index));
if (newIcon == null) {
long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
newIcon = makeIcon(hIcon, getLargeIcon);
disposeIcon(hIcon);
if (newIcon != null) {
imageCache.put(Integer.valueOf(index), newIcon);
}
}
}
} }
}
}
}
if (icon == null) { if (newIcon == null) {
// These are only cached per object // These are only cached per object
long hIcon = extractIcon(getParentIShellFolder(), getRelativePIDL(), getLargeIcon); long hIcon = extractIcon(getParentIShellFolder(),
icon = makeIcon(hIcon, getLargeIcon); getRelativePIDL(), getLargeIcon);
disposeIcon(hIcon); newIcon = makeIcon(hIcon, getLargeIcon);
} disposeIcon(hIcon);
}
if (newIcon == null) {
newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);
}
return newIcon;
}
});
if (getLargeIcon) { if (getLargeIcon) {
largeIcon = icon; largeIcon = icon;
} else { } else {
smallIcon = icon; smallIcon = icon;
} }
} }
if (icon == null) {
icon = super.getIcon(getLargeIcon);
}
return icon; return icon;
} }
...@@ -969,39 +1113,50 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -969,39 +1113,50 @@ final class Win32ShellFolder2 extends ShellFolder {
private static final int LVCFMT_CENTER = 2; private static final int LVCFMT_CENTER = 2;
public ShellFolderColumnInfo[] getFolderColumns() { public ShellFolderColumnInfo[] getFolderColumns() {
ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder()); return ShellFolder.getInvoker().invoke(new Callable<ShellFolderColumnInfo[]>() {
public ShellFolderColumnInfo[] call() throws Exception {
if (columns != null) { ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
List<ShellFolderColumnInfo> notNullColumns =
new ArrayList<ShellFolderColumnInfo>(); if (columns != null) {
for (int i = 0; i < columns.length; i++) { List<ShellFolderColumnInfo> notNullColumns =
ShellFolderColumnInfo column = columns[i]; new ArrayList<ShellFolderColumnInfo>();
if (column != null) { for (int i = 0; i < columns.length; i++) {
column.setAlignment(column.getAlignment() == LVCFMT_RIGHT ShellFolderColumnInfo column = columns[i];
? SwingConstants.RIGHT if (column != null) {
: column.getAlignment() == LVCFMT_CENTER column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
? SwingConstants.CENTER ? SwingConstants.RIGHT
: SwingConstants.LEADING); : column.getAlignment() == LVCFMT_CENTER
? SwingConstants.CENTER
column.setComparator(new ColumnComparator(getIShellFolder(), i)); : SwingConstants.LEADING);
notNullColumns.add(column); column.setComparator(new ColumnComparator(getIShellFolder(), i));
notNullColumns.add(column);
}
}
columns = new ShellFolderColumnInfo[notNullColumns.size()];
notNullColumns.toArray(columns);
} }
return columns;
} }
columns = new ShellFolderColumnInfo[notNullColumns.size()]; });
notNullColumns.toArray(columns);
}
return columns;
} }
public Object getFolderColumnValue(int column) { public Object getFolderColumnValue(final int column) {
return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column); return ShellFolder.getInvoker().invoke(new Callable<Object>() {
public Object call() throws Exception {
return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
}
});
} }
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2); private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx); private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx); private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
...@@ -1020,17 +1175,20 @@ final class Win32ShellFolder2 extends ShellFolder { ...@@ -1020,17 +1175,20 @@ final class Win32ShellFolder2 extends ShellFolder {
} }
// compares 2 objects within this folder by the specified column // compares 2 objects within this folder by the specified column
public int compare(File o, File o1) { public int compare(final File o, final File o1) {
if (o instanceof Win32ShellFolder2 return ShellFolder.getInvoker().invoke(new Callable<Integer>() {
&& o1 instanceof Win32ShellFolder2) { public Integer call() throws Exception {
// delegates comparison to native method if (o instanceof Win32ShellFolder2
return compareIDsByColumn(parentIShellFolder, && o1 instanceof Win32ShellFolder2) {
((Win32ShellFolder2) o).getRelativePIDL(), // delegates comparison to native method
((Win32ShellFolder2) o1).getRelativePIDL(), return compareIDsByColumn(parentIShellFolder,
columnIdx); ((Win32ShellFolder2) o).getRelativePIDL(),
} ((Win32ShellFolder2) o1).getRelativePIDL(),
return 0; columnIdx);
}
return 0;
}
});
} }
} }
} }
...@@ -31,7 +31,10 @@ import java.io.File; ...@@ -31,7 +31,10 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*; import java.util.*;
import java.util.concurrent.*;
import sun.security.action.LoadLibraryAction; import sun.security.action.LoadLibraryAction;
import static sun.awt.shell.Win32ShellFolder2.*; import static sun.awt.shell.Win32ShellFolder2.*;
...@@ -408,4 +411,102 @@ public class Win32ShellFolderManager2 extends ShellFolderManager { ...@@ -408,4 +411,102 @@ public class Win32ShellFolderManager2 extends ShellFolderManager {
return name1.compareTo(name2); return name1.compareTo(name2);
} }
} }
@Override
protected Invoker createInvoker() {
return new ComInvoker();
}
private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker {
private static Thread comThread;
private ComInvoker() {
super(1, 1, 0, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
allowCoreThreadTimeOut(false);
setThreadFactory(this);
final Runnable shutdownHook = new Runnable() {
public void run() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
shutdownNow();
return null;
}
});
}
};
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
Runtime.getRuntime().addShutdownHook(
new Thread(shutdownHook)
);
return null;
}
});
}
public synchronized Thread newThread(final Runnable task) {
final Runnable comRun = new Runnable() {
public void run() {
try {
initializeCom();
task.run();
} finally {
uninitializeCom();
}
}
};
comThread =
AccessController.doPrivileged(
new PrivilegedAction<Thread>() {
public Thread run() {
/* The thread must be a member of a thread group
* which will not get GCed before VM exit.
* Make its parent the top-level thread group.
*/
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread thread = new Thread(tg, comRun, "Swing-Shell");
thread.setDaemon(true);
return thread;
}
}
);
return comThread;
}
public <T> T invoke(Callable<T> task) {
try {
T result;
if (Thread.currentThread() == comThread) {
// if it's already called from the COM
// thread, we don't need to delegate the task
result = task.call();
} else {
Future<T> future = submit(task);
try {
result = future.get();
} catch (InterruptedException e) {
result = null;
future.cancel(true);
}
}
return result;
} catch (Exception e) {
Throwable cause = (e instanceof ExecutionException) ? e.getCause() : e;
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(cause);
}
}
}
static native void initializeCom();
static native void uninitializeCom();
} }
...@@ -225,6 +225,34 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolder2_initIDs ...@@ -225,6 +225,34 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolder2_initIDs
FID_folderType = env->GetFieldID(cls, "folderType", "Ljava/lang/String;"); FID_folderType = env->GetFieldID(cls, "folderType", "Ljava/lang/String;");
} }
/*
* Class: sun_awt_shell_Win32ShellFolderManager2
* Method: initializeCom
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolderManager2_initializeCom
(JNIEnv* env, jclass cls)
{
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr)) {
char c[64];
sprintf(c, "Could not initialize COM: HRESULT=0x%08X", hr);
JNU_ThrowInternalError(env, c);
}
}
/*
* Class: sun_awt_shell_Win32ShellFolderManager2
* Method: uninitializeCom
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolderManager2_uninitializeCom
(JNIEnv* env, jclass cls)
{
::CoUninitialize();
}
static IShellIcon* getIShellIcon(IShellFolder* pIShellFolder) { static IShellIcon* getIShellIcon(IShellFolder* pIShellFolder) {
// http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp // http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp
HRESULT hres; HRESULT hres;
...@@ -239,29 +267,6 @@ static IShellIcon* getIShellIcon(IShellFolder* pIShellFolder) { ...@@ -239,29 +267,6 @@ static IShellIcon* getIShellIcon(IShellFolder* pIShellFolder) {
return (IShellIcon*)NULL; return (IShellIcon*)NULL;
} }
// Fixed 6263669
//
// CoInitialize wrapper
// call CoInitialize to initialize COM in STA mode and check result
// RPC_E_CHANGED_MODE means COM has already been initialized in MTA mode,
// so don't set the flag to call CoUninitialize later
BOOL CoInit(BOOL& doCoUninit) { // returns TRUE if initialized successfully
switch(::CoInitialize(NULL)) {
case S_OK:
case S_FALSE:
doCoUninit = TRUE;
return TRUE;
break;
case RPC_E_CHANGED_MODE:
doCoUninit = FALSE;
return TRUE;
break;
default:
return FALSE;
}
}
/* /*
* Class: sun_awt_shell_Win32ShellFolder2 * Class: sun_awt_shell_Win32ShellFolder2
...@@ -507,10 +512,10 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getAttributes0 ...@@ -507,10 +512,10 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getAttributes0
/* /*
* Class: sun_awt_shell_Win32ShellFolder2 * Class: sun_awt_shell_Win32ShellFolder2
* Method: getFileSystemPath * Method: getFileSystemPath0
* Signature: (I)Ljava/lang/String; * Signature: (I)Ljava/lang/String;
*/ */
JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getFileSystemPath__I JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getFileSystemPath0
(JNIEnv* env, jclass cls, jint csidl) (JNIEnv* env, jclass cls, jint csidl)
{ {
LPITEMIDLIST relPIDL; LPITEMIDLIST relPIDL;
...@@ -611,18 +616,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_bindToObject ...@@ -611,18 +616,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_bindToObject
if (SUCCEEDED (hr)) { if (SUCCEEDED (hr)) {
return (jlong)pFolder; return (jlong)pFolder;
} }
if (IS_WINVISTA) {
BOOL doCoUninit;
if (CoInit(doCoUninit)) {
hr = pParent->BindToObject(pidl, NULL, IID_IShellFolder, (void**)&pFolder);
if (doCoUninit) {
::CoUninitialize();
}
if (SUCCEEDED (hr)) {
return (jlong)pFolder;
}
}
}
return 0; return 0;
} }
...@@ -650,7 +643,10 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation ...@@ -650,7 +643,10 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation
return NULL; return NULL;
} }
pParent->GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_FORPARSING, &strret); hres = pParent->GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_FORPARSING, &strret);
if (FAILED(hres)) {
return NULL;
}
switch (strret.uType) { switch (strret.uType) {
case STRRET_CSTR : case STRRET_CSTR :
...@@ -669,10 +665,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation ...@@ -669,10 +665,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation
break; break;
} }
BOOL doCoUninit;
if (!CoInit(doCoUninit)) {
return 0;
}
IShellLinkW* psl; IShellLinkW* psl;
hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&psl); hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&psl);
if (SUCCEEDED(hres)) { if (SUCCEEDED(hres)) {
...@@ -692,9 +684,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation ...@@ -692,9 +684,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation
} }
psl->Release(); psl->Release();
} }
if (doCoUninit) {
::CoUninitialize();
}
if (SUCCEEDED(hres)) { if (SUCCEEDED(hres)) {
return (jlong)pidl; return (jlong)pidl;
...@@ -741,7 +730,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_parseDisplayName0 ...@@ -741,7 +730,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_parseDisplayName0
/* /*
* Class: sun_awt_shell_Win32ShellFolder2 * Class: sun_awt_shell_Win32ShellFolder2
* Method: getDisplayNameOf * Method: getDisplayNameOf
* Signature: (JJ)Ljava/lang/String; * Signature: (JJI)Ljava/lang/String;
*/ */
JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getDisplayNameOf JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getDisplayNameOf
(JNIEnv* env, jclass cls, jlong parentIShellFolder, jlong relativePIDL, jint attrs) (JNIEnv* env, jclass cls, jlong parentIShellFolder, jlong relativePIDL, jint attrs)
...@@ -833,10 +822,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconIndex ...@@ -833,10 +822,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconIndex
} }
INT index = -1; INT index = -1;
BOOL doCoUninit;
if (!CoInit(doCoUninit)) {
return (jint)index;
}
HRESULT hres; HRESULT hres;
// http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp // http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp
...@@ -844,9 +829,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconIndex ...@@ -844,9 +829,6 @@ JNIEXPORT jint JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconIndex
hres = pIShellIcon->GetIconOf(pidl, GIL_FORSHELL, &index); hres = pIShellIcon->GetIconOf(pidl, GIL_FORSHELL, &index);
} }
if (doCoUninit) {
::CoUninitialize();
}
return (jint)index; return (jint)index;
} }
...@@ -866,10 +848,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon ...@@ -866,10 +848,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon
} }
HICON hIcon = NULL; HICON hIcon = NULL;
BOOL doCoUninit;
if (!CoInit(doCoUninit)) {
return (jlong)hIcon;
}
HRESULT hres; HRESULT hres;
IExtractIconW* pIcon; IExtractIconW* pIcon;
...@@ -894,9 +872,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon ...@@ -894,9 +872,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_extractIcon
} }
pIcon->Release(); pIcon->Release();
} }
if (doCoUninit) {
::CoUninitialize();
}
return (jlong)hIcon; return (jlong)hIcon;
} }
...@@ -994,14 +969,10 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getFileChooserB ...@@ -994,14 +969,10 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getFileChooserB
HINSTANCE libComCtl32; HINSTANCE libComCtl32;
HINSTANCE libShell32; HINSTANCE libShell32;
libShell32 = LoadLibrary(TEXT("shell32.dll")); libShell32 = LoadLibrary(TEXT("shell32.dll"));
if (libShell32 != NULL) { if (libShell32 != NULL) {
long osVersion = GetVersion();
BOOL isVista = (!(osVersion & 0x80000000) && (LOBYTE(LOWORD(osVersion)) >= 6));
hBitmap = (HBITMAP)LoadImage(libShell32, hBitmap = (HBITMAP)LoadImage(libShell32,
isVista ? TEXT("IDB_TB_SH_DEF_16") : MAKEINTRESOURCE(216), IS_WINVISTA ? TEXT("IDB_TB_SH_DEF_16") : MAKEINTRESOURCE(216),
IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
} }
if (hBitmap == NULL) { if (hBitmap == NULL) {
...@@ -1095,46 +1066,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconResource ...@@ -1095,46 +1066,6 @@ JNIEXPORT jlong JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconResource
} }
// Helper functions for workaround COM initialization:
static HRESULT GetDetailsOfFolder(
IShellFolder2 *folder,
LPCITEMIDLIST pidl,
UINT column,
SHELLDETAILS *psd)
{
HRESULT hr = folder->GetDetailsOf(pidl, column, psd);
if (IS_WINVISTA && FAILED (hr)) {
BOOL doCoUninit;
if (CoInit(doCoUninit)) {
hr = folder->GetDetailsOf(pidl, column, psd);
if (doCoUninit) {
::CoUninitialize();
}
}
}
return hr;
}
static HRESULT GetDetailsOf(
IShellDetails *details,
LPCITEMIDLIST pidl,
UINT column,
SHELLDETAILS *psd)
{
HRESULT hr = details->GetDetailsOf(pidl, column, psd);
if (IS_WINVISTA && FAILED (hr)) {
BOOL doCoUninit;
if (CoInit(doCoUninit)) {
hr = details->GetDetailsOf(pidl, column, psd);
if (doCoUninit) {
::CoUninitialize();
}
}
}
return hr;
}
/* /*
* Helper function for creating Java column info object * Helper function for creating Java column info object
*/ */
...@@ -1187,7 +1118,7 @@ JNIEXPORT jobjectArray JNICALL ...@@ -1187,7 +1118,7 @@ JNIEXPORT jobjectArray JNICALL
int colNum = -1; int colNum = -1;
hr = S_OK; hr = S_OK;
do{ do{
hr = GetDetailsOfFolder(pIShellFolder2, NULL, ++colNum, &sd); hr = pIShellFolder2->GetDetailsOf(NULL, ++colNum, &sd);
} while (SUCCEEDED (hr)); } while (SUCCEEDED (hr));
jobjectArray columns = jobjectArray columns =
...@@ -1202,7 +1133,7 @@ JNIEXPORT jobjectArray JNICALL ...@@ -1202,7 +1133,7 @@ JNIEXPORT jobjectArray JNICALL
colNum = 0; colNum = 0;
hr = S_OK; hr = S_OK;
while (SUCCEEDED (hr)) { while (SUCCEEDED (hr)) {
hr = GetDetailsOfFolder(pIShellFolder2, NULL, colNum, &sd); hr = pIShellFolder2->GetDetailsOf(NULL, colNum, &sd);
if (SUCCEEDED (hr)) { if (SUCCEEDED (hr)) {
hr = pIShellFolder2->GetDefaultColumnState(colNum, &csFlags); hr = pIShellFolder2->GetDefaultColumnState(colNum, &csFlags);
...@@ -1232,7 +1163,7 @@ JNIEXPORT jobjectArray JNICALL ...@@ -1232,7 +1163,7 @@ JNIEXPORT jobjectArray JNICALL
int colNum = -1; int colNum = -1;
hr = S_OK; hr = S_OK;
do{ do{
hr = GetDetailsOf(pIShellDetails, NULL, ++colNum, &sd); hr = pIShellDetails->GetDetailsOf(NULL, ++colNum, &sd);
} while (SUCCEEDED (hr)); } while (SUCCEEDED (hr));
jobjectArray columns = jobjectArray columns =
...@@ -1246,7 +1177,7 @@ JNIEXPORT jobjectArray JNICALL ...@@ -1246,7 +1177,7 @@ JNIEXPORT jobjectArray JNICALL
colNum = 0; colNum = 0;
hr = S_OK; hr = S_OK;
while (SUCCEEDED (hr)) { while (SUCCEEDED (hr)) {
hr = GetDetailsOf(pIShellDetails, NULL, colNum, &sd); hr = pIShellDetails->GetDetailsOf(NULL, colNum, &sd);
if (SUCCEEDED (hr)) { if (SUCCEEDED (hr)) {
jobject column = CreateColumnInfo(env, jobject column = CreateColumnInfo(env,
&columnClass, &columnConstructor, &columnClass, &columnConstructor,
...@@ -1288,7 +1219,7 @@ JNIEXPORT jobject JNICALL ...@@ -1288,7 +1219,7 @@ JNIEXPORT jobject JNICALL
if(SUCCEEDED (hr)) { if(SUCCEEDED (hr)) {
// The folder exposes IShellFolder2 interface // The folder exposes IShellFolder2 interface
IShellFolder2 *pIShellFolder2 = (IShellFolder2*) pIUnknown; IShellFolder2 *pIShellFolder2 = (IShellFolder2*) pIUnknown;
hr = GetDetailsOfFolder(pIShellFolder2, pidl, (UINT)columnIdx, &sd); hr = pIShellFolder2->GetDetailsOf(pidl, (UINT)columnIdx, &sd);
pIShellFolder2->Release(); pIShellFolder2->Release();
if (SUCCEEDED (hr)) { if (SUCCEEDED (hr)) {
STRRET strRet = sd.str; STRRET strRet = sd.str;
...@@ -1300,7 +1231,7 @@ JNIEXPORT jobject JNICALL ...@@ -1300,7 +1231,7 @@ JNIEXPORT jobject JNICALL
if(SUCCEEDED (hr)) { if(SUCCEEDED (hr)) {
// The folder exposes IShellDetails interface // The folder exposes IShellDetails interface
IShellDetails *pIShellDetails = (IShellDetails*) pIUnknown; IShellDetails *pIShellDetails = (IShellDetails*) pIUnknown;
hr = GetDetailsOf(pIShellDetails, pidl, (UINT)columnIdx, &sd); hr = pIShellDetails->GetDetailsOf(pidl, (UINT)columnIdx, &sd);
pIShellDetails->Release(); pIShellDetails->Release();
if (SUCCEEDED (hr)) { if (SUCCEEDED (hr)) {
STRRET strRet = sd.str; STRRET strRet = sd.str;
......
/*
* @test
* @bug 6570445
* @summary Checks if Win32ShellFolder2's COM-using methods work under a security manager
* @author Leonid Popov
*/
import javax.swing.filechooser.FileSystemView;
public class bug6570445 {
public static void main(String[] args) {
System.setSecurityManager(new SecurityManager());
// The next line of code forces FileSystemView to request data from Win32ShellFolder2,
// what causes an exception if a security manager installed (see the bug 6570445 description)
FileSystemView.getFileSystemView().getRoots();
System.out.println("Passed.");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册