Windows Form dies right after the constructor is done - c#

I'm currently writing an IRC bot/client and I have stumbled upon a small problem. Every channel is to be displayed in a separate window with its own set of controls. I have created an appropriate window class ChanWindow:
public partial class ChanWindow : Form
{
string Chan;
string Username;
IRCBot Caller;
public ChanWindow(string channame, string uname, IRCBot caller)
{
Caller = caller;
Caller.Join(channame);
InitializeComponent();
Chan = channame;
Name = Chan;
Username = uname;
}
//various functionalities required for window use
}
I'm storing the window objects in a ConcurrentDictionary<string, ChanWindow>, because there is (supposed to be) a separate thread sending appropriately edited messages to windows. They are all initialized in a loop:
foreach (string chan in Chanlist)
{
Chans[chan] = new ChanWindow(chan, Name, this);
Chans[chan].Show();
}
Where Chanlist is a List<string> of channels to join.
Having witnessed the windows die shortly after the program starts, I put a breakpoint in the line Username = uname; of ChanWindow constructor and noticed it being tripped immediately before the window's death.
My main question is: what is the cause and how can I avoid it? Is using a Dictionary a good idea? Should I build some sort of wrapper/container class which would handle the window's functionality?

I suspect you don't have
Application.Run(new ChanWindow());
in your main entry point (Program.Main() by default).
If you don't make the main GUI thread Show the form, and wait for it to close, nothing will stop the thread from completeing and your application closing.
If you want to start the message loop independently you will still need to call Application.Run() so the main thread will start handling events. If you call it without a parameter the message loop won't stop automatically so you'll have to call Application.Exit() explicitly when you want the message loop to stop. Then the main thread will then continue to the end of the program and end.
You could just do this to your code,
foreach (string chan in Chanlist)
{
Chans[chan] = new ChanWindow(chan, Name, this);
Chans[chan].Show();
}
Application.Run();
but, if you want to automatically exit the application you could inherit your own ApplicationContext, as outlined in the already linked MSDN documentation, and pass that to Application.Run(), that is what its for after all.

Related

Do I have to call Application.ExitThread()?

using System.Windows.Forms;
public class App
{
[STAThread]
public static void Main()
{
string fname;
using (var d = new OpenFileDialog())
{
if (d.ShowDialog() != DialogResult.OK)
{
return;
}
fname = d.FileName;
}
//Application.ExitThread();
for (; ;)
;
}
}
The above code shows me a file dialog. Once I select a file and press open, the for loop is executed, but the (frozen) dialog remains.
Once I uncomment Application.ExitThread() the dialog disappears as expected.
Does that work as intended? Why doesn't using make the window disappear? Where can I find more info about this?
You have discovered the primary problem with single-threaded applications... long running operations freeze the user interface.
Your DoEvents() call essentially "pauses" your code and gives other operations, like the UI, a chance to run, then resumes. The problem is that your UI is now frozen again until you call DoEvents() again. Actually, DoEvents() is a very problematic approach (some call it evil). You really should not use it.
You have better options.
Putting your long running operation in another thread helps to ensure that the UI remains responsive and that your work is done as efficiently as possible. The processor is able to switch back and forth between the two threads to give the illusion of simultaneous execution without the difficulty of full-blown multi-processes.
One of the easier ways to accomplish this is to use a BackgroundWorker, though they have generally fallen out of favor (for reasons I'm not going to get into in this post: further reading). They are still part of .NET however and have a lower learning curve then other approaches, so I'd still suggest that new developers play around with them in hobby projects.
The best approach currently is .NET's Tasks library. If your long running operation is already in a thread (for example, it's a database query and you are just waiting for it to complete), and if the library supports it, then you could take advantage of Tasks using the async keyword and not have to think twice about it. Even if it's not already in a thread or in a supported library, you could still spin up a new Task and have it executed in a separate Thread via Task.Run(). .NET Tasks have the advantage of baked in language support and a lot more, like coordinating multiple Tasks and chaining Tasks together.
JDB already explained in his answer why (generally speaking) your code doesn't work as expected. Let me add a small bit to suggest a workaround (for your specific case and for when you just need to use a system dialog and then go on like it was a console application).
You're trying to use Application.DoEvents(), OK it seems to work and in your case you do not have re-entrant code. However are you sure that all relevant messages are correctly processed? How many times you should call Application.DoEvents()? Are you sure you correctly initialize everything (I'm talking about the ApplicationContext)? Second problem is more pragmatic, OpenFileDialog needs COM, COM (here) needs STAThread, STAThread needs a message pump. I can't tell you in which way it will fail but for sure it may fail.
First of all note that usually applications start main message loop using Application.Run(). You don't expect to see new MyWindow().ShowDialog(), right? Your example is not different, let Application.Run(Form) overload creates the ApplicationContext for you (and handle HandleDestroyed event when form closes which will finally call - surprise - Application.ExitThread()). Unfortunately OpenFileDialog does not inherit from Form then you have to host it inside a dummy form to use Application.Run().
You do not need to explicitly call dlg.Dispose() (let WinForms manage objects lifetime) if you add the dialog inside the form with the designer.
using System;
using System.Windows.Forms;
public class App
{
[STAThread]
public static void Main()
{
string fname = AskForFile();
if (fname == null)
return;
LongRunningProcess(fname);
}
private static string AskForFile()
{
string fileName = null;
var form = new Form() { Visible = false };
form.Load += (o, e) => {
using (var dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() == DialogResult.OK)
fileName = dlg.FileName;
}
((Form)o).Close();
};
Application.Run(form);
return fileName;
}
}
No, you don't have to call Application.ExitThread().
Application.ExitThread() terminates the calling thread's message loop and forces the destruction of the frozen dialog. Although "that works", it's better to unfreeze the dialog if the cause of the freeze is known.
In this case pressing open seems to fire a close-event which doesn't have any chance to finish. Application.DoEvents() gives it that chance and makes the dialog disappear.

MediaElement freezes upon launch of video

I'm working on a project that is designed to play both audio and video files through a WPF Window through a MediaElement. This is the xaml for the window:
<Window x:Class="HomeSystem_CSharp.MediaWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MediaWindow" MinHeight="480" MinWidth="720" WindowStyle="None" ResizeMode="NoResize" Visibility="Visible" Cursor="None">
<Grid Background="Black">
<MediaElement LoadedBehavior="Manual" HorizontalAlignment="Stretch" Name="video" VerticalAlignment="Stretch" Cursor="None" MinHeight="480" MinWidth="720"/>
</Grid>
</Window>
This creates the window with no borders, that I plan on full-screening in the future. For now though, I want more room on my desktop. Here is my code for controlling my MediaElement:
private bool playing = false;
public MediaWindow(string dir)
{
InitializeComponent();
video.Source = new Uri(dir);
play();
}
public void play()
{
if (playing)
return;
if (!this.IsVisible)
this.Show();
video.Play();
playing = true;
}
This MediaWindow is created outside of the object, just by a simple MediaWindow mw = new MediaWindow("C:\\test.mp4");
No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays. I can hear the video in the background, but there is a broken window on my screen. Just a black box.
The biggest issue is that just the other day it was working fine, and suddenly it broke, and I have no clue what happened. I'm kinda new to c#, so I dont know a TON about what's going on, but I've worked with java for several years so I'm not totally new. Can anyone point out what I'm doing wrong? i can provide any other details but I think i got everything necessary to answer. Thank you for any help, this has been bothering me all day with no fix!
EDIT: Turns out, if I use
public void play()
{
if (playing)
return;
//if (!this.IsVisible)
// this.Show();
video.Play();
new Application().Run(this);
playing = true;
}
instead, it will run the GUI. However, that hangs up the console. Originally I fixed that hang up by using this.Show(), but now that's not working. I know that moving the whole project into a WPF project would fix this, however I'm really trying not to for other reasons. Only win32 for now. Any ideas why this is happening and how to fix it? I do have [STAThread] over my main function if that makes a difference.
EDIT 2:
This video file I'm playing is movie length, and runs perfectly in any other software to prevent that from being an issue with development. As for the MediaWindow creation. What I did is made a win32 console project and set up the user commands there. I then made a new WPF project, and created an xaml gui window. I took those code files, and copied them into the win32 project, and call it to launch in the main method with the MediaWindow mw = new MediaWindow("C:\\test.mp4"); I did it this way because for now I'm trying to keep away from using a pure WPF application, and because I'm kinda new to C# so I wasnt sure how to create the window I wanted without my copy paste method.
No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays.
I've managed to reproduce this. One important thing missing in your description is the exact way you create and show the window in your main() method. For example, the following freezes the video leaving the sound playing:
[STAThread]
static void Main(string[] args)
{
var w = new MediaWindow();
w.Show();
Console.ReadLine();
}
The next one "freezes" the console until you close the window:
[STAThread]
static void Main(string[] args)
{
var w = new MediaWindow();
w.ShowDialog();
Console.ReadLine();
}
And this gives you both working:
static void Main(string[] args)
{
var thread = new Thread(ShowMediaWindow);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
while (true) // Just to test console working
{
Console.Write("\r" + DateTime.Now);
Thread.Sleep(100);
}
}
static void ShowMediaWindow()
{
new MediaWindow().ShowDialog();
}
As you can see, the console and the WPF window simply can't work properly in a single thread.
The window class is as simple as this, by the way (the XAML is mostly the same as yours):
public partial class MediaWindow : Window
{
public MediaWindow()
{
InitializeComponent();
video.Source = new Uri(#"C:\video\test.asf");
Play();
}
public void Play()
{
video.Play();
}
}
I guess that'll do for showing a video player window from console.
OK, the whole hybrid console/GUI thing is a new one on me but I'm just going to assume there's a real need to do things that way.
The Application.Run method doesn't return until the application closes. That's why your console is locked up.
Don't create the Application object inside the window. Do it externally. Also, spawn another thread to kick off video playback. This will leave your console responsive.
I'm not gonna get heavy-duty into describing threading and delegates and so forth... you can look that up if you want. I'm just gonna go over what you need to do for this specific example. Somewhere in the class that launches the video, but not in a method, define a delegate type like this:
delegate void LaunchVideo(String s);
A delegate is essentially kind of a pointer to a function with a certain definition of return value and parameters. Here we've defined the delegate type as a function that takes a String parameter and returns nothing. Next, at the point in your code where you want to play the video, do this:
LaunchVideo lv = new delegate(String vidfile)
{
Application app = new Application();
app.Run(new MediaWindow(vidfile));
};
IAsyncResult result = lv.BeginInvoke( "C:\\vid.mp4", myVideoCompleted, null );
This creates the delegate variable and points it at an anonymous function that creates the app and launch video playback. Then it calls the delegate's BeginInvoke method, which is part of the basic delegate class. This spawns a new thread running in the function pointed to by the delegate.
Note that calling Application.Run with a window parameter like this will open the window but it won't call the play() method. You may want to move that code to the constructor, or add a call to it in the constructor.
Be aware that your main thread cannot safely call methods in objects created in the invoked thread unless you use the lock function to make things thread safe.
If you need "open" and "play" to be separately controlled events which are both invoked by the console then you'll have to figure out a means to pass messages from the console thread to the window thread.
The parameter list for BeginInvoke always starts off with whatever parameters are expected by the function you're invoking. So in this case, that's the string with the video filename. Next is the name of a callback function which will be called when the invoked function exits. It's a void function that takes an AsyncResult parameter, like this:
void myVideoCompleted(AsyncResult result)
{
// Do whatever here... Be aware this is still on the other thread
}
You can use 'null' instead of a function name, if you don't need anything called at the end. Be aware that if you do use a callback function, it runs on the new thread started by BeginInvoke, not the thread that called it.
The last parameter to BeginInvoke is an object that will be passed through to the callback function via the AsyncState member of the AsyncResult parameter. You can pass 'null' if you're not using a callback or if you have no parameters which will be needed by the callback.
You can also call the EndInvoke method of the delegate to get back any results that may've been returned by the function. However, be aware that this will block if the invoked function isn't finished yet. In this case you have no results to worry about.

Prevent the application from exiting when the Console is closed

I use AllocConsole() to open a Console in a winform application.
How can I prevent the application from exiting when the Console is closed?
EDIT
The update of completionpercentage from time to time is what I want to show in console
void bkpDBFull_PercentComplete(object sender, PercentCompleteEventArgs e)
{
AllocConsole();
Console.Clear();
Console.WriteLine("Percent completed: {0}%.", e.Percent);
}
I tried the richtextBox as the alternative
s =(e.Percent.ToString());
richTextBox1.Clear();
richTextBox1.AppendText("Percent completed: " +s +"%");
But I can't see the completionpercentage update time to time. It only appears when it is 100% complete.
Any alternative?
I know this is a task that seldom pops up but I had something similar and decided to go with a couple hacks.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/545f1768-8038-4f7a-9177-060913d6872f/disable-close-button-in-console-application-in-c
-Disable the "Close" button on a custom console application.
You're textbox solution should work as well. It sounds a lot like your calling a function from the main thread that is tying up the form which is also on the main thread and is causing you grief when updating your textbox. Consider creating a new thread and either an event handler to update your textbox or use the invoke methodinvoker from the new thread to update the textbox. Link below from an already answered question on how to complete this.
How to update textboxes in main thread from another thread?
public class MainForm : Form {
public MainForm() {
Test t = new Test();
Thread testThread = new Thread((ThreadStart)delegate { t.HelloWorld(this); });
testThread.IsBackground = true;
testThread.Start();
}
public void UpdateTextBox(string text) {
Invoke((MethodInvoker)delegate {
textBox1.AppendText(text + "\r\n");
});
}
}
public class Test {
public void HelloWorld(MainForm form) {
form.UpdateTextBox("Hello World");
}
}
Refer to the answers over here. As mentioned in the answers, there is no way to stop the application from getting closed.
But as a workaround, you can have your own text output solution described in one of the answers.
You can build first the console application that recieves arguments and writes it to the console. Place it, where the main application starts.
From main application you can first kill process and then reopen it with a new argument.
It's the altarnative way.

Changing the content of a form from outside?

I've got a form called MyForm, and I want to edit it's properties in the middle of the program's execution, from outside of the class itself.
Here's what I got:
static void Main()
{
MyForm main = new MyForm();
main.ShowDialog();
main.Text = "Hello";
}
However, it seems like ShowDialog() just freezes the program until I close the form, so main.Text = "Hello"; won't be executed until I close the form.
I've also tried using main.Show() but it just closes the form after I've executed all the code in my Main() function, so the text "Hello" will only flash quickly.
I would need it so that I can have the form open at all times and change it's controls in the middle of the program's execution, from outside of the class itself.
How can I achieve this? Should I run the form in a different thread or something?
There are multiple questions here, you should try to focus on one at a time, so let me highlight the questions.
Why does my program close when Main exits?
How can I avoid "freezing" the program when showing a form?
How can I access the contents of a form from elsewhere?
Answers:
That's the design of how the lifetime of a program is. When the main thread (running the Main method) terminates, the program closes. Any open forms are closed in the process. Solution is to not allow Main to exit, typically by using Application.Run(main); in your case, showing the form and waiting for it to close.
You use Show and not ShowDialog, but since you have no other form keeping the program open, your program closes. Show returns after showing the form, returning to whatever the program was doing. In your case, the program has nothing left to do, so it terminates.
You need to store a reference to your form somewhere the rest of your program can access it, you can use a static field/property somewhere, or you can pass the form around to the various parts that need it.
First of all, I don't know what is your specific scenario. Perhaps my solution is good for you, but if it isn't, please, try to tell us what exactly are you trying to achieve.
static void Main()
{
var main = new MyForm();
//Initialize a new thread with the `DoSomething()` method
//and pass the form as a parameter
var thread = new Thread(() => DoSomething(main)) {IsBackground = true};
thread.Start();
main.ShowDialog();
}
static void DoSomething(MyForm main) {
//Update the form title
main.Text = "Hello";
//Wait one second
Thread.Sleep(1000);
//Update the form title again
main.Text = "World";
}

Close another process when application is closing

I have a C# winform application that during its work opens another Winform process. The other process has UI of its own.
When I close the parent application, I want the other application to be closed automatically.
How can I achieve that?
Thanks
If you are using Process.Process there is the CloseMainWindow method. If you keep a reference to the object you can use it later.
Here's the relevant page in the MSDN
and the relevant code:
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();
There are several different options. I would suggest that you have your application keep track of the processes that it starts:
private Stack<Process> _startedProcesses = new Stack<Process>();
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
_startedProcesses.Push(newProcess);
}
When the application closes, you can call a method that will close all started child processes that are still running. You can use this either with the Kill method or by calling the CloseMainWindow and Close methods. CloseMainWindow/Close will perform a more graceful close (if you start Notepad and there are unsaved changes, Kill will lose them, CloseMainWindow/Close will make notepad ask if you want to save):
private void CloseStartedProcesses()
{
while (_startedProcesses.Count > 0)
{
Process process = _startedProcesses.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
The most graceful way to do this is probably to send a window message to the main from of the other process. You can get the handle of this main form simply using the Process.MainWindow.Handle property (I assume you are using the Process class, and then just use the PostMessage Win API call to send a message with a custom ID to the main window of this "child" process. Then, the message loop of the other process can easily detect this message (by overriding the WndProc method) and perform a proper shutdown accordingly. An alternative would be to send the standard WM_CLOSE method, which would mean you would just have to unload the application from the handler of the Form.Closed event, but may perhaps allow you less control (over whether to cancel the shutdown in certain situations).

Categories

Resources