Category: Development


Nach einer langen suche ist es mir endlich gellungen einen richtig einfachen global keyboard hook fuer wpf zu finden 🙂

Was ist ein global key hook?

er ermöglicht dir keys abzufangen die gedrueckt werden waerend deine app nicht aktiv ist. so wie es bei keyloggern eingesetzt wird. Abgesehen von dieser eher kriminellen idee giebt es auch pracktische anwendungsmoeglichkeiten. z. B. kann man befehle in einer applikation ausfuehren ohne das diese den fokus von der aktuellen anwendung stielt. um ein pracktisches beispiel zu nenen: Man wechselt mit einem shortcut „Ctrl + N“ zum naechsten musik titel in der playlist waerend man gerade zockt und nicht ins windows tappen will bzw kann.

und nun zum Fleisch!

oeffnet VisualStudio 2010. (sollte auch in aelteren funktionieren)

Erstellt oder offnet euer projekt indem ihr den key hook testen moechted. (ich habe einfach ein neues wpf projekt namens „global_key_hook“ erstellt wobei es mit ner consolen anwengung genau so geht.)

Dann erstellt eine neue klassen datei (DEINPROJEKT[Rechtsklick] -> Neu -> Klasse ) Name vergeben (wie GlobalKeyHook.cs)

dan fuege diesen code in die klassen datei ein

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace GlobalKeyHook.Keyboard

{
    /// <summary>
    /// Listens keyboard globally.
    ///
    /// <remarks>Uses WH_KEYBOARD_LL.</remarks>
    /// </summary>
    public class KeyboardListener : IDisposable
    {
        /// <summary>
        /// Creates global keyboard listener.
        /// </summary>
        public KeyboardListener()
        {
            // We have to store the HookCallback, so that it is not garbage collected runtime
            hookedLowLevelKeyboardProc = (InterceptKeys.LowLevelKeyboardProc)LowLevelKeyboardProc;

            // Set the hook
            hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);

            // Assign the asynchronous callback event
            hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
        }

        /// <summary>
        /// Destroys global keyboard listener.
        /// </summary>
        ~KeyboardListener()
        {
            Dispose();
        }

        /// <summary>
        /// Fired when any of the keys is pressed down.
        /// </summary>
        public event RawKeyEventHandler KeyDown;

        /// <summary>
        /// Fired when any of the keys is released.
        /// </summary>
        public event RawKeyEventHandler KeyUp;

        #region Inner workings
        /// <summary>
        /// Hook ID
        /// </summary>
        private IntPtr hookId = IntPtr.Zero;

        /// <summary>
        /// Asynchronous callback hook.
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode);

        /// <summary>
        /// Actual callback hook.
        ///
        /// <remarks>Calls asynchronously the asyncCallback.</remarks>
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
                if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYUP)
                    hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), null, null);

            return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
        }

        /// <summary>
        /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
        /// </summary>
        private KeyboardCallbackAsync hookedKeyboardCallbackAsync;

        /// <summary>
        /// Contains the hooked callback in runtime.
        /// </summary>
        private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;

        /// <summary>
        /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
        /// </summary>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode)
        {
            switch (keyEvent)
            {
                // KeyDown events
                case InterceptKeys.KeyEvent.WM_KEYDOWN:
                    if (KeyDown != null)
                        KeyDown(this, new RawKeyEventArgs(vkCode, false));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
                    if (KeyDown != null)
                        KeyDown(this, new RawKeyEventArgs(vkCode, true));
                    break;

                // KeyUp events
                case InterceptKeys.KeyEvent.WM_KEYUP:
                    if (KeyUp != null)
                        KeyUp(this, new RawKeyEventArgs(vkCode, false));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYUP:
                    if (KeyUp != null)
                        KeyUp(this, new RawKeyEventArgs(vkCode, true));
                    break;

                default:
                    break;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposes the hook.
        /// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
        /// </summary>
        public void Dispose()
        {
            InterceptKeys.UnhookWindowsHookEx(hookId);
        }

        #endregion
    }
    /// <summary>
    /// Raw KeyEvent arguments.
    /// </summary>
    public class RawKeyEventArgs : EventArgs
    {
        /// <summary>
        /// VKCode of the key.
        /// </summary>
        public int VKCode;

        /// <summary>
        /// WPF Key of the key.
        /// </summary>
        public Key Key;

        /// <summary>
        /// Is the hitted key system key.
        /// </summary>
        public bool IsSysKey;

        /// <summary>
        /// Create raw keyevent arguments.
        /// </summary>
        /// <param name="VKCode"></param>
        /// <param name="isSysKey"></param>
        public RawKeyEventArgs(int VKCode, bool isSysKey)
        {
            this.VKCode = VKCode;
            this.IsSysKey = isSysKey;
            this.Key = System.Windows.Input.KeyInterop.KeyFromVirtualKey(VKCode);
        }
    }

    /// <summary>
    /// Raw keyevent handler.
    /// </summary>
    /// <param name="sender">sender</param>
    /// <param name="args">raw keyevent arguments</param>
    public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);

    #region WINAPI Helper class
    /// <summary>
    /// Winapi Key interception helper class.
    /// </summary>
    internal static class InterceptKeys
    {
        public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
        public static int WH_KEYBOARD_LL = 13;

        public enum KeyEvent : int
        {
            WM_KEYDOWN = 256,
            WM_KEYUP = 257,
            WM_SYSKEYUP = 261,
            WM_SYSKEYDOWN = 260
        }

        public static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
    }
    #endregion
}

um den hook nun zu verwenden muss in der App.xaml noch zwei events hinzugefuegt werden:

Startup=“Application_Startup“
Exit=“Application_Exit“

<Application x:Class="global_key_hook.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             Startup="Application_Startup"
             Exit="Application_Exit"
             >
    <Application.Resources>

    </Application.Resources>
</Application>

und in die App.xaml.cs sollte ungefaer so aussehen:

using System.Data;
using System.Linq;
using System.Windows;
using GlobalKeyHook.Keyboard;

namespace global_key_hook
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {

        KeyboardListener KListener = new KeyboardListener();

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            KListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
        }

        void KListener_KeyDown(object sender, RawKeyEventArgs args)
        {
            MessageBox.Show(args.Key.ToString());
        }

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            KListener.Dispose();
        }
    }

}

nun kann man mit F5 das debugging starten und mann wird festellen das jeder gedrueckte key in einer MessageBox angezeigt wird und Fertig. so einfach kann es sein ^^

ein wenig tricky wird es noch wenn man versucht die keys an z.B. ein fenster zu senden. Da der KeyEvent in einem thread laeuft. muss man als erst einen Dispatcher aufrufen. in der Funktion KListener_KeyDown schlagen die key events auf. Ich habe in meinem MainWindow eine textbox hinzugefuegt in die ich die keys schreibe mit:

this.Dispatcher.BeginInvoke((System.Threading.ThreadStart)delegate
            {
                ((MainWindow)this.MainWindow).textBox1.Text += args.Key.ToString() + @"
";
            });

so das sollte erklaeren wie das ganze funktioniert. falls ich mich irgendwo vertahn habe oder ihr noch irgendwelche anregungen habt bitte Hinterlast mir ein kommentar. auser es geht um rechtschreibung in diesem fall ist jeder versuch zwecklos 😀

Ach ja ist nich so als waer das ganze auf meinem mist gewachse. Hier der link zum orginal(english)

Lokalisten SchnitzelScript

Update 27.08.2010: Da immer noch einige leute mit den suchwoertern „lokalisten script“ auf meine seite rauschen wollte ich diese personen  mal fragen was fuer ein script sie denn suchen. bitte hinterlaest mir ein kommentar mit dem script das ihr wollt. vieleicht lasst sich was machen.

Update 28.06.2010: Ich wolte nur sagen das ich es cool finde das mein acc anscheinend von den lokis vom schnitzel game gebannd wurde ^^. Ich hatte schon schlimmeres befuerchtet. Warscheinlich darf ich zu der naechsten lokalistenparty nicht mehr rein. Also ich finde keine schnitzel mehr und alle meine bisher gefundenen wurden geloescht.

Update:11.06.2010: Wie es scheind haben die lokis nun entlich draufgehauen. es ist nun wohl um einiges schwieriger geworden einen bot zu verwenden und damit hat sich das mit dem script erledigt. ich wede mir die neuen aenderungen nochmal ansehen glaub aber nicht das es eine neue version geben wird das alte hab ich auch rausgenommen.

Update:08.06.2010: so die neue version des scripts ist on. jetzt werden zufaellige seiten aufgerufen. Na endlich werden sich einige denken ^^. Es werden nun mit aller zufaelligkeit punkte aus dem menue aufgerufen.  eg gibt allerdings ein problem mit den seiten Groupe und events deswegen lass ich die einfach aus. Es kam mir nun auch so for als wuerde das script jetzt schneller einsammeln. Da nahezu auf jeder seite ein schnitzel gefunden wurde. natuerlich kann das auch daran liegen das ich es wie immer nicht lange laufen lies. Einfach nochmal auf den link unten klicken und neu Installieren.

wer unterschiedliche aufrufe haben will (obwohl die ladezeiten durch lags sowieso unterschiedlich sein sollten) kann sich einfach ein „Math.floor(Math.random() * 500)“ oder so an den intervalltimer haengen(ganz hinten im string die 2000).

Nochmal gilt ich bin nicht mehr wirklich zum testen gekommen falls es probleme gibt melden.

Update:04.06.2010: Die lokalisten haben scheinbar schon reagiert und das verwenden von bots ausgehebelt. Wie sie das machen und ob es wirklich nicht mehr moeglich ist bots zu verwenden  werd ich heute nachmittag rausfinden 😉

Anmerkung: Diese Script darf nicht fuer das glueckspiel eingesetzt werden!

Ich mochte damit aufzeigen wie einfach es ist einen „bot“ fuer das lokalisten gewinspiel zu schreiben und somit erklaeren wie es moeglich ist das Die Schnitzelkoennige innerhalb von 20h 6321 schnitzel einsammeln koennen.

Nach meinen berechnungen duerfte das Skript ziemlich auf das Selbe kommen.

Voraussetzungen:

als erstes muss der Firefox (download) installiert sein.

fuer den firefox muss das addon Greasemonkey (download) installiert sein.

und dann braeuchte man nur noch ein kleines javascript das nichtmehr zum download bereitsteht da es dank aenderungen der lokis nicht mehr geht.

Greasemonkey

JetBrains PHPStorm hat mich auf voller Linie ueberzeugt. Es hat einfach alles was man zum arbeiten mit Websites braucht.

Syntaxhighliting:

ok. Das hat wirklich jeder aber:

image Man kann wirklich alles bis ins kleinste detail selbst anpassen.

Autocompleate:

image Erkennt alle angelegten Variablen als auch eigene Klassen aus externen Files!

Weiter anzufueren sind Features wie

FTP upload(der automatisch alle geaenderten files hochlaedt)

Version Control integration (z.B auch Subversion das wir in der firma verwenden in der ich arbeite)

todo liste (wird anhand der todo eintraege im projekt erstellt)

php Debugging

code check

und sicher noch einige dinnge die ich nur noch nicht gefunden habe ^^

 

PHP Storm ist im moment noch in der testphase und noch kostenlos. Das wird sich jedoch aendern sobald das produkt serien reife erlangt hat. Wenn es dann auch 600 USD kostet wie sein zur java programierung optimierter bruder IntelliJ IDEA 9.0 wird PHPStorm sicher nicht mehr meine erste wahl sein.

Bis es soweit ist das ich vom preis geschockt werde freu ich mich ueber das beste tool das ich in diesem bereich je gefunden habe!!!!!!

in der firma in der ich arbeite haben wir uns angewohnt 3 Vorschlaege zu erstellen und diese dan dem Kunden vorzulegen das er sich einen aussuchen kann. In meinen 6 Jahren hab ich bis her erst einen erlebt der nicht den in meinen Augen haesslichsten Vorschlag genommen hatte. Bei dem einen Kunden ist mir der erste Vorschlag so gut gelungen das ich die anderen zwei so gut wie garnich gemacht hab ^^.

Auch ist es faszinierend mit welcher praezession Kunden vorschlaege machen die das erstellen einer seite unnoetig verkompliezieren und den ausgekluegelten aufbau aus dem gleichgewicht bringen.

Weiter ist es auch auffallend das Kundenkurz vor der fertigstellung der Page in einer art Torschusspanik noch mal alles aendern moechten

Abschliesend ist zu sagen das man sich davor in acht nehmen sollte dem Kunden zu viele moeglichkeiten anzubieten denn er wird sie ausnutzen wenn auch nur um danach sagen zu koennen „Das gefaellt mir nicht so koennten wir das nicht so und so machen.“

Die leute von Agency Fusion hatten wohl ganz aenliche brobleme und machten ein lustiges Video darueber.

Nach dem nicht so glanzvollen Auftreten von Windows Vista unter anderem auch im bereich rueckwaerts kompatibilitaet wurde nun Windows 7 um ein kostenloses feature erweitert das dieses Problem ein fuer alle mal aus der Welt schaffen soll. Windows XP Mode nennt sich das ganzeund besteht letzten endes einfach aus einer Virtuellen Maschine. Windows XP ist im download kostenlos fuer alle windows 7 besitzer enthalten.

Abgesehen von den mitlerweile normalen features wie USB, PrinterSharing, Clipboard Sharing und direkten zugriff auf die dateien des clients giebt es nun auch die funktion Seamless applications.

Applikationen des Clients koennen nun auch im Host mit einer einfachen verknuepfung gestarted werden. Die wirkliche neuerung an dem ganzen ist das die gestartete anwendung angezeigt wird als waer sie auf dem host installiert.

Zum Glueck ist auf der mitgelieferten XP version noch IE6 installiert. Das erleichtert einem die Arbeit beim erstellen einer Website enorm. Frueher hatte ich immer eine VMware auf meinem PC. Loest zwar auch das Problem aber ist bei weitem nicht so ellegant ^^.

UEber den sinn laest sich streiten allerdings hat sich dieses kleine tool schon oft als nuetzlich erwiesen.

Vorallem ist es intresannt zu sehen das die browser alle eine limitation eingebaut haben:

IE 8: 64 FPS

FF:  100 FPS

Safari: 65 FPS

Google Chrome: 250 FPS


//fps by 2DA Ver.: 0.01

    var FPSDIV;
    var fpscount = 0;
   var Sekunden = 0;
   
    var closure = function (){FPS();};
    this.bla = window.setTimeout(closure, 30);

    function FPS() {
   
        FPSDIV = document.getElementById("fps");

            var fpscalc = function ()
            {
               var Sekunden2 =  new Date().getSeconds();
               if(Sekunden != Sekunden2)
               {
                    FPSDIV.innerHTML = fpscount;
                    Sekunden = Sekunden2;
                    fpscount = 0;
               }
               fpscount++;
          };
          
        this.bla = window.setInterval(fpscalc, 1);
    }

 


<script type="text/javascript" language="javascript" src="fps.js"></script>

<div id="fps" style="width:200px;float:left;" ></div>

Ich weis der Code ist nicht ideal. ich bin in javascript nicht zuhause. es funktioniert mehr brauch ich nicht 🙂

Safari anmerkung:

Der Safari legt ein seltsames verhallten an den Tag. Zum ersten sind die schwankungen der frames in keinen anderen browser so auffallend. Dazu kommt noch das waerend jeder andere Browser die javascript bearbeitung einfriert waerend er minimiert ist steigert der safari seine frameslimit auf 100 FPS. Sobald ein anderes fenster activ ist springt die anzeige in dem bereich zwischen 60 und 75 FPS hin und her.