2

I am using jna to call Magnification api functions in Java.

MagImageScalingCallback.java

package jna.extra;

import com.sun.jna.Callback;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HRGN;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;

public interface MagImageScalingCallback extends Callback{
    public Boolean MagImageScalingCallback(HWND hwnd, Pointer srcdata,MAGIMAGEHEADER srcheader, Pointer destdata,MAGIMAGEHEADER destheader,RECT source,RECT clipped,HRGN dirty);
}

MAGIMAGEHEADER.java

package jna.extra;

import java.util.Arrays;
import java.util.List;

import com.sun.jna.platform.win32.Guid.GUID;


public class MAGIMAGEHEADER extends com.sun.jna.Structure {
    public int width;
    public int height;
    public GUID format;
    public int stride;
    public int offset;
    public int cbsize;

    public List getFieldOrder() {
        return Arrays.asList("width","height","format","stride","offset","cbsize");
    }
}

Magnification.java

package jna.extra;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;


public interface Magnification extends StdCallLibrary {

    Magnification INSTANCE = (Magnification) Native.loadLibrary("magnification", Magnification.class,
                                                W32APIOptions.DEFAULT_OPTIONS);

    public Boolean MagInitialize();

    public boolean MagSetWindowFilterList(HWND hwndMag, DWORD dword, int i,
            HWND[] excludeHWNDs);

    public boolean MagSetWindowSource(HWND hwndMag, RECT sourceRect);

    public void MagGetWindowFilterList(HWND hwndMag, DWORD dword, int i, HWND[] test);

    public boolean MagSetImageScalingCallback(HWND hwndMag,MagImageScalingCallback MagImageScalingCallback);

    public MagImageScalingCallback MagGetImageScalingCallback(HWND hwndMag);

}

WinGDIExtra.java

package jna.extra;

import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinGDI;

public interface WinGDIExtra extends WinGDI {

    public DWORD SRCCOPY = new DWORD(0x00CC0020);
    public DWORD SRCPAINT = new DWORD(0x00ee0086);
    public DWORD SRCAND = new DWORD(0x008800c6);
    public DWORD SRCINVERT = new DWORD(0x00660046);
    public DWORD SRCERASE = new DWORD(0x00440328);

    public DWORD NOTSRCCOPY = new DWORD(0x00330008);
    public DWORD NOTSRCERASE = new DWORD(0x001100a6);
    public DWORD MERGECOPY = new DWORD(0x00c000ca);
    public DWORD MERGEPAINT = new DWORD(0x00bb0226);

    public DWORD PATCOPY = new DWORD(0x00f00021);
    public DWORD PATPAINT = new DWORD(0x00fb0a09);
    public DWORD PATINVERT = new DWORD(0x005a0049);
    public DWORD DSTINVERT = new DWORD(0x00550009);
    public DWORD WHITENESS = new DWORD(0x00ff0062);
    public DWORD BLACKNESS = new DWORD(0x00000042);
    public DWORD CAPTUREBLT = new DWORD(0x00CC0020 | 0x40000000);
    public DWORD Black = new DWORD(0x00000000);

    public long WS_CHILD = 0x40000000L;
    public long WS_VISIBLE = 0x10000000L;
    public long MS_SHOWMAGNIFIEDCURSOR = 0x0001L; 


    public long WS_EX_TOPMOST = 0x00000008L;
    public long WS_EX_LAYERED = 0x00080000;
    public long WS_EX_TRANSPARENT = 0x00000020L;

    public long WS_CLIPCHILDREN = 0x02000000L;

    public long MW_FILTERMODE_EXCLUDE = 0;

}

My code

package jna.extra;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HRGN;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import luz.winapi.jna.User32;

public class screenSkip {

    public static void main(String[] args){
        if(!Magnification.INSTANCE.MagInitialize()){
            System.out.println("Cannot Intialize Magnification API");
            System.exit(0);
        }

        RECT desktopRect= new RECT();
        HWND desktop = User32.INSTANCE.GetDesktopWindow();
        if(desktop==null){
            System.out.println("Problem with Desktop");
            System.exit(0);
        }
        if(!User32Extra.INSTANCE.GetWindowRect(desktop, desktopRect)){
            System.err.println("Cannot get window rect");
            System.exit(0);
        }
        HWND top = User32Extra.INSTANCE.CreateWindowEx(new DWORD(WinGDIExtra.WS_EX_TOPMOST | WinGDIExtra.WS_EX_LAYERED | WinGDIExtra.WS_EX_TRANSPARENT), "#32770", "Parent Window", new DWORD(WinGDIExtra.WS_CLIPCHILDREN), desktopRect.left, desktopRect.top, desktopRect.right-desktopRect.left, desktopRect.bottom-desktopRect.top, desktop, null, null, null);

        if(top==null){
            System.out.println("Problem while creating Parent Window and the error is "+Native.getLastError());
            System.exit(0);
        }

        HWND hwndMag=null;
        System.out.println(Native.getLastError());
        hwndMag = User32Extra.INSTANCE.CreateWindowEx(null, "Magnifier", "MagWindow", new DWORD(WinGDIExtra.WS_CHILD | WinGDIExtra.MS_SHOWMAGNIFIEDCURSOR | WinGDIExtra.WS_VISIBLE), desktopRect.left, desktopRect.top, desktopRect.right-desktopRect.left, desktopRect.bottom-desktopRect.top, top, null, Kernel32.INSTANCE.GetModuleHandle(null), null);
        if(hwndMag==null){
            System.err.println("Problem while creating Magnifier Window and the error is "+Native.getLastError());
            System.exit(0);
        }

        RECT sourceRect= new RECT();

        if(!User32Extra.INSTANCE.GetWindowRect(desktop, sourceRect)){
            System.err.println("Cannot get window rect");
            System.exit(0);
        }

        final BufferedImage image = new BufferedImage(1366, 768, BufferedImage.TYPE_INT_RGB);
        if(!Magnification.INSTANCE.MagSetImageScalingCallback(hwndMag,new MagImageScalingCallback() {

            public Boolean MagImageScalingCallback(HWND hwnd, Pointer srcdata,
                    MAGIMAGEHEADER srcheader, Pointer destdata,
                    MAGIMAGEHEADER destheader, RECT source, RECT clipped, HRGN dirty) {
                image.setRGB(0, 0, srcheader.width, srcheader.height, srcdata.getIntArray(0, srcheader.width * srcheader.height ), 0, srcheader.width);
                return true;
            }
        })){
            System.err.println("Error occured while setting callback");
            System.exit(0);
        }
        if (!Magnification.INSTANCE.MagSetWindowSource(hwndMag, sourceRect))
        {
            System.err.println("Cannot copy");
            System.exit(0);
        }
        try {
            ImageIO.write(image, "JPEG", new File("printed1.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!Magnification.INSTANCE.MagSetWindowSource(hwndMag, sourceRect))
        {
            System.err.println("Cannot copy");
            System.exit(0);
        }
    }
}

If the MagSetWindowSource function is called the MagImageScalingCallback function is called.

The problem is if I run this code with jre7(64 bit) everything works fine. But if I run the same code in jre7(32 bit), I am getting the following error.

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x10002a21, pid=5552, tid=4884
#
# JRE version: Java(TM) SE Runtime Environment (7.0_80-b15) (build 1.7.0_80-b15)
# Java VM: Java HotSpot(TM) Client VM (24.80-b11 mixed mode, sharing windows-x86 )
# Problematic frame:
# C  [jna6797525900717560222.dll+0x2a21]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

Stack: [0x05c60000,0x05cb0000],  sp=0x05cae4d0,  free space=313k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [jna6797525900717560222.dll+0x2a21]
j  com.sun.jna.Pointer._getInt(J)I+0
j  com.sun.jna.Pointer.getInt(J)I+6
j  com.sun.jna.Pointer.getValue(JLjava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;+340
j  com.sun.jna.Structure.readField(Lcom/sun/jna/Structure$StructField;)Ljava/lang/Object;+168
j  com.sun.jna.Structure.read()V+82
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.convertArgument(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;+330
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback([Ljava/lang/Object;)Ljava/lang/Object;+95
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.callback([Ljava/lang/Object;)Ljava/lang/Object;+2
v  ~StubRoutines::call_stub
V  [jvm.dll+0x1429aa]
V  [jvm.dll+0x20743e]
V  [jvm.dll+0x142a2d]
V  [jvm.dll+0xcb7b2]
V  [jvm.dll+0xcd5df]
C  [jna6797525900717560222.dll+0x9dd2]
C  [jna6797525900717560222.dll+0xa47f]
C  [jna6797525900717560222.dll+0xc864]
C  [jna6797525900717560222.dll+0xcdee]
C  0x00b80012
C  [magnification.dll+0x5434]
C  [magnification.dll+0x5a2c]
C  [magnification.dll+0x5f5d]
C  [magnification.dll+0x612a]
C  [magnification.dll+0x28ab]
C  [USER32.dll+0x162fa]
C  [USER32.dll+0x16d3a]
C  [USER32.dll+0x1965e]
C  [USER32.dll+0x196c5]
C  [magnification.dll+0x22e2]
C  [jna6797525900717560222.dll+0xcc77]
C  [jna6797525900717560222.dll+0xc78a]
C  [jna6797525900717560222.dll+0x4561]
C  [jna6797525900717560222.dll+0x4d2e]
j  com.sun.jna.Function.invokeInt(I[Ljava/lang/Object;)I+0
j  com.sun.jna.Function.invoke([Ljava/lang/Object;Ljava/lang/Class;Z)Ljava/lang/Object;+315
j  com.sun.jna.Function.invoke(Ljava/lang/Class;[Ljava/lang/Object;Ljava/util/Map;)Ljava/lang/Object;+214
j  com.sun.jna.Library$Handler.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+341
j  com.sun.proxy.$Proxy5.MagSetWindowSource(Lcom/sun/jna/platform/win32/WinDef$HWND;Lcom/sun/jna/platform/win32/WinDef$RECT;)Z+20
j  org.redfire.screen.ScreenShare$CaptureScreen.run()V+674
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub
V  [jvm.dll+0x1429aa]
V  [jvm.dll+0x20743e]
V  [jvm.dll+0x142b75]
V  [jvm.dll+0x142bd7]
V  [jvm.dll+0xed5cf]
V  [jvm.dll+0x163c4c]
V  [jvm.dll+0x1646a7]
V  [jvm.dll+0x1a92f9]
C  [msvcr100.dll+0x5c556]
C  [msvcr100.dll+0x5c600]
C  [kernel32.dll+0x133aa]
C  [ntdll.dll+0x39f72]
C  [ntdll.dll+0x39f45]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  com.sun.jna.Pointer._getInt(J)I+0
j  com.sun.jna.Pointer.getInt(J)I+6
j  com.sun.jna.Pointer.getValue(JLjava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;+340
j  com.sun.jna.Structure.readField(Lcom/sun/jna/Structure$StructField;)Ljava/lang/Object;+168
j  com.sun.jna.Structure.read()V+82
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.convertArgument(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;+330
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback([Ljava/lang/Object;)Ljava/lang/Object;+95
j  com.sun.jna.CallbackReference$DefaultCallbackProxy.callback([Ljava/lang/Object;)Ljava/lang/Object;+2
v  ~StubRoutines::call_stub
j  com.sun.jna.Function.invokeInt(I[Ljava/lang/Object;)I+0
j  com.sun.jna.Function.invoke([Ljava/lang/Object;Ljava/lang/Class;Z)Ljava/lang/Object;+315
j  com.sun.jna.Function.invoke(Ljava/lang/Class;[Ljava/lang/Object;Ljava/util/Map;)Ljava/lang/Object;+214
j  com.sun.jna.Library$Handler.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+341
j  com.sun.proxy.$Proxy5.MagSetWindowSource(Lcom/sun/jna/platform/win32/WinDef$HWND;Lcom/sun/jna/platform/win32/WinDef$RECT;)Z+20
j  org.redfire.screen.ScreenShare$CaptureScreen.run()V+674
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub


How to solve this issue? How can I make this work on a 32-bit JRE? Thanks!

Vishnu
  • 11,614
  • 6
  • 51
  • 90

1 Answers1

3

Firstly, the MagImageScalingCallback is defined as a WINAPI function, which means you have to define the callback as being a child of StdCallCallback.

Secondly, the return type is a simple boolean (you should not use Boolean for this).

Thirdly, the parameters of the callback are MAGIMAGEHEADER, and not MAGIMAGEHEADER *, and the RECT is not a RECT *, which means they needs to be passed by value; so the callback declaration needs to be changed like so:

I added a RectByValue class, containing the following:

import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.Structure;

public class RectByValue extends WinDef.RECT implements Structure.ByValue {}

I changed the MagImageScalingCallback class with the following:

import com.sun.jna.win32.StdCallLibrary;

public interface MagImageScalingCallback extends StdCallLibrary.StdCallCallback {
   public boolean MagImageScalingCallback(HWND hwnd, Pointer srcdata,MAGIMAGEHEADER.ByValue srcheader, Pointer destdata,MAGIMAGEHEADER.ByValue destheader,RectByValue source,RectByValue clipped,HRGN dirty);
}

You will need to add ByValue support to MAGIMAGEHEADER; and also use a SIZE_T for the last element of the structure:

import java.util.Arrays;
import java.util.List;

import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;

public class MAGIMAGEHEADER extends Structure {
    public int width;
    public int height;
    public GUID format;
    public int stride;
    public int offset;
    public BaseTSD.SIZE_T cbsize;

    public List getFieldOrder() {
        return Arrays.asList("width","height","format", "stride","offset","cbsize");
    }

    public static class ByValue extends MAGIMAGEHEADER implements Structure.ByValue {
        public ByValue() {}

        public ByValue(MAGIMAGEHEADER magimageheader) {
            super (magimageheader.getPointer());

            width = magimageheader.width;
            height = magimageheader.height;
            format = magimageheader.format;
            stride = magimageheader.stride;
            offset = magimageheader.offset;
            cbsize = magimageheader.cbsize;
        }

        public ByValue(Pointer memory) {
            super(memory);
        }
    }

    public MAGIMAGEHEADER(Pointer memory) {
        super(memory);
        read();
    }

    public MAGIMAGEHEADER() {
    }
}

I changed the screen size to match my display on screenSkip so it's new BufferedImage(2160, 1440... instead of 1366, 786, and changed the callback code creator to:

if(!Magnification.INSTANCE.MagSetImageScalingCallback(hwndMag, new MagImageScalingCallback() {

    public boolean MagImageScalingCallback(HWND hwnd, Pointer srcdata,
            MAGIMAGEHEADER.ByValue srcheader, Pointer destdata,
            MAGIMAGEHEADER.ByValue destheader, RectByValue source, RectByValue clipped, HRGN dirty) {
        image.setRGB(0, 0, srcheader.width, srcheader.height, srcdata.getIntArray(0, srcheader.width * srcheader.height ), 0, srcheader.width);
        return true;
    }
})){
    System.err.println("Error occured while setting callback");
    System.exit(0);
}

The end product of running this in 32bit or 64bit mode is a file called This is about as minimal as I can think will achieve what you want.

.... huh ... it gives me a blank image on 32bit. That seems incorrect.

Anya Shenanigans
  • 91,618
  • 3
  • 107
  • 122
  • Thanks! I cannot find any `StdCallLibrary.StdCallback` interface. So, I have used `StdCallLibrary.StdCallCallback`. But I am getting the same error. – Vishnu Aug 17 '15 at 08:14
  • @Mahesh please add your definition for all the relevant classes (e.g. MAGIMAGEHEADER) as you may have defined them incorrectly. – Anya Shenanigans Aug 17 '15 at 09:06
  • @Mahesh there are a few changes needed. I'll explain in the answer – Anya Shenanigans Aug 17 '15 at 11:31
  • @Mahesh I have your code, with a minor change for `CreateWindowEx` - I used `User32Util.createWindowEx`, and added a class implementing `GetWindowRect` & `GetDesktopWindow` just to get past those missing classes working, producing the .jpg for java 7 32bit, java 8 32bit & java 8 64bit. – Anya Shenanigans Aug 17 '15 at 12:26
  • @Petesh can you add the modifications in the answer. – Vishnu Aug 17 '15 at 12:29
  • @Mahesh it's all pushed to github at: https://github.com/petesh/jna-magnification – Anya Shenanigans Aug 17 '15 at 12:33
  • @Petesh The `RECT` parameters are by value too. The header file reads: `typedef BOOL (CALLBACK* MagImageScalingCallback)(HWND hwnd, void * srcdata, MAGIMAGEHEADER srcheader, void * destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty );` – David Heffernan Aug 17 '15 at 12:37
  • @Petesh I just want to clarify. Do you get the desktop image if you use java 7 32 bit? – Vishnu Aug 17 '15 at 12:54
  • @DavidHeffernan oops, missed that, even though it worked (slap myself for insufficient checking). I've updated the github repo with the relevant changes and will update the answer – Anya Shenanigans Aug 17 '15 at 12:54
  • @Petesh It's a very weird callback prototype. Looks like it was written by somebody with little experience of Win32, and not properly reviewed. All of the structs should be passed by ref by convention. – David Heffernan Aug 17 '15 at 13:04
  • @Mahesh I get a blank image on a 32bit JRE, and it invokes the callback twice. The kicker is in the line [Note The Magnification API is not supported under WOW64; that is, a 32-bit magnifier application will not run correctly on 64-bit Windows.](https://msdn.microsoft.com/en-us/library/windows/desktop/ms692162%28v=vs.85%29.aspx). – Anya Shenanigans Aug 17 '15 at 13:22
  • As I've said to you before Mahesh, `MagImageScalingCallback` is deprecated. The documentation states that clearly. I'm surprised that you have ignored this advice. – David Heffernan Aug 17 '15 at 15:20
  • @Petesh Even if I run this on a 32 bit windows with 32 bit JRE, I am getting blank screen. What might be the reason for that? is this programatical error or limitation of Magnification API. – Vishnu Aug 19 '15 at 05:08
  • @DavidHeffernan `MagImageScalingCallback` is even working well in Windows 10. I don't know the reason why it is working(even though deprecated). As it is working I am using that. – Vishnu Sep 15 '15 at 05:49