We have a legacy program with a GUI that we want to use under control of a C# program to compute some values. We can successfully enter values in the numerical input controls, press the compute button, and read the produced answers from text display boxes.
But we can't seem to control a pair of radio buttons .
Calling CheckRadioButton() returns a code of success, but the control does not change state.
Sending a message of BM_CLICK does not change the state.
Attempts at sending WM_LBUTTONDOWN and WM_LBUTTONUP events haven't changed the state.
Has anyone been successful at "remote control" of radio buttons?
Portions of code to illustrate what we are doing:
[DllImport("user32.dll", EntryPoint="SendMessage")]
public static extern int SendMessageStr(int hWnd, uint Msg, int wParam, string lParam);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, long wParam, long lParam);
[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError=true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", EntryPoint="CheckRadioButton")]
public static extern bool CheckRadioButton(IntPtr hwnd, int firstID, int lastID, int checkedID);
static IntPtr GetControlById(IntPtr parentHwnd, int controlId) {
IntPtr child = new IntPtr(0);
child = GetWindow(parentHwnd, GetWindow_Cmd.GW_CHILD);
while (GetWindowLong(child.ToInt32(), GWL_ID) != controlId) {
child = GetWindow(child, GetWindow_Cmd.GW_HWNDNEXT);
if (child == IntPtr.Zero) return IntPtr.Zero;
}
return child;
}
// find the handle of the parent window
IntPtr ParenthWnd = new IntPtr(0);
ParenthWnd = FindWindowByCaption(IntPtr.Zero, "Legacy Window Title");
// set "N" to 10
IntPtr hwndN = GetControlById(ParenthWnd, 17);
SendMessageStr(hwndN.ToInt32(), WM_SETTEXT, 0, "10");
// press "compute" button (seems to need to be pressed twice(?))
int hwndButton = GetControlById(ParenthWnd, 6).ToInt32();
SendMessage(hwndButton, BM_CLICK, 0, 0);
SendMessage(hwndButton, BM_CLICK, 0, 0);
// following code runs succesfully, but doesn't toggle the radio buttons
bool result = CheckRadioButton(ParenthWnd, 12, 13, 12);
Send the BM_SETCHECK message. Be sure to use a tool like Spy++ to see the messages.
in this case i used another message BM_SETSTATE
SendMessage((IntPtr)hWnd, Win32Api.BM_SETSTATE, (IntPtr)newState, IntPtr.Zero);
Related
In the top of form1
const int WM_SETTEXT = 0X000C;
//include FindWindowEx
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
//include SendMessage
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
Then in the constructor
//getting notepad's process | at least one instance of notepad must be running
Process notepadProccess = Process.GetProcessesByName("devenv")[0];
//getting notepad's textbox handle from the main window's handle
//the textbox is called 'Edit'
IntPtr notepadTextbox = FindWindowEx(notepadProccess.MainWindowHandle, IntPtr.Zero, "Edit", null);
//sending the message to the textbox
SendMessage(notepadTextbox, WM_SETTEXT, 0, "Testing");
This is working for the notepad.
But if i want to send a message to another visual studio opened window of a project ?
For example i created a new project in a new visual studio and created in the project a new class called it test
And i want to send to the class some text for example:
using system;
For example this is the new class window:
After sending the message it will be like this:
I found command System.Windows.Forms.SendKeys.Send() for sending keypress some key. This function work if open external app like a notepad and set focus and I will be see that my Key printed in this text field. How do same but with key down event, System.Windows.Forms.SendKeys.SendDown("A");, for example?
I tried call in Timer this command System.Windows.Forms.SendKeys.Send() but have runtime error associated with very fast taped.
You can't use the SendKeys class for that, unfortunately. You will need to go to a lower level API.
Poking a window with a keydown message
In Windows, keyboard events are sent to windows and controls via the Windows message pump. A piece of code using PostMessage should do the trick:
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
const uint WM_KEYDOWN = 0x0100;
void SendKeyDownToProcess(string processName, System.Windows.Forms.Keys key)
{
Process p = Process.GetProcessesByName(processName).FirstOrDefault();
if (p != null)
{
PostMessage(p.MainWindowHandle, WM_KEYDOWN, (int)key, 0);
}
}
Note that the application receiving these events may not do anything with it until a corresponding WM_KEYUP is received. You can get other message constants from here.
Poking a control other than the main window
The above code will send a keydown to the "MainWindowHandle." If you need to send it to something else (e.g. the active control) you will need to call PostMessage with a handle other than p.MainWindowHandle. The question is... how do you get that handle?
This is actually very involved... you will need to temporarily attach your thread to the window's message input and poke it to figure out what the handle is. This can only work if the current thread exists in a Windows Forms application and has an active message loop.
An explanation can be found here, as well as this example:
using System.Runtime.InteropServices;
public partial class FormMain : Form
{
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll")]
static extern IntPtr AttachThreadInput(IntPtr idAttach,
IntPtr idAttachTo, bool fAttach);
[DllImport("user32.dll")]
static extern IntPtr GetFocus();
public FormMain()
{
InitializeComponent();
}
private void timerUpdate_Tick(object sender, EventArgs e)
{
labelHandle.Text = "hWnd: " +
FocusedControlInActiveWindow().ToString();
}
private IntPtr FocusedControlInActiveWindow()
{
IntPtr activeWindowHandle = GetForegroundWindow();
IntPtr activeWindowThread =
GetWindowThreadProcessId(activeWindowHandle, IntPtr.Zero);
IntPtr thisWindowThread = GetWindowThreadProcessId(this.Handle, IntPtr.Zero);
AttachThreadInput(activeWindowThread, thisWindowThread, true);
IntPtr focusedControlHandle = GetFocus();
AttachThreadInput(activeWindowThread, thisWindowThread, false);
return focusedControlHandle;
}
}
The good news-- if SendKeys worked for you, then you might not need to do all this-- SendKeys also sends messages to the main window handle.
I am trying to use sendkeys, but send it to a non focused program. For example, I want to use sendkeys to Notepad - Untitled, without it being focused. Sorry if it's unclear, I will elaborate. My current code is this:
string txt = Regex.Replace(richTextBox3.Text, "[+^%~()]", "{$0}");
System.Threading.Thread.Sleep(1000);
SendKeys.Send(txt + "{ENTER}");
If I use SendMessage, would anyone care to show me an example of what I should do? I don't find anything I found online very useful for my issue.
The following code I found from a previous SO answer. The PostMessage API will send a Windows message to a specific Windows handle. The code below will cause the key down event to fire for all instances of Internet Explorer, without giving focus, simulating that the F5 key was pressed. A list of additional key codes can be found here.
static class Program
{
const UInt32 WM_KEYDOWN = 0x0100;
const int VK_F5 = 0x74;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[STAThread]
static void Main()
{
while(true)
{
Process [] processes = Process.GetProcessesByName("iexplore");
foreach(Process proc in processes)
PostMessage(proc.MainWindowHandle, WM_KEYDOWN, VK_F5, 0);
Thread.Sleep(5000);
}
}
}
It took me a while to figure it out for myself
Also here a usefull list of Virtual Key Codes
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern bool PostMessage(int hWnd, uint Msg, int wParam, int lParam);
private void button1_Click(object sender, EventArgs e)
{
const int WM_SYSKEYDOWN = 0x0104;
const int VK_RETURN = 0x0D;
IntPtr WindowToFind = FindWindow(null, "Notepad - Untitled"); // Window Titel
PostMessage(WindowToFind, WM_SYSKEYDOWN, VK_RETURN, 0);
}
I use the following program for Hide/Show desktop Items using c#. It was working Fine.
public partial class Form1 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const int WM_COMMAND = 0x111;
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
IntPtr hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
public Form1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, EventArgs e)
{
ToggleDesktopIcons();
}
}
But my problem is I have only one button in form. If I press the button the hide and show desktop items will happen on the same button.
I need to separate those show and Hide. Which means i need to create one more button in a form, so totally i have 2 buttons now. If I press first button I need to hide desktop items. If I press second button I need to show Desktop Items. How can I do this?
You can store internal state inside Form1 class, for example some bool property.
If you click on Show button and bool property indicates, that your item is already shown - do nothing, else ToggleDesktopIcons. The same for hide button.
See the answer in the this question.
The answer contains an IsVisible method that allows you to see if the icons are hidden or shown.
In the show button: call IsVisible, if it returns false, execute ToggleDesktopIcons, otherwise return.
In the hide button: call IsVisible, if it returns true, execute ToggleDesktopIcons, otherwise return.
You can also use the result of calling IsVisible to decide if you want to enable/disable the show and hide buttons.
I wanna do a macro program for a game. But there is a problem with sending keys to only game application (game window). I am using keybd_event API for sending keys to game window. But I only want to send keys to the game window, not to explorer or any opened window while my macro program is running. When I changed windows its still sending keys. I tried to use Interaction.App with Visual Basic.dll reference. But Interaction.App only Focus the game window.
I couldn't find anything about my problem. Can anyone help me? Thanx
i fixed my problem.
in this field ;
PostMessage(hWnd, WM_KEYDOWN, key, {have to give lParam of the key});
otherwise it does not work.And we can control of ChildWindow Class with Spy++ tool of Microsoft.
Thanks everyone for helping.
Are you retrieving the handle of the window all the time, or are you remembering it?
If you use the FindWindow() API, you can simply store the Handle and use the SendMessage API to send key/mouse events manually.
FindWindow API:
http://www.pinvoke.net/default.aspx/user32.FindWindowEx
SendMessage API:
http://www.pinvoke.net/default.aspx/user32/SendMessage.html
VB
Private Const WM_KEYDOWN As Integer = &H100
Private Const WM_KEYUP As Integer = &H101
C#
private static int WM_KEYDOWN = 0x100
private static int WM_KEYUP = 0x101
class SendKeySample
{
private static Int32 WM_KEYDOWN = 0x100;
private static Int32 WM_KEYUP = 0x101;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, int Msg, System.Windows.Forms.Keys wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
public static IntPtr FindWindow(string windowName)
{
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.MainWindowHandle != IntPtr.Zero && p.MainWindowTitle.ToLower() == windowName.ToLower())
return p.MainWindowHandle;
}
return IntPtr.Zero;
}
public static IntPtr FindWindow(IntPtr parent, string childClassName)
{
return FindWindowEx(parent, IntPtr.Zero, childClassName, string.Empty);
}
public static void SendKey(IntPtr hWnd, System.Windows.Forms.Keys key)
{
PostMessage(hWnd, WM_KEYDOWN, key, 0);
}
}
Calling Code
var hWnd = SendKeySample.FindWindow("Untitled - Notepad");
var editBox = SendKeySample.FindWindow(hWnd, "edit");
SendKeySample.SendKey(editBox, Keys.A);
If you want to communicate with a game, you typically will have to deal with DirectInput, not the normal keyboard API's.