To clear things up before you mention the most obvious choice, I am already calling ShowDialog and not the Show method!!!
I would like to block the close (invoked from a different thread) off an WPF window if a OpenFileDialog is open.
Here is the code I have (reduced to show my problem):
public class FooWindow : Window
{
public FooWindow()
{
InitializeComponent();
this.Closing += OnClosing;
}
public void OpenDialogAndCloseMe()
{
var ofd = new OpenFileDialog();
Thread th = new Thread(() => CloseMe());
th.Start();
ofd.ShowDialog(this);
}
public void CloseMe()
{
System.Threading.Thread.Sleep(2000); //give the OpenFileDialog time to pop up...
//since this method gets called from a different thread invoke it...
this.Dispatcher.Invoke(() => this.Close());
}
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
e.Cancel = true;
}
}
The problem I am facing is the OnClosing part, how would I get the OpenFileDialog (or any other Dialog in that case).
I have searched the web and found Win32 methods like:
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
I have tried them both but they return 0 children, any ideas what's going wrong?
Here is the full code I tried so far:
//replace the above OnClosing with this implementation... all 3 return false
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
var hWnd = new WindowInteropHelper(this).Handle;
if (WindowHandling.GetChildren(hWnd).Any())
e.Cancel = true;
if (WindowHandling.GetChildrenV2(hWnd).Any())
e.Cancel = true;
if (WindowHandling.GetChildrenV3(hWnd).Any())
e.Cancel = true;
}
public static class WindowHandling
{
private delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
{
windowHandles.Add(windowHandle);
return true;
}
public static IEnumerable<IntPtr> GetChildren(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
return Enumerable.Empty<IntPtr>();
var x = new WindowHandleInfo(hWnd);
return x.GetAllChildHandles();
}
public static IEnumerable<IntPtr> GetChildrenV2(IntPtr hWnd)
{
var windowHandles = new ArrayList();
EnumedWindow callBackPtr = GetWindowHandle;
EnumChildWindows(hWnd, callBackPtr, windowHandles);
return windowHandles.OfType<IntPtr>();
}
public static IEnumerable<IntPtr> GetChildrenV3(IntPtr hParent)
{
var result = new List<IntPtr>();
var ct = 0;
var maxCount = 100;
var prevChild = IntPtr.Zero;
while (true && ct < maxCount)
{
var currChild = FindWindowEx(hParent, prevChild, null, null);
if (currChild == IntPtr.Zero)
break;
result.Add(currChild);
prevChild = currChild;
++ct;
}
return result;
}
//http://stackoverflow.com/questions/1363167/how-can-i-get-the-child-windows-of-a-window-given-its-hwnd
private class WindowHandleInfo
{
private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
private readonly IntPtr _mainHandle;
public WindowHandleInfo(IntPtr handle)
{
_mainHandle = handle;
}
public IEnumerable<IntPtr> GetAllChildHandles()
{
var childHandles = new List<IntPtr>();
var gcChildhandlesList = GCHandle.Alloc(childHandles);
var pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
try
{
var childProc = new EnumWindowProc(EnumWindow);
var x = EnumChildWindows(this._mainHandle, childProc, pointerChildHandlesList);
if (x == false)
{
var error = Marshal.GetLastWin32Error();
}
}
finally
{
gcChildhandlesList.Free();
}
return childHandles;
}
private static bool EnumWindow(IntPtr hWnd, IntPtr lParam)
{
var gcChildhandlesList = GCHandle.FromIntPtr(lParam);
if (gcChildhandlesList.Target == null)
return false;
var childHandles = gcChildhandlesList.Target as List<IntPtr>;
if (childHandles != null)
childHandles.Add(hWnd);
return true;
}
}
}
You can solve this with a boolean value tracking if it's open or not:
bool dialogOpen = false;
public void OpenDialogAndCloseMe()
{
var ofd = new OpenFileDialog();
Thread th = new Thread(() => CloseMe());
th.Start();
dialogOpen = true;
ofd.ShowDialog(this);
dialogOpen = false;
}
private void OnClosing(object sender, CancelEventArgs e)
{
//check if OpenFileDialog is still open and block the close...
if(dialogOpen)
{
e.Cancel = true;
}
}
Related
An object reference is required for the non-static field, method, or property 'injector.SI(uint, string)' I dont really understand what is the error or the mistake (Visual Studio C# windows form app it would be a really big help if anyone can fix this because I cant find the solution at all Ive been searching for quite a bit of time but still cant find any solution
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Hoshiyomi.functions
{
public partial class injector : Form
{
public injector()
{
InitializeComponent();
Process[] PC = Process.GetProcesses().Where(p => (long)p.MainWindowHandle !=0).ToArray();
comboBox1.Items.Clear();
foreach (Process p in PC)
{
comboBox1.Items.Add(p.ProcessName);
}
}
private void injector_Load(object sender, System.EventArgs e)
{
this.FormBorderStyle = FormBorderStyle.FixedSingle;
}
private static string DLLP { get; set; }
private void flatButton1_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog OFD = new OpenFileDialog();
OFD.InitialDirectory = #"C:\";
OFD.Title = "NotWeeb | Locate DLL";
OFD.DefaultExt = "dll";
OFD.Filter = "DLL Files (*.dll)|*.dll";
OFD.CheckFileExists = true;
OFD.CheckPathExists = true;
OFD.ShowDialog();
textBox1.Text = OFD.FileName;
DLLP = OFD.FileName;
}
catch (Exception ed)
{
MessageBox.Show(ed.Message);
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
DLLP = textBox1.Text;
}
private void flatButton3_Click(object sender, EventArgs e)
{
Process[] PC = Process.GetProcesses().Where(p => (long)p.MainWindowHandle != 0).ToArray();
comboBox1.Items.Clear();
foreach (Process p in PC)
{
comboBox1.Items.Add(p.ProcessName);
}
}
static readonly IntPtr INTPTR_ZERO = (IntPtr)0;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, GraphicsUnit dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr Module, string lpProcName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpBaseAddress, byte[] Buffer, uint size, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttribute, IntPtr dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
public static int Inject(string PN, string DLLP)
{
if (!File.Exists(DLLP)) { return 1; } //1 not exist
uint _procId = 0;
Process[] _procs = Process.GetProcesses();
for (int i = 0; i < _procs.Length; i++)
{
if (_procs[i].ProcessName == PN)
{
_procId = (uint)_procs[i].Id;
}
}
if (_procId == 0) { return 2; } //2 not exist
if (!SI(_procId, DLLP))
{
return 3; //3 inject fail
}
return 4; //4 inject success
}
public bool SI(uint P, string DLLP)
{
IntPtr hndProc = OpenProcess((0x2 | 0x8 | 0x10 | 0x20 | 0x400), 1, P);
if (hndProc == INTPTR_ZERO) { return false; }
IntPtr lpAddress = VirtualAllocEx(hndProc, (IntPtr)null, (IntPtr)DLLP.Length, (0x1000 | 0x2000), 0x40);
if (lpAddress == INTPTR_ZERO)
{
return false;
}
byte[] bytes = Encoding.ASCII.GetBytes(DLLP);
if (WriteProcessMemory(hndProc, lpAddress, bytes, (uint)bytes.Length, 0) == 0)
{
return false;
}
CloseHandle(hndProc);
return true;
}
private IntPtr OpenProcess(int v1, int v2, uint p)
{
throw new NotImplementedException();
}
private IntPtr VirtualAllocEx(IntPtr hndProc, IntPtr intPtr, IntPtr length, int v1, int v2)
{
throw new NotImplementedException();
}
private void flatButton2_Click(object sender, EventArgs e)
{
int Result = Inject(comboBox1.Text, DLLP);
if (Result == 1)
{
MessageBox.Show("File doesn't exist");
}
else if (Result == 2)
{
MessageBox.Show("Process doesn't exist");
}
else if (Result == 3)
{
MessageBox.Show("Injection fail");
}
else if (Result == 4)
{
MessageBox.Show("Injection Succeeded");
}
}
}
}
enter image description here
Im looking for a way that so Users that use my Program can change the Hotkeys theirself so its not bound to D,F12,K,F,A and so on.. i kinda want that ppl can change it via a Textbox or maybe via a Settings File. Been Stuck on it for couple Weeks now and i just cant find a way to make it happen, its my first Program im working on.
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.D:
// stuff
Salvagebtn.PerformClick();
break;
case Keys.F12:
// stuff
pausebtn.PerformClick();
break;
case Keys.K:
//stuff
Geardropbtn.PerformClick();
break;
case Keys.F:
//stuff
Gamblebtn.PerformClick();
break;
case Keys.A:
//stuff
LeftClickSpambtn.PerformClick();
break;
case Keys.H:
// stuff
openGRbtn.PerformClick();
break;
case Keys.B:
//stuff
gemupbtn.PerformClick();
break;
}
}
I had a few minutes, during commercials. So, I wrote the following:
EDIT : Code wrapped with a global keyboard handler. You will also need to add a reference to PresentationCore and WindowsBase ...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
[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);
[DllImport("user32.dll")]
static extern int MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
S_WM_KEYDOWN = 0x0104,
S_WM_KEYUP = 0x0105;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static Form1 hookForm;
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);
static bool bShortcutPressed;
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (hookForm.Handle != GetForegroundWindow())
{
if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)S_WM_KEYDOWN)))
{
int vkCode = Marshal.ReadInt32(lParam);
bool bCtrl = (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl));
bool bAlt = (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt));
bool bShift = (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
Keys hookKey = (Keys)vkCode;
hookKey = (bCtrl) ? ((Keys.Control | hookKey)) : hookKey;
hookKey = (bAlt) ? ((Keys.Alt | hookKey)) : hookKey;
hookKey = (bShift) ? ((Keys.Shift | hookKey)) : hookKey;
Debug.Print($"hookKey {hookKey} {bCtrl} {bAlt} {bShift}");
if (!bShortcutPressed && dicTest.ContainsValue(hookKey))
{
hookForm.OnKeyDown(new System.Windows.Forms.KeyEventArgs(hookKey));
bShortcutPressed = true; Debug.Print($"{bShortcutPressed}");
}
}
if (nCode >= 0 && (((wParam == (IntPtr)WM_KEYUP) || (wParam == (IntPtr)S_WM_KEYUP)))) { bShortcutPressed = false; Debug.Print($"{bShortcutPressed}"); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
static Dictionary<string, Keys> dicTest = new Dictionary<string, Keys>();
public void AddHotKey(Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false)
{
KeyDown += delegate (object sender, System.Windows.Forms.KeyEventArgs e) { if (IsHotkey(e, key, ctrl, shift, alt)) { function(); } };
}
public bool IsHotkey(System.Windows.Forms.KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) =>
eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt;
public Form1() => InitializeComponent();
private void Form1_Load(object sender, EventArgs e)
{
_hookID = SetHook(_proc);
hookForm = this;
KeyPreview = true;
String[] names = Enum.GetNames(typeof(Keys));
foreach (string key in names) { comboBox1.Items.Add(key); }
comboBox1.SelectedItem = comboBox1.Items[0];
KeyDown += Form1_KeyDown;
}
private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
Debug.Print($"Form1_KeyDown : {e.KeyData}");
}
protected override void OnFormClosing(FormClosingEventArgs e) => UnhookWindowsHookEx(_hookID);
private void button1_Click(object sender, EventArgs e)
{
Keys hookKey = (Keys)Enum.Parse(typeof(Keys), comboBox1.Text);
hookKey = (checkBox1.Checked) ? ((Keys.Control | hookKey)) : hookKey;
hookKey = (checkBox2.Checked) ? ((Keys.Alt | hookKey)) : hookKey;
hookKey = (checkBox3.Checked) ? ((Keys.Shift | hookKey)) : hookKey;
if (!dicTest.ContainsValue(hookKey))
{
Debug.Print($"Going to add : {hookKey} : to our Shortcut Dictionary<>");
dicTest.Add("test", hookKey);
AddHotKey(() => { button2.PerformClick(); }, (Keys)new KeysConverter().ConvertFrom(comboBox1.Text), checkBox1.Checked, checkBox3.Checked, checkBox2.Checked);
//checkBox1.Enabled = checkBox2.Enabled = checkBox3.Enabled = comboBox1.Enabled = button1.Enabled = false;
label2.Text = "Go ahead and tryout your Shortcut now ...";
}
else { label2.Text = "Shortcut key is already stored in our Dictionary<> ..."; }
}
private void button2_Click(object sender, EventArgs e) { Debug.Print($"Button2 was clicked"); }
}
}
You're presented with:
Trying the desired shortcut out:
Just an example, to show there are many ways to do things.
Easy to write them out to a file or something. If you were going to ... I'd change the AddHotKey Action param to a string which contains a function name and then using different methods you can have it invoke that function.
Really though, I'd incorporate a Dictionary as the one talked about below.
There's a myriad of different ways you could handle this. Personally, I'd opt for using a Dictionary with the Keycode as the Key, and a delegate/action as the value. Then on the keypress, you just call the function in the dictionary that's associated with that keycode. Something along the lines of:
Dictionary<KeyCode, System.Action> keyDict = new Dictionary<KeyCode, System.Action>();
// populate it however you wish. Whether it's user entry or from a file or something.
keyDict.Add(KeyCode.D, SomeFunction);
If you want to change the delegate in the dictionary:
keyDict[KeyCode.D] = SomeOtherFunction;
Then later in the event handler:
public void gHook_KeyDown(object sender, KeyEventArgs e)
{
keyDict[e.KeyCode].Invoke();
}
public void SomeFunction()
{
Console.WriteLine("called some function");
}
(This question might have a bit more detail if needed: Create dictionary with dynamic keys in C#)
In a C# Windows Form I would like transcode information:
when user press CTRL+I, the app detect the key press combination, it takes the code in the clipboard and transcode it.
I find out this code:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (msg.Msg)
{
case 0x100:
case 0x104:
switch (keyData)
{
case Keys.Control | Keys.I:
MessageBox.Show("Ctrl + I pressed");
break;
}
break;
}
return base.ProcessCmdKey(ref msg, keyData);
}
This works fine when windows form has focus.
I would like detect the combination when the app is minimized as tray icon.
SOLUTION:
keyboardhook.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TrayIconForm
{
public static class Constants
{
public const int NOMOD = 0x0000;
public const int ALT = 0x0001;
public const int CTRL = 0x0002;
public const int SHIFT = 0x0004;
public const int WIN = 0x0008;
public const int WM_HOTKEY_MSG_ID = 0x0312;
}
public class KeyHandler
{
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private int modifier;
private int key;
private IntPtr hWnd;
private int id;
public KeyHandler(int modifier, Keys key, Form form)
{
this.modifier = modifier;
this.key = (int)key;
this.hWnd = form.Handle;
id = this.GetHashCode();
}
public override int GetHashCode()
{
return modifier ^ key ^ hWnd.ToInt32();
}
public bool Register()
{
return RegisterHotKey(hWnd, id, modifier, key);
}
public bool Unregiser()
{
return UnregisterHotKey(hWnd, id);
}
}
}
Information.cs (Form):
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TrayIconForm
{
public partial class Information : Form
{
private KeyHandler ghk;
public Information()
{
InitializeComponent();
ghk = new KeyHandler(Constants.CTRL, Keys.I, this);
ghk.Register();
}
private void HandleHotkey()
{
string s = Get_Copy();
notifyIcon1.BalloonTipText = s;
notifyIcon1.BalloonTipTitle = "You have pressed CTRL+i";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(500);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == Constants.WM_HOTKEY_MSG_ID)
HandleHotkey();
base.WndProc(ref m);
}
private string Get_Copy()
{
string r;
// Retrieves data from Clipboard
IDataObject iData = Clipboard.GetDataObject();
// Is Data Text?
if (iData.GetDataPresent(DataFormats.Text))
r = (String)iData.GetData(DataFormats.Text);
else
r = "nothing";
return r;
}
private void Information_Resize(object sender, EventArgs e)
{
if (FormWindowState.Minimized == this.WindowState)
{
notifyIcon1.BalloonTipText = "My application still working...";
notifyIcon1.BalloonTipTitle = "My Sample Application";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(500);
this.Hide();
}
else if (FormWindowState.Normal == this.WindowState)
{
notifyIcon1.Visible = false;
}
}
}
}
This works just instantiate it, subscibe to the event and you are done. It s not my script. A while ago I searched long for this but I found it and it works great. There were you subscribed to the event you can look if the pressed key (stored in the event args) is your key and when it is you can look with Keyboard.iskeydown(key.lctrl) if control is pressed to and then you can do what the combination should do.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace <yournamespace>
{
public class LowLevelKeyboardListener
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
[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 delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public event EventHandler<KeyPressedArgs> OnKeyPressed;
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
public LowLevelKeyboardListener()
{
_proc = HookCallback;
}
public void HookKeyboard()
{
_hookID = SetHook(_proc);
}
public void UnHookKeyboard()
{
UnhookWindowsHookEx(_hookID);
}
private 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 IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (OnKeyPressed != null) { OnKeyPressed(this, new KeyPressedArgs(KeyInterop.KeyFromVirtualKey(vkCode))); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
public class KeyPressedArgs : EventArgs
{
public Key KeyPressed { get; private set; }
public KeyPressedArgs(Key key)
{
KeyPressed = key;
}
}
}
[DllImport("user32.dll")]
public static extern int GetAsyncKeyState(Keys vKeys);
then
var ctrl = GetAsyncKeyState(Keys.ControlKey) & 0x8000;
var key = GetAsyncKeyState(Keys.F10) & 0x8000; //F10 for example
if(ctrl != 0 && key != 0)
{
//do sth
}
I am trying to minimize the window with the title located in the string p (specified by the user at runtime). The window is guaranteed to be a main window, because the user can only choose from main windows. I have tried showCmd, flags, and a mixture of the two, but every time, regardless of whether the window is minimized, I am shown the dialog box that states "Minimizing". How can I fix it?
private void button1_Click(object sender, EventArgs e)
{
foreach (Process proc in Process.GetProcesses())
{
if (proc.MainWindowTitle.Contains(p))
{
IntPtr handle = proc.Handle;
Program.WINDOWPLACEMENT wp = new Program.WINDOWPLACEMENT();
Program.GetWindowPlacement(handle, ref wp);
if ((wp.showCmd & 2) == 2)
{
wp.showCmd = 9;
MessageBox.Show("Restoring");
}
else
{
wp.showCmd = 2;
MessageBox.Show("Minimizing");
}
wp.length = Marshal.SizeOf(wp);
Program.SetWindowPlacement(handle, ref wp);
break;
}
}
}
What I'm using as p -
string p;
PictureBox i;
bool windowShowing = false;
bool minimized = false;
public TaskbarItem(string window)
{
InitializeComponent();
p = window;
button1.Text = window;
}
To get the window:
class WindowEnumerator
{
public delegate bool EnumDelegate(IntPtr hWnd, int lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
[DllImport("user32.dll", EntryPoint = "EnumDesktopWindows", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDelegate lpEnumCallbackFunction, IntPtr lParam);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
public static LinkedList<IntPtr> EnumMethod2(bool includeChild)
{
LinkedList<IntPtr> pss = new LinkedList<IntPtr>();
Process[] ps = Process.GetProcesses();
foreach (Process p in ps)
{
string s = p.MainWindowTitle;
if (s.Equals("") || s.Equals("Mod_Taskbar"))
{
continue;
}
LinkedListNode<IntPtr> node = new LinkedListNode<IntPtr>(p.MainWindowHandle);
pss.AddLast(node);
if (includeChild){
List<IntPtr> l = GetChildWindows(p.MainWindowHandle);
foreach (IntPtr i in l)
{
StringBuilder stb = new StringBuilder(255);
WindowEnumerator.GetWindowText(i, stb, 255);
if (stb.ToString().StartsWith("Address:") || stb.ToString().EndsWith("View") || stb.ToString().EndsWith("Control") ||
stb.ToString().EndsWith("Bar") || stb.ToString().EndsWith("bar") || stb.ToString().Contains("Host"))
{
continue;
}
if (Vars.excludes.Contains(stb.ToString()))
{
continue;
}
pss.AddAfter(node, i);
}
}
}
return pss;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
list.Add(handle);
return true;
}
}
And:
private void LoadWindows(object sender, EventArgs e)
{
panel1.Controls.Clear();
foreach (IntPtr p in WindowEnumerator.EnumMethod2(false))
{
StringBuilder stb = new StringBuilder(255);
WindowEnumerator.GetWindowText(p, stb, 255);
TaskbarItem t = new TaskbarItem(stb.ToString());
t.Dock = DockStyle.Left;
panel1.Controls.Add(t);
}
}
Did you check what value the wp.showCmd has?
Try with this code:
foreach (Process proc in Process.GetProcesses())
{
if (proc.MainWindowTitle.Contains(p))
{
IntPtr handle = proc.Handle;
Program.WINDOWPLACEMENT wp = new Program.WINDOWPLACEMENT();
Program.GetWindowPlacement(handle, ref wp);
if ((wp.showCmd & 2) == 2)
{
wp.showCmd = 9;
MessageBox.Show("Restoring");
}
else
{
wp.showCmd = 2;
MessageBox.Show("Minimizing");
}
wp.length = Marshal.SizeOf(wp);
Program.SetWindowPlacement(handle, ref wp);
break;
}
}
this.WindowState = FormWindowState.Minimized;
Does anyone know how to use the RegisterHotKey/UnregisterHotKey API calls in a console application? I assume that setting up/removing the hotkey is the same, but how do I get the call back when the key was pressed?
Every example I see is for Winforms, and uses protected override void WndProc(ref Message m){...}, which isn't available to me.
update: what I have is below, but the event is never hit. I thought it could be because when you load ConsoleShell it does block further execution, but even if I put SetupHotkey into a different thread nothing happens. Any thoughts?
class Program
{
static void Main(string[] args)
{
new Hud().Init(args);
}
}
class Hud
{
int keyHookId;
public void Init(string[] args)
{
SetupHotkey();
InitPowershell(args);
Cleanup();
}
private void Cleanup()
{
HotKeyManager.UnregisterHotKey(keyHookId);
}
private void SetupHotkey()
{
keyHookId = HotKeyManager.RegisterHotKey(Keys.Oemtilde, KeyModifiers.Control);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
}
void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
//never executed
System.IO.File.WriteAllText("c:\\keyPressed.txt", "Hotkey pressed");
}
private static void InitPowershell(string[] args)
{
var config = RunspaceConfiguration.Create();
ConsoleShell.Start(config, "", "", args);
}
}
What you can do is Create a hidden window in your Console application which is used to handle the hotkey notification and raise an event.
The code HERE demonstrates the principal. HERE is an article on handling messages in a Console application, using this you should be able to enhance HotKeyManager to run in a Console Application.
The following update to the HotKeyManager creates a background thread which runs the message loop and handles the windows messages.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleHotKey
{
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
int id = System.Threading.Interlocked.Increment(ref _id);
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, (uint)modifiers, (uint)key);
return id;
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hwnd, id, modifiers, key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
if (HotKeyManager.HotKeyPressed != null)
{
HotKeyManager.HotKeyPressed(null, e);
}
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
Thread messageLoop = new Thread(delegate()
{
Application.Run(new MessageWindow());
});
messageLoop.Name = "MessageLoopThread";
messageLoop.IsBackground = true;
messageLoop.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = this.Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
HotKeyManager.OnHotKeyPressed(e);
}
base.WndProc(ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError=true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
public class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
this.Key = key;
this.Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
uint param = (uint)hotKeyParam.ToInt64();
Key = (Keys)((param & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)(param & 0x0000ffff);
}
}
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}
Here is an example of using HotKeyManager from a Console application
using System;
using System.Windows.Forms;
namespace ConsoleHotKey
{
class Program
{
static void Main(string[] args)
{
HotKeyManager.RegisterHotKey(Keys.A, KeyModifiers.Alt);
HotKeyManager.HotKeyPressed += new EventHandler<HotKeyEventArgs>(HotKeyManager_HotKeyPressed);
Console.ReadLine();
}
static void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
{
Console.WriteLine("Hit me!");
}
}
}
I just wanted to offer an alternative solution.
I was answering a question for someone who was using this script and I figured this might help someone else who has trouble setting up a global key hook.
Edit: Don't forget to add a reference to System.Windows.Forms
You can do this by selecting Projectš¢Add Reference and checking System.Windows.Forms
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleKeyhook
{
class Hooky
{
///////////////////////////////////////////////////////////
//A bunch of DLL Imports to set a low level keyboard hook
///////////////////////////////////////////////////////////
[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);
////////////////////////////////////////////////////////////////
//Some constants to make handling our hook code easier to read
////////////////////////////////////////////////////////////////
private const int WH_KEYBOARD_LL = 13; //Type of Hook - Low Level Keyboard
private const int WM_KEYDOWN = 0x0100; //Value passed on KeyDown
private const int WM_KEYUP = 0x0101; //Value passed on KeyUp
private static LowLevelKeyboardProc _proc = HookCallback; //The function called when a key is pressed
private static IntPtr _hookID = IntPtr.Zero;
private static bool CONTROL_DOWN = false; //Bool to use as a flag for control key
public static void Main()
{
_hookID = SetHook(_proc); //Set our hook
Application.Run(); //Start a standard application method loop
}
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);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //A Key was pressed down
{
int vkCode = Marshal.ReadInt32(lParam); //Get the keycode
string theKey = ((Keys)vkCode).ToString(); //Name of the key
Console.Write(theKey); //Display the name of the key
if (theKey.Contains("ControlKey")) //If they pressed control
{
CONTROL_DOWN = true; //Flag control as down
}
else if (CONTROL_DOWN && theKey == "B") //If they held CTRL and pressed B
{
Console.WriteLine("\n***HOTKEY PRESSED***"); //Our hotkey was pressed
}
else if (theKey == "Escape") //If they press escape
{
UnhookWindowsHookEx(_hookID); //Release our hook
Environment.Exit(0); //Exit our program
}
}
else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
{
int vkCode = Marshal.ReadInt32(lParam); //Get Keycode
string theKey = ((Keys)vkCode).ToString(); //Get Key name
if (theKey.Contains("ControlKey")) //If they let go of control
{
CONTROL_DOWN = false; //Unflag control
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam); //Call the next hook
}
}
}
I came up with a solution based on Chris' answer that uses WPF instead of WinForms:
public sealed class GlobalHotkeyRegister : IGlobalHotkeyRegister, IDisposable
{
private const int WmHotkey = 0x0312;
private Application _app;
private readonly Dictionary<Hotkey, Action> _hotkeyActions;
public GlobalHotkeyRegister()
{
_hotkeyActions = new Dictionary<Hotkey, Action>();
var startupTcs = new TaskCompletionSource<object>();
Task.Run(() =>
{
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
_app = new Application();
_app.Startup += (s, e) => startupTcs.SetResult(null);
_app.Run();
});
startupTcs.Task.Wait();
}
public void Add(Hotkey hotkey, Action action)
{
_hotkeyActions.Add(hotkey, action);
var keyModifier = (int) hotkey.KeyModifier;
var key = KeyInterop.VirtualKeyFromKey(hotkey.Key);
_app.Dispatcher.Invoke(() =>
{
if (!RegisterHotKey(IntPtr.Zero, hotkey.GetHashCode(), keyModifier, key))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
public void Remove(Hotkey hotkey)
{
_hotkeyActions.Remove(hotkey);
_app.Dispatcher.Invoke(() =>
{
if (!UnregisterHotKey(IntPtr.Zero, hotkey.GetHashCode()))
throw new Win32Exception(Marshal.GetLastWin32Error());
});
}
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
{
if (msg.message != WmHotkey)
return;
var key = KeyInterop.KeyFromVirtualKey(((int) msg.lParam >> 16) & 0xFFFF);
var keyModifier = (KeyModifier) ((int) msg.lParam & 0xFFFF);
var hotKey = new Hotkey(keyModifier, key);
_hotkeyActions[hotKey]();
}
public void Dispose()
{
_app.Dispatcher.InvokeShutdown();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}
public class Hotkey
{
public Hotkey(KeyModifier keyModifier, Key key)
{
KeyModifier = keyModifier;
Key = key;
}
public KeyModifier KeyModifier { get; }
public Key Key { get; }
#region ToString(), Equals() and GetHashcode() overrides
}
[Flags]
public enum KeyModifier
{
None = 0x0000,
Alt = 0x0001,
Ctrl = 0x0002,
Shift = 0x0004,
Win = 0x0008,
NoRepeat = 0x4000
}
To use this, you need to add references to PresentationFramework.dll and WindowsBase.dll.
public static void Main()
{
using (var hotkeyManager = new GlobalHotkeyManager())
{
var hotkey = new Hotkey(KeyModifier.Ctrl | KeyModifier.Alt, Key.S);
hotkeyManager.Add(hotkey, () => System.Console.WriteLine(hotkey));
System.Console.ReadKey();
}
}
Changed the HotKeyManager class
public static class HotKeyManager
{
public static event EventHandler<HotKeyEventArgs> HotKeyPressed;
public static int RegisterHotKey(Keys key, HotKeyEventArgs.KeyModifiers modifiers)
{
_windowReadyEvent.WaitOne();
_wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, Interlocked.Increment(ref _id), (uint)modifiers, (uint)key);
return Interlocked.Increment(ref _id);
}
public static void UnregisterHotKey(int id)
{
_wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
}
private delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
private delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
{
RegisterHotKey(hWnd: hwnd, id: id, fsModifiers: modifiers, vk: key);
}
private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
{
UnregisterHotKey(_hwnd, id);
}
private static void OnHotKeyPressed(HotKeyEventArgs e)
{
HotKeyPressed?.Invoke(null, e);
}
private static volatile MessageWindow _wnd;
private static volatile IntPtr _hwnd;
private static ManualResetEvent _windowReadyEvent = new ManualResetEvent(false);
static HotKeyManager()
{
new Thread(delegate ()
{
Application.Run(new MessageWindow());
})
{
Name = "MessageLoopThread",
IsBackground = true
}.Start();
}
private class MessageWindow : Form
{
public MessageWindow()
{
_wnd = this;
_hwnd = Handle;
_windowReadyEvent.Set();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_HOTKEY)
{
var e = new HotKeyEventArgs(hotKeyParam: m.LParam);
OnHotKeyPressed(e);
}
base.WndProc(m: ref m);
}
protected override void SetVisibleCore(bool value)
{
// Ensure the window never becomes visible
base.SetVisibleCore(false);
}
private const int WM_HOTKEY = 0x312;
}
[DllImport("user32", SetLastError = true)]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32", SetLastError = true)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
private static int _id = 0;
}
Class HotKeyEventArgs:
public partial class HotKeyEventArgs : EventArgs
{
public readonly Keys Key;
public readonly KeyModifiers Modifiers;
public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
{
Key = key;
Modifiers = modifiers;
}
public HotKeyEventArgs(IntPtr hotKeyParam)
{
Key = (Keys)(((uint)hotKeyParam.ToInt64() & 0xffff0000) >> 16);
Modifiers = (KeyModifiers)((uint)hotKeyParam.ToInt64() & 0x0000ffff);
}
}
And class: HotKeyEventArgs
public partial class HotKeyEventArgs
{
[Flags]
public enum KeyModifiers
{
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
NoRepeat = 0x4000
}
}