JNI - Ajuda Urgente - Cliques do Mouse no Desktop

9 respostas
KIMBA

Seguinte pessoas…

Tenho uma aplicação Comercial feita em JAVA Desktop com swing, com Cad. Clientes, Produtos, Vendas, Compras, Caixa, E por aí afora… uma coisa meio monstra de grande.

Agora, estou precisando urgentemente, arrumar uma forma de colocar essa aplicação para enxergar o desktop do windows… explicando melhor:

Num determinado momento, o usuário deverá clicar em determinadas áreas do Desktop, botões, ícones, menu iniciar e etc, e eu gostaria que o programa trouxesse mensagens informativas com referência aquele ítem.
Por exemplo, minha aplicação tá lá, bonitinha, rodando, e o usuário clica no Internet Explorer… o sistema deverá capaturar o evento do click, e exibir uma mensagem por exemplo: “Visite o site de nossa empresa em http://www.minhaempresa.com
Ou, ao clicar no relógio por exemplo, Aparecesse a mesnagem: " Está na hora de rodar a auditoria do sistema, Deseja iniciar agora?"

E mais ou menos isso… bom, meu problema na realidade é apenas um, conseguir que o Java “escute” os clickes fora da aplicação.

Pela classe Robot, eu consigo pegar o posição do mouse, e até definir onde a mensagem será exibida na tela, porém, ao que todas minhas pesquisas indicam, apenas com JNI eu consigo “escutar” os cliques. Bom, em sendo essa a unica solução que pelo menos eu consegui enxergar, ou seja, JNI, eu pergunto aos senhores…

Alguém por acaso saberia como fazer essa biblioteca em C e como passar os eventos para o Java? Pq não sei absolutamente nada de Linguacem C, e pouco, muito pouco de JNI, não passei ainda do HeloWorld.

Agradeço desde já.

9 Respostas

KIMBA

Bom após muita busca cheguei ao seguinte resultado parcial:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */



import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Hook{
 public  native void InitJavaInterface (String dirpath, String name, String kname, String mname);
 public native void StopJavaInterface ();
 public native int SetKeyboardHook (int value);
 public native int SetMouseHook (int value);
  
  static{
    System.loadLibrary("myhooks");
    }
  
public static void main(String [] args){
  Hook hk = new Hook();
  hk.InitJavaInterface("-Djava.class.path=c:\\temp\\testhook\\","Test","kbd","mouse");
  
  /**int t = hk.SetMouseHook(1);
  System.out.println(t +"\n");**/
  
  int t = hk.SetKeyboardHook (1);
  System.out.println(t +"\n");
  t  = hk.SetMouseHook(1);
   System.out.println(t +"\n");
   
   
}
}

myhooks.cpp

// myhooks.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "myhooks.h"

#include <stdlib.h>
#include <stdio.h>
#include <jni.h>


#pragma data_seg(".SharedData")
#pragma data_seg( )
HHOOK hLLKeyboardHook = NULL;						
HHOOK hLLMouseHook = NULL;							
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *g_env;       /* pointer to native method interface */
jclass cls ;
jmethodID midKBD = 0;
jmethodID midMOUSE = 0;
DWORD hookThreadId = 0;



#ifdef _MANAGED
#pragma managed(push, off)
#endif
kbcallback kbfun=0;
mscallback msfun=0;
LRESULT CALLBACK LowLevelKeyboardFilterProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK LowLevelMouseFilterProc(int nCode, WPARAM wParam, LPARAM lParam);
HKEY hModuleKey = NULL;								// Key used to save settings
HINSTANCE hInstance = NULL;							// This instance of the DLL

void MsgLoop() {
MSG message;

while (GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}

DllExport BOOL SetMouseFilterHook(BOOL activate)
{
	if (activate)
	{
		
		if (hLLMouseHook == NULL)
		{
			// Start up the hook...
			hLLMouseHook = SetWindowsHookEx(
				WH_MOUSE_LL,					// Hook in before msg reaches app
				(HOOKPROC) LowLevelMouseFilterProc,			// Hook procedure
				hInstance,						// This DLL instance
				0L								// Hook in to all apps
				);
			if (hLLMouseHook == NULL)
				return FALSE;
		}
		return TRUE;
	} else {
		if (hLLMouseHook != NULL)
		{
			// Stop the hook...
			if (!UnhookWindowsHookEx(hLLMouseHook))
				return FALSE;
			hLLMouseHook = NULL;
		}
		return TRUE;
	}
}
DllExport BOOL SetKbdFilterHook(BOOL activate)
{
	printf("callled kbd hook set\n");
	if (activate)
	{
		
		printf("setting kbd hook\n");
		if (hLLKeyboardHook == NULL)
		{
			// Start up the hook...
			printf("kbd hook is null now setting\n ");
			hLLKeyboardHook = SetWindowsHookEx(
				WH_KEYBOARD_LL,					// Hook in before msg reaches app
				(HOOKPROC) LowLevelKeyboardFilterProc,			// Hook procedure
				hInstance,						// This DLL instance
				0L								// Hook in to all apps
				);
			printf("setting kbd hook now hook:%x\n", hLLKeyboardHook);
			if (hLLKeyboardHook == NULL){
				printf("could not set kbd hook now hook:%x\n", hLLKeyboardHook);
				return FALSE;
			}
		}
		return TRUE;
	} else {
		if (hLLKeyboardHook != NULL)
		{
			// Stop the hook...
			printf("stopping kbd hook now hook:%x\n", hLLKeyboardHook);
			if (!UnhookWindowsHookEx(hLLKeyboardHook)){
				return FALSE;
			}
			hLLKeyboardHook = NULL;
		}
		return TRUE;
	}
}


DllExport BOOL SetKbCallBack(kbcallback bk){
	if (kbfun==NULL)
		kbfun = bk;
	else{
		kbfun = bk;
		return FALSE;
	}

return TRUE;
}
DllExport BOOL SetMsCallBack(mscallback bk){
	if (msfun==NULL)
		msfun = bk;
	else{
		msfun = bk;
		return FALSE;
	}

return TRUE;
}



BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
 	
	switch (ul_reason_for_call)
	{
		
	case DLL_PROCESS_ATTACH:
		hInstance = (HINSTANCE)hModule;
		return TRUE;
		
	case DLL_PROCESS_DETACH:
		return TRUE;
		
	default:
		return TRUE;
	}
}

LRESULT CALLBACK LowLevelKeyboardFilterProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	int ret =0;
	printf("got a call now printing\n");
	// Are we expected to handle this callback?
	if (nCode == HC_ACTION)
	{
		
		
		// Is this keyboard event "real" or "injected"
		// i.e. hardware or software-produced?werwerwer
		KBDLLHOOKSTRUCT *hookStruct = (KBDLLHOOKSTRUCT*)lParam;
		if (!(hookStruct->flags & LLKHF_INJECTED)) {
			
			if(midKBD!=0){
				printf("got a call now calling for printing\n");
				ret = g_env->CallStaticIntMethod(cls, midKBD,hookStruct->scanCode, hookStruct->vkCode,hookStruct->flags,hookStruct->time);
			}
			if(kbfun)
				ret = kbfun(hookStruct);

			if(ret>0)
			return ret;
		}
		
	}
	// Otherwise, pass on the message
	return CallNextHookEx(hLLKeyboardHook, nCode, wParam, lParam);
}

// Hook procedure for LowLevel Mouse filtering
LRESULT CALLBACK LowLevelMouseFilterProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	int ret = 0 ;
	// Are we expected to handle this callback?
	if (nCode == HC_ACTION)
	{
		// Is this mouse event "real" or "injected"
		// i.e. hardware or software-produced?
		MSLLHOOKSTRUCT *hookStruct = (MSLLHOOKSTRUCT*)lParam;
		if (!(hookStruct->flags & LLMHF_INJECTED)) {
			if(midMOUSE!=0){
				ret = g_env->CallStaticIntMethod(cls, midMOUSE,hookStruct->flags,hookStruct->mouseData,hookStruct->pt.x,hookStruct->pt.y,hookStruct->time);
			}
				if(msfun)
				ret = msfun(hookStruct);

		}
	if(ret>0)
	  return ret;
	}
	
	// Otherwise, pass on the message
	return CallNextHookEx(hLLMouseHook, nCode, wParam, lParam);
}







/*
"-Djava.class.path=C:\\Documents and Settings\\HP_Administrator\\workspace\\ii\\classes\\";
*/
DllExport void InitJavaInterface(const char *classPathWithOption,const char* className,const char *kbdMethod, const char* mouseMethod){
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
	char buffer[256]={0};
	strcpy(buffer,classPathWithOption);
    options[0].optionString = buffer;
    vm_args.version = JNI_VERSION_1_4;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm,(void**) &g_env, &vm_args);
    delete options;
	printf("jvm created: %x  \n",jvm);
	printf("env created: %x  \n",g_env);
    /* invoke the Main.test method using the JNI */
	printf("Found the class :%x  \n",cls);
    cls = g_env->FindClass(className);
	printf("Found the class :%x  \n",cls);
    midKBD = g_env->GetStaticMethodID(cls, kbdMethod, "(IIII)I");
	printf("Found the kbd method:%d  \n",midKBD);
	midMOUSE = g_env->GetStaticMethodID(cls, mouseMethod,"(IIIII)I");
	printf("Found the mouse method: %d  \n",midMOUSE);
	printf("found class and method %d, %d  \n",midKBD, midMOUSE);
 }
DllExport void StopJavaInterface(){
   midKBD = 0;
   midMOUSE = 0;
   if(jvm)
   jvm->DestroyJavaVM();
	
   jvm = 0;
   cls = 0;

}

JNIEXPORT void JNICALL Java_Hook_InitJavaInterface (JNIEnv * env2, jobject obj,jstring dirpath, jstring className,jstring kbdMethod, jstring mouseMethod){
	

	const char * strClassName = g_env->GetStringUTFChars(className, 0);
	const char * strKbdMethod = g_env->GetStringUTFChars(kbdMethod, 0);
	const char * strMouseMethod = g_env->GetStringUTFChars(mouseMethod, 0);
	

	printf("Found the class :%x \n",cls);
    cls = g_env->FindClass(strClassName);
	printf("Found the class :%x  \n",cls);
    midKBD = g_env->GetStaticMethodID(cls, strKbdMethod, "(IIII)I");
	printf("Found the kbd method:%d  \n",midKBD);
	midMOUSE = g_env->GetStaticMethodID(cls, strMouseMethod,"(IIIII)I");
	printf("Found the mouse method: %d  \n",midMOUSE);
	printf("found class and method %d, %d  \n",midKBD, midMOUSE);

   
	g_env->ReleaseStringUTFChars(className, strClassName);
	g_env->ReleaseStringUTFChars(kbdMethod, strKbdMethod);
	g_env->ReleaseStringUTFChars(mouseMethod, strMouseMethod);
	hookThreadId = GetCurrentThreadId();
	MsgLoop();

}
JNIEXPORT void JNICALL Java_Hook_StopJavaInterface (JNIEnv * env2, jobject obj){
   PostThreadMessage(hookThreadId, WM_QUIT, 0, 0L);
   midKBD = 0;
   midMOUSE = 0;
   JavaVM *jvm;       
   g_env->GetJavaVM(&jvm);
   jvm->DetachCurrentThread();
   cls = 0;
}
	JNIEXPORT int JNICALL Java_Hook_SetKeyboardHook (JNIEnv * env2, jobject obj,jint setOrReset){
		return SetKbdFilterHook(setOrReset);
	}
	JNIEXPORT int JNICALL Java_Hook_SetMouseHook (JNIEnv * env2, jobject obj,jint setOrReset){
	return SetMouseFilterHook(setOrReset);
	
	}




	JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     jint res;
     /* cache JNIEnv2 interface pointer in global variable */
     res = vm->GetEnv((void **)&g_env, JNI_VERSION_1_4);
	 printf("got the env : %x  \n",&g_env);
     if (res < 0) {
         return res;
     }
	 
	 
     return JNI_VERSION_1_4; /* the required JNI version */
 }
#ifdef _MANAGED
#pragma managed(pop)
#endif

e

myhooks.h

#if !defined(_MYHOOKS_DLL_)
#define _MYHOOKS_DLL_

#include <windows.h>
#include <jni.h>


#define DllImport __declspec(dllimport)
#define DllExport __declspec(dllexport)


typedef int (*kbcallback)(KBDLLHOOKSTRUCT *hookStruct);
typedef int (*mscallback)(MSLLHOOKSTRUCT *hookStruct );

extern "C"
{
	DllExport BOOL SetKbCallBack(kbcallback bk);
	DllExport BOOL SetMsCallBack(mscallback bk);

	DllExport void InitJavaInterface(const char *classPathWithOption,const char* className,const char *kbdMethod, const char* mouseMethod);
	DllExport BOOL SetMouseFilterHook(BOOL activate);
	DllExport BOOL SetKbdFilterHook(BOOL activate);
	DllExport void StopJavaInterface();

	JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
	JNIEXPORT void JNICALL Java_Hook_InitJavaInterface (JNIEnv * env, jobject obj,jstring dirpath, jstring className,jstring kbdMethod, jstring mousemethod);
	JNIEXPORT void JNICALL Java_Hook_StopJavaInterface (JNIEnv * env, jobject obj);
	JNIEXPORT int JNICALL Java_Hook_SetKeyboardHook (JNIEnv * env, jobject obj,jint setOrReset);
	JNIEXPORT int JNICALL Java_Hook_SetMouseHook (JNIEnv * env, jobject obj,jint setOrReset);
}

#endif

Blz, supimpa, maravilha, mas não to entendendo uma coisa... o código funfa blz, só que na hora que mais preciso, que é o evento CLICK do mouse, ele não diferencia... ou seja, tanto faz, Mover, CLicar, ou qq outro evento, o flag de retorno sempre é zero...

Será que a partir disso, alguém consegue achar o erro?

Valeu galera!

T

Eu já lidei com hooks de teclado (que é uma técnica “pro”; melhor feita em Assembly ou C puro) e aprendi duas ou três coisas:

  • Programas anti-spywares desligam ou desativam os hooks porque eles tipicamente são usados para aplicações do tipo “roubar senhas” ou “espiar a senha do usuário”, portanto esse tipo de solução vem sendo gradativamente abandonada porque normalmente não funciona na máquina de um usuário corporativo, que tem anti-spywares, anti-vírus e outros anti-qualquercoisa. (Muitos desses programas são anti-produtividade também… :stuck_out_tongue: )
    Eu tinha um programa desses para um dispositivo que enviava dados pelo teclado, e esse programa parou de funcionar faz tempo.

  • No hook de teclado você não pode fazer quase nada, porque ele se instala em TODOS os programas que estão rodando no desktop do usuário. Não é só no seu programa Java. Portanto, ele tem um espaço muito reduzido de stack para trabalhar.

O máximo que você pode fazer é dar um PostMessage (nem SendMessage) para alguma janela que você tenha reservado para receber notificações para sua aplicação, ou melhor ainda, só ligar um flagzinho ou coisas mais inocentes. O que você fez (chamar um callback da JVM) nem sabia que podia funcionar (ou não funciona, hein?)

  • Toda vez que houver qualquer probleminha em um hook de teclado ou mouse (que possa causar um GPF, exception etc.), o Windows desliga esse hook sem lhe avisar.
KIMBA

Minha idéia é dar um plus no sistema, levando em cosideração que todos meus clientes hoje utilizam Windows como base operacional, eu tava achando interessante uma solução desta forma, que desse mais alguns atrativos ao programa, e não adianta não dizer, que todo cliente gosta de firulinhas em programas… hehehehe

Mas voltando ao assunto, na ralidade, o hook do teclado eu não vou usar, pelo menos por enquanto, meus planos são ainda só pro mouse, como ilustrei no primeiro post.

Achei também uma forma interessante de aprender a lidar com JNI, uma vez que sou um zero a esquerda neste assunto.

Bom, mesmo assim, vale pelos toques, levarei tudo que disse em consideração.

Abraços

KIMBA

Olha só…

Achei um outro exemplo, que funciona legal, mas ainda tem um problema pra mim…

[link]http://www.ibm.com/developerworks/java/library/j-jniobs/[/link]

Neste exemplo, exite um arquivo main.cpp, que é o “programa principal”, ou seja, ele chama as classes Java, que chamam a biblioteca listener.dll através de JNI, que por sua vez, devolve os resultados ao main, queria ver se alguém consegue mudar isso, pra executar a partir do JAVA…

Sei que requer tempo e disposição, mas eu não estou tendo nem idéia de por onde começar…

Fiz alguns testes tentando traduzir o main.cpp para JAVA, mas não deu muito certo, pois meus conhecimentos de C são um pouco abaixo de ZERO.

Valeu

S

Nesse codigo que você colocou do myhooks, eu não consegui compilar, estou usando o Dev-C++ não estou encontrando a biblioteca “stdafx.h”. Você conseguiria me arrumar essa dependecias.

Cara estou precisando monitorar o teclado e o mouse, para verificar se a aplicação está ociosa.

Acho que esse hooks que você fez irá funcionar, lógico que terei que adaptar!

Falow

KIMBA

stdafx.h

Cara até onde sei, essa biblioteca é nativa do C, mas, confesso que não é meu forte.
Eu utilizei o Microsoft Visual Studio pra compilar, e não deu problema nenhum…
Pra te arrumar a biblioteca, eu não teria nem idéia de como fazer isso, mas dá um googlada que penso não ser dificil.

Abraços e boa sorte!

T

sbandre:
Nesse codigo que você colocou do myhooks, eu não consegui compilar, estou usando o Dev-C++ não estou encontrando a biblioteca “stdafx.h”. Você conseguiria me arrumar essa dependecias.

Cara estou precisando monitorar o teclado e o mouse, para verificar se a aplicação está ociosa.

Acho que esse hooks que você fez irá funcionar, lógico que terei que adaptar!

Falow

Usualmente “stdafx.h” é um arquivo que contém somente:

#pragma once
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#define _WIN32_WINNT 0x0500
#include <windows.h>

Normalmente não é necessário acrescentar mais nada.

S

Muito obrigado pela dica!

Cara não conheço nada de c++ e preciso implementar essa função de escutar o mouse e o teclado.
Estou até lendo algo a respeito do JNA. Para utilizar dll nativa da M$.

Agora parou em outro ponto do codigo.

myhooks.cpp linha 196 =>
C:\pessoal\Projetos-C\myhooks.cpp In function LRESULT LowLevelMouseFilterProc(int, WPARAM, LPARAM)': 196 C:\pessoal\Projetos-C\myhooks.cppLLMHF_INJECTED’ undeclared (first use this function)

Na verdade eu achei esse codigo na net e queria testa-lo, nem sei o que está fazendo exatamente.

Se puder me ajudar!

T

No seu caso você precisa de mais alguns arquivos do Platform SDK (não sei exatamente quais) que devem ser incluídos em stdafx.h.

Procure o nome dessas estruturas em msdn.microsoft.com , e veja quais são os arquivos .h e .lib necessários para serem incluídos em stdafx.h.

Criado 11 de janeiro de 2008
Ultima resposta 22 de jul. de 2008
Respostas 9
Participantes 3