无法在Java / C++中为外部应用程序设置always-on-top

2024-02-06

我正在寻找解决方案,使外部应用程序(不是像记事本或 calc.exe 这样的 Windows 应用程序)在按下 Java GUI 中的按钮后始终保持在最上面。 我在 C++ 中使用此代码来获取桌面上所有打开的窗口,并将其进程 ID (PID) 与发送的 PID(来自我的 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);   

         }

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);

    }

在 Java 中,我使用此代码来获取所选进程的 PID:

    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;
    }

}

我的程序与 MS Windows 应用程序配合良好,使它们始终位于最前面。不幸的是,外部应用程序并不总是占据首位。我正在使用 SetWindowPos();来自 C++ 的方法。当我通过 GetForegroundWindow() 选择外部程序窗口并将此窗口句柄 (HWND) 作为 SetWindowPos() 的参数时,它会起作用; 这是与外部应用程序一起使用的代码,始终位于顶部(但我必须自己选择应用程序窗口 - 通过鼠标选择):

    #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;
       }

是否可以从 JNI 中获取 C++ 方法的实现,并使用 JNA,使用 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                   


}

请帮忙。 我无法将外部应用程序(非 MS Windows 软件)设置为始终位于顶部。 我使用的是 JNA 3.0.0 版本。

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

我正在尝试将 C++ Win Api 方法(来自上面的第一个问题) - EnumWindowsProc、EnumWindows GetWindowThreadProcessId 和 SetWindowPos - 应用于 Java 代码实现,以简化我的应用程序的代码。 我尝试将 C++/JNI 代码的功能移至 JNA。不幸的是,我只能打印所有带有窗口标题的桌面窗口的句柄(HWND),而没有 PID。

我想将Java中打开的程序(exe文件)的进程ID发送到Java JNA中EnumWindows的实现,并通过Java JNA中桌面上每个打开的窗口(在EnumWindowsProc方法中)搜索该进程ID。然后我想将发送进程ID的windowHandle与桌面上打开的窗口的windowHandle进行比较。找到发送进程 ID 的 windowHandle 后,我想调用方法 SetWindowPos,它允许我将打开的窗口设置为始终位于顶部(最顶部)。 换句话说,我想通过JNA将函数从C++/JNI复制到Java代码。

这是我的代码:

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);
  }
 }

我的第二个问题是,当我尝试为外部应用程序设置 Aways On Top 时,该 exe 文件会生成多个 PID(进程 ID),因为它不起作用。可能出什么问题了?对于仅生成一个进程 ID 的软件,它可以正常工作,对于另一种生成多个 PID 的软件(exe 文件)(例如,当我打开 Adob​​e Reader 时,它为一个 exe 文件生成两个 pid),它无法正常工作。

我将非常感谢您将功能从 C++/JNI 代码转移到 JNA 的帮助。 我想通过 JNA 解决 Java 代码中的这些问题。


如果找到进程,您的 JNI/C++ 实现将在第一个窗口之后停止枚举窗口。如果该窗口是虚拟的不可见窗口,则您无法处理该进程的其他窗口:您应该始终返回TRUE in EnumWindowsProc.

另外,不用费心去使用SetWindowPos对于隐形窗户。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法在Java / C++中为外部应用程序设置always-on-top 的相关文章

随机推荐