Global Hotkeys in Java?

Schusta

Grünschnabel
Hi Leute!

Ich arbeite seit einiger Zeit an einem MP3/WAV-Player in Java. Bisher funktioniert alles so wie ich mir das vorstelle, aber jetzt stehe ich ein bisschen ratlos da.

Ich würde gern für meinen Player "Global Hotkeys" verwenden. Manche kennen das vielleicht aus Winamp. Auf jeden Fall soll wenn ich zB meinen Webbrowser offen habe und ich STRG+PFEIL_NACH_RECHTS drücke mein Player anfängt das File abzuspielen.

Ich denke das zu verwirklichen ist nur unter der Verwendung von Java nicht möglich, aber ich habe gehört, dass es mit einer C++-DLL die mit dem Javaprogramm kommuniziert funktionieren soll.

Leider hab ich nur spärliche Erfahrung in C++ und weiß nicht wie ich das bewerkstelligen soll. Deshalb wäre meine Frage ob das schonmal jemand gemacht hat oder ob jemand evtl. Tipps hat wie ich das machen soll.

Falls der Thread eher ins C++-Forum passt, wäre es net wenn ihn ein Mod verschieben könnte.

Besten Dank schonmal im Vorraus und fG,

Schusta
 
Hallo Schusta,

Programmweite Hotkeys sind in Java möglich. D.h. solange die Applikation den Fokus hat wird es jegliche gesetze Shortcuts empfangen.

Siehe hierzu:
http://www.tutorials.de/forum/java/242472-abbruch-mit-escape.html

Weiterhin besteht die Möglichkeit, eine Java Applikation mit Dateitypen zu assoziieren, sodass bei doppelklick auf eine Datei dieser Formate die Javaanwendung startet. Hier beispielsweise mit wav und mp3.

Siehe hierzu:
https://jdic.dev.java.net/
https://jdic.dev.java.net/documentation/Examples.html


Systemweite Hotkeys kann man wie schon von dir erwähnt mit java nicht setzen.
Wenn man bei MSDN nach "hotkey" sucht, findet man zwar viele Artikel und Doku dazu. Allerdings kenne ich mich weder mit c/c++/visualbasic noch JNI aus.
http://msdn1.microsoft.com/en-us/default.aspx


Vg Erdal
 
Hier noch ein deutschsprachiger Artikel:

Systemweite Hotkeys mit Visual Basic 5/6 erzeugen und verwenden
http://www.microsoft.com/germany/ms...sualBasic56ErzeugenUndVerwenden.mspx?mfr=true

Auch dabei ist ein vollständiger Beispielcode.

Könnte vielleicht jemand hier aus dem Forum eine Java Klasse implementieren welche dieses Visual Basic Script aufrufen kann. Auch sollte die Hotkey Kombination nicht fest im Basic Code sein, sondern möglichst von der Java Klasse aus als Parameter übergeben werden können. Vielleicht liest der Weihnachtsmann auch im Forum mit :-) , und er postet die Lösung, wenn wir bis zum Ende des Jahres brav bleiben ^^ .


Vg Erdal
 
Wo hakt es denn? ;)
Im Prinzip ist die Idee mit der C++-Bibliothek richtig.
Grundlegend ist die Kommunikation Java->C++(JNI)->Java(JNI) nötig. Ich habe mal in ein eigenes Javaprojekt Global Hotkeys eingebaut; allerdings noch rudimentär. Das ist sicherlich noch verbesserungswürdig, da ich die Tasten vorher festgelegt habe und das Ganze somit ohne Parameterübergabe gemacht habe (wollte mich da bei JNI nicht zusätzlich noch damit belasten).
 
Hey das ist interessant - nach dem Thread hatte ich das ganze auch mal versucht, allerdings habe ich es nicht geschafft einen Aufruf einer Java Methode von C++ aus zu machen, da ich kein Handle auf ein JNIEnv habe..
Würde mich echt brennend interessieren wie du das gelöst hast :D

Gruß
Tobias

P.S. Wenn du willst kann ich dir auch mal meinen bisherigen Code schicken, hab auch schon so Sachen wie Shift Alt etc. prinzipiell implementiert..
 
Thema Rückkommunikation:
Ja, das ist ein wenig knifflig. Bei mir ist das gelöst dadurch, dass ich ein und dasselbe Objekt genommen habe. Sprich ein Javaobjekt kommuniziert zu C++ über JNI und aktiviert die Global Hotkeys. Und genau zu diesem Objekt wird später die Rückverbindung wieder über JNI gebaut.

Dazu ein wenig Codegeschnipsel:

- In meinem C++ - Code gibt es eine native Methode namens OnLoad. Selbige wird automatisch aufgerufen, wenn ich die DLL später in Java einbinde. Dort wird ein Verweis auf die aktuelle Java Virtual Machine gesichert.

- Es gibt in meiner Javaklasse eine native Methode, die die Global Hotkeys aktiviert. In der entsprechenden C++-Methode wird die Referenz der Klasse, welche die Aktivierung durchführt, also ein Objekt, gesichert, ebenso die Methode, welche der Global Hotkey in dem Javaobjekt aufrufen soll. Es gibt also in diesem Fall eine Methode in meiner Javaklasse namens hotkey_F1().

- In meinem C++ - Code gibt es auch eine Methode, namens hotkey_F1 (hätte sie vermutlich zur Besseren Unterscheidung zu der Methode in der Javaklasse unterschiedlich benennen sollen). Selbige soll aufgerufen werden, wenn der Hotkey gedrückt wird. Das ist dann in C++ zu regeln.
Diese Methode muss dann dem aktuell laufenden Thread der JVM (die in OnLoad gesichert wurde) zugeordnet werden (mit AttachCurrentThread). Das war es dann im Prinzip. Beim Drücken des Global Hotkeys wird dann hotkey_F1 in C++ ausgeführt und selbige ruft dann in meinem Javaobjekt die Methode auf, die sich hinter hk_F1 verbirgt.

JavaVM * jvm = NULL;
jobject guiObj;
jmethodID hk_F1;

void hotkey_F1(void *)
{
JNIEnv * env;

if (jvm->AttachCurrentThread((void **)&env, NULL) < 0) {
printf("Error AttachCurrentThread\n");
return;
}
env->CallVoidMethod(guiObj, hk_F1);
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * _jvm, void * reserved) {
jvm = _jvm;
return JNI_VERSION_1_4;
}

JNIEXPORT void JNICALL Java_gui_Gui_activateGlobalHotkeys
(JNIEnv * env, jobject obj) {
guiObj = env->NewGlobalRef(obj);
hk_F1 = env->GetMethodID(env->GetObjectClass(obj), "hotkey_F1", "()V");
........
}

Deinen Code würde ich natürlich gerne sehen :)
 
Ja.. also erstmal vielen Dank für den Tipp :)
Allerdings konnte ich ihn noch nicht ganz testen, weil jetzt was anderes nicht mehr klappt :(
Ich hatte vorher 2 DLL's, eine JNI Dll und diese hat noch eine Keyhook DLL geladen..
Da deine Version allerdings nicht mit 2 Dll zu klappen scheint, hab ich das einfach mal alles in eine DLL reingestrickt..
Nun hab ich aber das Problem, das nach dem Aufruf
Java:
System.loadLibrary("jni_keyhook");
die DLLMain der Methode nicht aufgerufen wird - Problem bestand vorher nicht, da der Aufruf in der DLL war.. (Und die hat die DLLMAin der keyhook-DLL aufgerufen)..
Kann das an meinem Linker liegen? (mingw).. Hab mir das mal angeguckt und der übergibt dem Linker (genauer dlltool) immer
Code:
--exclude-symbol=DllMainCRTStartup@12
.. Ich vermute mal das liegt daran, krieg das aber nicht abgestellt da der das intern irgendwie macht -.-

Aber nun zu meiner Keyhook Methode - die leider im Moment halt nicht klappt:
C++:
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 
{ 
    CHAR szBuf[128]; 
    size_t cch; 
    size_t * pcch;
    HRESULT hResult;
 
    WORD wLHigh = (WORD) (lParam >> 16);
    BOOL fReleasedKey = wLHigh & KF_UP; //aequivalent zu lParam & 0x80000000
    BOOL fAltDown = wLHigh & KF_ALTDOWN; // lParam & (KF_ALTDOWN << 16)
    BOOL fLCtrlDown = GetKeyState( VK_LCONTROL ) & 0x8000;
    BOOL fRCtrlDown = GetKeyState( VK_RCONTROL ) & 0x8000;
    BOOL fLShiftDown = GetKeyState( VK_LSHIFT ) & 0x8000;
    BOOL fRShiftDown = GetKeyState( VK_RSHIFT ) & 0x8000;    
    BOOL fTabDown = GetKeyState( VK_TAB ) & 0x8000;
    BOOL fLWinDown = GetKeyState( VK_LWIN ) & 0x8000;
    BOOL fRWinDown = GetKeyState( VK_RWIN ) & 0x8000;

    if (nCode < 0)  //Message nicht verarbeiten
        return CallNextHookEx(g_hKeyHook, nCode, wParam, lParam);

    if ( IsModifier( wParam ) ) //Event einer Modifiertaste (Alt, Shift, Ctrl...)
        return CallNextHookEx(g_hKeyHook, nCode, wParam, lParam);
 
    if ( fReleasedKey ) // Taste wurde losgelassen - für Hotkeys uninteressant
    {
        c = 0;
        //MessageBox(NULL, "Taste losgelassen!", "Out", MB_OK);
        return CallNextHookEx(g_hKeyHook, nCode, wParam, lParam);
    }

    hResult = sprintf(szBuf, "%d ", wParam);
    c++;
    if (FAILED(hResult))
    {
       MessageBox(NULL, "FAIL1!", "Out", MB_OK);
    }
    hResult = sizeof(szBuf, 128/sizeof(TCHAR), pcch);
    if (FAILED(hResult))
    {
       MessageBox(NULL, "FAIL2!", "Out", MB_OK);
    }
    if (c == 1) //Nur wenn Taste zum erstenmal gedrueckt wurde reagieren
    {
         //MessageBox(NULL, "ou", "ou", MB_OK);
         
         JavaVMAttachArgs args;
         args.version= JNI_VERSION_1_4;
         args.name="user";
         args.group=NULL;
         
         jint res = javavm->AttachCurrentThread( (void**)&jniEnv, &args );
   
         if ( res <= 0)
           MessageBox(NULL, "Out", "Fehler", MB_OK);

         //res = (*javavm)->GetEnv( javavm, (void**)&jniEnv, JNI_VERSION_1_2 );
         jclass jclazz;
         jmethodID mId;
         jclazz = jniEnv->GetObjectClass( globClass );
         mId = jniEnv->GetMethodID( jclazz, "jniActionPerformed", "(III)V" );
         jniEnv->CallVoidMethod( globClass, mId, (jint) 10, (jint)0, (jint)0 );

         if ( res != JNI_OK)
           MessageBox(NULL, "Out", "Fehler", MB_OK);

//         Call();
         javavm->DetachCurrentThread();
    }
      
    return CallNextHookEx(g_hKeyHook, nCode, wParam, lParam);
}

Ist halt geplant an jniActionPerformed Keycode und Shiftstate etc zu übergeben, aber kann das leider nicht testen, da der den Keyhook nicht richtig macht..
(Da ich halt kein hModule Handle hab - da DLLMain nicht aufgerufen wird - mahc ich das so, klappt aber nicht - er macht keinen Fehler aber die Callback Methode wird nie aufgerufen)
C++:
g_hKeyHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(NULL), 0);

Gruß
Tobias
 
Hmm, so ganz verstehe ich glaube ich das Problem noch nicht. Wieso geht das denn nicht, wenn Du alles in einer DLL hast. Du müsstest doch alles aufrufen können, was Du willst.

Meine Lösung integrierte übrigens eine Klasse, die ich irgendwo mal gefunden hatte, die mir in C++ ein Fenster generierte und dort die Hotkeys registrierte. Ich hab sieh einfach dann adaptiert und für mich so in meine dll eingebaut, dass alles funktionierte. Wenn Interesse an den Klassen besteht, lasse ich sie auch gerne mal zukommen. Falls Du Visual Studio nutzt auch gerne als komplettes Projekt.
 
Zurück