Create Graph in another Thread in C#

Hi,

Hopefully a simple question for some but i'm stumped.

i have a winforms app in c#. i have my buildGraph in another class. when i declare this class i am parsing a few variables like the form handle and picturebox handle, the filename and such.

Everything is working but i'm finding that the UI hangs until the graph is built. i would like to show my wait animation while the graph is being built but can't workout how to build the graph on another thread.

I haven't been able to parse parameters to my buildgraph class via thread th = new thread(new threadstart(.......)); for example.

any help or pointers would be great.
drewbuckleyAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

anarki_jimbelSenior DeveloperCommented:
Not quite clear: "I haven't been able to parse parameters to my buildgraph..."

Could you pls give more details?

Probably, you need to use parameterizedThreadStart:

http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx
0
drewbuckleyAuthor Commented:
Sorry about that,

When i build my graph from my main form i am doing this,

streamPlayer = new streamPlayer(this, picVideo, IPAddress, IPPort, fileName);

I've been successful in the past with threads without parameters. i'll have a look at parameterizedthreadstart, it sound familiar in that i think i looked at it before. i'll check it again.
0
lucky_jamesCommented:
You can multiple techniques, but yes, most elegant seems to be parameterizedthreadstart.
check out:
http://www.yoda.arachsys.com/csharp/threads/parameters.shtml

You can pass one param only as parameter. So, you can look at using the objects or structures for creating new threads.
0
Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

lucky_jamesCommented:
0
drewbuckleyAuthor Commented:
Thanks again,

I've been trying this but having issues.

i need to create a new class of my streamplayer and then i am calling the buildgraph from the initializer of the class.

i tried to change the params from the streamplayer( params) to the buildGraph( params) and then tried to create a new streamPlayer and then use the paramthread.

Could not get this to work:

streamPlayer = new streamPlayer();
Thread th = new Thread( new ParameterizedThreadStart(streamPlayer.BuildGraph));
MainForm mf = new MainForm();
mf.Params = ...........
th.Start(mf);

i have the problem with the second line.

0
lucky_jamesCommented:
I am not getting.....code looks fine to me....please put more clarity on the issue.....
what is the problem you are getting with the second line? exception? if yes, what is it?

0
drewbuckleyAuthor Commented:
Sorry for the late reply, just got home from work.

i'm getting:

No overload for buildGraph matches delegate System.Threading.ParameterizedThreadStart (CS0123)
0
drewbuckleyAuthor Commented:
OK, some progress but still problems

i changed the params at the buildGraph end to object and created a List<> in the main form. after dealing with all the crossthreads, it now compiles and plays but now there's no audio or video.
0
drewbuckleyAuthor Commented:
The problem seems to be that i can't parse the picturebox handle to the buildGraph without getting a crossthread. i've even tried to create a picturebox within the buildGraph and i still get a crossthread.

I must be doing something wrong?
0
drewbuckleyAuthor Commented:
streamPlayer = new streamPlayer();
Thread th = new Thread( new ParameterizedThreadStart(streamPlayer.BuildGraph));
MainForm mf = new MainForm();
mf.Params = ...........
th.Start(mf);

the line "MainForm mf = new MainForm(); is creating a new MainForm now that i can't render the video to the MainForm picturebox control. still tried to create a picturebox withing the buildGraph and the graph is building (can hear audio and other parts are working) but still just see the original MainForm :(

about to give up i think
0
lucky_jamesCommented:
dont give up so easily. :)

you are creating the new form. try making the existing form static and then see......

could you post some sample code which I can try out here and see what is going wrong?
0
lucky_jamesCommented:
>>No overload for buildGraph matches delegate System.Threading.ParameterizedThreadStart (CS0123)
what is the signature of the buildgraph method?
ensure that it is public void buildgraph(object data )
0
drewbuckleyAuthor Commented:
Thanks lucky james i appreciate your help. i feel like giving up as i've had a hard day at work. i won't though :)

Here is the code i'm using:

This is from the MainForm that's being called from another class:

            static MainForm mf;
            ArrayList param = new ArrayList();
            public string ip = "";
            public string port = "";
            public string thip = "";
            public string thport = "";
            public string div = "";
            public string filename = "";

            public delegate void startStreamVideoDelegate(string path);
            public void startStreamVideo(string path)
            {    
                  try
                  {
                        if (this.InvokeRequired)
                        {        
                              this.Invoke(new startStreamVideoDelegate(this.startStreamVideo), path);  
                        }    
                        else    
                        {        
                              Application.DoEvents();
                              tmrDuration.Enabled = false;
                          canCountDown = false;
                              if (streamDesktop != null)
                          {
                                streamDesktop.CloseGSSFInterfaces();
                                streamDesktop = null;
                          }
                          if (streamPlayer != null)
                          {
                                streamPlayer.CloseStreamPlayerInterfaces();
                                streamPlayer = null;
                          }
                          streamPlayer = new LaunchVue_Display_WinForm.RTP_Streamers.StreamPlayer();
                          mf = new MainForm();
                          mf.ip = getIPAddress();
                          mf.port = getIPPort().ToString();
                          mf.thip = getThumbIPAddress();
                          mf.thport = getThumbPort().ToString();
                          mf.div = getDisplayNumber().ToString();
                          mf.filename = path;
                          Thread th = new Thread(new ParameterizedThreadStart(streamPlayer.buildSendGraph));
                          th.Start(mf);
                          
                          tmrDuration.Enabled = true;
                          canCountDown = true;
                        }
                  }
                  catch(Exception ex)
                  {
                    MessageBox.Show(ex.Message + " ;; " + ex.StackTrace);
                  }
            }

This is the buildGraph (streamPlayer) part;

            public void buildSendGraph(object param)
            {
                  main = param as MainForm;
                  //createViewer();
                  hostControl = main.picVideo;// picViewer;
                  rtpMulticastAddress = main.ip;
                  rtpMulticastPort = main.port;
                  rtpDeviceNumber = main.div;
                  rtpThumbnailAddress = main.thip;
                  rtpThumbnailPort = main.thport;
                  videoFileName = main.filename;
                  JoinRtpSession();
                  JoinRtpSessionThumb();
                  isCompleted = false;
                  me = null;
                  InitInterfaces();
                int hr = 0;
               
                string ext = System.IO.Path.GetExtension(videoFileName);
               
                if (ext == ".avi")
                {
                      isAVI = true;
                }
                else
                {
                      isAVI = false;
                }

               // me = fg as IMediaEventEx;

                  //hr = me.SetNotifyWindow( hostControl.Handle , WM_GRAPHNOTIFY, IntPtr.Zero );
                //DsError.ThrowExceptionForHR( hr );
            
                IBaseFilter sourceFilter;
            hr = gb.AddSourceFilter(videoFileName, "Source", out sourceFilter);
            DsError.ThrowExceptionForHR(hr);

            ICaptureGraphBuilder2 capGraph = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            capGraph.SetFiltergraph(gb);
           
              //create and config the samplegrabber
              ms = (ISampleGrabber) new SampleGrabber();
              IBaseFilter baseGrabFilter = (IBaseFilter) ms;
              
              configureSampleGrabber(ms);
                  
              string strHaali = @"@device:sw:{083863F1-70DE-11D0-BD40-00A0C911CE86}\{564FD788-86C9-4444-971E-CC4A243DA150}";
                  IBaseFilter haalisplitter = Marshal.BindToMoniker(strHaali) as IBaseFilter;
                  hr = gb.AddFilter(haalisplitter, "Haali Splitter");
                  DsError.ThrowExceptionForHR( hr );
              
                  string stFFDShow = @"@device:sw:{083863F1-70DE-11D0-BD40-00A0C911CE86}\{04FE9017-F873-410E-871E-AB91661A4EF7}";
                  IBaseFilter FFDShow = Marshal.BindToMoniker(stFFDShow) as IBaseFilter;
                  hr = gb.AddFilter(FFDShow, "FFDShow");
                  DsError.ThrowExceptionForHR( hr );
              
            NullRenderer nullrenderer = new NullRenderer();
            hr = gb.AddFilter((IBaseFilter)nullrenderer, "Null Renderer");
            DsError.ThrowExceptionForHR(hr);
              
            hr = gb.AddFilter(baseGrabFilter, "SampleGrabber");
              DsError.ThrowExceptionForHR(hr);
           
              IPin pinOut;
              IPin pinIn;
              
             // if (isAVI)
             // {
               //     pinOut = DsFindPin.ByDirection(sourceFilter, PinDirection.Output, 0);
             //     pinIn = DsFindPin.ByDirection(haalisplitter, PinDirection.Input, 0);
             //     hr = gb.Connect(pinOut, pinIn);
             //     DsError.ThrowExceptionForHR(hr);
             //    
             //     pinOut = DsFindPin.ByDirection(haalisplitter, PinDirection.Output, 0);
             //     pinIn = DsFindPin.ByDirection(baseGrabFilter, PinDirection.Input, 0);
             //     hr = gb.Connect(pinOut, pinIn);
             //     DsError.ThrowExceptionForHR(hr);
             // }
             // else
             // {
                    pinOut = DsFindPin.ByDirection(sourceFilter, PinDirection.Output, 0);
                  pinIn = DsFindPin.ByDirection(baseGrabFilter, PinDirection.Input, 0);
                  hr = gb.Connect(pinOut, pinIn);
                  DsError.ThrowExceptionForHR(hr);
             // }
              
            pinOut = DsFindPin.ByDirection(baseGrabFilter, PinDirection.Output, 0);
            pinIn = DsFindPin.ByDirection((IBaseFilter)nullrenderer, PinDirection.Input, 0);
            hr = gb.Connect(pinOut, pinIn);
            DsError.ThrowExceptionForHR(hr);
                       
                   Marshal.ReleaseComObject( pinOut );
                   Marshal.ReleaseComObject( pinIn );
                    
                 saveSizeInfo(ms);
                 listGraphFilters();
              
            // Add and Configure the GSSF Filter
            ipsb = (IBaseFilter)new GenericSampleSourceFilter();

            try
            {
                // Get the pin from the filter so we can configure it
                IPin ipin = DsFindPin.ByDirection(ipsb, PinDirection.Output, 0);

                try
                {
                    // Configure the pin using the provided BitmapInfo
                    ConfigurePusher((IGenericSampleConfig)ipin);
                }
                finally
                {
                    Marshal.ReleaseComObject(ipin);
                }

                // Add the filter to the graph
                hr = gb.AddFilter(ipsb, "GenericSampleSourceFilter");
                Marshal.ThrowExceptionForHR( hr );

                renderer = new VideoRenderer();
                hr = gb.AddFilter((IBaseFilter)renderer, "MyRenderer");
                   
                IPin pinGSSF = DsFindPin.ByDirection(ipsb, PinDirection.Output, 0);
                hr = gb.Render(pinGSSF);
                Marshal.ReleaseComObject(pinGSSF);
            }
            finally
            {
               // Marshal.ReleaseComObject(ipsb);
            }

            // Configure the Video Window
            videoWindow = gb as IVideoWindow;
            hr = videoWindow.put_Owner( hostControl.Handle );
              if (hr >= 0) // If there is video
              {
                  // Set the window style
                  hr = videoWindow.put_WindowStyle( (WindowStyle.Child | WindowStyle.ClipChildren | WindowStyle.ClipSiblings) );
                  DsError.ThrowExceptionForHR( hr );
      
                  // Make the window visible
                  hr = videoWindow.put_Visible( OABool.True );
                  DsError.ThrowExceptionForHR( hr );

                  // Set the Message Drain for feeback
                  hr = videoWindow.put_MessageDrain( hostControl.Handle );
                  DsError.ThrowExceptionForHR( hr );
      
                  // Position the playing location
                  Rectangle rc = hostControl.ClientRectangle;
                  hr = videoWindow.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
                  DsError.ThrowExceptionForHR( hr );
              }
              
            setupDecoder();
              
              hr = mc.Run();
              DsError.ThrowExceptionForHR( hr );
              m_State = GraphState.Running;

              main.picVideo.Visible = true;
            }

The graph is now building ok, i can hear the audio. it just seams like it now has nothing to do with the original main form.
0
lucky_jamesCommented:
-> Do you want to create another form? or you want to reuse already displayed form?
->              hostControl = main.picVideo;// picViewer;
                  rtpMulticastAddress = main.ip;
                  rtpMulticastPort = main.port;
                  rtpDeviceNumber = main.div;
                  rtpThumbnailAddress = main.thip;
                  rtpThumbnailPort = main.thport;
                  videoFileName = main.filename;
You are passing the object to pass the above data. This data can be passed as a structure.
The error you are getting should end here. Because it is reagrding the data passing to the delegate.

-> I tried creating the sample app, the code is attached herewith. It works fine at my machine.

namespace WindowsApplication2
{
//this class is on lines of your streamPlayer class
    public class test
    {
//this method will be called
        public static void Param_test(object _obj)
        {
            Form1 _obj_form = (Form1)_obj;
            MessageBox.Show(_obj_form._str);
        }
    }
    public partial class Form1 : Form
    {
        public String _str = "this is here";
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
           Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
           thread.Start(this);
        }
    }
}

Open in new window

0
drewbuckleyAuthor Commented:
Thanks very much lucky james.

I can now see the video but i was still having issues with a crossthread on the mainform picturebox control. i enclosed the buildGraph in the other class into a delegate and it now seems to work.

is this right or was there something else i needed to do?
0
drewbuckleyAuthor Commented:
Sorry to hastle you,

I've found that if i enclose the buildGraph in a delegate, whilst it works it still locks the UI. If i don't enclose it in a delegate, it doesn't lock the UI but i can't get past the MainForm crossthread and MainForm PictureBox control crossthead issues.
0
drewbuckleyAuthor Commented:
I just made a complete app with your code and found that it work while trying to reference a string to the messagebox but if i try to access a control or the form like _obj_form.Text = _obj._str; i get the same crossthread that i'm getting in my app.

I just can't seem to get past this crossthread thing. i found that with the delegates mention above, it actually puts the buildgraph back onto the main thread. that's obviously why it works but i still get the same UI lockup that i got before.

So now i guess my only remaining issue with using ParameterizedThreadStart is the crossthread issues.

Once again i appreciate your time and efforts.
0
lucky_jamesCommented:
ah, looks like now I got what is happening out there. The design needs to be modified, you are trying to do UI operations using the new thread.
Use event based mechanism or thread signalling to tell  UI thread to do this job. in short, use control.invoke.

check out:
http://msdn.microsoft.com/en-us/magazine/cc300429.aspx
http://www.ezcodesample.com/workerGUI/workerGUI_article.html

let me know if you need my help.
0
lucky_jamesCommented:
just made a complete app with your code and found that it work while trying to reference a string to the messagebox but if i try to access a control or the form like _obj_form.Text = _obj._str; i get the same crossthread that i'm getting in my app.

btw, this works on my machine, can you post of the test app in which you are facing the issue:

public partial class Form1 : Form
    {
        public String _str = String.Empty;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _str = "test";
            Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
            thread.Start(this);
        }
        public class test
        {
            public static void Param_test(object _obj)
            {
                Form1 _obj_form = (Form1)_obj;
                MessageBox.Show(_obj_form._str);
                _obj_form.textBox1.Text = _obj_form._str;
            }
        }
    }

Open in new window

0
drewbuckleyAuthor Commented:
Thaks for that,

This is what i have from your example. i still get a crossthread error.

using System:
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

namespace Params_Thread_Test
{
    public partial class MainForm : Form
    public string _str = String.Empty;

    public MainForm()
    {
        InitializeComponent();
    }

    void MainFormLoad(object sender, EventArgs e)
    {
        _str = "test";
        Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
        thread.Start(this);
    }
    }
    public class test
    {
        public static void Param_test(object _obj)
        {
            MainForm _obj_form = (MainForm)_obj;
            _obj_form.label1.Text = _obj_form._str;      (crossthread here)
        }
    }
}


I'll piece together the code i'm using as there are a fair few hundred lines. may take up a bit of space here though. is that OK?
0
lucky_jamesCommented:
unfortunately, this code works on my machine.....

did you try using the invoke method in the test app? refer to my previous post( ID: 31836624).
public partial class Form1 : Form
    {
        public String _str = String.Empty;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _str = "test";
            Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
            thread.Start(this);
        }
        public void SetText() 
        {
            MessageBox.Show("here");
            this.textBox1.Text = this._str;
            this.ShowDialog();
        }

        public class test
        {
            public static void Param_test(object _obj)
            {
                Form1 _obj_form = (Form1)_obj;
                _obj_form.textBox1.Text = _obj_form._str;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            _str = "test";
            Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
            thread.Start(this);
        }
    }

Open in new window

0
drewbuckleyAuthor Commented:
That's amazing. I have tried 3 machines and i get the same result. 2 machines with XP and 1 with windows 7.

i'll have to read up for awhile on your other suggestion as i know nothing about it.

thanks again, i'll post back once i have learnt.
0
lucky_jamesCommented:
hey I noticed one crossthread happening.....but not with this code.....if you create another form instance inside the new thread and do some UI operations then it goes into issue.....

Could you check and revert if the below code is working. I have created the new instance of form as well as did some UI operation in the UI thread itself and it fixes the above said issue in this post.

Hope it helps.
public partial class Form1 : Form
    {
        public String _str = String.Empty;
        static bool _launched = false;
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _str = "test";
            Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
            thread.Start(this);
        }
        public void SetText() 
        {
            MessageBox.Show("here");
            Form1 _obj2 = new Form1();
            _obj2.textBox1.Text = this._str;
            _obj2.ShowDialog();
        }

        public class test
        {
            public static void Param_test(object _obj)
            {
                if (!_launched)
                {
                    _launched = true;
                    Form1 _obj_form = (Form1)_obj;
                    //_obj_form.textBox1.Text = _obj_form._str;
                    _obj_form.Invoke(new MethodInvoker(_obj_form.SetText));
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            _str = "test";
            Thread thread = new Thread(new ParameterizedThreadStart(test.Param_test));
            thread.Start(this);
        }
    }

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
lucky_jamesCommented:
>>i'll have to read up for awhile on your other suggestion as i know nothing about it.
I have used invoke in my post above....hope this will help you in understanding it...alongwith the link I sent. But first confirm whether the code above works for you or not. If it works thenwe know the all we need to do is to use invoke and let the UI thread work for us.
0
drewbuckleyAuthor Commented:
Sorry about that, i just thought it was a copy of what i added.

Ok i tried that i get the message box and when i click on the message box i get another form with the word "test" in my label.

It is obviously doing something but i must admit (pardon my lack of knowledge) i'm not sure how i would use that in my situation. (how to translate it i mean)

You've been great and i appreciate your time. this is the closest i have come so far though
0
drewbuckleyAuthor Commented:
Sorry again,

Posted too early, i tried this.

public void SetText()
{
    label1.Text = this._str;
}

and it worked with the _obj_form.Invoke(new MethodInvoker.......

I'll now try and see if this works within my buildGraph class to get the picturebox handle and set it to visible.
0
lucky_jamesCommented:
no problem. I was about to reply with the further explanations. I think you got it now. :)

Let me know if need my help.

James
0
drewbuckleyAuthor Commented:
Thanks,

Here is what i have done so far.

i call this from the buildGraph class:
this.main = (MainForm)form;
main.Invoke(new MethodInvoker(main.getPicVideoHandle)); // this.main.picVideo;

this is the function on the main form:
            public void getPicVideoHandle()
            {
                  streamPlayer.hostControl = picVideo;
            }

now i am getting the pictubox as a control and can to my ConfigureVideoWindow and i can see the video but it still locks the app up while constructing the graph.

i actually have no hair but if i did ............................................

Sorry to take up your time when i'm obviously doing something wrong.
0
lucky_jamesCommented:
construction has to be done by the UI thread. Thats one limitation I feel.
At the same time, can you reduce the load of the activites the UI thread is doing over the app.
At the best, by certan modifications the design can be modified to the locking time where UI element does "pure UI" job. Means rest of the operations related to db , file i/o and even streaming (looks like your development is realted to video streaming) can be taken care by background thread(s).

Look into the following link:
http://msdn.microsoft.com/en-us/magazine/cc300429.aspx#S4

i have attached the figures from that link herewith for your reference.
Untitled.jpg
0
drewbuckleyAuthor Commented:
<< construction has to be done by the UI thread. Thats one limitation I feel.

I can see clearly now the rain has gone. I thought i was an idiot or something. The only thing i'm wanting to do on the UI while the graph is being built is show an animated gif that shows that the video is loading. that's all. When the graph is being built, the gif stops it's animation. i've even tried showing this gif in it's own thread away from everything without much luck.

Given all of this, is there a way i can have the gif run while the graph is being built?

BTW, if nothing else i have learnt a lot from you and will award the points.
0
drewbuckleyAuthor Commented:
Hi lucky_james,

can you let me know if it's best i close this one now (award you the points of course) and open another question on how to keep an animated gif running while building a graph?
0
lucky_jamesCommented:
thats fine, its your choice. :)
0
drewbuckleyAuthor Commented:
I meant in that if you had an answer for the gif animation stopping. if not i'll close and award.
0
lucky_jamesCommented:
I am working on it....may take some time. you can choose to wait for atleast next one hour.
0
lucky_jamesCommented:
stuck in some other work....doesnt look like it may take some more time to look at it. As an immediate solution:
either
you can run the GIF and keep track of the video. Once the video is ready, close the gif and invalidate the graph.
OR
you can invalidate both Gif and graph in synchronous manner.

Hope it might have helped you.
0
drewbuckleyAuthor Commented:
Thanks lucky_james i appreciate both your time and effort i'll close and award and open another question
0
drewbuckleyAuthor Commented:
This user was very patient and helpfull
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Game Programming

From novice to tech pro — start learning today.