JDK-4622892 : FileSystemView.getSystemIcon crashes Windows
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.0,1.4.1
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_98,windows_xp
  • CPU: x86
  • Submitted: 2002-01-12
  • Updated: 2003-08-18
  • Resolved: 2003-08-18
Related Reports
Duplicate :  
Description

Name: jk109818			Date: 01/11/2002


FULL PRODUCT VERSION :
java version "1.4.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode)


FULL OPERATING SYSTEM VERSION :

Windows Millennium [Version 4.90.3000]



A DESCRIPTION OF THE PROBLEM :
FileSystemView.getSystemIcon is not releasing GDI resources
back to Windows, which causes the system to crash after
repeated invocations (even spread across different runs).

I first noticed this problem in a real program, so it
doesn't take an artificial testcase like this for it to
occur.  A few runs of a Java program which uses system
icons can exhaust GDI memory and force a reboot.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached testcase on a Windows system.
I'm on Windows ME, but I highly doubt that it makes a
difference.

The testcase will attempt to get icons for every file on
your system.  It frequently forces the garbage collector to
run just to prove that that doesn't help.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected result:  Testcase prints a bunch of periods and
exits normally.

Actual result:  After a minute or so, your system should
have crashed in one of a variety of ways.  I have seen
everything from the program simply hanging (but due to no
GDI resources you get an error message if you try to start
any other programs) to shutting my system down (as in I
saw, for no apparent reason, "It is now safe to turn off
your computer" and then the computer turned itself off).

This bug can be reproduced always.


---------- BEGIN SOURCE ----------

import java.io.*;
import javax.swing.filechooser.*;

public class TestCase {
    static int count;
    private static FileSystemView fileSystemView = FileSystemView.getFileSystemView();
    
    public static void scanDirectory(File parent) {
        File[] files = parent.listFiles();
        if (files == null)
            return;
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory())
                scanDirectory(files[i]);
            else {
                try {
                    fileSystemView.getSystemIcon(files[i]);
                    System.out.print(".");
                }
                catch (Exception e) { /* ignore */ }
            }
            if (++count >= 10) {
                count = 0;
                // gc and sleep just to prove finalizer doesn't help
                System.gc();
                try { Thread.sleep(100); } catch (InterruptedException e) { }
            }
        }
    }
    
    
    public static void main(String[] arg) {
        File[] roots = fileSystemView.getRoots();
        for (int i = 0; i < roots.length; i++)
            scanDirectory(roots[i]);
    }
}



---------- END SOURCE ----------
(Review ID: 138198) 
======================================================================

Name: rmT116609			Date: 01/16/2003


FULL PRODUCT VERSION :
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]

ADDITIONAL OPERATING SYSTEMS :
All of Windows platform

A DESCRIPTION OF THE PROBLEM :
Win32ShellFolder.getIconBits() is not releasing GDI resources.
It's copy of Bug ID 4622892.  I wrote the patch of this bug to 
http://developer.java.sun.com/developer/bugParade/bugs/4622892.html. however there is no reaction.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached test on a Windows system. If implemented correctly, all GDI resources are released correctly. However GDI resources are not released. You can check it by TaskManager of Windows(see GDI Object).


EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected result : print "success." at last.

Actual result : print "used up GDI resources." at count 5000(on WindowsXP, in the case of Win9x, used up GDI resources at a fewer count.)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.FileNotFoundException;
import java.awt.Image;
import sun.awt.shell.ShellFolder;

public class test{
	private static int count;

	public static void main(String[] args){
		File[] roots = File.listRoots();
		count     = 0;

		for(int i = 0; i < roots.length; i++){
			try{
                 ShellFolder shell = ShellFolder.getShellFolder( roots[i] );  	                 Image icon = shell.getIcon(false);
				System.out.println((++count) + " " + icon);
if( icon == null ){
					System.out.println( "used up GDI resources." );
					System.exit( 1 );
				}
	}catch( FileNotFoundException exception ){
			}
			func(roots[i]);
		}
		System.out.println( "success." );
	}

	private static void func(File dir){
		File[] files = dir.listFiles();
		if(files != null){
	for(int i = 0; i < files.length; i++){
				try{
					         ShellFolder shell = ShellFolder.getShellFolder( files[i] );
					         Image icon = shell.getIcon(false);
					         System.out.println((++count)+ " " + icon);
					          if( icon == null ){
						        System.out.println  "used up GDI resources." );
						         System.exit( 1 );
					}
 }catch( FileNotFoundException exception ){
				}
				if(files[i].isDirectory()){
					func(files[i]);
				}
			}
		}
	}
}


---------- END SOURCE ----------

SUGGESTED FIX :
the following is not work around. It's the debuging method of this bug.

Please
correct /j2se/src/win32/native/sun/windows/ShellFolder.cpp
as follows

JNIEXPORT jintArray JNICALL
Java_sun_awt_shell_Win32ShellFolder_getIconBits
    (JNIEnv* env, jobject folder, jlong hicon, jint
iconSize)
{
    // Get the icon info
    ICONINFO iconInfo;
    if (!fn_GetIconInfo((HICON)hicon, &iconInfo)) {
        return NULL;
    }
    // Get the screen DC
    HDC dc = GetDC(NULL);
    if (dc == NULL) {
        DeleteObject( iconInfo.hbmColor ); //add
        DeleteObject( iconInfo.hbmMask );  //add
        return NULL;
    }
    // Set up BITMAPINFO
    BITMAPINFO bmi;
    memset(&bmi, 0, sizeof(BITMAPINFO));
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = iconSize;
    bmi.bmiHeader.biHeight = -iconSize;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    // Extract the color bitmap
    int nBits = iconSize * iconSize * 32 / 8;
    long colorBits[4096];
    GetDIBits(dc, iconInfo.hbmColor, 0, iconSize,
colorBits, &bmi, DIB_RGB_COLORS);
    // Extract the mask bitmap
    long maskBits[4096];
    GetDIBits(dc, iconInfo.hbmMask, 0, iconSize, maskBits,
&bmi, DIB_RGB_COLORS);
    // Copy the mask alphas into the color bits
    for (int i = 0; i < nBits; i++) {
        colorBits[i] = colorBits[i] | ((maskBits[i] !=
0) ? 0 : 0xff000000);
    }
    // Release DC
    ReleaseDC(NULL, dc);
    // Release bitmap handle in icon info
    DeleteObject( iconInfo.hbmColor ); //add
    DeleteObject( iconInfo.hbmMask );  //add
    // Create java array
    jintArray iconBits = env->NewIntArray(nBits);
    // Copy values to java array
    env->SetIntArrayRegion(iconBits, 0, nBits, colorBits);
    return iconBits;
}

I think that fllowing is also foreget releasing the GDI
resource, please correct it.

JNIEXPORT jlong JNICALL
Java_sun_awt_shell_Win32ShellFolder_getIcon__JJZ
    (JNIEnv* env, jobject folder, jlong
parentIShellFolder, jlong
relativePIDL,
     jboolean getLargeIcon)
{
    IShellFolder* pParent = (IShellFolder*)
parentIShellFolder;
    if (pParent == NULL) {
        return 0;
    }
    LPITEMIDLIST pidl = (LPITEMIDLIST)relativePIDL;
    if (pidl == NULL) {
        return 0;
    }
    IExtractIcon* pIcon;
    if (pParent->GetUIObjectOf(NULL, 1,
const_cast<LPCITEMIDLIST*>(&pidl),
        IID_IExtractIcon, NULL, (void**)&pIcon) != S_OK) {
        return 0;
    }
    CHAR szBuf[MAX_PATH];
    INT index;
    UINT flags;
    if (pIcon->GetIconLocation(
        GIL_FORSHELL, szBuf, MAX_PATH, &index, &flags)
        != S_OK) {
        pIcon->Release();
        return 0;
    }
    HICON hIcon;
    HICON hIconLarge;
    if (pIcon->Extract(szBuf, index, &hIconLarge, &hIcon,
(16 << 16) + 32) != S_OK) {
        pIcon->Release();
        return 0;
    }

    //return (jlong)(getLargeIcon ? hIconLarge : hIcon);
    pIcon->Release();               //add
    if (getLargeIcon){              //add
        DestroyIcon(hIcon);         //add
        return (jlong)hIconLarge;   //add
    }else{                          //add
        DestroyIcon(hIconLarge);    //add
        return (jlong)hIcon;        //add
    }                               //add
}
(Review ID: 180015)
======================================================================