I have a C# WinForms application that appears in the system tray. The application is hiding the main form on load:
private void MainForm_Load(object sender, System.EventArgs e)
{
Hide();
}
For the same main form, I've overriden the WndProc to catch a custom window message (that message is registered to Windows with the RegisterWindowMessage Win32 API call).
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYCUSTOM_MESSAGE)
{
// Handle custom message
}
}
From an external C++ application I'm broadcasting the same custom window message with PostMessage.
UINT msg = RegisterWindowMessage(L"WM_MYCUSTOM_MESSAGE");
PostMessage(HWND_BROADCAST, msg, NULL, NULL);
When I execute the above code after the C# application was first started, it doesn't get into the WndProc. After the main form is shown (by a double click on the system tray icon, which in essence does a Show()), catching the broadcast message works and keeps working after hiding the form with Hide().
Can anyone explain me why it doesn't work after the first Hide() from the MainForm_Load event handler? Am I hiding the form too early?
EDIT1: It seems like it has nothing to do with the Hide on load. Even without the initial Hide, my main form WndProc will only start accepting broadcast posts after it is hidden and re-shown...
After creating a small test application, I have found out that PostMessage() to HWND_BROADCAST doesn't arrive in Form.WndProc if Form.ShowInTaskbar is set to false while SendMessage() to HWND_BROADCAST does.
Even though the MSDN note about sending or posting messages to HWND_BROADCAST is exactly the same.
So it had nothing to do with the hiding or showing of the form and this seems like another undocumented feature of the Windows API.
There's something else going on, calling Hide() in the form's Load event handler doesn't actually hide the window. Try it with a little test Winforms app to see this.
If you don't actually see the window then the simple explanation is that the window just wasn't created. Which entirely explains why the message isn't being received. Override OnHandleCreated() and set a breakpoint on it to double-check this theory. The Show() method must be called to create the native window, apparently your NotifyIcon event handler is the first one to do so. Which happens when you call Application.Run() but don't pass a form instance.
Check this answer for a way to ensure that that a form is created but not made visible.
With Broadcast, the message is posted to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows. The message is not posted to child windows (according to MSDN). Use an application, like WinSpy to make sure your windows is a top level one right after the initial startup and hiding. Also this http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/ might (or might not) give some insights on how the OnLoad works... Seems it's a little bit more complicated. Try to hide the MainForm somewhere else, not on the OnLoad.
Related
It is possible that when I use:
MessageBox.Show("hello");
..to still use the form but the MessageBox is open like a second window?
because currently, when I show the MessageBox, I need to first click yes or no then I can use my form again.
Yes, you can!
If you simply want to display a MessageBox, don't care what happens with it, and don't want to wait until it's closed, you may launch it on a separate thread. The easiest way to do that would be using Task.Run().
Here's an example:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => MessageBox.Show("hello"));
// The remaining code will run without waiting for the MessageBox to be closed.
}
A couple of notes worth mentioning:
Only use this for simple message boxes where you don't care about the result. If you want to act based on the result and execute something (on the main thread), things get a little bit trickier.
You won't be able to keep the MessageBox on top of the form. Once you interact with the form, it will come on top. If you need to keep the MessageBox on top and still have the ability to interact with the form, a custom MessageBox (i.e., form) would be better in that case because you can set the Owner property to keep it on top.
No. The application focus remains on the Message Box until it is dismissed. According to the documentation:
It is a modal window, blocking other actions in the application until the user closes it.
What you can do is create your own form, style it to look like a standard Message Box, and show that form using the .Show() functionality on an instance of it:
var messageBox = new MyCustomMessageBox("hello");
messageBox.Show();
If you want the functionality to be identical in usage, you could even add a static method to your custom form which encapsulates it:
public static void Show(string message)
{
new MyCustomMessageBox(message).Show();
}
Though what I wouldn't recommend doing is calling your custom form MessageBox, that would just be asking for confusion.
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.
I imagined that WndProc is called on another thread rather than main UI thread.
I was right, so I thought that simply using InvokeRequired and Invoke on the form was enough to show a messagebox on the UI thread.
I was wrong. And I don't understand why.
How can I go around this problem?I'm looked around google but didn't find a solution.
What I'm trying to do is simply raising a custom event (ClipboardUpdate) when clipboard changes
the messagebox was just a test but didn't work, while just changing something like a string (a private field of the form) works, but it's not a good thing this behaviour because is a cross-thread operation in an unsafe way.
Update 1:
I don't have the code here because I created it on a friend's computer, however I can explain exactly what I wrote Because is short.
I created a basic winform with visual studio, without anything.
I used AddClipboardFormatListener (interop, but it's quite easy as a function, return int and accept IntPtr) function (on a windows 7 OS) to just detect WM_CLIPBOARDUPDATE message, inside winproc (a simple if, *if (e.Msg == ClipboardExtension.WM_CLIPBOARDUPDATE) DoClipboardUpdate();*).
Now the DoClipboardUpdate do this:
if (InvokeRequired)
Invoke(new VoidDelegate(OnClipboardUpdate));//Void delegate it's a delegate that doesn't take
// Params and returns void
else
OnClipboardUpdate();
Quite easy right? OnClipboardUpdate just do this:
if (ClipboardUpdate != null) ClipboardUpdate(null,EventArgs.Empty);
ClipboardUpdate is an event declared in this way:
public event EventHandler<EventArgs> ClipboardUpdate;
In the end, the only method subscrived to ClipboardUpdate event has this inside:
MessageBox.Show("test");
What happens when I run the code? The event is triggered (I tried with an exception and it works) and the messagebox doesn't popup, however I can't interact anymore with my form because it behaves as if a popup was opened (this is the "normal" behaviour when you open a popup on a different thread, that's why I said that).
Any suggestion on how to solve this?
I didn't understand why it happens but I created a new project and opening a messagebox from WndProc works fine, maybe some thread corrupted main thread memory, I don't know this but as others stated wndproc is the ui thread and should work
I have a C# windows forms application. The way I currently have it set up, when Form1_Load() runs it checks for recovered unsaved data and if it finds some it prompts the user if they want to open that data. When the program runs it works alright but the message box is shown right away and the main program form (Form1) does not show until after the user clicks yes or no. I would like the Form1 to pop up first and then the message box prompt.
Now to get around this problem before I have created a timer in my Form, started the timer in the Form1_Load() method, and then performed the check and user prompt in the first Timer Tick Event. This technique solves the problem but is seems like there might be a better way.
Do you guys have any better ideas?
Edit: I think I have also used a background worker to do something similar. It just seems kinda goofy to go through all the trouble of invoking the method to back to the form thread and all that crap just to have it delayed a couple milliseconds!
I would use Form1_Shown()
Use the Shown event. It seems to suit what you need, and will only display the first time the form is shown.
Form f1 = new Form();
f1.Shown += new EventHandler(f1_Shown);
public void f1_Shown(object sender, EventArgs e)
{
// Show dialog in here
}
Try the "Shown" event:
Form.Show Event
Using a Windows.Forms.Timer is a good, stable, well-known, and easily understood technique for doing what you want. I would avoid any other timer objects.
The form's Shown event works well.
Overload / override the Show method. (My preferred technique for greater control.) In this method, I would do the checking needed. When ready, I would call the base.Show method, then do any other processing, such as message boxes, prompts, logging, or whatever.
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.