Introduction
Do you have a process that watches an HTML page? Are you writing a test harness? Do you sometimes get a popup which brings your process to a halt?
If so then this article will hopefully provide you a way of automatically clicking the button on the popup dialog box.
Background
I'm writing a test harness for work to test some old Classic ASP pages. Yes, I know there are testing frameworks out there but I have some very specific requirements
and I kept running into problem after problem. I created a hack test project to identify problems and find solutions. While doing that, I found that I had a problem
automatically clicking a popup button. After Googling for awhile, I found that others were having the same problem when using the WPF
WebBrowser
and no solution was available. So, I posted a question to the Microsoft WPF Forum for help with the WebBrowser
control.
Title: WebBrowser - How to click button on javascript confirm popup
I'm writing a test harness for an older classic ASP application that we have.
Some of the pages have JavaScript confirm dialog boxes like the following
var answer = confirm('Are you sure you want to delete the record.\n\nOK – proceed with delete Cancel – no change');
I would like to be able to click on the "OK" or "Cancel" button from my C# test harness application if possible.
This will allow for the rest of the code to be tested.
Any thoughts on how to fix this problem is appreciated and no I can't rewrite the classic ASP application.
Within a few hours, the forum admin responded with
Classic ASP application is a very old product which is out of the support scope of this forum.
We will move this thread to Off-topic forum. Thank you for your understanding.
No, I frankly don't understand how a HTML page was rendered is relevant to making a WPF
WebBrowser
control work.
Off Googling again I go. I found an obscure posting by someone saying maybe you can get the handle of the window and do something. In case "getting
the handle of a window" doesn't mean anything to you, you're in for a treat, P/Invoke. This article is not going to describe P/Invoke as it is a topic
unto itself, but rather provide you with a working example of how to click the button using the P/Invoke methodology.
A site that contains good information on P/Invoke is PINVOKE .NET
Using the code
The following class is at the heart of the solution. The sample project included has some additional classes to make the implementation a little easier.
This class will find a window by its title. This doesn't necessarily mean a JavaScript or VBScript window. It could be used to find an instance
of NotePad.exe running but would have to be modified to be of use. There are better ways to get to a running application and as executables
fall outside of the scope of this article, I will limit the remainder of the article to the HTML script languages.
In the SearchForWindow
method you will see code for FindWindow
and
FindWindowByCaption
. They are basically the same thing. Using either method will work.
If your requirements mandate that you find specific text on the popup, you will have to look for other P/Invoke options to make that work.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AutoClickPopup
{
public class Popup
{
#region Constants
const int BM_CLICK = 0x00F5;
#endregion
#region P/Invoke Methods
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr parentHandle,
IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
#endregion
private readonly string _windowTitle;
private IntPtr _window;
public Popup(string windowTitle)
{
_windowTitle = windowTitle;
}
public bool IsFound { get { return _window != IntPtr.Zero; } }
public bool SearchForWindow()
{
try
{
_window = FindWindow(null, _windowTitle);
}
catch
{
}
return IsFound;
}
public void ClickButton(string buttonText)
{
try
{
IntPtr btn = FindWindowEx(_window, IntPtr.Zero, "Button", buttonText);
if (btn != IntPtr.Zero)
{
IntPtr rc = SendMessage(btn, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
BreakOnError(rc);
}
}
catch
{
}
}
[Conditional("DEBUG")]
public void BreakOnError(IntPtr code)
{
if (code != IntPtr.Zero)
Debugger.Break();
}
}
}
One thing to note is that this code will have to be run on another thread. You ask...Why? Because, once the script window is popped up, the UI Thread is blocking until the button is pushed which kind of defeats the purpose of this class. So, make sure you are on another thread.
As a final note, the sample application is setup to allow the popup to be displayed for a period of time (5 seconds) before the button is clicked. You will want to fix this when using the code in your project. See the note in the constructor of
ClickButtonTask
.
Running the sample application
Compile and start the application. It will start the application and load a sample HTML page. If you get the security banner shown below, you will have to click the "Allow Blocked Content..." option. If you don't then JavaScript and VB Script will not run. After that security nag, you get another one asking if you are sure... Click "Yes".
To test, click on one of the WPF buttons at the top of the screen (red box). This will create a task that will look for a popup. Next click on the corresponding HTML button below (blue box) to create the popup window. You will note that the window goes away once the button is "pushed" from the code running in the task.
The HTML confirm button will pop up another window. You will have to manually click the button for the box to go away. I did this to show that the script will continue after the button is pushed.
History
None.