MediaElement freezes upon launch of video - c#

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.

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.

timer in console application

I am creating a device application using .NET compact framework 2.0. There is a system.threading.timer in my application which executes some code. It works fine. My problem is when I am running the app by double clicking on the exe in the bin folder, the timer starts and execute all it works but it never stops. It runs in the background even after closing the app by clicking the X-button or from the file menu close button. I don't understand how and where I stop or dispose of the timer so that it doesn't run after closing the app. May be something like a form_closing event in window form application. I had searched a lot in Google but did't find any proper answer.
The application is use to generate digital output for a device
here is some code of timer event:
public static void Main()
{
// Some code related to the device like open device etc
// Then the timer
System.Threading.Timer stt =
new System.Threading.Timer(new TimerCallback(TimerProc), null, 1, 5000);
Thread.CurrentThread.Join();
}
static void TimerProc(Object stateInfo)
{
// It is my local method which will execute in time interval,
// uses to write value to the device
writeDigital(1, 0);
GC.Collect();
}
It is working fine when I run the code in debug mode, timer stops when I stop the program. But not working when I run the exe.
You could create and dispose it in Main() and pass it to any methods that require it?
private static void Main()
{
using (var timer = new System.Threading.Timer(TimerProc))
{
// Rest of code here...
}
}
More importantly, this line of code:
Thread.CurrentThread.Join();
will never return, because you are asking the current thread to wait for the current thread to terminate. Think about that for a moment... ;)
So your solution is probably to just remove that line of code.
All about GC.Collect();
Your stt object is used once and after that is pointed out to being removed and its memory reclaimed.
If you don't belive call stt.ToString(); at the end of main function, it will extend the stt live till the end of main function.
Solution(s)?
You can define the stt object as a static - it guarantees that it will be alive till the end of live of you program
recommended solution is to use GC.KeepAlive(stt); which you can call at the end of main function which will keep the stt away from destroying process.

Windows Form dies right after the constructor is done

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.

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";
}

Cross-threading forms in C# with Mono

I'm creating an application that uses .Net and Mono, it uses cross-threaded forms as I was having bad response from the child windows.
I created a test program with 2 forms: the first (form1) has a single button (button1) and the second (form2) is blank, code snippet below.
void openForm()
{
Form2 form2 = new Form2();
form2.ShowDialog();
}
private void button1_Click(object sender, EventArgs e)
{
Thread x = new Thread(openForm);
x.IsBackground = true;
x.Start();
}
This works fine in .Net, but with Mono, the first window will not gain focus when you click it (standard .ShowDialog() behaviour) rather than .Show() behaviour as .Net uses.
When I use .Show(), on .Net and Mono the window just flashes then disappears. If I put a 'MessageBox.Show()' after 'form2.Show()' it will stay open until you click OK.
Am I missing something in that code or does Mono just not support that? (I'm using Mono 2.8.1)
Thanks in advance, Adrian
EDIT: I realised I forgot 'x.IsBackground = true;' in the code above so child windows will close with the main window.
It's almost never the right thing to do in a Windows app to have more than one thread talk to one window or multiple windows which share the same message pump.
And it's rarely necessary to have more than one message pump.
The right way to do this is either to manually marshal everything back from your worker threads to your Window, using the 'Invoke' method, or use something like BackgroundWorker, which hides the details for you.
In summary:
Don't block the UI thread for time-consuming computation or I/O
Don't talk to the UI from more than one thread.
If you use Winforms controls, you shold "touch" the object always in main UI thread.
And at least - calling new Form.ShowDialog() in new thread does not make sense.
EDIT:
If you want easy work with Invoke/BeginInvoke you can use extension methods:
public static class ThreadingExtensions {
public static void SyncWithUI(this Control ctl, Action action) {
ctl.Invoke(action);
}
}
// usage:
void DoSomething( Form2 frm ) {
frm.SyncWithUI(()=>frm.Text = "Loading records ...");
// some time-consuming method
var records = GetDatabaseRecords();
frm.SyncWithUI(()=> {
foreach(var record in records) {
frm.AddRecord(record);
}
});
frm.SyncWithUI(()=>frm.Text = "Loading files ...");
// some other time-consuming method
var files = GetSomeFiles();
frm.SyncWithUI(()=>{
foreach(var file in files) {
frm.AddFile(file);
}
});
frm.SyncWithUI(()=>frm.Text = "Loading is complete.");
}

Categories

Resources