I get an JVM exit code -1073740940 when running this icon extractor code on Windows. Please advise.

770 views
Skip to first unread message

Dávid Karnok

unread,
Feb 18, 2019, 1:28:16 PM2/18/19
to Java Native Access
The following code enumerates windows and tries to get their icons, or tries to extract one from their exe file. For some reason, the JVM crashes with exit code -1073740940, which is 0xC0000374 - heap corruption on Windows 10 x64. The code itself worked fine back in 2010 on Windows 7 x86. When I step through it, it fails after random rounds. The Windows API definitions I use are here. JNA 5.2.0, Java 8u202.

Please advise.

Native.setProtected(true);

List<ProcessData> result = new ArrayList<ProcessData>();
User32 u32 = User32.INSTANCE;
Kernel32 k32 = Kernel32.INSTANCE;
char[] strOut = new char[1024];
int nxt = u32.GetTopWindow(0);

do {
   
if (nxt != 0 && u32.IsWindowVisible(nxt)) {
       
ProcessData pd = new ProcessData();

        pd
.Handle = nxt;
        u32
.GetWindowTextW(nxt, strOut, strOut.length);

        pd
.WindowText = Native.toString(strOut);
       
if (pd.WindowText.length() > 0) {
            u32
.GetClassNameW(nxt, strOut, strOut.length);

            pd
.ClassName = Native.toString(strOut);
            pd
.Instance = u32.GetWindowLongW(nxt, User32.GWL_HINSTANCE);

           
if (!u32.GetClassInfoExW(pd.Instance, new WString(
                    pd
.ClassName), pd.ClassWindowEx)) {
                pd
.Error = k32.GetLastError();
                pd
.ErrorStr = getLastErrorStr(pd.Error);
           
}
           
IntByReference iref = new IntByReference();
            u32
.GetWindowThreadProcessId(nxt, iref);

            pd
.ProcessID = iref.getValue();
           
int hIconSm = u32.SendMessageW(nxt, User32.WM_GETICON, User32.ICON_SMALL2, 0);

           
if (hIconSm == 0) {
                hIconSm
= u32.SendMessageW(nxt, User32.WM_GETICON, User32.ICON_SMALL, 0);

           
}
           
boolean destroyIcon = false;
           
if (hIconSm == 0) {
               
Psapi ps = Psapi.INSTANCE;
               
Arrays.fill(strOut, '\0');
               
int hProcess = k32.OpenProcess(Kernel32.PROCESS_VM_READ | Kernel32.PROCESS_QUERY_INFORMATION, false, pd.ProcessID);

               
if (hProcess != 0) {
                    ps
.GetModuleFileNameExW(hProcess, 0, strOut, strOut.length);

                    k32
.CloseHandle(hProcess);

                    pd
.exeFile = Native.toString(strOut);
                   
int[] ints = new int[1];
                   
if (Shell32.INSTANCE.ExtractIconExW(new WString(pd.exeFile), 0, null, ints, 1) == 1) {

                        hIconSm
= ints[0];
                        destroyIcon
= true;
                   
}
               
} else {
                    printLastError
();
               
}
           
}
           
int size = 16;

           
int[] iconBits = getIconBits(hIconSm, size);
           
if (iconBits != null) {
               
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
                bi
.setRGB(0, 0, size, size, iconBits, 0, size);
                pd
.icon16 = new ImageIcon(bi);
           
}
           
if (destroyIcon) {
                u32
.DestroyIcon(hIconSm);

           
}
            result
.add(pd);
       
}
   
}
    nxt
= u32.GetWindow(nxt, User32.GW_HWNDNEXT);

} while (nxt != 0);


The getIconBits is defined as follows:

    public static int[] getIconBits(int hIcon, int size) {
       
User32 u32 = User32.INSTANCE;
        GDI32 g32
= GDI32.INSTANCE;
       
IconInfo iconinfo = new IconInfo();
       
// get icon information
       
if (!u32.GetIconInfo(hIcon, iconinfo)) {
           
return null;
       
}
       
int dc = u32.GetDC(0);
       
if (dc == 0) {
            g32
.DeleteObject(iconinfo.hbmColor);
            g32
.DeleteObject(iconinfo.hbmMask);
           
return null;
       
}
       
int nBits = size * size * 4;
       
BitmapInfo bmi = new BitmapInfo();
        bmi
.initColors(1);
        bmi
.bmiHeader.biWidth = size;
        bmi
.bmiHeader.biHeight = -size;
        bmi
.bmiHeader.biPlanes = 1;
        bmi
.bmiHeader.biBitCount = 32;
        bmi
.bmiHeader.biCompression = GDI32.BI_RGB;
       
Memory colorBitsMem = new Memory(nBits);
       
// Extract the color bitmap
       
int result = g32.GetDIBits(dc, iconinfo.hbmColor, 0, size, colorBitsMem, bmi,
                GDI32
.DIB_RGB_COLORS);
       
if (result == 0) {
            result
= Kernel32.INSTANCE.GetLastError();
           
System.err.println("hbmColor:" + result + " - " + getLastErrorStr(result));
       
}
       
       
int[] colorBits = colorBitsMem.getIntArray(0, size * size);
       
Memory maskBitsMem = new Memory(nBits);
       
// Extract the mask bitmap
        result
= g32.GetDIBits(dc, iconinfo.hbmMask, 0, size, maskBitsMem, bmi,
                GDI32
.DIB_RGB_COLORS);


       
/*
        if (result == 0) {
            result = Kernel32.INSTANCE.GetLastError();
            System.err.println("hbmMask:" + result + " - " + getLastErrorStr(result));
        }
        */



       
int[] maskBits = maskBitsMem.getIntArray(0, size * size);
       
// Copy the mask alphas into the color bits
       
for (int i = 0; i < colorBits.length; i++) {
            colorBits
[i] = colorBits[i] | (maskBits[i] != 0 ? 0 : 0xFF000000);
       
}
       
// Release DC
        u32
.ReleaseDC(0, dc);


       
// Release bitmap handle in icon info
        g32
.DeleteObject(iconinfo.hbmColor); // add
        g32
.DeleteObject(iconinfo.hbmMask); // add


       
return colorBits;
   
}

Matthias Bläsing

unread,
Feb 18, 2019, 3:50:04 PM2/18/19
to jna-...@googlegroups.com
Hey Dávid,

some people on this list might recognise my answer: Java got more aggressive in recent 
time regarding memory management. GC memory is faster reclaimed and thus code that
worked in the past by sheer luck fails today. Oh and platforms are also a good failure 
source.

You will need to check all datatypes you are using, whether they depend on the platform
and if they correctly adapt.

In you case I spottet:

int nxt = u32.GetTopWindow(0);

HWND is a pointer and as such is 32bit wide on x86 and 64bit wide on x64. On the latter
platform, you will get an invalid value and you will damage the heap, because 32bit are
allocated for the value and 64bit are written.

The same holds true for structures and such. You need to use the correct datatypes.

I suggest to have a look at the platform package JNA provides. There is a huge number
of functions already bound. I won't claim it is bug free (I was already bitten hard by it),
but that being common code, there is a pretty good chance that someone before
you already found the bugs.

Greetings

Matthias
--
You received this message because you are subscribed to the Google Groups "Java Native Access" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dávid Karnok

unread,
Feb 19, 2019, 8:08:50 AM2/19/19
to jna-...@googlegroups.com
Thanks! My experience with the Windows API dates back to the 32bit era and I haven't been keeping up or needing to talk to it up until now.
--
Best regards,
David Karnok
Reply all
Reply to author
Forward
0 new messages