Java Native Access (JNA) class Vs interface

Fermé
Oliv - Modifié le 8 août 2020 à 02:32
 Oliv - 11 août 2020 à 01:16
Bonjour,

Voilà mon petit(?) soucis :
J'utilisais l'interface pour récupérer user32.dll de la manière suivante :
private interface User32 extends StdCallLibrary {
 static final User32 INSTANCE = (User32)Native.load (
  "user32", User32.class, W32APIOptions.DEFAULT_OPTIONS
 );
 class WINDOWPLACEMENT extends Structure {
  public int length;
  public int flags;
  public int showCmd;
  public POINT ptMinPosition;
  public POINT ptMaxPosition;
  public RECT rcNormalPosition;
  private WINDOWPLACEMENT(){
   length = size();
   
  // Lignes totalement inutiles, juste pour ne pas avoir de @SuppressWarnings("unused")
   List<Object>_null = Arrays.asList (
    length, flags, showCmd, ptMinPosition, ptMaxPosition, rcNormalPosition
   );
   _null.isEmpty();
  //===================================================================================
  }
  @Override
  protected List<String>getFieldOrder(){
   return Arrays.asList (
    "length", "flags", "showCmd", "ptMinPosition", "ptMaxPosition", "rcNormalPosition"
   );
  }
 }
 int GetWindowRect(HWND h_wnd, int _rect[]);
 int GetWindowTextA(HWND h_wnd, byte lp_string[], int n_max_count);
 boolean EnumWindows(WNDENUMPROC lp_enum_func, Pointer _pointer);
 boolean IsWindowVisible(HWND h_wnd);
 UINT SendInput(DWORD cInputs, INPUT pInputs[], int cbSize);
 BOOL SetForegroundWindow(HWND h_wnd);
 BOOL GetWindowPlacement(HWND h_wnd, WINDOWPLACEMENT lp_wnd_pl);
 BOOL SetWindowPlacement(HWND h_wnd, WINDOWPLACEMENT lp_wnd_pl);
 BOOL SetWindowPos (
  HWND h_wnd, HWND h_wnd_insert_after, int _x, int _y, int _cx, int _cy, int u_flag
 );
}



Je faisais tant bien que mal ce que je recherchais.
Las, j'ai voulu passer par la
class
au lieu de l'
interface
en faisant :
private static class User32 {
 static {
  Native.register("user32");
 }
 public static class WINDOWPLACEMENT extends Structure {
  public int length;
  public int flags;
  public int showCmd;
  public POINT ptMinPosition;
  public POINT ptMaxPosition;
  public RECT rcNormalPosition;
  public WINDOWPLACEMENT(){
   length = size();
   
   // Lignes totalement inutiles, juste pour ne pas avoir de @SuppressWarnings("unused")
   List<Object>_null = Arrays.asList (
    length, flags, showCmd, ptMinPosition, ptMaxPosition, rcNormalPosition
    );
   _null.isEmpty();
   //===================================================================================
  }
  @Override
  protected List<String>getFieldOrder(){
   return Arrays.asList (
    "length", "flags", "showCmd", "ptMinPosition", "ptMaxPosition", "rcNormalPosition"
   );
  }
 }
 private static native BaseTSD.LONG_PTR SetWindowLongPtrA(HWND hWnd, int nIndex, BaseTSD.LONG_PTR dwNewLongPtr);
 private static native HWND GetForegroundWindow();
 //private static native HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, int dwThreadId);
 //private static native UINT SendInput(DWORD cInputs, INPUT pInputs[], int cbSize);
 private static native BOOL GetWindowPlacement(HWND h_wnd, WINDOWPLACEMENT lp_wnd_pl);
 private static native BOOL SetWindowPlacement(HWND h_wnd, WINDOWPLACEMENT lp_wnd_pl);
 private static native Pointer SetWindowLongA(HWND hWnd, int nIndex, Pointer dwNewLong);
 private static native Pointer SetWindowLongPtrA(HWND hWnd, int nIndex, Pointer dwNewLongPtr);
 private static native LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, Pointer lParam);
 private static native LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam);
 private static native LRESULT DefWindowProcA(HWND hWnd, int Msg, WPARAM wParam, LPARAM lParam);
// private static native LRESULT DispatchMessage(MSG lpMsg);
 private static native void PostMessageA(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam);
 private static native void PostQuitMessage(int nExitCode);
// private static native int GetMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);
 private static native int GetWindowTextLengthA(HWND hWnd);
 private static native int GetWindowTextA(HWND hWnd, char[] lpString, int nMaxCount);
 private static native int GetWindowThreadProcessId(HWND hWnd, IntByReference lpdwProcessId);
 private static native int SetWindowLongA(HWND hWnd, int nIndex, int dwNewLong);
 private static native boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer data);
 private static native boolean GetWindowRect(HWND hWnd, RECT rect);
 private static native boolean IsWindowVisible(HWND hWnd);
 private static native boolean PeekMessageA(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax, int wRemoveMsg);
 private static native boolean SetForegroundWindow(HWND hWnd);
 private static native boolean SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
 private static native boolean ShowWindow(HWND hWnd, int nCmdShow);
 private static native boolean TranslateMessage(MSG lpMsg);
 private static native boolean UnhookWindowsHookEx(HHOOK hhk);
}

Et patatras !
Les fonctions natives en commentaires renvoient une erreur : méthode introuvable.
Et les fonctions :
SetWindowLongPtr
DefWindowProc
PostMessage
GetWindowTextLength
GetWindowText
SetWindowLong
PeekMessage

aussi, bien que les mêmes mais avec un "A" au bout de leur nom semblent fonctionner, mais sont-elles vraiment équivalentes ?.

Pourquoi ?
Comment obtenir la liste des fonctions natives et leur constructions pour les utiliser au sein d'une
class
?
Comment placer un
HHOOK
pour suivre les évènements des fenêtres (extérieures à la JVM) ? Rien ne semble remplacer
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, int dwThreadId)
LRESULT DispatchMessage(MSG lpMsg)
int GetMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax)

Doit-on créer une
interface
en plus de la
class
pour elles ?????

Quand à :
UINT SendInput(DWORD cInputs, INPUT pInputs[], int cbSize)

dont j'avais besoin pour pouvoir utiliser :
SetForegroundWindow(HWND hWnd)

Il ne compile carrément pas,
INPUT
ne serait pas un argument valide...

Que dois-je faire ?

Configuration: Windows / Chrome 79.0.3945.117
A voir également:

3 réponses

KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
8 août 2020 à 07:37
Bonjour,

Comme dans ta précédente discussion le problème concerne plus Windows que Java mais je dirais que tu n'as pas la bonne version de user32.dll (j'imagine qu'elle évolue d'un OS à un autre) et que les méthodes que tu essayes d'utiliser ne sont tout simplement pas dans la DLL
0
Ben non...
Merci quand même, mais comme expliqué (c'est même par là que je commence), si je fais les appels au natif par l'interface, toutes les méthodes sont bien là. Par la classe, non. Pour être encore plus clair :
Dans le même fichier du même programme, si je mets une classe ET une interface, avec les mêmes méthodes dans l'une et l'autre, le programme plante (méthode introuvable, argument non valide...) quand c'est la classe qui s'en sert, pas quand c'est l'interface.
Et encore une fois JAVA Native Access, c'est du Java. Ouvrir cette discussion dans un forum C ou Windows n'aurait aucun sens : je ne comprendrais rien aux réponses, si tant est qu'elles soient pertinentes.
Du coup j'enlève même le tag "Windows".
0
Bon. J'ai une première piste, du moins en ce qui concerne la fonction
UINT SendInput(DWORD c_inputs, INPUT p_inputs[], int cb_size)

Selon
https://java-native-access.github.io/jna/4.2.1/

"Use direct mapping of methods
Using direct mapping of methods makes native calls more efficiently than does interface mapping. Direct mapping does not support varargs calls or arrays of Pointer, String, or WString as an argument or return value. For optimium results, use only primitive arguments and return values; you'll have to convert to and from objects yourself explicitly."

Si je comprend bien ce serait le tableau d'
INPUT
qui poserait problème et non
INPUT
et ce, uniquement en cas de direct-mapping (
class
au lieu d'
interface
), ce qui est le cas.
Mais on ne nous dit pas comment y remédier. Les objets pour la conversion en question, on les devine comment ?
Et je ne sais toujours pas pourquoi les quelques autres fonctions (qui n'ont pourtant pas de tableau en paramètre) fonctionnent par l'
interface
et pas par la
class
.
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
9 août 2020 à 08:53
Je n'ai pas de PC avec moi ce week-end, je pourrais tester et te répondre lundi.
0
Oliv > KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024
Modifié le 9 août 2020 à 21:01
Oki, cool merci.
En attendant, j'avance un peu. Mais c'est pas simple, vu que :

contrairement à ce qui est indiqué là :
https://java-native-access.github.io/jna/4.2.0/com/sun/jna/platform/win32/User32.html

La procédure
GetWindowText(HWND hWnd, char[] lpString, int nMaxCount)

ne veut vraiment rien savoir,. En revanche, la procédure
GetWindowTextA(HWND hWnd, char[] lpString, int nMaxCount)

a au moins le mérite de faire comme si elle existait, en dépit de son absence de la doc.
Par contre, si elle renvoie "bien" la longueur en char du titre, rien à faire pour tirer quoi que ce soit de la copie du titre lui même.
char _copy[] = new char[512];
GetWindowTextA(h_wnd, _copy, 512);
System.out.println(new String(_copy));// --> seul le saut de ligne apparait
System.out.println(Native.toString(_copy));// --> tous les caractères sauf le dernier
                                           //       sont des "?"

Mais... En faisant :
GetWindowTextA(HWND hWnd, byte[] lpString, int nMaxCount)
// et oui, faut qu'ils arrêtent leur char

et
Native.toString(_copy)

Ouiiii. Bon. En fait la
 class WindowUtils
du jna contient une méthode
 static 
:
getWindowTitle(HWND hWnd)

Pfff.

Second point, j'ai pu résoudre le problème de :
SendInput(DWORD cInputs, INPUT pInputs[], int cbSize);

tiré de la même doc.
Impossible de trouver quoi que ce soit nul part pour m'aider, à part ce petit filet tiré de la doc et cité dans le commentaire précédant.
En désespoir de cause, je me dis que c'est quand même très bête.
Puis.. Que si c'est vraiment bête, ça donne quoi si on joue au plus bête ?
Pas de tableau en paramètre ni en retour, dans certains cas ? Ok. Alors on oublie les trucs compliqués, cast et consorts, et on vire juste les crochets :
SendInput(DWORD cInputs, INPUT pInputs, int cbSize);

Ouiiiii. Alors je ne sais pas ce qui est effectivement envoyé, vu que c'est seulement pour pouvoir gérer la fenêtre de premier plan (extérieure à la JVM donc), grâce à :
SetForegroundWindow(HWND hWnd)

Mais en tous cas, il me le permet. Hallucinant. Si si.

Pour les autres procédures (ouais tant qu'à faire... java c'est des méthode, C c'est des fonctions, ben jna -au moins dans Eclipse, c'est des procédures, na), rien de neuf, toujours inexistantes, invalides, intransformables... indevinables...
J'aimerais bien pouvoir disposer du
SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, int dwThreadId)

si jamais j'arrive à me faire aider du côté de la DLL (aaaah, ben voilà, j'irai du côté du forum Windows, les énerver un peu) qui ferait la même chose que mon code (dans l'autre discussion) mais en agissant sur les message reçus et envoyés par les fenêtres au lieu de corriger leurs conséquences après coup.
Bon. Je m'y remet...
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
10 août 2020 à 15:46
Bonjour,

J'ai regardé un peu plus en détail le fonctionnement de JNA en particulier sur l'interface StdCallLibrary que tu as utilisé et il se trouve que tu pourrais aller plus loin car il y a une interface User32 prête à l'emploi :

Exemple :
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.RECT;

public class TestUser32 {
    public static void main(final String[] args) {
        final User32 user32 = User32.INSTANCE;
        final RECT rect = new RECT();
        user32.GetWindowRect(user32.GetForegroundWindow(), rect);
        System.out.println(rect);
    }
}

Voir la source ici : https://github.com/java-native-access/jna/blob/master/contrib/platform/src/com/sun/jna/platform/win32/User32.java
0
User32.INSTANCE
EST StdCallLibrary
Cette discussion-ci a pour but de se débarrasser de l'interface, au profit du direct-mapping (une classe) censé être plus performant.
Effectivement, tout ce que je décris ici ne concerne pas l'interface, où tout est fonctionnel (voir code complet et fonctionnel dans l'autre discussion). Mais... bla bla bla, tout relire, c'est de ça que ça parle.
0