Pregunta

I'm looking for solution to make external application (not windows application like notepad or calc.exe) to stay always-on-top after pressing the button in Java GUI. I'm using this code in C++ for taking all opened windows on desktop and matching their Process ID (PID) with sent PID (from my app in Java) :

     #include "cjni.h"
     #include <cstdlib>
     #include <iostream>
     #include <windows.h>

     using namespace std;

     BOOL CALLBACK EnumWindowsProc(HWND windowHandle, LPARAM lParam){

 DWORD searchedProcessId = (DWORD)lParam;
 DWORD windowProcessId = 0;
 GetWindowThreadProcessId(windowHandle, &windowProcessId);
 printf("process id=%d\n", windowProcessId);


 if(searchedProcessId == windowProcessId) {
    HWND hwnd = windowHandle;
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
    printf("Process ID found !");
    return FALSE;
}
return TRUE;
      }




       JNIEXPORT void JNICALL Java_gui_CJNI_AlwaysOnTop
      (JNIEnv *env, jclass jobj, jint processId) {

     //(*env)->EnumWindows(&EnumWindowsProc, (LPARAM)processId);  
     EnumWindows(&EnumWindowsProc, (LPARAM)processId);   

         }

Implementation in Java JNI:

    package gui;

    public class CJNI {

    static {
    System.loadLibrary("cjni");
    }

    static native void AlwaysOnTop(int processId);



    public void metoda(final int processId) {

        //AlwaysOnTop(processId);

    }

In Java I'm using this code for getting PID of selected process:

    public int getPID(Process p) {

    try {
        Field f = p.getClass().getDeclaredField("handle");
        f.setAccessible(true);
        long handl = f.getLong(p);

        Kernel32 kernel = Kernel32.INSTANCE;
        WinNT.HANDLE handle = new WinNT.HANDLE();
        handle.setPointer(Pointer.createConstant(handl));

        return kernel.GetProcessId(handle);

    } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
        return -1;
    }

}

My program works fine with MS windows applications, makes them always on top. Unfortunately, external applications didn't take always on top. I'm using SetWindowPos(); method from C++. It works when I select external program window by GetForegroundWindow() and put this window Handle (HWND) as an argument on SetWindowPos(); Here's code working with external applications with always on top (but I must select application window by my own - selecting by mouse):

    #include <windows.h>
    #include <iostream>

     using namespace std;


    int main(){

    cout << "Select window within 2 seconds\n";
        Sleep(2000);
        HWND hWnd = GetForegroundWindow();

    //HWND hWnd = (HWND)0x8036c;

     SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

         cout <<"Number hwnd: " << hWnd << endl;
         cout << "Always on top set on window.\n";
         return 0;
       }

Is it possible to takie the implementation of method in C++ from JNI, and use JNA, to open external and set applications ALWAYS ON TOP, using Java GUI ?

         /*
          * To change this license header, choose License Headers in Project Properties.
          * To change this template file, choose Tools | Templates
          * and open the template in the editor.
          */
          package gui;

          import com.sun.jna.Pointer;
          import com.sun.jna.platform.win32.Kernel32;
          //import com.sun.jna.platform.win32.User32;
          //import com.sun.jna.platform.win32.WinDef;
          import com.sun.jna.platform.win32.WinNT;
          //import com.sun.jna.platform.win32.WinUser;

          //import com.sun.jna.win32.StdCallLibrary;


         import java.io.IOException;
         import java.lang.reflect.Field;
         import java.util.logging.Level;
         import java.util.logging.Logger;


        //import com.sun.jna.platform.win32.WinDef.DWORD;
        //import com.sun.jna.platform.win32.WinNT.HANDLE;

        /**
          *
          * @author adrians
          */
 public class Test extends javax.swing.JFrame /*implements WndEnumProc*/ {


   long startTime;
   long stopTime;

/**
 * Creates new form Test
 */
public Test() {

    initComponents();
}



/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    jButton1 = new javax.swing.JButton();
    jButton2 = new javax.swing.JButton();
    jButton3 = new javax.swing.JButton();
    jButton4 = new javax.swing.JButton();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    jButton1.setText("Kalkulator");
    jButton1.setMaximumSize(new java.awt.Dimension(87, 23));
    jButton1.setMinimumSize(new java.awt.Dimension(87, 23));
    jButton1.setPreferredSize(new java.awt.Dimension(83, 23));
    jButton1.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jButton1ActionPerformed(evt);
        }
    });

    jButton2.setText("Notatnik");
    jButton2.setMaximumSize(new java.awt.Dimension(87, 23));
    jButton2.setMinimumSize(new java.awt.Dimension(87, 23));
    jButton2.setPreferredSize(new java.awt.Dimension(83, 23));
    jButton2.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jButton2ActionPerformed(evt);
        }
    });

    jButton3.setText("SeaNet Pro");
    jButton3.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jButton3ActionPerformed(evt);
        }
    });

    jButton4.setText("Paint");
    jButton4.setMaximumSize(new java.awt.Dimension(87, 23));
    jButton4.setMinimumSize(new java.awt.Dimension(87, 23));
    jButton4.setPreferredSize(new java.awt.Dimension(87, 23));
    jButton4.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jButton4ActionPerformed(evt);
        }
    });

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(50, 50, 50)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jButton4, javax.swing.GroupLayout.DEFAULT_SIZE, 127, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
                    .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
                    .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)))
            .addContainerGap(53, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(30, 30, 30)
            .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(25, 25, 25)
            .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(25, 25, 25)
            .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(25, 25, 25)
            .addComponent(jButton4, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(30, Short.MAX_VALUE))
    );

    pack();
}// </editor-fold>                        

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    run("calc.exe");
}                                        

private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    run("notepad.exe");
}                                        

private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    run("\"C:\\Program Files\\vlc-2.1.1\\vlc.exe\"");
}                                        

private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {                                         
    run("mspaint.exe");
}                                        

public void run(String name) {

    try {
        Process process = Runtime.getRuntime().exec(name);
        final int pid = getPID(process);

        System.out.println("Program name: " + name + ", PID=" + pid);

        new Thread(new Runnable() {
            public void run() {
                try {

                    startTime = System.currentTimeMillis();

                    Thread.sleep(150);
                    CJNI.AlwaysOnTop(pid);

                    stopTime = System.currentTimeMillis() - startTime;
                    System.out.println("Time: "+stopTime);

                } catch (InterruptedException ex) {
                    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }).start();
    } catch (IOException ex) {
        Logger.getLogger(Okno.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public int getPID(Process p) {

    try {
        Field f = p.getClass().getDeclaredField("handle");
        f.setAccessible(true);
        long handl = f.getLong(p);

        Kernel32 kernel = Kernel32.INSTANCE;
        WinNT.HANDLE handle = new WinNT.HANDLE();
        handle.setPointer(Pointer.createConstant(handl));

        //final User32 user32 = User32.INSTANCE;

        return kernel.GetProcessId(handle);

    } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
        return -1;
    }

}


/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(Test.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(Test.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(Test.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(Test.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new Test().setVisible(true);
        }
    });
}

// Variables declaration - do not modify                     
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JButton jButton4;
// End of variables declaration                   


}

Please help. I can't set always on top for external applications (non MS windows software). I'm using JNA 3.0.0 version.

-----------------------------------------------

I'm trying to take C++ Win Api methods (from my first question above) - EnumWindowsProc, EnumWindows GetWindowThreadProcessId and SetWindowPos - to the Java code implementation, to simplify code of my application. I tried to move functionality of C++/JNI code to the JNA. Unfortunately, I am able to print only the handles (HWND) of all desktop windows with the window titles, without the PID.

I would like to send process ID of oppened program (exe file) in Java, to implementation of EnumWindows in Java JNA, and to search this Process ID by every opened window on desktop (in EnumWindowsProc method) in Java JNA. Then I would like to compair windowHandle of send process ID with windowHandle`s of opened windows on the desktop. After finding the windowHandle of send process ID i would like to call method SetWindowPos which allows me to set opened window on Always on Top (Top Most). In other words, I would like to copy functions from C++/JNI to the Java code by JNA.

Here's my code:

import com.sun.jna.Pointer;
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.win32.StdCallLibrary;

   public class n {
     // Equivalent JNA mappings
      public interface User32 extends StdCallLibrary {
        User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);

        interface WNDENUMPROC extends StdCallCallback {
        boolean callback(Pointer hWnd, Pointer arg);
        }

        boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg);

       int GetWindowTextA(Pointer hWnd, byte[] lpString, int nMaxCount);

       //int GetWindowThread(Pointer hWnd, int windowProcessId);
   }

  public static void main(String[] args) {
    final User32 user32 = User32.INSTANCE;

    user32.EnumWindows(new User32.WNDENUMPROC() {

        int count;

        public boolean callback(Pointer hWnd, Pointer userData) {

            /*
            Pointer searchedProcessId = userData;
            int windowProcessId = 0;

            user32.GetWindowThread(searchedProcessId, windowProcessId);

            System.out.println("Process id = "+user32.GetWindowThread(searchedProcessId, windowProcessId));
            */


            byte[] windowText = new byte[512];
            user32.GetWindowTextA(hWnd, windowText, 512);
            String wText = Native.toString(windowText);
            wText = (wText.isEmpty()) ? "" : "; text: " + wText;
            System.out.println("Found window " + hWnd + ", total " + ++count + wText);


            return true;
        }
    }, null);
  }
 }

My second problem is when I try to set Aways On Top for external app, which exe file generates more then one PID (process ID), because it's not working. What could be wrong? For software, that generates only one Process ID it's working, for another software (exe file) which generates more then one PID (for example when I open Adobe Reader, which generates two pid for one exe file) it's not working.

I would be very grateful for help in moving functionalities from my code in C++/JNI to the JNA. I would like to resolves those problems in Java code by JNA.

¿Fue útil?

Solución

Your JNI/C++ implementation stops enumerating windows after the first one if found for a process. If that window is a dummy invisible window, you can't process the others for that process: you should always return TRUE in EnumWindowsProc.

Also, don't bother to use SetWindowPos for invisible Windows.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top