I'm using this webbrowswer feature of C#. Trying to log in a website through my application. Everything goes fine, except when a wrong ID or password is entered there's little message box (that is set on the webpage itself) which pops up and blocks everything until "Ok" is clicked.
So the question is: Is there any possible way to manage this little window (like reading the text inside of it)? If it is then great!
But if there's no way to do that then is there anyway to simply make this message box go away programatically?
You can "manage" the message box dialog by importing some window functions from user32.dll and getting the messagebox dialog's handle by it's class name and window name. For example to click its OK button:
public class Foo
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private void ClickOKButton()
{
IntPtr hwnd = FindWindow("#32770", "Message from webpage");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Button", "OK");
uint message = 0xf5;
SendMessage(hwnd, message, IntPtr.Zero, IntPtr.Zero);
}
}
Some reading material from MSDN.
Copy paste from Sires anwser: https://stackoverflow.com/a/251524/954225
private void InjectAlertBlocker() {
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
string alertBlocker = "window.alert = function () { }";
element.text = alertBlocker;
head.AppendChild(scriptEl);
}
here is refined version of Saeb's answer. Saeb's code was not working for me, I added one more step to activate the button and then clicking on it.
using System;
using System.Runtime.InteropServices;
namespace IE_Automation
{
public class IEPoppupWindowClicker
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int BM_CLICK = 0xF5;
private const uint WM_ACTIVATE = 0x6;
private const int WA_ACTIVE = 1;
public void ActivateAndClickOkButton()
{
// find dialog window with titlebar text of "Message from webpage"
var hwnd = FindWindow("#32770", "Message from webpage");
if (hwnd != IntPtr.Zero)
{
// find button on dialog window: classname = "Button", text = "OK"
var btn = FindWindowEx(hwnd, IntPtr.Zero, "Button", "OK");
if (btn != IntPtr.Zero)
{
// activate the button on dialog first or it may not acknowledge a click msg on first try
SendMessage(btn, WM_ACTIVATE, WA_ACTIVE, 0);
// send button a click message
SendMessage(btn, BM_CLICK, 0, 0);
}
else
{
//Interaction.MsgBox("button not found!");
}
}
else
{
//Interaction.MsgBox("window not found!");
}
}
}
}
Related
This code opens a Dialog by clicking a Button on a Form
IntPtr m = FindWindow("TForm1", "Smart Design");
IntPtr b = FindWindowEx(m, IntPtr.Zero, "TButton", "Update List");
SendMessage(b, BM_CLICK, 0, 0);
How to click OK button on the opened dialog?
I tried this code but it fails:
IntPtr d = FindWindow("TDialog4", "Information");
IntPtr k = FindWindowEx(d, IntPtr.Zero, "TButton7", "OK");
SendMessage(k, BM_CLICK, 0, 0);
I'd try sending the dialog a WM_COMMAND instead.
private const uint WM_COMMAND = 0x0111;
private const int BM_CLICKED = 245;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint msg,
int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle,
IntPtr childAfter, string className, string windowTitle);
SendMessage(k, WM_COMMAND, (BM_CLICKED << 16) | 1, k);
I have to enter the file path in a text box of windows popup
I have tried a piece of the code which can return the handle of a window based on its name.
class Program
{
private const int WM_GETTEXT = 13;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Main(string[] args)
{
IntPtr Hwnd = FindWindow(null, "Choose File to Upload");
// Alloc memory for the buffer that recieves the text
IntPtr Handle = Marshal.AllocHGlobal(100);
// send WM_GWTTEXT message to the notepad window
int NumText = (int)SendMessage(Hwnd, WM_GETTEXT, (IntPtr)50, Handle);
// copy the characters from the unmanaged memory to a managed string
string Text = Marshal.PtrToStringUni(Handle);
}
my pop looks like:
I need to set data in a text box of the window.
As the title suggests, I'm trying to simulate a button-click in a MessageBox programmatically. I earlier tried to close the MessageBox by finding its handle via its caption, and applying WM_CLOSE or SC_CLOSE in SendMessage(). However, due to the presence of Yes/No buttons, that did not work (the X button is grayed out).
Now I'm trying to click the No button as follows -:
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
IntPtr Window_hWnd = CloseMessageBox.FindWindowByCaption("#32770", "LastQuestion"); //Could use null as the first argument too. "#32770" represents classname Dialog.
CloseMessageBox.EnumChildWindows(Window_hWnd, (hWnd, lParam) =>
{
StringBuilder sb = new StringBuilder();
foreach (var control in GCHandle.FromIntPtr(lParam).Target as List<IntPtr>)
{
CloseMessageBox.GetWindowText(control, sb, 250);
if (sb.Equals("&No"))
{
CloseMessageBox.PostMessage(hWnd, CloseMessageBox.MouseDown, 0, 0);
CloseMessageBox.PostMessage(hWnd, CloseMessageBox.MouseUp, 0, 0);
}
}
return false;
}, GCHandle.ToIntPtr(listHandle));
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
Having come this far on the advice of someone from IRC, I find that a few edits earlier, I was getting the button handle (only the "&Yes" button) but not all of them. He then suggested this approach, but the control List is not populated and hence it never goes inside the foreach. What do I do to remedy this?
Here you go.
// A delegate which is used by EnumChildWindows to execute a callback method.
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
// This method accepts a string which represents the title name of the window you're looking for the controls on.
public static void ClickButtonLabeledNo(string windowTitle)
{
try
{
// Find the main window's handle by the title.
var windowHWnd = FindWindowByCaption(IntPtr.Zero, windowTitle);
// Loop though the child windows, and execute the EnumChildWindowsCallback method
EnumChildWindows(windowHWnd, EnumChildWindowsCallback, IntPtr.Zero);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private static bool EnumChildWindowsCallback(IntPtr handle, IntPtr pointer)
{
const uint WM_LBUTTONDOWN = 0x0201;
const uint WM_LBUTTONUP = 0x0202;
var sb = new StringBuilder(256);
// Get the control's text.
GetWindowCaption(handle, sb, 256);
var text = sb.ToString();
// If the text on the control == &No send a left mouse click to the handle.
if (text == #"&No")
{
PostMessage(handle, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
PostMessage(handle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
}
return true;
}
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "GetWindowText", CharSet = CharSet.Auto)]
private static extern IntPtr GetWindowCaption(IntPtr hwnd, StringBuilder lpString, int maxCount);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
I am trying to click on 'OK' button on a message box of C# windows form using winapi. Below is the code that I am working on.
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(int hWnd, int msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
//this works
hwnd = FindWindow(null, "Message");
if(hwnd!=0)
SendMessage(hwnd, WM_CLOSE, 0, IntPtr.Zero);
//this doesn't work.
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "ok");
SendMessage((int)hwndChild, BN_CLICKED, 0, IntPtr.Zero);
Though i get a value in hwndChild, it is not recognising BN_CLICKED.
I am not sure what am I missing. any help?
I am trying to close the message box button of another application and this is what I am doing. But, I m still missing something.
IntPtr hwndChild = IntPtr.Zero;
hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero,' '"Button", "OK");
SendMessage((int)hwndChild, WM_COMMAND, (BN_CLICKED '<<16) | IDOK, hwndChild);
BN_CLICKED is not a message. You need to send a WM_COMMAND message containing the BN_CLICKED notification and the button ID in the wParam and the button handle in lParam.
The parent window of the button receives this notification code
through the WM_COMMAND message.
private const uint WM_COMMAND = 0x0111;
private const int BN_CLICKED = 245;
private const int IDOK = 1;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
SendMessage(hwndChild, WM_COMMAND, (BN_CLICKED << 16) | IDOK, hwndChild);
Finallu, this works for me.
First click probably activates the window and second click clicks the button.
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
I am working on internet explorer automation and part of it involves downloading files from a site whcih is hosted on asp 2.0 and uses forms based authentication, so to create end to end automation I used browser automation.
I was able to reach to the step where I can get to click on a URL which brings the "File Download" dialog of the browser, then I was trying to make use of SendKeys to click on the save button but to no avail it was not working.
Here is the code where I make use of FindWindow method to get the hWnd pointer of the File Download Dialog, and then using setActiveWindow I make it the active window so that the SendKeys commands works on it and then using SendKeys I tried to send Alt + S but it didn't work. I observed that, Tab, Escape and Enter works, but then Enter on Save button doesn't work.
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
private void Form1_Load(object sender, EventArgs e)
{
IntPtr hwnd = FindWindow(null, "File Download");
IntPtr nullptr = (IntPtr)0;
if (hwnd != nullptr)
{
SetActiveWindow(hwnd);
SendKeys.SendWait("%S");
}
}
Using the same code I was able to access notepad by changing the value in FindWindow to "Untitled - Notepad".
Do I need to do something different as it is a dialog and now a window? I am using IE8.
This is the alternate code I tried after the answer.
IntPtr hwnd = FindWindow(null, "File Download");
IntPtr hokBtn = IntPtr.Zero;
hokBtn = FindWindowEx(hwnd, hokBtn, "Button", IntPtr.Zero);
hokBtn = FindWindowEx(hwnd, hokBtn, "Button", IntPtr.Zero);
uint id = GetDlgCtrlID(hokBtn);
SetActiveWindow(hwnd);
IntPtr res = SendMessage(hokBtn, (int)0x00F5, 0, IntPtr.Zero);
if (res.ToInt32() == 1)
MessageBox.Show("success");
For clarity I am adding the screen of the dialog.
alt text http://www.freeimagehosting.net/uploads/4f23586401.png
Try the following which seemed to work for me:
IntPtr hwnd = FindWindow(null, "File Download");
IntPtr hokBtn = FindWindowEx(hwnd, null, "Button", "Cancel");
uint id = GetDlgCtrlID(hokBtn);
SetActiveWindow(hwnd);
IntPtr res = SendMessage(hokBtn, (int)0x00F5, 0, IntPtr.Zero);
if (res.ToInt32() == 1)
MessageBox.Show("success");
I would suggest you check the returns from each function though.
This works in C++
Note that the 'Save' button is named '&Save' not 'Save'
CString Title;
Title=_T("File Download");
HWND FEX = ::FindWindowEx( NULL,NULL,NULL,Title);
if (FEX != NULL)
{
//press the Save button on the dialog.
HWND hokBtn = ::FindWindowEx(FEX, NULL, L"Button", L"&Save");
if (hokBtn != NULL)
{
UINT id = ::GetDlgCtrlID(hokBtn);
::SetActiveWindow(hokBtn);
::PostMessage(hokBtn, WM_KEYDOWN, 0x20, 0);
::PostMessage(hokBtn, WM_KEYUP, 0x20, 0);
}
}
well, you have to find window with title of downloading dialog. and than you have to find window with title of download button/ and then send to that window click message
BM_CLICK = 0x00F5
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr next, string sClassName, IntPtr sWindowTitle);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern uint GetDlgCtrlID(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
//hDialog - handle of dialog window. idBtn - Id of button
public static bool ClickButtonOnDialog(IntPtr hDialog, UInt32 idBtn)
{
IntPtr res = IntPtr.Zero;
uint id;
IntPtr hOkBtn = IntPtr.Zero;
int attempt = 0;
do
{
Thread.Sleep(300);
//searching for button
hOkBtn = User32.FindWindowEx(hDialog, hOkBtn, "Button", IntPtr.Zero);
id = User32.GetDlgCtrlID(hOkBtn);
attempt++;
} while (id != idBtn && attempt < 20);
if (!hOkBtn.Equals(IntPtr.Zero))
{
//click the button
res = User32.SendMessage(hOkBtn, (int)WindowsMessages.BM_CLICK, 1, IntPtr.Zero);
}
if (res.ToInt32() == 1)
return true;
return false;
}
and you can use winspector (analog of spy++). it's very useful utility. You can discovery many things about windows;)
I found a way to do this with Internet Explorer 6 in Windows XP.
(Sorry, VBA code)
'ButtonHwnd is the pointer to the Save button
Private Declare Function SetCursorPos Lib "user32" (ByVal X As Integer, ByVal Y As Integer) As Long
Private Declare Sub mouse_event Lib "user32.dll" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Const MOUSEEVENTF_LEFTDOWN As Long = &H2
Private Const MOUSEEVENTF_LEFTUP As Long = &H4
Dim pos As RECT
' We get the button position
GetWindowRect ButtonHwnd, pos
' We simulate an entering of the cursor in the button. IE think this is a human :-).
' We need three steps: out, entering and in.
' Out
SetCursorPos (pos.Left - 10), (pos.Top - 10)
Sleep 100
' Entering
SetCursorPos pos.Left, pos.Top
Sleep 100
' In
SetCursorPos (pos.Left + pos.Right) / 2, (pos.Top + pos.Bottom) / 2
' We do clic with the left button. You can use SendInput instead
' With 400 miliseconds it works.
mouse_event MOUSEEVENTF_LEFTDOWN, (pos.Left + pos.Right) / 2, (pos.Top + pos.Bottom) / 2, 0, 0
Sleep 400
mouse_event MOUSEEVENTF_LEFTUP, (pos.Left + pos.Right) / 2, (pos.Top + pos.Bottom) / 2, 0, 0
Please, tell if it works for you.
I found most of this on StackOverflow: How to handle Message Boxes while using webbrowser in C#?
and i modified it for me
using System.Runtime.InteropServices; //for the dll import (to press a key)
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private async void downloadstuff()
{
await Task.Delay(40000); //i need this delay, but you might not :)
{
IntPtr hwnd = FindWindow("#32770", "File Download"); //this is the window it finds
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Button", "&Save"); //this is the button to pres
uint message = 0xf5;
SendMessage(hwnd, message, IntPtr.Zero, IntPtr.Zero);
}
await Task.Delay(1000);
{
IntPtr hwnd2 = FindWindow("#32770", "Save As");
hwnd2 = FindWindowEx(hwnd2, IntPtr.Zero, "Button", "&Save");
uint message2 = 0xf5;
SendMessage(hwnd2, message2, IntPtr.Zero, IntPtr.Zero);
}
await Task.Delay(1000); //i press it anyway, just in case :)
{ //this is the download complete box (if its checked it doesn't show up)
IntPtr hwnd3 = FindWindow("#32770", "Download complete");
hwnd3 = FindWindowEx(hwnd3, IntPtr.Zero, "Button", "Close");
uint message3 = 0xf5;
SendMessage(hwnd3, message3, IntPtr.Zero, IntPtr.Zero);
}
}
IntPtr hwnd = FindWindow(null, "File Download");
IntPtr hokBtn = FindWindowEx(hwnd, null, "Button", "Cancel");
uint id = GetDlgCtrlID(hokBtn);
SetActiveWindow(hwnd);
IntPtr res = SendMessage(hokBtn, (int)0x00F5, 0, IntPtr.Zero);
if (res.ToInt32() == 1)
MessageBox.Show("success");
That was actually a false positive if I'm not wrong; the default behaviour seemingly as a failsafe appears to be to close the dialog box. Rather, it was a positive that the cancel button can be clicked, but attempting to click Open or Save will generate the same yet in that context unwanted response...
It seems that this is a dialog box we're just going to have to deal with unless someone else could gratefully confirm otherwise?
None of the code suggested worked, I ended up to use AutoIt script to close a Print Dialog, the code follows:
Local $hWnd = WinWait("[CLASS:#32770]", "Print", 20)
WinActivate($hWnd)
WinWaitActive("[CLASS:#32770]", "Print", 10)
Sleep(100)
Send("{ENTER}")