I am creating a simple C# app to monitor vended application on the server and if any message boxes come up with a particular window name to close the window and continue with the process.
Vendor has informational message boxes come up randomly which hangs the application until X or OK buttons are clicked. There are two types of message boxes that come up and one gets closed with no problem using my application but the other one does not. They both look very similar its just that one has embedded sql text as part of the message. Both of the Message Boxes are found by FindWindow its just when the SendMessage gets run it does not close the second type of the message box even though it sees it. I have also tried PostMessage and I get the same issue.
This is what I have:
private const int WM_CLOSE = 0x10;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
IntPtr ErrorPopUp = FindWindow(null, WindowToClose.Trim());
if (ErrorPopUp != IntPtr.Zero) {
try {
Thread.Sleep(200);
SendMessage(ErrorPopUp, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
_WMExceptionDal.LogErrorMsg(_WMException);
} catch (Exception ex) {
_WMException.txt_iferror = "Unable to close the popup window";
_WMException.txt_sqlerrtext = ex.ToString();
_WMExceptionDal.LogErrorMsg(_WMException);
}
}
Any suggestions of other approach or if I am doing something wrong of why some message boxes would get closed by WM_CLOSE and why some wouldn't I would really appreciate it.
The answer is simpler than you think: the popups you get for errors have an 'OK' button, and no 'Close' button in the title bar area of the window.
They are very, very primitive: the window proc's message-handling is limited to the functions behind the dialog buttons:
SendMessage(ErrorPopUp, WM_COMMAND, IDOK, IntPtr.Zero);
If you send it WM_CLOSE there's nobody home: there is nothing in the message-handler for that window that will respond to that message. Yes, there was a time when I thought that all windows responded to WM_CLOSE messages, too.
There are also restrictions on dialog windows responding to 'Close' messages coming in from other threads, but I don't think that's what's happening here. (However, if the dialog box does have an 'x' button to close it in the title bar, and it's not responding to your WM_CLOSE messages, this is the most likely explanation).
Either way, you're sending it the wrong message: the dialog was created to respond to the user inputs listed on the buttons, and those are the messages you send - WM_COMMAND with a message identifier:
private const int WM_COMMAND = 0x111;
private const int dlgOK = 0x1;
private const int dlgCANCEL = 0x2;
private const int dlgABORT = 0x3;
private const int dlgRETRY = 0x4;
private const int dlgIGNORE = 0x5;
private const int dlgYES = 0x6;
private const int dlgNO = 0x7;
The question 'Which Identifier?' is usually irrelevant - you know it's 'Ok' here - but the User32 function GetDlgItem() will check which dialog box functions are present if you need to ask.
Also: watch out for dialogs with a 'Cancel' button - there are implementations of the Cancel function in a dialog that enable the window menu and give you a close 'x' button in the title bar, and some of these implementations respond to the WM_CLOSE message (they ought to respond to WM_SYSCOMMAND, SC_CLOSE too); but you would be unwise to rely on anything other than WM_COMMAND, dlgCANCEL.
Related
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;
}
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 have written a program that change the windows theme but after changing the theme personalization window remains open and I want to close it. I tried using process.kill() with familiar process name but it didn't work. Thank you.
The code for what I am doing is as below:
ProcessStartInfo theinfo = new ProcessStartInfo(themepath + "aero.theme");
theinfo.CreateNoWindow = true;
Process thepr = new Process();
thepr.StartInfo = theinfo;
thepr.Start();
where "themepath" is String location to aero.theme.
I have even enabled CreateNoWindow to true then also it opens up Personalization to change theme but didn't close it automatically.
First use find window to get the window from their name by Using FindWindow..
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);
It returns you the handle of the window you want now you can use send message to close it..
[DllImport("User32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
private void closeWindow()
{
// retrieve the handler of the window
int iHandle = FindWindow("CabinetWClass", "Personalization");
if (iHandle > 0)
{
SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
You need to obtain the window handle by it's name and then send it a close message. This prevents having to kill any processes. See this article for information on obtaining the windows. See this one for closing windows from the handle.
After seeing the code and doing a little digging, you can accomplish this with two registry edits. You should read this article and just have your program edit the two registry keys in question.
I send mouse events to another application in the following way. The problem is, this works for some applications but not for others.
Why?
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int downclick = 0x201;
private const int upclick = 0x202;
IntPtr handle = IntPtr.Zero;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
foreach (Process p in Process.GetProcessesByName("mspaint"))
{
IntPtr handle = p.MainWindowHandle;
int X = 50;
int Y = 380;
IntPtr lParam = (IntPtr)((Y << 16) | X);
IntPtr wParam = IntPtr.Zero;
SendMessage(handle, downclick, wParam, lParam);
SendMessage(handle, upclick, wParam, lParam);
}
}
}
}
Using Spy++ I see that the application recieves the following data:
<00062> 0004052C S WM_LBUTTONDOWN fwKeys:0000 xPos:50 yPos:380
<00063> 0004052C R WM_LBUTTONDOWN
<00064> 0004052C S WM_LBUTTONUP fwKeys:0000 xPos:50 yPos:380
<00065> 0004052C R WM_LBUTTONUP
I assume that the events themselves are correct. But I don't know why it works for some software but not for others. How can I send mouse messages from one window to another?
The software where I want to send the messages is not always visible.
Is it possible at all?
No it's not possible in any reliable way - as you've found out in your testing. The mouse messages are only one part of the input. Windows keeps an input state and just sending messages will not update that input state. And you're also ignoring mouse move messages, etc.
For example in your WinForms application you can use the MousePosition property to get the current mouse positon. Sending messages can't simulate that.
Also you can't send the mouse message to the main window handle, you would have to find the exact button you want to click on and send the message directly to the correct button.
So maybe it will work if the application is only listening for mouse messages this will work, but if not they it won't.
They supported way to simulate mouse clicks, is the SendInput function. But that won't work with minimized applications. It literally goes through the entire Windows input process and will move the mouse cursor - which means that the application has to be visible on the screen.
Here's some information, it talks about keyboard events, but similar logic applies:
http://blogs.msdn.com/b/oldnewthing/archive/2005/05/30/423202.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2010/12/21/10107494.aspx
I am new to WINAPI and have figured out how to send a message to another program. The program I am using however I would like to be able to have it click on a specific button. From what I have learned by viewing Spy++ windows handles change for the programs every time they are reloaded and so do the handles for their controls. The control ID stays the same. After two days of trying to figure it out I am here.
under SendMesssageA if I specify the current handle as viewable by Spy++ and use that and run the code it works fine and clicks the button on my external application. I am attempting to use GetDlgItem as I have read that I can get the handle for the control (child window) using it. I am doing something wrong however since no matter what I do it returns 0 or 'null'.
How can I get GetDlgItem to return the child control handle so that I may use it to sendmessage to click that control in the external application?
Thanks for your help an input ahead of time.
[DllImport("User32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
Process[] myProcess = Process.GetProcessesByName("program name here");
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam, uint lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDlgItem(int hwnd, int childID);
public const int WM_LBUTTONDOWN = 0x0201;
public const int WM_LBUTTONUP = 0x0202;
public void SendClick()
{
IntPtr hwnd = myProcess[0].MainWindowHandle;
SetForegroundWindow(hwnd);
int intCID = 1389;
IntPtr ptrTest = GetDlgItem(hwnd, intCID);
SendKeys.SendWait(" ");
Thread.Sleep(1000);
SendKeys.SendWait("various text to be sent here");
Thread.Sleep(1000);
SendKeys.SendWait("{ENTER}");
Thread.Sleep(1000);
SendMessageA(ptrTest, WM_LBUTTONDOWN, WM_LBUTTONDOWN, 0);
}
I think you have to use the Win32 API to find the "receiving" application window, and then find a child window of that handle.
This is something I found googling Win32 API FindWindow
http://www.c-sharpcorner.com/UploadFile/shrijeetnair/win32api12062005005528AM/win32api.aspx