How to send keyboard key in my application? - c#

I'm currently using SendKeys.SendWait(text); in C#
but SendKeys sending the key global and I have to activate my application and then send it. And another problem is when I type something in my keyboard (in another app) and the SendKeys function activates (in my app) mistakes happen.
So how can I send a message to my application regardless what application is active and what I type in my keyboard?

SendMessage() does what you want. You'll need to use it like:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
const UInt32 WM_CHAR = 0x0102;
const int VK_Q = 0x51; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
SendMessage(handleToTheInputForm, WM_CHAR, VK_Q, 1);

You will need to get a handle on the other application's window so that you can bring it to focus and reliably send your keystrokes to it,
Have a look at this tutorial
http://www.codeproject.com/KB/cs/SendKeys.aspx

Related

Using SendMessage() to minimize all windows, but need a more efficient way than Thread.Sleep() to wait until the legacy desktop is showing

In my C# console application, I am using SendMessage() to minimize all windows, effectively showing the Windows 8 Legacy Desktop. This works great, but I have to use a Thread.Sleep(1000) in order to wait for the Legacy Desktop to actually show before before I try to do anything else.
IntPtr lHwnd = FindWindow("Shell_TrayWnd", null);
SendMessage(lHwnd, WM_COMMAND, (IntPtr)MIN_ALL, IntPtr.Zero);
Thread.Sleep(1000);
I really want to replace the Thread.Sleep() with a more efficient way to detect that the Legacy Desktop is showing before continuing on.
Any ideas?
Edit:
Here are the Interop wrappers and constants. just in case it helps..
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);
const int WM_COMMAND = 0x111;
const int MIN_ALL = 419;
const int MIN_ALL_UNDO = 416;
I'm not sure if this will work any better for you, but perhaps it's worth a try...
(1) Add to your project a reference to "Shell32" (via Add Reference -> COM -> Microsoft Shell Controls and Automation).
(2) Set the reference's "Embed Interop Types" to false.
(3) Use the following code to minimise all the windows:
dynamic shell = new Shell32.ShellClass();
shell.MinimizeAll();
However, I suspect that this is just an alternative way of doing the SendMessage().

How to close the window by its name?

I want to close window with some name (any application, for example, calculator and etc.). How to do it in C#? Import WinAPI functions?
Yes, you should import the Windows API functions: FindWindow(), SendMessage(); and WM_CLOSE constant.
Native definitions of the Windows API functions:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
/// <summary>
/// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
/// </summary>
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
const UInt32 WM_CLOSE = 0x0010;
Client code:
IntPtr windowPtr = FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad");
if (windowPtr == IntPtr.Zero)
{
Console.WriteLine("Window not found");
return;
}
SendMessage(windowPtr, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
You're trying to close windows belonging to other processes. That isn't something you can assume will go reliably. For one thing, YOU don't own those windows so YOU don't really have any automatic entitlement to go and mess with the other processes' windows.
As the other answer suggests, you can try sending a WM_CLOSE to the window but it comes with the caveat that the other process isn't really entitled to honour it. The response to WM_CLOSE can be anything to acceptance and a clean shutdown to outright rejection. In the latter case, you've really got no option. It's not your process. In between, as you've seen, there could be any sort of intermediate windows, dialog boxes, etc, that you'd have to contend with.
So what are you trying to achieve here? Why are you trying to close windows belonging to other processes? It might help to clarify what the aim is.

ERROR_PROC_NOT_FOUND on PInvoke call to SendMessageW

I need to automate a third party program, and the only course of action is to simulate a click on some buttons.
I do this by finding the HWND handle of the button with EnumChildWindows. When I've found the "window" (the button), I try to send BM_CLICK to it with SendMessageW. This works, my tests show that the button indeed think it was clicked.
The problem arise in my error handling. There is no feedback given by the BM_CLICK message, so I don't really know if it has been clicked. I thought I should be diligent and check for any error codes though with Marshal.GetLastWin32Error.
This returns ERROR_PROC_NOT_FOUND, which is not really what I would expect from a successful message handling.
I import SendMessageW as follows:
[DllImport("User32.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Winapi,
SetLastError = true)]
public static extern IntPtr SendMessageW(
HandleRef hWnd,
UInt32 Msg,
UIntPtr wParam,
IntPtr lParam);
The code doing the call is:
User32.SendMessageW(
buttonHandle,
(uint)ButtonControlMessages.BM_CLICK, // value of BM_CLICK = 0x00F5.
UIntPtr.Zero,
IntPtr.Zero);
int error = Marshal.GetLastWin32Error();
if (error != ErrorCodes.ERROR_SUCCESS) // love the name of this error code.
throw new Win32Exception(error);
My tests are just using a simple Windows Forms with a button control attached. Thus, I can procure the handle through button.Handle. It gets clicked; could it be that this error is completely unrelated?
It sure would be nice to get rid of it though, I'd like some way to be sure that the call to SendMessageW at least didn't fail.
I'm on Windows 7 x86-32 with .NET 4.
The calling convention should be Stdcall, but since that is the default you can just drop it.
I think your SendMessage P/Invoke looks a bit odd but that's probably not the cause of the issue. In any case I would do it like this:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
I think what's happening here is that SendMessage() is working but isn't assigning the last error. The only thing that the documentation for SendMessage() mentions about errors is:
When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).
The return value for SendMessage() is dependent on the message sent. In the case of BM_CLICK there is apparently no message sent. In other words you simply do not get any feedback.

Send Keystrokes to a program even if its in background using c#

I wanna send key stroke to a program even if it is running in background. But I can do this only for NOTEPAD like this,
[DllImport("user32.dll")]
protected static extern byte VkKeyScan(char ch);
[DllImport("user32.dll", SetLastError = true)]
protected static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
protected static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
char Key = // key value to send
IntPtr hWnd = FindWindowEx(_handle, IntPtr.Zero, "edit", null); // _handle is the windows handle of the program (here its notepad)
PostMessage(hWnd, WM_KEYDOWN, VkKeyScan(Key), 0);
But for all other applications I can't send keystrokes if its in background. Since I don't know the lpszClass of that program (I think this is the userControl name of the typing area in that program. For NotePad it is "edit". I found this surfing internet).
For all other applications what I'm doing is, get the application to foreground, then send the key, then again get my program foreground. I need my program to be run as foreground always.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
SetForegroundWindow(_handle); // _handle is the windows handle of the program
System.Threading.Thread.Sleep(50); // Waiting few milliseconds till application coming to foreground.
wsh.SendKeys(Key.ToString(), ref wait); // wsh is WshShellClass wsh= new WshShellClass();
SetForegroundWindow(_mainHandle); // _mainHandle is the windows handle of my application
But this way is not working. some keys getting missed and the program foreground->background->foregound->background...... like its dancing...
How to send keys to other applications if its running in background.
or are there any way/source to find the lpszClass of a program ?
Sorry if I have missed any required information. this is a large application. I have posted only required parts here. If someone needs any additional information, pls ask.
I think you'll need to have the background program install a low-level keyboard hook via the win32 function SetWindowsHookEx().
Here's the MSDN documentation for SetWindowsHookEX()
http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx
And here's the KB article on how to do it from C#
http://support.microsoft.com/kb/318804
This article goes into some detail, too: http://www.codeguru.com/columns/vb/article.php/c4829
I expect your app will get caught by various spyware/anti-virus software as a keyboard logger, though.
Good luck.
You may be able to figure out the lpszClass of the program using an inspection tool such as WinSpy++. It gives you a crosshair that you can drag and position over the desired control. This was able to easily provide me with the "edit" class name for notepad.
If things aren't working, click the "More>>" button in the lower right of WinSpy++, then click the "Locate" button to view the control hierarchy; you may need to post the WM_KEYDOWN message to one of the parent or child controls instead.

Start a screensaver when explorer is not the shell

I'm running my own kiosk application as the shell (replacing HKLM/Software/Microsoft/Windows NT/winlogon/shell).
The application needs to be able to turn off the monitor and I was using Process.Start("scrnsave.scr") to do this. It works on my dev machine but not when the shell is replaced.
It's clearly because the UseShellExecute is set to true, but when I set it to false I can't get the screensaver to run. Using explorer.exe as the command and scrnsave.scr as the argument just causes an explorer window to open.
Is there a switch I can pass to explorer to get it to run the screensaver or is there another way to achieve the same thing?
Thanks.
You can start the screen saver by sending a windows message to the system.
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_SCREENSAVE, 0)
You will need the following definitions
static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff);
static readonly IntPtr SC_SCREENSAVE = new IntPtr(0xf140);
const uint WM_SYSCOMMAND = 0x112;
[DllImport("User32",SetLastError=true)]
extern static int SendMessage(
IntPtr hWnd,
uint Msg,
IntPtr wParam,
IntPtr lParam);
Which you can then use as follows
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_SCREENSAVE, IntPtr.Zero);

Categories

Resources