I have an odd problem with Sendkeys, what my application basically should do is that, when I press ALT+ J, it'll simulate a CTRL+C operation (on any windows) to copy some highlighted text, however the CTRL+C simulation is not working.
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
private void Form1_Load(object sender, EventArgs e)
{
RegisterHotKey(this.Handle,
this.GetType().GetHashCode(), 1, (int)'J'); // Here it's waiting for the ALT+J
}
protected override void WndProc(ref Message m) // Function to c
{
if (m.Msg == 0x0312) // If ALT+J pressed
{
Copier(); // .. Simulate CTRL+C (but doesn't work)
}
base.WndProc(ref m);
}
public void Copier() // Function to simulate the CTRL+C
{
Debug.WriteLine("Ok ");
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.VK_C); // First way
SendKeys.Send("^(c)"); // Second way
}
I think this is happening because when you send CTRL+C you already have pressed the ALT modifier of ALT+J.
If you put
Thread.Sleep(1000);
just before send keys, you'll have time to release ALT+J and then CTRL+C will work.
Also, if you plan to check when your hotkey is released, check this.
Related
I'm trying to simulate a keystroke ("z") on another window, a game in particular.
I've implemented in my program a simple timer that sends the key with PostMessage every 1000ms, but the "action" related to the pressing of that key doesn't start.
I've analysed the Messages sent to the window of the game with Spy++, but the strange thing is that i can see the exact sequence of messages (KEYDOWN, CHAR and KEYUP), whether i press it manually or send it through my application. Obviously if i press "z" manually the game's function gets called correctly.
Here i report the messages that i get from Spy++, the first 3 are from me hitting z manually, the last 3 are from my software.
Messages from Spy++
And here i include the code that i'm using
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, uint lParam);
private void SendKeys(IntPtr proc_hwnd, IntPtr key)
{
PostMessage(proc_hwnd, 0x100, key, 0x002C0001);
Thread.Sleep(100);
PostMessage(proc_hwnd, 0x101, key, 0xC02C0001);
}
Process[] proc;
private void Button1_Click(object sender, EventArgs e)
{
proc = Process.GetProcessesByName("Proc_name");
if (proc.Length == 0)
{
MessageBox.Show("No process found");
return;
}
tmr_raccogli.Interval = (int)(num_raccogli.Value * 1000);
tmr_raccogli.Start();
}
private void tmr_raccogli_Tick(object sender, EventArgs e)
{
SendKeys(proc[0].MainWindowHandle, (IntPtr)Keys.Z);
}
I don't get why it's not working since from that point of view the two actions are identical.
i don't have an exact answer but i might able to guide you in the right direction.
The focus and foreground state of your window maybe important as well as the input thread.
SetFocus
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setfocus
SetForegroundWindow
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
AttachThreadInput
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
Your windows receives the messages but i decides to ignore them, this depends on how the implementation of windows msgs is programmed in the target. I can remember creating something similar and AttachThreadInput fixed most of my code interoperability problems.
I have an application that displays video using Direct3D written by a 3rd party which is a huge ordeal to get them to change anything so I want to work around that by writing my own code to handle when a user hits Ctrl-Alt-Del to lock their workstation, but doesn't. When this happens the 3rd party library throws an exception with the message 'Direct3D device lost', however, I'm attempting to write error handling logic to reset the 3rd party library once this occurs, the problem is if the user doesn't actually lock their workstation I don't know how to tell when they come back. Situation:
User hits Ctrl-Alt-Del
Windows displays screen for user to choose lock, sign off, change
password, etc.
User hits Esc or clicks the Cancel button
Windows displays user's desktop.
I don't know how to tell when the user does the above situation at step #3 and I want to know that the Direct3D device is basically OK to reset the 3rd party library at step #4. I've already hooked into the SystemEvents.SessionEnding and SystemEvents.SessionSwitch events, yet those aren't fired if the user hits Esc or clicks the Cancel button.
Any thoughts on how I can capture the events from #3, 4?
Using SetWinEventHook you can listen to some events from other processes and register a WinEventProc callback method to receive the event when the event raised.
Here we are interested to EVENT_SYSTEM_DESKTOPSWITCH.
Some windows like the Ctrl + Alt + Del Window, or User Account Control Dialog open in another desktop and when those desktops open or when those desktops close and switch back to the original desktop, the EVENT_SYSTEM_DESKTOPSWITCH event will raise.
Here we are interested to that instance of the event which happens when we close the other desktop and come back to the original desktop, so in the event handler, you can check if your window is the active window, then it means you have switched back.
Example
Here is what I tried and worked well:
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint WINEVENT_SKIPOWNPROCESS = 0x0002;
private const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020;
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
uint idThread, uint dwFlags);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
IntPtr hook = IntPtr.Zero;
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
hook = SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
IntPtr.Zero, new WinEventDelegate(WinEventProc),
0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
protected override void OnFormClosing(FormClosingEventArgs e) {
UnhookWinEvent(hook);
base.OnFormClosing(e);
}
void WinEventProc(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) {
if (GetForegroundWindow() == this.Handle)
MessageBox.Show("Back to the original screen!");
}
This topic was recently covered on the blog The Old New Thing by #RaymondChen. His basic suggestion was if the graphics resource is the thing you are trying to acquire, to just try to acquire it and see if it succeeds (which avoids false positives and TOCTOU).
As an aside, he also answers the original question the same way as #RezaAghaei by suggesting EVENT_SYSTEM_DESKTOPÂSWITCH, and gives an option for polling if your thread is on the input desktop:
// From https://devblogs.microsoft.com/oldnewthing/20200429-00/?p=103715
BOOL IsCurrentThreadOnInputDesktop()
{
BOOL result;
HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
return desktop &&
GetUserObjectInformation(desktop, UOI_IO, &result,
sizeof(result), nullptr) &&
result;
}
While working on the main screen, I want to send a right click command to Firefox, which is opened on the second vertical screen. But I don't want Firefox to become active when I send commands. I tried this but it didn't work:
const uint WM_KEYDOWN = 0x0100;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
private void button1_Click(object sender, EventArgs e)
{
var processes = Process.GetProcessesByName("firefox");
foreach (var proc in processes)
{
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, (int)Keys.Right, 0);
// I have to use SendKeys.SendWait for the messages to be processed.
SendKeys.SendWait("{RIGHT}");
}
}
And this works, but every time I send a command, Firefox becomes active. It doesn't work when I disable the SetForegroundWindow method.
[DllImport("User32.dll")]
private static extern int SetForegroundWindow(IntPtr ptr);
private void button1_Click(object sender, EventArgs e)
{
var p = Process.GetProcessesByName("firefox").FirstOrDefault();
if (p != null)
{
IntPtr h = p.MainWindowHandle;
SetForegroundWindow(h);
SendKeys.SendWait("{RIGHT}");
}
}
A Click is a WM_KEYDOWN and WM_KEYUP. Can you try calling the functions using the first method like so
foreach (var proc in processes)
{
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, (int)Keys.RButton, (IntPtr)0x001E0001);
PostMessage(proc.MainWindowHandle, WM_KEYUP, (int)Keys.RButton, (IntPtr)0xC01E0001);
// I have to use SendKeys.SendWait for the messages to be processed.
SendKeys.SendWait("{RIGHT}"); //I dont believe you need this with the above approach
}
You can also try sending the WM_CONTEXTMENU message if that is what you are after
I must also point out that Keys.Right is for the Right arrow key and not the right mouse click, you should use Keys.RButton instead
Here is the List of KeyCodes and more info on KeyStrokes in win32
I'm working on a WinForms app and I'd like to use Mnemonics key. It appears that due to a Windows parameter, you can choose to show them while using the application only after pressing ALT (this option is like it as default). I got aware of this option thanks to this question (btw related but not duplicate).
I changed this option, and the Mnemonics underline shows properly at the start. But I'd like to avoid users have to either turn this option on or have to press ALT in order to see the underlined keys.
So my question is : Is there anyway in-app to force the underline of an Mnemonic key without changing settings or pressing ALT ?
For MenuStrip, you need to create a custom renderer to always show mnemonics regardless of pressing or not pressing Alt key. To do so derive from ToolStripProfessionalRenderer and override its OnRenderItemText, removing NoPrefix and HidePrefix flags from e.TextFormat. Then register the renderer for ToolStripManager.Renderer.
For other controls to show mnemonics, you can override WndProc method of the form and handle WM_UPDATEUISTATE message and set WParam to combination of UISF_HIDEACCEL as high order word and UIS_CLEAR as low order words. This way all the controls will show mnemonic underline.
Example
Just copy and paste the following code in your form and run your application. The form will show underlines for all mnemonics without needing to press Alt:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
const int WM_UPDATEUISTATE = 0x0128;
const int UISF_HIDEACCEL = 0x2;
const int UIS_CLEAR = 0x2;
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
ToolStripManager.Renderer = new MyRenderer();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_UPDATEUISTATE)
m.WParam = (IntPtr)((UISF_HIDEACCEL & 0x0000FFFF) | (UIS_CLEAR << 16));
base.WndProc(ref m);
}
public class MyRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
e.TextFormat &= ~TextFormatFlags.NoPrefix;
e.TextFormat &= ~TextFormatFlags.HidePrefix;
base.OnRenderItemText(e);
}
}
I have a button on which I press it sends F5 to the application "Tibia". Tibia is not focused, and it sends the F5 in the background without a problem.
I wonder how can I send a string of text like "hello world".
Or send a CTRL+V. That way I could set my clipboard text to "hello world" first, and then send it.
It seems like I can only press 1 key at a time no matter what I do.
Please help me. Here's my code:
const UInt32 WM_KEYDOWN = 0x0100;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
public Form1()
{
InitializeComponent();
}
// Sends F5 to Tibia
private void button1_Click(object sender, EventArgs e)
{
Process[] processes = Process.GetProcessesByName("Tibia");
foreach (Process proc in processes)
{
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, (int)System.Windows.Forms.Keys.F5, 0);
}
}
Note: I do NOT want to focus on the Tibia window when I do this. It should be done "in the background" like now with the F5 press.