❔ ✅ Is there a Way to extend this code from this yt tutorial

so ive been following this yt tutorial https://www.youtube.com/watch?v=nTKTPqqFtrk, which shows a way to implement a keyboard hook(i think "^^) and it does work so far, but i was wondering if i could extend the code to show me which button is pressed and released rather than just givig me a messagebox with the notofication.(i apologize if my question is written poorly) here is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testform2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Hotkeys.Init(comboBox1);
}

protected override void WndProc(ref Message hotkey)
{
bool Visible = this.Visible;
base.WndProc(ref hotkey);

if(Visible && hotkey.Msg == 0x0312)
{
textBox1.Text += hotkey.Msg.ToString() + "\n\r";
//MessageBox.Show("Global Hotkey Pressed");
}
}

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Hotkeys.SetHotKey(comboBox1, Handle);
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testform2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Hotkeys.Init(comboBox1);
}

protected override void WndProc(ref Message hotkey)
{
bool Visible = this.Visible;
base.WndProc(ref hotkey);

if(Visible && hotkey.Msg == 0x0312)
{
textBox1.Text += hotkey.Msg.ToString() + "\n\r";
//MessageBox.Show("Global Hotkey Pressed");
}
}

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Hotkeys.SetHotKey(comboBox1, Handle);
}
}
}
tez kidd
YouTube
C# Global Hotkeys
Setting up Global Hotkeys in C# Win Forms By my mistake, I accidentally added the imports ReleaseCapture & SendMessage. You do not need these.
123 Replies
RohesKätzchen
RohesKätzchen9mo ago
and the class:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace testform2
{
internal class Hotkeys
{
[DllImport("user32.dll")]

public static extern int SendMessage(IntPtr hWnD, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]

public static extern bool ReleaseCapture();
[DllImport("user32.dll")]

public static extern bool RegisterHotKey(IntPtr hWnD, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]

public static extern int UnregisterHotKey(IntPtr hWnd, int id);

public static void Init(ComboBox comboBox)
{
var hotkeys = new List<string>() { "DEL", "INSERT", "HOME" }; //liste mit allen Tasten
comboBox.DataSource = hotkeys;
}

public static void SetHotKey(ComboBox comboBox, IntPtr hwnd)
{
UnregisterHotKey(hwnd, 0);

if(comboBox.Text == "DEL")
{
RegisterHotKey(hwnd,0,0, Keys.Delete.GetHashCode());
}
if (comboBox.Text == "INSERT")
{
RegisterHotKey(hwnd, 0, 0, Keys.Insert.GetHashCode());
}
if (comboBox.Text == "HOME")
{
RegisterHotKey(hwnd, 0, 0, Keys.Home.GetHashCode());
}

/*RegisterHotKey(hwnd, 0, 0, Keys.Home.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.Insert.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.Delete.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.A.GetHashCode());*/

}

}
}
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace testform2
{
internal class Hotkeys
{
[DllImport("user32.dll")]

public static extern int SendMessage(IntPtr hWnD, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]

public static extern bool ReleaseCapture();
[DllImport("user32.dll")]

public static extern bool RegisterHotKey(IntPtr hWnD, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]

public static extern int UnregisterHotKey(IntPtr hWnd, int id);

public static void Init(ComboBox comboBox)
{
var hotkeys = new List<string>() { "DEL", "INSERT", "HOME" }; //liste mit allen Tasten
comboBox.DataSource = hotkeys;
}

public static void SetHotKey(ComboBox comboBox, IntPtr hwnd)
{
UnregisterHotKey(hwnd, 0);

if(comboBox.Text == "DEL")
{
RegisterHotKey(hwnd,0,0, Keys.Delete.GetHashCode());
}
if (comboBox.Text == "INSERT")
{
RegisterHotKey(hwnd, 0, 0, Keys.Insert.GetHashCode());
}
if (comboBox.Text == "HOME")
{
RegisterHotKey(hwnd, 0, 0, Keys.Home.GetHashCode());
}

/*RegisterHotKey(hwnd, 0, 0, Keys.Home.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.Insert.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.Delete.GetHashCode());
RegisterHotKey(hwnd, 0, 0, Keys.A.GetHashCode());*/

}

}
}
JakenVeina
JakenVeina9mo ago
if by "extend" the code, you mean "modify" it, then sure
RohesKätzchen
RohesKätzchen9mo ago
yes sorry i mean modify :)
arion
arion9mo ago
There are a number of Nuget Packages available that can already do this if you want a more structured approach, H.Hooks features an event based notification for when a key is pressed Down and Up and whether to intercept the keys (dont send them to other applications). There's also the GlobalHotkey approach from StackOverflow which has a sort of "register a callback" approach where you declare something like
GlobalHotKey.RegisterHotKey("Alt + Shift + S", () => DoSomething());
GlobalHotKey.RegisterHotKey("Alt + Shift + S", () => DoSomething());
or in your case
GlobalHotKey.RegisterHotKey("Alt + Shift + S", () => {
// Your code goes here.
});
GlobalHotKey.RegisterHotKey("Alt + Shift + S", () => {
// Your code goes here.
});
Additionally there's the advanced approach of doing WinAPI calls for low level keyboard procs which removes the need for your application in most cases needing to be on the same (or higher) elevation level as the focused application. Example use of H.Hooks is
using var keyboardHook = new LowLevelKeyboardHook();
keyboardHook.Up += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Up)}: {args}");
keyboardHook.Down += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Down)}: {args}");

keyboardHook.Start();

Console.ReadKey();
using var keyboardHook = new LowLevelKeyboardHook();
keyboardHook.Up += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Up)}: {args}");
keyboardHook.Down += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Down)}: {args}");

keyboardHook.Start();

Console.ReadKey();
Also, looking at the code you provided before (and some StackOverflow), if you'd want the key you can do this in your WndProc:
protected override void WndProc(ref Message hotkey)
{
const int WM_HOTKEY = 0x312;
base.WndProc(ref hotkey);

if (Visible && hotkey.Msg == WM_HOTKEY)
{
var key = (Keys)(((int)hotkey.LParam >> 16) & 0xFFFF);
textBox1.Text = key.ToString();
//MessageBox.Show("Global Hotkey Pressed");
}
}
protected override void WndProc(ref Message hotkey)
{
const int WM_HOTKEY = 0x312;
base.WndProc(ref hotkey);

if (Visible && hotkey.Msg == WM_HOTKEY)
{
var key = (Keys)(((int)hotkey.LParam >> 16) & 0xFFFF);
textBox1.Text = key.ToString();
//MessageBox.Show("Global Hotkey Pressed");
}
}
RohesKätzchen
RohesKätzchen9mo ago
Thx for the help im going to look into it when i have some time :) Btw im a beginner incase that wasnt clear, so i will probably need aome time to understand woah ive been reading your comments and i must say im truly thankfull arion <3
arion
arion9mo ago
ThumbsUp
RohesKätzchen
RohesKätzchen9mo ago
ive been looking into this H.Hooks and i would like to try it but i dont know how to use it, there is this install package thing but i dont know where to use it "^^?
No description
arion
arion9mo ago
Thats one way to install a package, another is using Visual Studio's nuget package manager
arion
arion9mo ago
most of your nuget packages will likely be installed this way
RohesKätzchen
RohesKätzchen9mo ago
thanks you are a great help <3
RohesKätzchen
RohesKätzchen9mo ago
ive copied your code and got this error...
No description
RohesKätzchen
RohesKätzchen9mo ago
so ive modified the code with the help of chat gpt, but when i launch it now i get this error message, would you mind helping me further?
No description
JakenVeina
JakenVeina9mo ago
can you translate that?
arion
arion9mo ago
oh, since its a winforms app, just remove the Console.ReadKey line for these:
keyboardHook.Up += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Up)}: {args}");
keyboardHook.Down += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Down)}: {args}");
keyboardHook.Up += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Up)}: {args}");
keyboardHook.Down += (_, args) => Console.WriteLine($"{nameof(keyboardHook.Down)}: {args}");
you can even create methods for them if you want like:
private LowLevelKeyboardHook keyboardHook;
private void Form1_Load(object sender, EventArgs e)
{
keyboardHook = new LowLevelKeyboardHook();
keyboardHook.Up += OnGlobalKeyUp;
keyboardHook.Down += OnGlobalKeyDown;
keyboardHook.Start();
}

private void OnGlobalKeyUp(object? sender KeyboardEventArgs args)
{

}
private void OnGlobalKeyUp(object? sender KeyboardEventArgs args)
{

}
private LowLevelKeyboardHook keyboardHook;
private void Form1_Load(object sender, EventArgs e)
{
keyboardHook = new LowLevelKeyboardHook();
keyboardHook.Up += OnGlobalKeyUp;
keyboardHook.Down += OnGlobalKeyDown;
keyboardHook.Start();
}

private void OnGlobalKeyUp(object? sender KeyboardEventArgs args)
{

}
private void OnGlobalKeyUp(object? sender KeyboardEventArgs args)
{

}
its likely something like, the app isnt accepting std-in or is not a designated console app so therefore InvalidOperationException
Accord
Accord9mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
RohesKätzchen
RohesKätzchen9mo ago
sorry for not replying for so long i was busy
RohesKätzchen
RohesKätzchen9mo ago
ive intalled the english language model and chaged it to english but for some reason the error is still in german i already restatet vs idk how to translate it?
No description
JakenVeina
JakenVeina9mo ago
I mean, do you read German? You seem to speak English.
RohesKätzchen
RohesKätzchen9mo ago
german is my native language but ofc i use english to write with you guys bc not everyone speaks german :) but i take that as a compliment :D
JakenVeina
JakenVeina9mo ago
anyway, the exception being in German is a result of .NET, not Visual Studio I dunno if it's a setting you could change, without having to reinstall an English verison of .NET, or if it's based on your system language settings
RohesKätzchen
RohesKätzchen9mo ago
this is how my code looks so far what does the "?" do?. and how can i get the key ive typed to go in my messagebox?
No description
RohesKätzchen
RohesKätzchen9mo ago
it looks like this error is in english so the langauge thing seems to be fixed :)
JakenVeina
JakenVeina9mo ago
? can mean a lot of things, but when attached to a type specification like that, it indicates that the type is allowed to be null specifically, because it's attached to a reference type, it's a metadata annotation for the compiler, and is basically meaningless to code functionality as the error indicates, you are not using a version of the C# language that supports that feature it was added in C#8 you're using C#7.3
RohesKätzchen
RohesKätzchen9mo ago
oh i guess i should update my c#
JakenVeina
JakenVeina9mo ago
you cannot annotate reference types as nullable or non-nullable, all reference types are assumed to always be nullable or remove the annotation
RohesKätzchen
RohesKätzchen9mo ago
i think i want to update, there is probably a reason that this was added so sooner or later i might need it anyway
JakenVeina
JakenVeina9mo ago
there is indeed a reason
RohesKätzchen
RohesKätzchen9mo ago
but how can i get the key i typed to into the messagebox?
JakenVeina
JakenVeina9mo ago
it allows the compiler to better analyze your code for flaws it allows the compiler to do this....
public void DoSomething(SomeObject? target)
{
target.DoSomething(); // Compiler will warn you of a possible NullReferenceException here, because it knows that target can be null
}
public void DoSomething(SomeObject? target)
{
target.DoSomething(); // Compiler will warn you of a possible NullReferenceException here, because it knows that target can be null
}
thus allowing you to either fix it with a null check
public void DoSomething(SomeObject? target)
{
target?.DoSomething();
}
public void DoSomething(SomeObject? target)
{
target?.DoSomething();
}
or change the annotation, if your intention is that target should never be null
public void DoSomething(SomeObject target)
{
target.DoSomething();
}
public void DoSomething(SomeObject target)
{
target.DoSomething();
}
THAT means that the compiler will warn you if you try and CALL DoSomething() with a value for target that it thinks could be null etc. etc. okay, so.... you are typing a key into a messagebox?
RohesKätzchen
RohesKätzchen9mo ago
thanks for the explanation, i want to show me the key once i pressed it in a messagebox and later in a textbox for now the messagebox will do
JakenVeina
JakenVeina9mo ago
what messagebox? oh, wait okay opposite of what you said so, you want to CAPTURE a keypress and display it later
RohesKätzchen
RohesKätzchen9mo ago
yep im going to update my c# later for now im going to remove the "?"
RohesKätzchen
RohesKätzchen9mo ago
i figured it out
No description
JakenVeina
JakenVeina9mo ago
gg
RohesKätzchen
RohesKätzchen9mo ago
is there a limit to wich key can be captured or is every key "captureable"?
JakenVeina
JakenVeina9mo ago
is there a limit to wich key can be captured
not really, no
RohesKätzchen
RohesKätzchen9mo ago
oh thats nice
RohesKätzchen
RohesKätzchen9mo ago
so when i want to write my text into a textbox i get this exception
No description
RohesKätzchen
RohesKätzchen9mo ago
chatgpt translation: "System.InvalidOperationException: 'Invalid cross-thread operation: Access to control textBox1 was performed from a thread other than the one it was created for.'"
JakenVeina
JakenVeina9mo ago
error says it all
RohesKätzchen
RohesKätzchen9mo ago
this seems to fix it, but i dont understand why, but u guess as long as it works i should be fine
No description
JakenVeina
JakenVeina9mo ago
Invoke() marshals an action onto the thread that owns the control I.E. your Form which is precisely what you want the global key handler you registered is invoked on a thread entirely outside of your application, managed by the OS
RohesKätzchen
RohesKätzchen9mo ago
oh thanks i am going to need some of this stuff in school and present and explain it for a grade, this is going to be interesting xD
RohesKätzchen
RohesKätzchen9mo ago
okay this is everything i wanted to know i guess, now im going to implement the mouse (this should be similar), and in the future i want to replay the inputs but im going to do that far in the future
No description
arion
arion9mo ago
you dont necessarily need to add new Action( but instead
Invoke(()=>
{
});
Invoke(()=>
{
});
so this, becomes this:
Invoke(()=>
{
textbox1.Text += $"Pressed: {args}\n";
});
Invoke(()=>
{
textbox1.Text += $"Pressed: {args}\n";
});
RohesKätzchen
RohesKätzchen9mo ago
oh okay, what does the new action part do?
arion
arion9mo ago
uh, it calls itself. ()=> {}; is essentially an action, so doing that twice wont help much
RohesKätzchen
RohesKätzchen9mo ago
uhh something went wrong xD
No description
RohesKätzchen
RohesKätzchen9mo ago
i think i leave the code as it is, bc im not realy sure what happening
arion
arion9mo ago
What does the error say? since it works on mine
RohesKätzchen
RohesKätzchen9mo ago
No description
arion
arion9mo ago
What does "Show potential fixes" say? because it doesnt complain on my side:
Invoke(() =>
{
textBox1.Text += "Delay: " + time + Environment.NewLine;
});
Invoke(() =>
{
textBox1.Text += "Delay: " + time + Environment.NewLine;
});
RohesKätzchen
RohesKätzchen9mo ago
do you mean this?
No description
RohesKätzchen
RohesKätzchen9mo ago
how can i view potentioal fixes?
arion
arion9mo ago
try this option?
RohesKätzchen
RohesKätzchen9mo ago
now i got this
No description
arion
arion9mo ago
@V.EINA Jaken Any ideas?
JakenVeina
JakenVeina9mo ago
what is the error there?
RohesKätzchen
RohesKätzchen9mo ago
same as befor
No description
JakenVeina
JakenVeina9mo ago
I'm not sure why the compiler can't infer the delegate type there, but it obviously can't .Invoke() has two different overloads that could fit that signature
public void Invoke(Action method);

public object? Invoke(Delegate method);
public void Invoke(Action method);

public object? Invoke(Delegate method);
since Action actually derives from Delegate the compiler ought to give precedence to the Action overload, but it's trying to bind to the Delegate one instead and it doesn't know what type of delegate to create put new Action() back whatever the combination of reasons, Roslyn isn't smart enough to infer the delegate type here, you need to specify it manually
arion
arion9mo ago
imma try booting up my Visual Studio and see if its a VS issue cuz im using Rider most of the time
JakenVeina
JakenVeina9mo ago
also, to answer the earlier question, new Action(() => something()) and () => something are functionally and semantically identical the second is simply shorthand for the first, when the compiler is smart enough to infer that an Action is needed
arion
arion9mo ago
hmmm, are u using Visual Studio 2017 or earlier by any chance?
JakenVeina
JakenVeina9mo ago
no
arion
arion9mo ago
@RohesKätzchen
JakenVeina
JakenVeina9mo ago
oh, him
arion
arion9mo ago
xD
JakenVeina
JakenVeina9mo ago
actually, he's using C#7.3, established earlier that could explain the lack of inferrability
RohesKätzchen
RohesKätzchen9mo ago
i think visual studio 2022
JakenVeina
JakenVeina9mo ago
drop your lang version to 7.3 arion
RohesKätzchen
RohesKätzchen9mo ago
how can i change the lang version, couldnt figure it out earlier?
JakenVeina
JakenVeina9mo ago
in your csproj file
arion
arion9mo ago
I get nullability errors but nothing else when dropping to 7.3
JakenVeina
JakenVeina9mo ago
hmmm remove the brackets?
arion
arion9mo ago
still no error odd
JakenVeina
JakenVeina9mo ago
:/ @RohesKätzchen if you put a new Action() wrapper back around that, does that eliminate the error?
RohesKätzchen
RohesKätzchen9mo ago
yes
No description
JakenVeina
JakenVeina9mo ago
@arion you're testing against WinForms?
arion
arion9mo ago
yeah, from my csproj:
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
JakenVeina
JakenVeina9mo ago
I mean, like, that .Invoke() calls is System.Windows.Forms.Control.Invoke?
arion
arion9mo ago
yeah
JakenVeina
JakenVeina9mo ago
@RohesKätzchen what framework are you running?
arion
arion9mo ago
Since its to method body it changes from Action to Func<string>
JakenVeina
JakenVeina9mo ago
wait, why the hell is it resolving to the Func<T> overload?
arion
arion9mo ago
setting the string returns the string
RohesKätzchen
RohesKätzchen9mo ago
uhh windows forms? this one
No description
arion
arion9mo ago
noooooo
JakenVeina
JakenVeina9mo ago
yeah
arion
arion9mo ago
There we have the problemo xD
JakenVeina
JakenVeina9mo ago
not really a problem, just the reason that you're getting different behavior
RohesKätzchen
RohesKätzchen9mo ago
huh, i dont understand anything whats going on xD
JakenVeina
JakenVeina9mo ago
you're running .NET Framework arion's running on .NET 5+ the Action and Func<T> overloads of .Invoke() don't exist for you only the Delegate overload so you literally HAVE to specify the delegate type
RohesKätzchen
RohesKätzchen9mo ago
oh okay
arion
arion9mo ago
Basically .NET Framework is from like 10-20 years ago, .NET Core is more modern
JakenVeina
JakenVeina9mo ago
somewhat it's just a different version, different stuff exists
RohesKätzchen
RohesKätzchen9mo ago
btw, thank both of you for all the help, you realy saved me <3
arion
arion9mo ago
Regarding this btw, it transforms Action to Func<string> xD
MODiX
MODiX9mo ago
arion
REPL Result: Success
var x = "yes";
string y = x += "no";

Console.WriteLine(y);
var x = "yes";
string y = x += "no";

Console.WriteLine(y);
Console Output
yesno
yesno
Compile: 596.034ms | Execution: 75.072ms | React with ❌ to remove this embed.
JakenVeina
JakenVeina9mo ago
yeah
arion
arion9mo ago
Weird interaction TeeHee
RohesKätzchen
RohesKätzchen9mo ago
how hard will it be to simulate mouse and key inputs outside of the application?
JakenVeina
JakenVeina9mo ago
simulate? like, for testing?
RohesKätzchen
RohesKätzchen9mo ago
like i can record my inputs and later on in my project i want to ,,play,, them like a macro
JakenVeina
JakenVeina9mo ago
hard
RohesKätzchen
RohesKätzchen9mo ago
damn, i should have taken a easier project for school :(
JakenVeina
JakenVeina9mo ago
it rather goes against one of the OS's core jobs of keeping separate programs from interfering with one another same as capturing global key input, you're going well outside of the realm of C# to generate global key input
RohesKätzchen
RohesKätzchen9mo ago
do you think there is another nuget library for that, bc that made it a lot easier? well thats a diffrent topic ig, so for now all my questions are answered. i probably will open another topic about simulating input in the near future again huge thanks to you <3 so if you are also done, i would /close this thread can i still read it after i closed it?
arion
arion9mo ago
From purely looking at the readme here https://www.nuget.org/packages/H.InputSimulator#readme-tab this might work
JakenVeina
JakenVeina9mo ago
possibly yes
RohesKätzchen
RohesKätzchen9mo ago
well i will look into this so ig thats it kind of emotional for me to close it :( well bye 🥲
arion
arion9mo ago
You can always create additional help posts if you're stuck on anything else nod
RohesKätzchen
RohesKätzchen9mo ago
i will 👋 ayo incase one of you is still reading how can i get the scroll event for my mouse in H.Hooks?
arion
arion9mo ago
var x = new LowLevelMouseHook();

x.Wheel += (sender, eventArgs) =>
{
// Your code goes here
};

x.Dispose();
var x = new LowLevelMouseHook();

x.Wheel += (sender, eventArgs) =>
{
// Your code goes here
};

x.Dispose();
if you want to send the scroll keys:
var x = new InputSimulator();


x.Mouse.VerticalScroll(3);
var x = new InputSimulator();


x.Mouse.VerticalScroll(3);
3 being the amount of scrolls upwards i believe
RohesKätzchen
RohesKätzchen9mo ago
thanks a lot <3
Accord
Accord9mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.