Threading Problem

I am trying to build a simple media player. I want it to show the current position in the file, so I decided to try out threading. When it starts the thread, though it never continues with the rest of the application. When I debug it I can see the values for timerbox.Text change but the changes are never reflected on the screen? I know I am probably doing something wrong with the thread, this is my first time using them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Media;
using System.Runtime.InteropServices;
using System.Threading;



namespace JoshPlayer
{
    
    public partial class MidiForm : Form
    {
        private String filename;
        private String filelength;
        private String currenttime;
        private bool go;
        delegate void playcallback();
        Thread playthread;

        public MidiForm(String f)
        {
            InitializeComponent();
            filename = f;
            this.Text = filename;
        }

        private void MidiForm_Load(object sender, EventArgs e)
        {
            doload();
           
        }

        private void doload()
        {
            this.Text = filename;
            open();
           
            playthread = new Thread(new ThreadStart(play));
            playthread.Start();
           
        }

        [DllImport("winmm.dll")]
        private static extern long mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);
        [DllImport("Winmm.dll")]
        private static extern long PlaySound(byte[] data, IntPtr hMod, UInt32 dwFlags);

        public void play()
        {
            StringBuilder temptimer=new StringBuilder();
            mciSendString("Play mymidi", null, 0, IntPtr.Zero);

            if (this.timelabel.InvokeRequired)
            {
                playcallback i = new playcallback(play);
                this.Invoke(i);
            }
            else
            {
                while (go)
                {
                    mciSendString("status mymidi position", temptimer, 128, IntPtr.Zero);
                    this.timelabel.Text = temptimer.ToString() + " \\ " + filelength;
                   // playthread.Suspend();
                }

            }
        }

        public void open()
        {
            StringBuilder temptime=new StringBuilder(128);
            mciSendString("open \"" + filename + "\" alias " + "mymidi", null, 0, IntPtr.Zero);
            mciSendString("set mymidi time format samples", null, 0, IntPtr.Zero);
            mciSendString("status mymidi length", temptime, 128, IntPtr.Zero);
            filelength = temptime.ToString();
            currenttime = "0";
            
            timelabel.Text = currenttime + " \\ " + filelength;
            go=true;
         
        }

  
        public void stop()
        {
            mciSendString("close mymidi", null, 0, IntPtr.Zero);
            go=false;
            playthread.Abort();
        }

        public void pause()
        {
            mciSendString("pause mymidi", null, 0, IntPtr.Zero);
            
        }

        private void MidiForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            stop();
        }

        private void stopbutton_Click(object sender, EventArgs e)
        {
            stop();
        }

        private void playbutton_Click(object sender, EventArgs e)
        {
            playthread = new Thread(new ThreadStart(play));
            playthread.Start();
        }

        private void pausebutton_Click(object sender, EventArgs e)
        {
            pause();
        }

       


    }
}
Most often this is is due to people not updating the value properly in their rendering loop, I can't confirm this because you haven't posted it. Have the thread function update a GLOBAL variable in a reoccuring loop, and have your rendering context watch that variable to determine the position of the cursor (or what ever you use). Keep in mind that since this variable has to be accessable by both functions in real time it should be global. Having your rendering context wait for the thread to return a value defeats the purpose of multithreading.

Also when it comes to exiting from your thread, Windows uses a "dirty" method to close them in "ExitThread(...)". They even say in the documentation for this function that us C++ writers should return from our thread rather then use their method in order to ensure that the proper destructors are called, this is to prevent run time memory leaks. Source: http://msdn.microsoft.com/en-us/library/ms682659(VS.85).aspx
Last edited on
I understand what you are saying here, but my issue is that the thread runs exclusively and won't let the program do anything else, so even if I do this it won't work. Once the thread starts I can't click any buttons or move anything even on the parent window. Perhaps I could email you the project if you're willing? It's got eleven files so far so I can't really post it
Seeing as it's 11 pages I guess you could PM it to me but hold off on that for now you might not need to.

When you say it won't let you do anything else that tells me that either the thread is spiking the CPU or else your main loop is waiting for it to return, which as I said above it should not be doing.

Could you post your loop and the section of code that involkes it? I don't need classes or custom header files to see an issue like the one I suspect.
Last edited on
I hope this is what you wanted. FYI this is a Windows Forms Application. Thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
namespace JoshPlayer
{
    partial class MidiForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.playbutton = new System.Windows.Forms.Button();
            this.stopbutton = new System.Windows.Forms.Button();
            this.pausebutton = new System.Windows.Forms.Button();
            this.fileprogress = new System.Windows.Forms.ProgressBar();
            this.timelabel = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // playbutton
            // 
            this.playbutton.Location = new System.Drawing.Point(12, 200);
            this.playbutton.Name = "playbutton";
            this.playbutton.Size = new System.Drawing.Size(75, 23);
            this.playbutton.TabIndex = 0;
            this.playbutton.Text = "PLAY";
            this.playbutton.UseVisualStyleBackColor = true;
            this.playbutton.Click += new System.EventHandler(this.playbutton_Click);
            // 
            // stopbutton
            // 
            this.stopbutton.Location = new System.Drawing.Point(103, 200);
            this.stopbutton.Name = "stopbutton";
            this.stopbutton.Size = new System.Drawing.Size(75, 23);
            this.stopbutton.TabIndex = 1;
            this.stopbutton.Text = "STOP";
            this.stopbutton.UseVisualStyleBackColor = true;
            this.stopbutton.Click += new System.EventHandler(this.stopbutton_Click);
            // 
            // pausebutton
            // 
            this.pausebutton.Location = new System.Drawing.Point(193, 200);
            this.pausebutton.Name = "pausebutton";
            this.pausebutton.Size = new System.Drawing.Size(75, 23);
            this.pausebutton.TabIndex = 2;
            this.pausebutton.Text = "PAUSE";
            this.pausebutton.UseVisualStyleBackColor = true;
            this.pausebutton.Click += new System.EventHandler(this.pausebutton_Click);
            // 
            // fileprogress
            // 
            this.fileprogress.Location = new System.Drawing.Point(12, 109);
            this.fileprogress.Name = "fileprogress";
            this.fileprogress.Size = new System.Drawing.Size(536, 23);
            this.fileprogress.TabIndex = 3;
            // 
            // timelabel
            // 
            this.timelabel.AutoSize = true;
            this.timelabel.Location = new System.Drawing.Point(435, 146);
            this.timelabel.Name = "timelabel";
            this.timelabel.Size = new System.Drawing.Size(0, 13);
            this.timelabel.TabIndex = 5;
            this.timelabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            this.timelabel.TextChanged += new System.EventHandler(this.timelabel_TextChanged);
            // 
            // MidiForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(571, 256);
            this.Controls.Add(this.timelabel);
            this.Controls.Add(this.fileprogress);
            this.Controls.Add(this.pausebutton);
            this.Controls.Add(this.stopbutton);
            this.Controls.Add(this.playbutton);
            this.Name = "MidiForm";
            this.Text = "MidiForm";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MidiForm_FormClosing);
            this.Load += new System.EventHandler(this.MidiForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button playbutton;
        private System.Windows.Forms.Button stopbutton;
        private System.Windows.Forms.Button pausebutton;
        private System.Windows.Forms.ProgressBar fileprogress;
        private System.Windows.Forms.Label timelabel;
    }
}


namespace JoshPlayer
{
    public class MidiPlayer
    {
        public MidiPlayer(String filename)
        {
            MidiForm theplayer = new MidiForm(filename);
            theplayer.Show();
        }
    }
}

namespace JoshPlayer
{
   public class player
    {
        private String filename;
        private String ext;
        
        public player(String instr)
        {
            filename = instr;
            bool go = true;
            for (int i = filename.Length; go; --i)
            {
                
                
                if (filename.ElementAt(i-1) == '.')
                {
                    ext = filename.Substring(i);
                    go = false;
                }
            }
            this.play();
        }

        public void play()
        {
            try
            {

            if (ext == "wav" || ext == "WAV")
            {
                new WavePlayer(filename);
            }

            else if (ext == "ico" || ext == "ICO" || ext == "bmp" || ext == "BMP" || ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG" || ext == "gif" || ext == "GIF" || ext == "png" || ext == "PNG")
            {
                new BitmapPlayer(filename);
            }
            else if (ext == "TXT" || ext == "txt"||ext=="cs"||ext=="c"||ext=="html"||ext=="htm"||ext=="h"||ext=="cpp"||ext=="xaml")
            {
                new TextPlayer(filename);
            }
            else if (ext == "MID" || ext == "mid")
            {
                new MidiPlayer(filename);
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("No Decoder for file!", filename);
            }
            }
            catch (System.IO.FileNotFoundException)
            {
                System.Windows.Forms.MessageBox.Show("File No Longer Exists!");
            }


           
        }

    }
}

Ok, I'm not terribly familiar with the way Windows Forums works, but this is close enough to C++ for me to know what is going on. I see that you're relying on this template to do the multi-threading for you which is fine, that's what it's there for.

I don't see definitions for your functions "WavePlayer(), BitmapPlayer()" and so on, the only search results that come back for those on MSDN are for C# in which case I'm out of my element.

EDIT: With out knowing what the functions that you are envoking do, I cannot tell if they launch the new thread or if you should launch them from a new thread.
Last edited on
All the players are classes I wrote myself to handle the different file types. The only one I am having a problem with is the midiplayer. Joshplayer.cs is the main form, it manages the playlist. When you select a file and hit play it creates an instance of player, then invokes player.play(filename). Which selects the proper class and passes the file name to it. midiplayer creates the form MidiForm which contains the MCI code and the thread I am having issues with. Maybe there is an easier way to update the progress indicator without using a thread?
An "Easier" way would be to wait for the progress indicators function to return with the new position but it would take all of the life out of this project.
Yeah, and I really do need to learn the threading cause it seems to be pretty important.
Important? Not so much, you can get by on a lot of stuff without it. But it is insanly useful and speeds programs up a lot more then I first thought.
yeah I used parallel on a geocoding program I did last week, took the run time from 30 minutes to 5.
I had a VBS script to query the status of something on 200+ machines, the time to finish was something like over an hour and a half. Re wrote it in C++ with multi-threading from SFML and knocked that down to (network_timeout * 3) seconds, which is because I limited the number of threads executing at once.
Last edited on
What I ended up doing was to have a positionupdate() method that calls Invalidate() which calls Paint() where I added a call to positionupdate(); lol, take that Microsoft!
Topic archived. No new replies allowed.