Method doesn't work when executed in a thread, but works otherwise - C#
I am a beginner in C# and I want to do the following:
Thread t = new Thread(new ThreadStart(this.play));
t.Start();
This is the method play
:
private void play()
{
playSong("path\\to\\song.mp3");
}
private static void playSong(String path)
{
mciSendString("open \"" + path + "\" type mpegvideo alias MediaFile", null, 0, IntPtr.Zero);
mciSendString("play MediaFile", null, 0, IntPtr.Zero);
}
[DllImport("winmm.dll")]
private static extern long mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);
When I execute the method just like this: play();
, without using threading, the song plays perfectly, but it doesn't play if I do like in the first code excerpt.
I suspect that it happens because I need to call the mciSendString
method in the main thread. If so, will someone tell me how can I do that? Or tell me how can I get it working using a thread?
I appreciate your help.
-- Edited from here --
I don't know if I should post the code, cause it is kind of large, but I will reduce it, to show what I want to do:
I have a windows forms application that starts a socket listening on a port when it begins, which works as a server. This server receives as requests paths to songs that it'll play. If I use the play
method as shown below, the form crashes, but the application still listens to my requests. I understand that I have to do it in the background, so that the form controls won't crash, I just don't know the better way to do it.
//Constructor
public Main()
{
InitializeComponent();
play();
}
play()
is something like this:
private void play()
{
//Does the socket initialization
do
{
//...
while (true)
开发者_如何学C {
//Reads text from client
//The text contains a string, indicating the path to the song.
byte[] bytesFrom = new byte[10025];
networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
string dataFromClient = System.Text.Encoding.Default.GetString(bytesFrom);
//Plays the song from the received path
playSong(dataFromClient);
//...Do other stuff
}
} while (_serverOn);
//Closes the socket connection
}
private static void playSong(String path)
{
mciSendString("open \"" + path + "\" type mpegvideo alias MediaFile", null, 0, IntPtr.Zero);
mciSendString("play MediaFile", null, 0, IntPtr.Zero);
}
I had similar problem a while back... Pain in the butt.
You are doing cross thread operations, that is not safe. That is the reason why it crashes. You are absolutely right about where you problem is.
Here is msdn documentation on this thread operations http://msdn.microsoft.com/en-us/library/ms171728.aspx
You need to add delegate definition for mciSendString => mciSendStringCallBack
delegate void mciSendStringCallBack(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);
In private static void playSong(String path) you need to check if InvokeRequired, if it is you need to instantiate callback and invoke it.
mciSendStringCallBack method = new mciSendStringCallBack(....);
this.Invoke(method , new object[] { .....});
Look through the example on the msdn site, they do a good job demonstrating who it works.
--
You might want to try using background worker, it gives you multi threaded way of doing things, easier to work with.
I suspect that MCI functions may needs a Message Loop running. Besides, MCI requests are unblocking so your thread will die once you invoke the MCI operation. You need to keep running the message loop on that thread (using Application.Run) till MCI finishes (you will know that by requesting the callback by passing the window handle). See this article that illustrates how it is done.
Edit: Perhaps a hidden form launched over new thread can reduce the work do be done. But you still need to receive callback from MCI to terminate the thread.
The accepted answer above still runs mciSendString
on the UI thread. I needed to move it off the UI thread because it was taking too long (>200ms to save a file), so I came up with this solution:
var t = new Thread(Dispatcher.Run);
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
var d = Dispatcher.FromThread(t)
d.InvokeAsync(() => mciSendString(command, null, 0, IntPtr.Zero));
精彩评论