How to detect when a winforms application creates a new window? - c#

I'm using a webbrowser control in my form application and I want to block any popup/alert/prompt window that it can create.
Currently, I am implementing various methods to block popups like:
Canceling various events that fire when a new window is created.
Changing global IE settings through the registry to make it show less
alerts and prompts.
Using browser feature controls to block some popups.
Injecting javascript into every page to disable functions that can create new windows.
Extending the web browser control with new events by implementing things like IDocHostShowUI which allow me to
block certain popups.
Using the "hidden" events of the base activeX webbrowser object like NewWindow2 and NewWindow3.
All of this combined blocks 99% of all the windows that the webbrowser control can create (the 1% being some extremely rare cases like a javascript prompt() function called from within an iframe which document is located on a different domain than the parent window, still haven't found a way to block that :D).
But it's a lot of code, making it a big mess which can sometime interfere with normal browsing.
I want to know if there is a different approach. Since most of the windows that are created by the webbrowser control are actually created by my application's process, is there a way to detect when my application creates any kind of window that is not the main form and close it instantly or block it's creation entirely?
UPDATE:
I have tried overriding the WndProc method for the webbrowser control, but no messages are sent when a prompt appears.
When overriding it for the entire form, I do get some messages when a prompt appears, but they are related to losing focus and not to the actual creation of a prompt. So I am able to detect when a prompt is created, but still not able to block it's creation.

If you just want to close any window except the current one, you can use the code below inside the timer_tick event:
private void timer1_Tick(object sender, EventArgs e)
{
for (int i = 0; i < Application.OpenForms.Count; i++)
{
Form form = Application.OpenForms[i];
if (form != this)
{
form.Close();
i--;
}
}
}

Related

Use UIA to get notified when a button is available in an external WPF app

I'm working on a small app that uses UI Automation and sits atop a third party app to intercept certain events and inject and extract values into/from input fields. The other app seems to be using a ContentControl in WPF to show all the different screens, within one single Window. All of my code seems to be working well, IF the control it's expecting exists already. At this point I'm trying to tie everything together by adding the button click automation handlers when the button has been created, but I'm not sure the best way to do it.
I can currently capture button click events within the external app, if I call this code when the button has been created:
var cond = new PropertyCondition(AutomationElement.NameProperty, "SearchButton");
var searchButtonElement = _rootWindowElement?.FindFirst(TreeScope.Descendants, cond);
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, searchButtonElement, TreeScope.Element, ((o, args) =>
{
MessageBox.Show("Hello World");
}));
Everything works well if that screen is already up and showing that button, but if it hasn't been instantiated yet, searchButtonElement is null. What's the best way for me to wait for the button "SearchButton" to become instantiated and valid so I can attach my automation invoke handler to it?
Also, what would be the best way to detach my handler when it's no longer available (... is that even necessary)?
Basically, the external app starts up on a main menu screen, once the "Show search screen" button is clicked, I need to run code when the next screen's "Search" button is clicked.
Thanks

Form gets disposed on Hide()

I have a C# WinForms application running on .NET Framework 4.0.
When the user is inactive for a certain period of time, I want it to hide all the displayed forms and show an icon in the notification area. When the user clicks that icon, a login form appears and if the credentials are valid, it opens the exact forms that were open before.
To do this, I store the list of open forms in a List of Form objects and hide them, like this. This method is called by a Timer:
private void LogOut()
{
foreach (Form form in Application.OpenForms)
if (form.Visible)
{
GlobalVariables.formList.Add(form);
form.Hide();
}
}
When the credentials are validated, I try to make the forms visible again, like this:
//Show the previous forms.
foreach (Form form in GlobalVariables.formList)
form.Visible = true;
//Clear the forms list.
GlobalVariables.formList.Clear();
If I only have the MainForm open when I hide the forms, it shows it back fine when logging back in. If I have any other forms open (which are opened using ShowDialog() from the MainForm), the program will crash on form.Visible = true; and give me the following error message:
ObjectDisposedException was unhandled
Cannot access a disposed object
How can I fix this problem? An alternative way of doing what I'm trying to achieve would also be great.
Please note that using a try - catch block to determine if the form has been disposed and just relaunch the form is not an option as the user may have unsaved input in the hidden forms.
I couldn't manage to find anything related online in over 3 hours of search so any help would be much appreciated!
EDIT: After trying various things, I have noted that the problem only occurs on forms I have opened forms using ShowDialog(). If I only have forms opened using Show(), everything works fine.
However in my case, using Show() is not an option because I cannot have the user click on things in the parent form. Hiding the parent form is not an option either as he needs to see information in the parent form.
Clearly hiding a form is more impactful than you counted on. Your code was involved in a security review that Microsoft conducted on Winforms. Very thorough, not often visible in the way it behaves but very visible in the source code. One rule is imposes is that a user should never lose control over the application.
A dialog is very troublesome that way. The core problem is that ShowDialog() creates a modal window that disables all the other windows. That creates an opportunity for malware, very easy to take advantage of, all it has to do is hide a dialog and you snookered the user. There isn't any way that the user can gain control of the app again. The one window that was enabled is hidden with no way for the user to re-activate it again. All the other windows are disabled so trying to click on them, or their taskbar button, will not have any effect. All that's left is for the user to use Task Manager to kill the app. And if the user account is locked down then that's not an option either.
I can hear you sputter by now: "But, but, it is my code that hides the dialog, not malware!" That's not the way it works in Windows, there's no way to tell that it actually was your code that did it. Not only because it could be injected code, it doesn't even have to be code that runs in your process. Any code can do it, it is part of the winapi.
So there's a specific counter-measure against this built into Winforms, it will automatically close a form if it is hidden while operating in dialog mode. Which of course has a big impact, code that was written after the ShowDialog() call will now run. Anything is possible, but a sure-fire mishap in your case is that this disposes another window and an attempt to revive it will die.
The rough guidance here is that you are doing it wrong. You are trying to build a security system on top of one that's already highly secure and heavily tested. And it is very risky, handling passwords yourself is a very good way to make the overall system much less secure. The average user will of course favor picking the same password as he used to login to Windows. Makes it much easier for an attacker to harvest that password.
Call LockWorkStation() instead.
After many hours of trial and error, I found out what the problem was.
My understanding of modal forms was that code would continue executing in the parent form only after the modal form was closed. In fact, the specification found on MSDN states:
A modal form or dialog box must be closed or hidden before you can continue working with the rest of the application.
This introduced a subtle bug in the way I handled the forms. This is the code I used to display the forms:
using (var theForm = new CreateInvoice())
{
theForm.ShowDialog();
if (theForm.Updated)
{
GetInvoiceStatus();
}
}
The using statement disposes of theForm as soon as the statement exits. Normally, this works perfectly fine as it would be called only when the user closes theForm. However, because ShowDialog() permits the parent form to continue its work when it is hidden, this meant that the code actually exited the using statement, which effectively disposed of theForm, resulting in my error.
Testing, it seems that Hide()ing a modal dialog - closes it. It actually triggers the FormClosing event.
Tested like this: (Also, see this answer.)
private void button1_Click(object sender, EventArgs e)
{
Form1 f1 = new Form1();
f1.ShowDialog();
}
private void button2_Click(object sender, EventArgs e)
{
Hide();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
MessageBox.Show("Closing");
}
EDIT
I noticed this actually doesn't solve the mystery, just adds more information. Here's another piece of information: When you're setting Visible to true - you're not showing it modal again. This time it's equivalent to Show().
One suggestion to the design: instead of saving forms(views), you should save the data that the form holds(Model) and destroy the form. When you need the form again, create it back with the data(model). First, this can resolve this mysterious dispose problem, second, each form will need GDI resource, which are limited, if two many forms, you'll encounter the memory and GDI problems.
As how to do this, please refer the MVC or MVP design pattern.
BTW my guess on this problem: when you make the form visible, it will try find its parent, but its parent may be already disposed. I encountered this problem once, it throws the object disposed exception.

How to close winform with TopMost=true, when another application opens on top of my app

I have a C# .Net 3.5, winform (displaying graph) that needs to remain open as a front screen. I refresh the graph during the datagridview RowEnter event of the calling window. I open graph winform as show(), and use TopMost = true. Everything works fine till I open another application like Word; Graph window still remains in the front of Word.
Is there an application event gets triggered when another application opens on top of my application, when I can close the open graph screen. Or, please let me know if you have a suggestion about the different approach.
Look at Form.Deactivate Event. It is raised when the form loses focus and is no longer the active form. You can use this event to Close the form
Sorry but I can't get the sense of your question. You set TopMost=true and the next moment you want it to hide behind another application... ????
This solution method is 100% working, considering that the login form name is loginForm.
Simply create the following method:
private void hideLogin()
{
if (System.Windows.Forms.Application.OpenForms["loginForm"] != null)
{
System.Windows.Forms.Application.OpenForms["loginForm"].Hide();
}
}
Call this method through task:
Task HideLoginTask = new Task(hideLogin);
HideLoginTask.Start();

Calling a C# asynchronus method from a VB6 application

I have a C# dll that when the main method is called, it opens a windows form. This dll is currently accessed through VBA by a vendor application. The problem is that when the dll is called and the form opens, the user loses the ability to interact with the vendor application. I was hoping that if I could modify the dll to support an asynchronus call, then the control could be returned to the calling application to allow the user to turn pages, zoom in or out, etc. Then once the user completes the pop-up form we could have a callback or something to return the information to the vba app customizations.
Rather than making the async call from VB, it would be far easier to modify the C# code to open the window asynchronously, and then return.
You could do this just by changing the line that opens the window from .ShowDialog() to .Show().
Obviously, this may not be an option if you don't have access to the C# dll's code, but I thought I would suggest it.
If you do decide to change to use Show() instead of ShowDialog() you may have to handle your dialog result differently.
You're new code will look something like this:
MyForm win = new MyForm();
win.Show();
A non modal dialog doesn't block the calling code, so your calling code will continue without waiting for a result.
The simplest way to handle this would be to attach an event handler to the form's .OnClosed event. This will then be triggered when the user closes the form.
MyForm win = new MyForm();
win.OnClosed += new EventHandler<FormClosedEventArgs>(YourEventHandlerMethod)
win.Show();
You can add a public boolean property to the form (with a private setter) that you set internally when the ok/cancel buttons are pressed (Along with calling the close method). You can then check this property after the form has closed from within your on closed handler. (The sender property will be your reference to the form, you'll just have to cast it to the correct type first).
So your event handler method will look like this:
private void EventHandler<FormClosedEventArgs> YourEventHandlerMethod(Object sender, FormClosedEventArgs e)
{
MyForm f = (MyForm)sender;
bool result = f.TheResultProperty;
// Now do your callback.
}
Finally, now you have some code that runs after the form closes, and has access to the forms result, you just need to let your VBA code know it's ready - I'll leave this up to you.

How to do two things with one click in Windows Form

On my main form, there is another (floatable) window. This floatable window works sort of like a popupwindow in that it will close when the user clicks somewhere else outside of this window. This is handled by the Deactivate event. But what I want to do is, if the user clicks on a different control (say a button), I want to both close this float window and then activate that button with just one click. Currently, the user has to click twice (one to deactivate the window and once more to activate the desired button). Is there a way to do this with just one click?
foreach(Control c in parentForm.Controls)
{
c.Click += delegate(object sender, EventArgs e)
{
if(floatyWindow != null && floatyWindow.IsFloating)
{
floatyWindow.Close();
}
};
}
And then add your handlers as normal. This additional handler can close the floaty window.
Make sure you floaty window isn't a dialog too as this will not allow your parent form's controls to be clicked.
I had a slightly hacky solution. In your Deactivate event, fire another custom event to your main form. Then when you main form is handling the custom event, enumerate through your control(this.Controls) and locate the control under the mouse by checking all their bound then call Focus(). You might need to sort by the one with the smallest surface area, or you can have a separate list of "focus-able" control like button just for this purpose.
Another way might be to switch focus to your main form immediately after OnMouseLeave of the floatable window, or OnMouseHover of your main window, but keep the floatable windows on top, just no focus. Handle the global mouse down of your main form, and close the floatable window by then.
These are just theories, not tested.
I had an issue like this once too, when a customer wanted "floaty" windows all over there application. I used used an approach similar to the one described in this article:
http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/article.asp
Code sample available here:
http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/Popup_Form_Demonstration.asp
By extending this a bit we created "floaty" windows similar to the ones VS uses when you get a runtime error while debugging code.
At the very least reading the code may give you some insight, however, quarrelsome's response may be the more simple solution.

Categories