changeset 1083:51148b9aed43

6491795: COM should be initialized for Shell API calls in ShellFolder2.cpp Reviewed-by: peterz, loneid
author rupashka
date Thu, 12 Mar 2009 14:00:26 +0300
parents 02b64d5fad60
children 4f7dd74de2e3
files src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java src/share/classes/sun/awt/shell/ShellFolder.java src/share/classes/sun/awt/shell/ShellFolderManager.java src/share/classes/sun/swing/FilePane.java src/windows/classes/sun/awt/shell/Win32ShellFolder2.java src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java src/windows/native/sun/windows/ShellFolder2.cpp test/javax/swing/JFileChooser/6570445/bug6570445.java
diffstat 8 files changed, 714 insertions(+), 445 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java	Thu Mar 12 14:00:26 2009 +0300
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.util.*;
+import java.util.concurrent.Callable;
 import javax.swing.*;
 import javax.swing.filechooser.*;
 import javax.swing.event.*;
@@ -223,113 +224,115 @@
             this.fid = fid;
         }
 
-        private void invokeLater(DoChangeContents runnable) {
-            runnables.addElement(runnable);
-            SwingUtilities.invokeLater(runnable);
-        }
-
         public void run() {
             run0();
             setBusy(false, fid);
         }
 
         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()) {
-                return;
-            }
+                    if (isInterrupted()) {
+                        return null;
+                    }
 
-            // run through the file list, add directories and selectable files to fileCache
-            for (File file : list) {
-                if (filechooser.accept(file)) {
-                    acceptsList.addElement(file);
-                }
-            }
+                    // run through the file list, add directories and selectable files to fileCache
+                    for (File file : list) {
+                        if (filechooser.accept(file)) {
+                            acceptsList.addElement(file);
+                        }
+                    }
 
-            if (isInterrupted()) {
-                return;
-            }
+                    if (isInterrupted()) {
+                        return null;
+                    }
 
-            // First sort alphabetically by filename
-            sort(acceptsList);
+                    // First sort alphabetically by filename
+                    sort(acceptsList);
 
-            Vector<File> newDirectories = new Vector<File>(50);
-            Vector<File> newFiles = new Vector<File>();
-            // run through list grabbing directories in chunks of ten
-            for(int i = 0; i < acceptsList.size(); i++) {
-                File f = acceptsList.elementAt(i);
-                boolean isTraversable = filechooser.isTraversable(f);
-                if (isTraversable) {
-                    newDirectories.addElement(f);
-                } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
-                    newFiles.addElement(f);
-                }
-                if(isInterrupted()) {
-                    return;
-                }
-            }
+                    Vector<File> newDirectories = new Vector<File>(50);
+                    Vector<File> newFiles = new Vector<File>();
+                    // run through list grabbing directories in chunks of ten
+                    for (int i = 0; i < acceptsList.size(); i++) {
+                        File f = acceptsList.elementAt(i);
+                        boolean isTraversable = filechooser.isTraversable(f);
+                        if (isTraversable) {
+                            newDirectories.addElement(f);
+                        } else if (!isTraversable && filechooser.isFileSelectionEnabled()) {
+                            newFiles.addElement(f);
+                        }
+                        if (isInterrupted()) {
+                            return null;
+                        }
+                    }
 
-            Vector<File> newFileCache = new Vector<File>(newDirectories);
-            newFileCache.addAll(newFiles);
+                    Vector<File> newFileCache = new Vector<File>(newDirectories);
+                    newFileCache.addAll(newFiles);
 
-            int newSize = newFileCache.size();
-            int oldSize = fileCache.size();
+                    int newSize = newFileCache.size();
+                    int oldSize = fileCache.size();
 
-            if (newSize > oldSize) {
-                //see if interval is added
-                int start = oldSize;
-                int end = newSize;
-                for (int i = 0; i < oldSize; i++) {
-                    if (!newFileCache.get(i).equals(fileCache.get(i))) {
-                        start = i;
-                        for (int j = i; j < newSize; j++) {
-                            if (newFileCache.get(j).equals(fileCache.get(i))) {
-                                end = j;
+                    if (newSize > oldSize) {
+                        //see if interval is added
+                        int start = oldSize;
+                        int end = newSize;
+                        for (int i = 0; i < oldSize; i++) {
+                            if (!newFileCache.get(i).equals(fileCache.get(i))) {
+                                start = i;
+                                for (int j = i; j < newSize; j++) {
+                                    if (newFileCache.get(j).equals(fileCache.get(i))) {
+                                        end = j;
+                                        break;
+                                    }
+                                }
                                 break;
                             }
                         }
-                        break;
+                        if (start >= 0 && end > start
+                            && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
+                            if (isInterrupted()) {
+                                return null;
+                            }
+                            return new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid);
+                        }
+                    } else if (newSize < oldSize) {
+                        //see if interval is removed
+                        int start = -1;
+                        int end = -1;
+                        for (int i = 0; i < newSize; i++) {
+                            if (!newFileCache.get(i).equals(fileCache.get(i))) {
+                                start = i;
+                                end = i + oldSize - newSize;
+                                break;
+                            }
+                        }
+                        if (start >= 0 && end > start
+                            && 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 (isInterrupted()) {
+                            cancelRunnables(runnables);
+                        }
+                        return new DoChangeContents(newFileCache, 0, fileCache, 0, fid);
+                    }
+                    return null;
                 }
-                if (start >= 0 && end > start
-                    && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
-                    if(isInterrupted()) {
-                        return;
-                    }
-                    invokeLater(new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid));
-                    newFileCache = null;
-                }
-            } else if (newSize < oldSize) {
-                //see if interval is removed
-                int start = -1;
-                int end = -1;
-                for (int i = 0; i < newSize; i++) {
-                    if (!newFileCache.get(i).equals(fileCache.get(i))) {
-                        start = i;
-                        end = i + oldSize - newSize;
-                        break;
-                    }
-                }
-                if (start >= 0 && end > start
-                    && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
-                    if(isInterrupted()) {
-                        return;
-                    }
-                    invokeLater(new DoChangeContents(null, 0, new Vector<File>(fileCache.subList(start, end)),
-                                                     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);
             }
         }
 
--- a/src/share/classes/sun/awt/shell/ShellFolder.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/share/classes/sun/awt/shell/ShellFolder.java	Thu Mar 12 14:00:26 2009 +0300
@@ -31,6 +31,7 @@
 import java.io.*;
 import java.io.FileNotFoundException;
 import java.util.*;
+import java.util.concurrent.Callable;
 
 /**
  * @author Michael Martak
@@ -461,6 +462,35 @@
         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
      */
--- a/src/share/classes/sun/awt/shell/ShellFolderManager.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/share/classes/sun/awt/shell/ShellFolderManager.java	Thu Mar 12 14:00:26 2009 +0300
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.concurrent.Callable;
 
 /**
  * @author Michael Martak
@@ -96,9 +97,23 @@
     }
 
     public boolean isFileSystemRoot(File dir) {
-        if (dir instanceof ShellFolder && !((ShellFolder)dir).isFileSystem()) {
+        if (dir instanceof ShellFolder && !((ShellFolder) dir).isFileSystem()) {
             return false;
         }
         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);
+            }
+        }
+    }
 }
--- a/src/share/classes/sun/swing/FilePane.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/share/classes/sun/swing/FilePane.java	Thu Mar 12 14:00:26 2009 +0300
@@ -34,6 +34,7 @@
 import java.text.MessageFormat;
 import java.util.*;
 import java.util.List;
+import java.util.concurrent.Callable;
 
 import javax.swing.*;
 import javax.swing.border.*;
@@ -900,6 +901,16 @@
             }
         }
 
+        @Override
+        public void sort() {
+            ShellFolder.getInvoker().invoke(new Callable<Void>() {
+                public Void call() throws Exception {
+                    DetailsTableRowSorter.super.sort();
+                    return null;
+                }
+            });
+        }
+
         public void modelStructureChanged() {
             super.modelStructureChanged();
             updateComparators(detailsTableModel.getColumns());
--- a/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java	Thu Mar 12 14:00:26 2009 +0300
@@ -32,6 +32,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.*;
+import java.util.concurrent.*;
 import javax.swing.SwingConstants;
 
 // NOTE: This class supersedes Win32ShellFolder, which was removed from
@@ -184,15 +185,20 @@
         boolean disposed;
         public void dispose() {
             if (disposed) return;
-            if (relativePIDL != 0) {
-                releasePIDL(relativePIDL);
-            }
-            if (absolutePIDL != 0) {
-                releasePIDL(absolutePIDL);
-            }
-            if (pIShellFolder != 0) {
-                releaseIShellFolder(pIShellFolder);
-            }
+            ShellFolder.getInvoker().invoke(new Callable<Void>() {
+                public Void call() throws Exception {
+                    if (relativePIDL != 0) {
+                        releasePIDL(relativePIDL);
+                    }
+                    if (absolutePIDL != 0) {
+                        releasePIDL(absolutePIDL);
+                    }
+                    if (pIShellFolder != 0) {
+                        releaseIShellFolder(pIShellFolder);
+                    }
+                    return null;
+                }
+            });
             disposed = true;
         }
     }
@@ -218,50 +224,59 @@
      */
     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
      * desktop or Network Neighborhood.
      */
-    Win32ShellFolder2(int csidl) throws IOException {
+    Win32ShellFolder2(final int csidl) throws IOException {
         // Desktop is parent of DRIVES and NETWORK, not necessarily
         // other special shell folders.
-        super(null,
-              (getFileSystemPath(csidl) == null)
-                ? ("ShellFolder: 0x"+Integer.toHexString(csidl)) : getFileSystemPath(csidl));
-        if (csidl == DESKTOP) {
-            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;
+        super(null, composePathForCsidl(csidl));
+        ShellFolder.getInvoker().invoke(new Callable<Void>() {
+            public Void call() throws Exception {
+                if (csidl == DESKTOP) {
+                    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 {
+                            break;
+                        }
                     }
-                } else {
-                    break;
                 }
+                return null;
             }
-        }
+        });
 
         sun.java2d.Disposer.addRecord(this, disposer);
     }
@@ -281,17 +296,26 @@
     /**
      * Creates a shell folder with a parent and relative PIDL
      */
-    Win32ShellFolder2(Win32ShellFolder2 parent, long relativePIDL) {
-        super(parent, getFileSystemPath(parent.getIShellFolder(), relativePIDL));
+    Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) {
+        super(parent,
+            ShellFolder.getInvoker().invoke(new Callable<String>() {
+                public String call() throws Exception {
+                    return getFileSystemPath(parent.getIShellFolder(), relativePIDL);
+                }
+            })
+        );
         this.disposer.relativePIDL = relativePIDL;
         getAbsolutePath();
         sun.java2d.Disposer.addRecord(this, disposer);
     }
 
     // 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();
+
     // Initializes a special, non-file system shell folder
     // 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);
 
     /** Marks this folder as being the My Documents (Personal) folder */
@@ -311,26 +335,30 @@
      * drive (normally "C:\").
      */
     protected Object writeReplace() throws java.io.ObjectStreamException {
-        if (isFileSystem()) {
-            return new File(getPath());
-        } else {
-            Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
-            if (drives != null) {
-                File[] driveRoots = drives.listFiles();
-                if (driveRoots != null) {
-                    for (int i = 0; i < driveRoots.length; i++) {
-                        if (driveRoots[i] instanceof Win32ShellFolder2) {
-                            Win32ShellFolder2 sf = (Win32ShellFolder2)driveRoots[i];
-                            if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
-                                return new File(sf.getPath());
+        return ShellFolder.getInvoker().invoke(new Callable<File>() {
+            public File call() throws Exception {
+                if (isFileSystem()) {
+                    return new File(getPath());
+                } else {
+                    Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
+                    if (drives != null) {
+                        File[] driveRoots = drives.listFiles();
+                        if (driveRoots != null) {
+                            for (int i = 0; i < driveRoots.length; i++) {
+                                if (driveRoots[i] instanceof Win32ShellFolder2) {
+                                    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 @@
     static native void releasePIDL(long pIDL);
 
     // 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);
 
     /**
@@ -371,18 +400,28 @@
      */
     public long getIShellFolder() {
         if (disposer.pIShellFolder == 0) {
-            assert(isDirectory());
-            assert(parent != null);
-            long parentIShellFolder = getParentIShellFolder();
-            if (parentIShellFolder == 0) {
-                throw new InternalError("Parent IShellFolder was null for " + getAbsolutePath());
-            }
-            // 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.
-            disposer.pIShellFolder = bindToObject(parentIShellFolder, disposer.relativePIDL);
-            if (disposer.pIShellFolder == 0) {
-                throw new InternalError("Unable to bind " + getAbsolutePath() + " to parent");
-            }
+            disposer.pIShellFolder =
+                ShellFolder.getInvoker().invoke(new Callable<Long>() {
+                    public Long call() throws Exception {
+                        assert(isDirectory());
+                        assert(parent != null);
+                        long parentIShellFolder = getParentIShellFolder();
+                        if (parentIShellFolder == 0) {
+                            throw new InternalError("Parent IShellFolder was null for "
+                                    + getAbsolutePath());
+                        }
+                        // 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;
     }
@@ -472,24 +511,42 @@
         return false;
     }
 
-    private static boolean pidlsEqual(long pIShellFolder, long pidl1, long pidl2) {
-        return (compareIDs(pIShellFolder, pidl1, pidl2) == 0);
+    private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2) {
+        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 Boolean cachedIsFileSystem;
+
     /**
      * @return Whether this is a file system shell folder
      */
-    public boolean isFileSystem() {
-        return hasAttribute(ATTRIB_FILESYSTEM);
+    public synchronized boolean isFileSystem() {
+        if (cachedIsFileSystem == null) {
+            cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
+        }
+
+        return cachedIsFileSystem;
     }
 
     /**
      * Return whether the given attribute flag is set for this object
      */
-    public boolean hasAttribute(int attribute) {
-        // Caching at this point doesn't seem to be cost efficient
-        return (getAttributes0(getParentIShellFolder(), getRelativePIDL(), attribute) & attribute) != 0;
+    public boolean hasAttribute(final int attribute) {
+        return ShellFolder.getInvoker().invoke(new Callable<Boolean>() {
+            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 @@
      * Could plausibly be used for attribute caching but have to be
      * very careful not to touch network drives and file system roots
      * 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);
 
     // Return the path to the underlying file system object
-    private static String getFileSystemPath(long parentIShellFolder, long relativePIDL) {
-        int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
-        if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
-            getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
+    private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
+        return ShellFolder.getInvoker().invoke(new Callable<String>() {
+            public String call() throws Exception {
+                int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
+                if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
+                        getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
 
-            String s =
-                getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
-                                  getLinkLocation(parentIShellFolder, relativePIDL, false));
-            if (s != null && s.startsWith("\\\\")) {
-                return s;
+                    String s =
+                            getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
+                                    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
-    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.
     // Path is assumed to be non-null
@@ -557,24 +630,33 @@
      */
     // Returns an IEnumIDList interface for an IShellFolder.  The value
     // returned must be released using releaseEnumObjects().
-    private long getEnumObjects(long pIShellFolder, boolean includeHiddenFiles) {
-        boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
-        return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
+    private long getEnumObjects(long pIShellFolder, final boolean includeHiddenFiles) {
+        final boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
+        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
     // 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,
                                        boolean includeHiddenFiles);
     // Returns the next sequential child as a relative PIDL
     // from an IEnumIDList interface.  The value returned must
     // 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);
     // 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);
 
     // Returns the IShellFolder of a child from a parent IShellFolder
     // and a relative PIDL.  The value returned must be released
     // 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);
 
     /**
@@ -582,60 +664,64 @@
      *         object. The array will be empty if the folder is empty.  Returns
      *         <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();
         if (security != null) {
             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();
-        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 {
-                if (Thread.currentThread().isInterrupted()) {
+        return ShellFolder.getInvoker().invoke(new Callable<File[]>() {
+            public File[] call() throws Exception {
+                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];
                 }
-                childPIDL = getNextChild(pEnumObjects);
-                boolean releasePIDL = true;
-                if (childPIDL != 0 &&
-                    (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
-                    Win32ShellFolder2 childFolder = null;
-                    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);
+
+                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);
                 }
-                if (releasePIDL) {
-                    releasePIDL(childPIDL);
-                }
-            } while (childPIDL != 0);
-            releaseEnumObjects(pEnumObjects);
-        }
-        return list.toArray(new ShellFolder[list.size()]);
+                return Thread.currentThread().isInterrupted()
+                    ? new File[0]
+                    : list.toArray(new ShellFolder[list.size()]);
+            }
+        });
     }
 
 
@@ -644,33 +730,43 @@
      *
      * @return The child shellfolder, or null if not found.
      */
-    Win32ShellFolder2 getChildByPath(String filePath) {
-        long pIShellFolder = getIShellFolder();
-        long pEnumObjects =  getEnumObjects(pIShellFolder, true);
-        Win32ShellFolder2 child = null;
-        long childPIDL;
+    Win32ShellFolder2 getChildByPath(final String filePath) {
+        return ShellFolder.getInvoker().invoke(new Callable<Win32ShellFolder2>() {
+            public Win32ShellFolder2 call() throws Exception {
+                long pIShellFolder = getIShellFolder();
+                long pEnumObjects = getEnumObjects(pIShellFolder, true);
+                Win32ShellFolder2 child = null;
+                long childPIDL = 0;
 
-        while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
-            if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
-                String path = getFileSystemPath(pIShellFolder, childPIDL);
-                if (path != null && path.equalsIgnoreCase(filePath)) {
-                    long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
-                    child = new Win32ShellFolder2(this, childIShellFolder, childPIDL, path);
-                    break;
+                while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
+                    if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
+                        String path = getFileSystemPath(pIShellFolder, childPIDL);
+                        if (path != null && path.equalsIgnoreCase(filePath)) {
+                            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
      */
-    public boolean isLink() {
-        return hasAttribute(ATTRIB_LINK);
+    public synchronized boolean isLink() {
+        if (cachedIsLink == null) {
+            cachedIsLink = hasAttribute(ATTRIB_LINK);
+        }
+
+        return cachedIsLink;
     }
 
     /**
@@ -682,6 +778,7 @@
 
 
     // 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,
                                         long relativePIDL, boolean resolve);
 
@@ -693,38 +790,52 @@
         return getLinkLocation(true);
     }
 
-    private ShellFolder getLinkLocation(boolean resolve)  {
-        if (!isLink()) {
-            return null;
-        }
+    private ShellFolder getLinkLocation(final boolean resolve) {
+        return ShellFolder.getInvoker().invoke(new Callable<ShellFolder>() {
+            public ShellFolder call() throws Exception {
+                if (!isLink()) {
+                    return null;
+                }
 
-        ShellFolder location = null;
-        long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
-                                                getRelativePIDL(), resolve);
-        if (linkLocationPIDL != 0) {
-            try {
-                location =
-                    Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
-                                                                               linkLocationPIDL);
-            } catch (InternalError e) {
-                // Could be a link to a non-bindable object, such as a network connection
-                // TODO: getIShellFolder() should throw FileNotFoundException instead
+                ShellFolder location = null;
+                long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
+                        getRelativePIDL(), resolve);
+                if (linkLocationPIDL != 0) {
+                    try {
+                        location =
+                                Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
+                                        linkLocationPIDL);
+                    } catch (InternalError e) {
+                        // Could be a link to a non-bindable object, such as a network connection
+                        // TODO: getIShellFolder() should throw FileNotFoundException instead
+                    }
+                }
+                return location;
             }
-        }
-        return location;
+        });
     }
 
     // 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 {
-            return parseDisplayName0(getIShellFolder(), name);
-        } catch (IOException e) {
-            throw new FileNotFoundException("Could not find file " + name);
+            return ShellFolder.getInvoker().invoke(new Callable<Long>() {
+                public Long call() throws Exception {
+                    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;
 
     // 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,
                                                   long relativePIDL,
                                                   int attrs);
@@ -734,12 +845,19 @@
      */
     public String getDisplayName() {
         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 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);
 
     /**
@@ -747,7 +865,13 @@
      */
     public String getFolderType() {
         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;
     }
@@ -774,11 +898,16 @@
     private static Map smallLinkedSystemImages = 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);
+
+    // 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);
 
     // Return the icon of a file system shell folder in the form of an HICON
     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,
                                            boolean getLargeIcon);
 
@@ -799,7 +928,12 @@
 
     private long getIShellIcon() {
         if (pIShellIcon == -1L) {
-            pIShellIcon = getIShellIcon(getIShellFolder());
+            pIShellIcon =
+                ShellFolder.getInvoker().invoke(new Callable<Long>() {
+                    public Long call() throws Exception {
+                        return getIShellIcon(getIShellFolder());
+                    }
+                });
         }
         return pIShellIcon;
     }
@@ -850,50 +984,60 @@
     /**
      * @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;
         if (icon == null) {
-            long parentIShellIcon = (parent != null) ? ((Win32ShellFolder2)parent).getIShellIcon() : 0L;
-            long relativePIDL = getRelativePIDL();
+            icon =
+                ShellFolder.getInvoker().invoke(new Callable<Image>() {
+                    public Image call() throws Exception {
+                        Image newIcon = null;
+                        if (isFileSystem()) {
+                            long parentIShellIcon = (parent != null)
+                                ? ((Win32ShellFolder2) parent).getIShellIcon()
+                                : 0L;
+                            long relativePIDL = getRelativePIDL();
 
-            if (isFileSystem()) {
-                // These are cached per type (using the index in the system image list)
-                int index = getIconIndex(parentIShellIcon, relativePIDL);
-                if (index > 0) {
-                    Map imageCache;
-                    if (isLink()) {
-                        imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
-                    } else {
-                        imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
+                            // These are cached per type (using the index in the system image list)
+                            int index = getIconIndex(parentIShellIcon, relativePIDL);
+                            if (index > 0) {
+                                Map imageCache;
+                                if (isLink()) {
+                                    imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
+                                } else {
+                                    imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
+                                }
+                                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 (newIcon == null) {
+                            // These are only cached per object
+                            long hIcon = extractIcon(getParentIShellFolder(),
+                                getRelativePIDL(), getLargeIcon);
+                            newIcon = makeIcon(hIcon, getLargeIcon);
+                            disposeIcon(hIcon);
+                        }
+
+                        if (newIcon == null) {
+                            newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);
+                        }
+                        return newIcon;
                     }
-                    icon = (Image)imageCache.get(Integer.valueOf(index));
-                    if (icon == null) {
-                        long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
-                        icon = makeIcon(hIcon, getLargeIcon);
-                        disposeIcon(hIcon);
-                        if (icon != null) {
-                            imageCache.put(Integer.valueOf(index), icon);
-                        }
-                    }
-                }
-            }
-
-            if (icon == null) {
-                // These are only cached per object
-                long hIcon = extractIcon(getParentIShellFolder(), getRelativePIDL(), getLargeIcon);
-                icon = makeIcon(hIcon, getLargeIcon);
-                disposeIcon(hIcon);
-            }
-
+                });
             if (getLargeIcon) {
                 largeIcon = icon;
             } else {
                 smallIcon = icon;
             }
         }
-        if (icon == null) {
-            icon = super.getIcon(getLargeIcon);
-        }
         return icon;
     }
 
@@ -969,39 +1113,50 @@
     private static final int LVCFMT_CENTER = 2;
 
     public ShellFolderColumnInfo[] getFolderColumns() {
-        ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
+        return ShellFolder.getInvoker().invoke(new Callable<ShellFolderColumnInfo[]>() {
+            public ShellFolderColumnInfo[] call() throws Exception {
+                ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
 
-        if (columns != null) {
-            List<ShellFolderColumnInfo> notNullColumns =
-                    new ArrayList<ShellFolderColumnInfo>();
-            for (int i = 0; i < columns.length; i++) {
-                ShellFolderColumnInfo column = columns[i];
-                if (column != null) {
-                    column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
-                            ? SwingConstants.RIGHT
-                                : column.getAlignment() == LVCFMT_CENTER
-                            ? SwingConstants.CENTER
-                                : SwingConstants.LEADING);
+                if (columns != null) {
+                    List<ShellFolderColumnInfo> notNullColumns =
+                            new ArrayList<ShellFolderColumnInfo>();
+                    for (int i = 0; i < columns.length; i++) {
+                        ShellFolderColumnInfo column = columns[i];
+                        if (column != null) {
+                            column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
+                                    ? SwingConstants.RIGHT
+                                    : column.getAlignment() == LVCFMT_CENTER
+                                    ? SwingConstants.CENTER
+                                    : SwingConstants.LEADING);
 
-                    column.setComparator(new ColumnComparator(getIShellFolder(), i));
+                            column.setComparator(new ColumnComparator(getIShellFolder(), i));
 
-                    notNullColumns.add(column);
+                            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) {
-        return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
+    public Object getFolderColumnValue(final int 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);
 
+    // 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);
 
+    // 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);
 
 
@@ -1020,17 +1175,20 @@
         }
 
         // compares 2 objects within this folder by the specified column
-        public int compare(File o, File o1) {
-            if (o instanceof Win32ShellFolder2
-                    && o1 instanceof Win32ShellFolder2) {
-                // delegates comparison to native method
-                return compareIDsByColumn(parentIShellFolder,
-                        ((Win32ShellFolder2) o).getRelativePIDL(),
-                        ((Win32ShellFolder2) o1).getRelativePIDL(),
-                        columnIdx);
-            }
-            return 0;
+        public int compare(final File o, final File o1) {
+            return ShellFolder.getInvoker().invoke(new Callable<Integer>() {
+                public Integer call() throws Exception {
+                    if (o instanceof Win32ShellFolder2
+                            && o1 instanceof Win32ShellFolder2) {
+                        // delegates comparison to native method
+                        return compareIDsByColumn(parentIShellFolder,
+                                ((Win32ShellFolder2) o).getRelativePIDL(),
+                                ((Win32ShellFolder2) o1).getRelativePIDL(),
+                                columnIdx);
+                    }
+                    return 0;
+                }
+            });
         }
     }
-
 }
--- a/src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java	Thu Mar 12 14:00:26 2009 +0300
@@ -31,7 +31,10 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.*;
+import java.util.concurrent.*;
+
 import sun.security.action.LoadLibraryAction;
 
 import static sun.awt.shell.Win32ShellFolder2.*;
@@ -408,4 +411,102 @@
             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();
 }
--- a/src/windows/native/sun/windows/ShellFolder2.cpp	Thu Feb 26 11:44:43 2009 +0300
+++ b/src/windows/native/sun/windows/ShellFolder2.cpp	Thu Mar 12 14:00:26 2009 +0300
@@ -225,6 +225,34 @@
     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) {
     // http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp
     HRESULT hres;
@@ -239,29 +267,6 @@
     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
@@ -507,10 +512,10 @@
 
 /*
  * Class:     sun_awt_shell_Win32ShellFolder2
- * Method:    getFileSystemPath
+ * Method:    getFileSystemPath0
  * 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)
 {
     LPITEMIDLIST relPIDL;
@@ -611,18 +616,6 @@
     if (SUCCEEDED (hr)) {
         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;
 }
 
@@ -650,7 +643,10 @@
         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) {
       case STRRET_CSTR :
@@ -669,10 +665,6 @@
         break;
     }
 
-    BOOL doCoUninit;
-    if (!CoInit(doCoUninit)) {
-        return 0;
-    }
     IShellLinkW* psl;
     hres = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&psl);
     if (SUCCEEDED(hres)) {
@@ -692,9 +684,6 @@
         }
         psl->Release();
     }
-    if (doCoUninit) {
-        ::CoUninitialize();
-    }
 
     if (SUCCEEDED(hres)) {
         return (jlong)pidl;
@@ -741,7 +730,7 @@
 /*
  * Class:     sun_awt_shell_Win32ShellFolder2
  * Method:    getDisplayNameOf
- * Signature: (JJ)Ljava/lang/String;
+ * Signature: (JJI)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL Java_sun_awt_shell_Win32ShellFolder2_getDisplayNameOf
     (JNIEnv* env, jclass cls, jlong parentIShellFolder, jlong relativePIDL, jint attrs)
@@ -833,10 +822,6 @@
     }
 
     INT index = -1;
-    BOOL doCoUninit;
-    if (!CoInit(doCoUninit)) {
-        return (jint)index;
-    }
 
     HRESULT hres;
     // http://msdn.microsoft.com/library/en-us/shellcc/platform/Shell/programmersguide/shell_int/shell_int_programming/std_ifaces.asp
@@ -844,9 +829,6 @@
         hres = pIShellIcon->GetIconOf(pidl, GIL_FORSHELL, &index);
     }
 
-    if (doCoUninit) {
-        ::CoUninitialize();
-    }
     return (jint)index;
 }
 
@@ -866,10 +848,6 @@
     }
 
     HICON hIcon = NULL;
-    BOOL doCoUninit;
-    if (!CoInit(doCoUninit)) {
-        return (jlong)hIcon;
-    }
 
     HRESULT hres;
     IExtractIconW* pIcon;
@@ -894,9 +872,6 @@
         }
         pIcon->Release();
     }
-    if (doCoUninit) {
-        ::CoUninitialize();
-    }
     return (jlong)hIcon;
 }
 
@@ -994,14 +969,10 @@
     HINSTANCE libComCtl32;
     HINSTANCE libShell32;
 
-
     libShell32 = LoadLibrary(TEXT("shell32.dll"));
     if (libShell32 != NULL) {
-        long osVersion = GetVersion();
-        BOOL isVista = (!(osVersion & 0x80000000) && (LOBYTE(LOWORD(osVersion)) >= 6));
-
         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);
     }
     if (hBitmap == NULL) {
@@ -1095,46 +1066,6 @@
 }
 
 
-// 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
  */
@@ -1187,7 +1118,7 @@
         int colNum = -1;
         hr = S_OK;
         do{
-            hr = GetDetailsOfFolder(pIShellFolder2, NULL, ++colNum, &sd);
+            hr = pIShellFolder2->GetDetailsOf(NULL, ++colNum, &sd);
         } while (SUCCEEDED (hr));
 
         jobjectArray columns =
@@ -1202,7 +1133,7 @@
         colNum = 0;
         hr = S_OK;
         while (SUCCEEDED (hr)) {
-            hr = GetDetailsOfFolder(pIShellFolder2, NULL, colNum, &sd);
+            hr = pIShellFolder2->GetDetailsOf(NULL, colNum, &sd);
 
             if (SUCCEEDED (hr)) {
                 hr = pIShellFolder2->GetDefaultColumnState(colNum, &csFlags);
@@ -1232,7 +1163,7 @@
         int colNum = -1;
         hr = S_OK;
         do{
-            hr = GetDetailsOf(pIShellDetails, NULL, ++colNum, &sd);
+            hr = pIShellDetails->GetDetailsOf(NULL, ++colNum, &sd);
         } while (SUCCEEDED (hr));
 
         jobjectArray columns =
@@ -1246,7 +1177,7 @@
         colNum = 0;
         hr = S_OK;
         while (SUCCEEDED (hr)) {
-            hr = GetDetailsOf(pIShellDetails, NULL, colNum, &sd);
+            hr = pIShellDetails->GetDetailsOf(NULL, colNum, &sd);
             if (SUCCEEDED (hr)) {
                 jobject column = CreateColumnInfo(env,
                                     &columnClass, &columnConstructor,
@@ -1288,7 +1219,7 @@
     if(SUCCEEDED (hr)) {
         // The folder exposes IShellFolder2 interface
         IShellFolder2 *pIShellFolder2 = (IShellFolder2*) pIUnknown;
-        hr = GetDetailsOfFolder(pIShellFolder2, pidl, (UINT)columnIdx, &sd);
+        hr = pIShellFolder2->GetDetailsOf(pidl, (UINT)columnIdx, &sd);
         pIShellFolder2->Release();
         if (SUCCEEDED (hr)) {
             STRRET strRet = sd.str;
@@ -1300,7 +1231,7 @@
     if(SUCCEEDED (hr)) {
         // The folder exposes IShellDetails interface
         IShellDetails *pIShellDetails = (IShellDetails*) pIUnknown;
-        hr = GetDetailsOf(pIShellDetails, pidl, (UINT)columnIdx, &sd);
+        hr = pIShellDetails->GetDetailsOf(pidl, (UINT)columnIdx, &sd);
         pIShellDetails->Release();
         if (SUCCEEDED (hr)) {
             STRRET strRet = sd.str;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/swing/JFileChooser/6570445/bug6570445.java	Thu Mar 12 14:00:26 2009 +0300
@@ -0,0 +1,20 @@
+/*
+ * @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.");
+    }
+}