I ran into some problems with my C# program. I want to create stopwatch (countdown from certain value), that is started, when a certain key is pressed. To handle keys i use low-level keyboard hook. But this class has static methods, so if I want to call a method from different class, that is not static, i have to create a new instance. With the countdown, I want to change the Text
property of the TextBox
element every tick (second). The problem is, how to change the properties of TextBox
every tick (in the Countdown
class), when I have to create a new instance of Countdown
class in the static method, thus the TextBox
will no longer response to the previous TextBox
. My code works perfectly fine, the keys are recognized, the timer is counting down and showing the value of seconds in separate MessageBox
'es (for debug), but it won't change the text in the form.
If it would help you to understand what I wrote above, I can give you my code. Just say so in the comment.
Thanks for help in advance.
The code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace stopwatch2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
InterceptKeys.InterceptInit();
}
private void Form1_Closing(object sender, CancelEventArgs e)
{
InterceptKeys.Unhook();
}
public void changeText(string text)
{
MessageBox.Show(text); //for debug
textBox1.Text = text;
}
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void InterceptInit()
{
_hookID = SetHook(_proc);
}
public static void Unhook()
{
UnhookWindowsHookEx(_hookID);
}
private 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);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
public static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Countdown timer = new Countdown(); //creating new instance
if ((Keys)vkCode == Keys.Home)
{
timer.StartTimer();
}
if ((Keys)vkCode == Keys.End)
{
timer.StopTimer();
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
public partial class Countdown : Form1
{
public System.Windows.Forms.Timer timer1;
public int counter = 60;
public void StartTimer()
{
timer1 = new System.Windows.Forms.Timer();
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Interval = 1000; // 1 second
timer1.Start();
changeText(counter.ToString());
}
public void timer1_Tick(object sender, EventArgs e)
{
counter--;
if (counter == 0)
counter = 60;
changeText(counter.ToString());
}
public void StopTimer()
{
timer1.Stop();
}
}
}
}