Results 1 to 7 of 7

Thread: Actions Per Minute Tool

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Feb 2008
    Posts
    113

    Lightbulb Actions Per Minute Tool

    My main objective here is to calculate how many times W, A, S, D, along with mouse clicks and a few other keys are pressed while playing a full screen game. I'm not sure if any of you guys are familiar with Starcraft (the game) but they have this tool built-in to their game for players to see their average APM (actions per minute) and I'd like to utilize my own app for a different game.

    Using GetAsyncKeyState with a timer to add a value to the total actions integer will add many times if the user holds down the key. I'm looking for a way to add to the integer "total actions" on key_up, therefor yielding the result I need. So I messed around with IMessageFilter to do this.

    The problem now is IMessageFilter will only work when my form has focus. I need this app to work while my game has focus. Any thoughts or suggestions would be greatly appreciated.

    What I have now:

    Code:
    Public Class Form1
    
        Private WithEvents apmMonitor As New ActionPerMinuteMonitor
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Application.AddMessageFilter(apmMonitor)
        End Sub
    
        Private Sub apmMonitor_APM(ByVal apmAverage As Double, ByVal apmInstantaneous As Double) Handles apmMonitor.APM
            Label1.Text = apmAverage.ToString("0") & " avg / " & apmInstantaneous.ToString("0") & " inst"
        End Sub
    
    End Class
    
    Public Class ActionPerMinuteMonitor
        Implements IMessageFilter
    
        Public Sub New()
            SW.Start()
            TMR.Interval = TimeSpan.FromSeconds(1).TotalMilliseconds
            TMR.Start()
        End Sub
    
        Private Const WM_KEYDOWN As Integer = &H100
        Private Const WM_LEFTMOUSEDOWN As Integer = &H201
        Private Const WM_RIGHTMOUSEDOWN As Integer = &H203
        Private Const WM_MIDDLEMOUSEDOWN As Integer = &H207
    
        Private SW As New Stopwatch
        Private ActionCounter As Long
        Private ActionStampsInLastMinute As New List(Of DateTime)
        Private WithEvents TMR As New System.Windows.Forms.Timer
        Public Event APM(ByVal apmAverage As Double, ByVal apmInstantaneous As Double)
        Private MillisecondsPerMinute As Integer = TimeSpan.FromMinutes(1).TotalMilliseconds
    
        Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
            Select Case m.Msg
                Case WM_KEYDOWN, WM_LEFTMOUSEDOWN, WM_RIGHTMOUSEDOWN, WM_MIDDLEMOUSEDOWN
                    ActionCounter = ActionCounter + 1
                    ActionStampsInLastMinute.Add(DateTime.Now)
    
            End Select
    
            Return False ' Do NOT suppress any messages, allowing passage of all messages and thus normal operation of the application
        End Function
    
        Private Sub TMR_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles TMR.Tick
            Dim apmAvg As Double = 0
            Dim apmInst As Double = 0
    
            If ActionCounter > 0 Then
                Dim mills As Long = SW.ElapsedMilliseconds
                apmAvg = CDbl(ActionCounter) / CDbl(mills) * MillisecondsPerMinute
    
                If ActionStampsInLastMinute.Count > 0 Then
                    Dim OneMinuteAgo As DateTime = DateTime.Now.AddMinutes(-1)
                    While ActionStampsInLastMinute.Count > 0 AndAlso ActionStampsInLastMinute(0) < OneMinuteAgo
                        ActionStampsInLastMinute.RemoveAt(0)
                    End While
    
                    If mills < MillisecondsPerMinute Then
                        apmInst = apmAvg
                    Else
                        apmInst = ActionStampsInLastMinute.Count
                    End If
                End If
            End If
    
            RaiseEvent APM(apmAvg, apmInst)
        End Sub
    
    End Class

  2. #2

    Thread Starter
    Lively Member
    Join Date
    Feb 2008
    Posts
    113

    Re: Actions Per Minute Tool

    So I got rid of iMessageFilter since it requires focus and am using a low-level keyboard hook to handle key_up events which I really did not want to do because keyboard hooks usually falsely trigger anti-virus software as a type of keylogger, which are NOT my intentions.

    Until I can find an alternative to a keyboard hook I'll be using this for personal use, I've described an annoying error I'm currently encountering which I've described in my post below.

    Thanks guys
    Last edited by JoshA56; Jun 24th, 2014 at 10:27 PM.

  3. #3

    Thread Starter
    Lively Member
    Join Date
    Feb 2008
    Posts
    113

    Re: Actions Per Minute Tool

    Edit: the new keyboard hook I'm using does indeed count keystrokes while the form is inactive, however it is NOT counting keystrokes while I'm playing the fullscreen game. Although I'm not receiving any messages from the game concerning the keyboard hook, is it possible that the game just does not allow hooking the keyboard? Only thing I can think of.

    Hook class (public, open source):
    Code:
    Imports System.Runtime.InteropServices
    
    Public Class KeyboardHook
    
        <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
        Private Overloads Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal HookProc As KBDLLHookProc, ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
        End Function
        <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
        Private Overloads Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        End Function
        <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
        Private Overloads Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean
        End Function
    
        <StructLayout(LayoutKind.Sequential)> _
        Private Structure KBDLLHOOKSTRUCT
            Public vkCode As UInt32
            Public scanCode As UInt32
            Public flags As KBDLLHOOKSTRUCTFlags
            Public time As UInt32
            Public dwExtraInfo As UIntPtr
        End Structure
    
        <Flags()> _
        Private Enum KBDLLHOOKSTRUCTFlags As UInt32
            LLKHF_EXTENDED = &H1
            LLKHF_INJECTED = &H10
            LLKHF_ALTDOWN = &H20
            LLKHF_UP = &H80
        End Enum
    
        Public Shared Event KeyDown(ByVal Key As Keys)
        Public Shared Event KeyUp(ByVal Key As Keys)
    
        Private Const WH_KEYBOARD_LL As Integer = 13
        Private Const HC_ACTION As Integer = 0
        Private Const WM_KEYDOWN = &H100
        Private Const WM_KEYUP = &H101
        Private Const WM_SYSKEYDOWN = &H104
        Private Const WM_SYSKEYUP = &H105
    
        Private Delegate Function KBDLLHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    
        Private KBDLLHookProcDelegate As KBDLLHookProc = New KBDLLHookProc(AddressOf KeyboardProc)
        Private HHookID As IntPtr = IntPtr.Zero
    
        Private Function KeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
            If (nCode = HC_ACTION) Then
                Dim struct As KBDLLHOOKSTRUCT
                Select Case wParam
                    Case WM_KEYDOWN, WM_SYSKEYDOWN
                        RaiseEvent KeyDown(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
                    Case WM_KEYUP, WM_SYSKEYUP
                        RaiseEvent KeyUp(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
                End Select
            End If
            Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
        End Function
    
        Public Sub New()
            HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
            If HHookID = IntPtr.Zero Then
                Throw New Exception("Could not set keyboard hook")
            End If
        End Sub
    
        Protected Overrides Sub Finalize()
            If Not HHookID = IntPtr.Zero Then
                UnhookWindowsHookEx(HHookID)
            End If
            MyBase.Finalize()
        End Sub
    
    End Class
    How I'm calling the hook:
    Code:
    Dim keycount As Integer
    Private Sub kbHook_KeyUp(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyUp
               keycount += 1
               textbox1.text += keycount
            End If
    End Sub
    Last edited by JoshA56; Jun 24th, 2014 at 10:14 PM.

  4. #4
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,464

    Re: Actions Per Minute Tool

    textbox1.text += keycount???

    a string + a number???

    how have you been a member for 6 years without turning Option Strict on?

  5. #5

    Thread Starter
    Lively Member
    Join Date
    Feb 2008
    Posts
    113

    Re: Actions Per Minute Tool

    If you set textbox1's text to "0" on default then yes you can use that as an integer my friend.

  6. #6
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,464

    Re: Actions Per Minute Tool

    Yes you can, but if you turn Option Strict on, you can't. It might help you find other problems too.

  7. #7
    I don't do your homework! opus's Avatar
    Join Date
    Jun 2000
    Location
    Good Old Europe
    Posts
    3,863

    Re: Actions Per Minute Tool

    Quote Originally Posted by JoshA56 View Post
    How I'm calling the hook:
    Code:
    Dim keycount As Integer
    Private Sub kbHook_KeyUp(ByVal Key As System.Windows.Forms.Keys) Handles kbHook.KeyUp
               keycount += 1
               textbox1.text += keycount
            End If
    End Sub
    Beside the point about adding an integer to a string, what do you expect your textbox1 to show?
    1. Event Keycount=1 textbox1.text="1"
    2. Event Keycount=2 textbox1.text="3"

    ???????????????
    You're welcome to rate this post!
    If your problem is solved, please use the Mark thread as resolved button


    Wait, I'm too old to hurry!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width