Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 748
Vlad Schnakovszki Avatar asked Feb 13 '23 19:02

Vlad Schnakovszki


1 Answers

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.

like image 85
Hans Passant Avatar answered Feb 23 '23 13:02

Hans Passant