c# - WebBrowser Control Slideshow Hanging after Running for Extended Time -


i'm new developer , 1 has me stumped.

my winforms application slideshow websites rotates through list of urls, fading-in/out on each transition using second form "curtain". it's meant run indefinite period of time consistently hangs on transition after running couple of days.

form1:

httpwebresponse response = null; list<slide.doc> slist = null;  bool repeatslideshow = true; bool pageloaded = false;  double curtainanimstep = 0.05; int errorcount = 0;  public form1() {     initializecomponent();      cursorshown = false;      this.visible = true;     this.formborderstyle = formborderstyle.none;     this.windowstate = formwindowstate.maximized;      webbrowser1.scrollbarsenabled = false;     webbrowser1.scripterrorssuppressed = true;      slideshow(environment, channel); }   public void slideshow(string environment, string channel)     {                    while (repeatslideshow)         {             try             {                 slist = slide.convertjsontoslide(slide.getparams(environment, channel));             }             catch (exception)             {                 form2 curtain = new form2(curtainanimstep);                 curtain.show();                 waitforfade(curtain, 1);                 displayerror();                 raisecurtain(curtain, curtainanimstep);                 waitforfade(curtain, 0);                 curtain.dispose();                 waitaround(30);                 continue;             }              foreach (slide.doc s in slist)             {                  bool slidewasdisplayed = false;                  form2 curtain = new form2(curtainanimstep);                 curtain.show();                 waitforfade(curtain, 1);                 slidewasdisplayed = displayslide(s.url_text);                 if (slidewasdisplayed == false)                 {                     webbrowser1.documenttext = "<html><body style='background-color: #1c1c1c;'></body></html>";                     redrawpage();                 }                 raisecurtain(curtain, curtainanimstep);                 waitforfade(curtain, 0);                 curtain.dispose();                 if (slidewasdisplayed == true)                 {                     waitaround(s.display_sec);                 }              }              if (errorcount == slist.count)             {                 form2 curtain = new form2(curtainanimstep);                 curtain.show();                 waitforfade(curtain, 1);                 displayerror();                 raisecurtain(curtain, curtainanimstep);                 waitforfade(curtain, 0);                 curtain.dispose();                 waitaround(30);                               }              errorcount = 0;              utilities.web.webbrowserhelper.webbrowserhelper.clearcache();         }     }      public bool displayslide(string slideurl)     {         httpwebrequest request = (httpwebrequest)webrequest.create(slideurl);         request.timeout = 1000;         try         {             response = (httpwebresponse)request.getresponse();             webbrowser1.navigate(slideurl);             redrawpage();             response.dispose();             return true;         }         catch (webexception)         {             errorcount++;             return false;         }     }      public void redrawpage()     {         while (pageloaded == false)         {             application.doevents();         }         webbrowser1.invalidate();         application.doevents();         pageloaded = false;     }      public void raisecurtain(form curtain, double curtainanimstep)     {         while (curtain.opacity > 0)         {             curtain.opacity -= curtainanimstep;             application.doevents();             system.threading.thread.sleep(10); // how long between shifts in opacity (not interval between slides)         }     }      public void waitaround(int duration)     {         datetime dt2 = datetime.now;         while (dt2.addseconds(duration) > datetime.now)         {             application.doevents();         }     }      public void waitforfade(form curtain, int finalopacity)     {         while (curtain.opacity != finalopacity)         {             datetime dt = datetime.now;             dt = dt.addseconds(1);             while (dt > datetime.now)             {                 application.doevents();             }         }     }      private void webbrowser1_documentcompleted(object sender, webbrowserdocumentcompletedeventargs e)     {         pageloaded = true;     } 

form2:

public form2(double animstep)         {             initializecomponent();             this.animstep = animstep;         }          public double animstep { get; set; }          private async void form2_load(object sender, eventargs e)         {             while (opacity < 1.0)             {                 await task.delay(10);                 opacity += animstep;             }             opacity = 1;         } 

i've been working on long time, have admit genuinely don't know should looking @ point.

could use of application.doevents responsible? leaving them out breaks application, can't figure out alternative appproach.

looking @ code (and indicated noseratio) 1 of things advice rid of need doevents calls. remember in windows there dedicated ui thread used update controls on form. doing lot of stuff (in loops, calling bunch of methods) on same ui thread windows controls depends on cooperation share time them, hence calls doevents.

i'm going use backgroundworker , timer , waithandle schedule commands update ui background thread. little needed on ui thread.

form load

form1 have webbrowsercontrol , backgroundworker. queue hold commands needs executed. load event start backgroundworker.

    form2 frm2 = new form2();     queue<icommandexecutor> commands = new queue<icommandexecutor>();      private void form1_load(object sender, eventargs e)     {         frm2.show();         frm2.bringtofront();         commands.enqueue(new loadslideshow(this, frm2, commands));         backgroundworker1.runworkerasync();     } 

backgroundworker

the backgroundworker dowork event engine runs on it's own background thread. runs long there commands found in queue. after fetching command it's execute method fired. if command supports disposing dispose method called , command processed , start on again.

    private void backgroundworker1_dowork(object sender, doworkeventargs e)     {                         while(commands.count>0)         {             icommandexecutor cmd = commands.dequeue();             try             {                 cmd.execute();                 // dispose if can                 idisposable sync = cmd idisposable;                 if (sync != null)                 {                     sync.dispose();                 }             }             catch(exception exp)             {                 // add commands here                 trace.writeline("error" + exp.message);             }         }        } 

commands

there standard interface available implement command pattern. icommandexecutor has single method, execute. can create different classes implement interface. each class holds own state , references , can simple timer of complex loading new batch of urls show.

    public class showslide:icommandexecutor     {         string url;         form1 form;         autoresetevent done = new autoresetevent(false);         public showslide(form1 form, string url)         {             this.url = url;             this.form = form;         }          public void execute()         {             // if not on ui thread...             if (form.invokerequired)             {                 // ... switch it...                 form.invoke(new methodinvoker(execute));             }             else             {                 // .. on ui thread                 // reused code                 form.displayslide(url);             }         }     } 

here timer. notice how timer class used , timerdone waithandle make backgroundthread continue work if timer has finished when dispose called.

    public class waitforseconds: icommandexecutor, idisposable     {         int ms;         system.threading.timer timer;         manualresetevent timerdone = new manualresetevent(false);         public waitforseconds(int secs)         {             this.ms = secs * 1000;         }          public void execute()         {             // use timer             timer = new system.threading.timer(                (state) => timerdone.set() // signal done                );             timerdone.reset();             timer.change(this.ms, timeout.infinite);         }          public void dispose()         {             timerdone.waitone();             timerdone.dispose();             timer.dispose();         }     } 

to setup commands in correct order use following command class implememntation takes command queue, form1 , form2 parameters on constructor. execute command loads url's fed webbrowser control. each url adds commands needs executed queue. @ end this instance added queue means class used again if commands have been processed. queue there never empty.

    public class loadslideshow: icommandexecutor     {         readonly queue<icommandexecutor> commands;         readonly form1 form;         readonly form2 form2;         public loadslideshow(form1 form, form2 form2, queue<icommandexecutor> cmds)         {             this.form = form;             commands = cmds;             this.form2 = form2;         }          public void execute()         {             var list = slide.convertjsontoslide(null);             foreach (var slide in list)             {                 commands.enqueue(new showslide(form, slide.url_text));                 commands.enqueue(new waitforseconds(1));                 //commands.enqueue(new lowercurtain(form2));                 commands.enqueue(new waitforseconds(slide.display_sec));                 //commands.enqueue(new raisecurtain(form2));             }             commands.enqueue(this);          }     } 

this there is needed basic slideshow going.

for called curtain going similar form2 i'll use backgroundworker_progress event well.

form2 curtain

form2 act curtain changing it's opacity in loop. has it's own backgroundworker:

    manualresetevent statechange = new manualresetevent(false);     public manualresetevent statechangedone = new manualresetevent(false);      private void form2_load(object sender, eventargs e)     {         backgroundworker1.runworkerasync();       }      private void backgroundworker1_dowork(object sender, doworkeventargs e)     {         while(statechange.waitone())         {             statechange.reset();             var progressdone = new autoresetevent(false);             int progress = 0;             using(var timer = new system.threading.timer(_=>                 {                      backgroundworker1.reportprogress(progress);                     progress += 2;                     if (progress>=100)                     {                         progressdone.set();                     }                 }, null, 0, 25))             {                 progressdone.waitone();             }             statechangedone.set();         }     } 

the background worker calls resportprogress int indicating prpgress. causes progresschanged event raised. based on state curtain needs in, calculate correct value opacity.

    private void backgroundworker1_progresschanged(object sender, progresschangedeventargs e)     {         switch(state)         {             case curtain.up:                 this.opacity = e.progresspercentage / 100.0;                 break;             case curtain.down:                 this.opacity = (100 - e.progresspercentage) / 100.0;                 break;         }     } 

to started create 2 public methods called , down:

    enum curtain     {         up,         down     }      curtain state;      public void up()     {         state = curtain.up;         statechange.set();         statechangedone.reset();     }      public void down()     {         state = curtain.down;         statechange.set();         statechangedone.reset();     } 

with left implementation of command classes added command queue , handled background worker of form1:

    public class raisecurtain:icommandexecutor, idisposable     {         readonly form2 form2;          public raisecurtain( form2 form2)         {             this.form2 = form2;         }          public void execute()         {             if (form2.invokerequired)             {                 form2.invoke(new methodinvoker(execute));             }             else             {                 form2.bringtofront();                 form2.up();             }         }          public void dispose()         {             form2.statechangedone.waitone();         }     }      public class lowercurtain : icommandexecutor,idisposable     {         readonly form2 form2;          public lowercurtain(form2 form2)         {             this.form2 = form2;         }          public void execute()         {             if (form2.invokerequired)             {                 form2.invoke(new methodinvoker(execute));             }             else             {                 form2.down();             }         }          public void dispose()         {             form2.statechangedone.waitone();         }     } 

that it. have eliminated use of doevents.

there 1 caveat: doesn't guarantee application stop again after couple of hours/days. reason possible memory-leak in webbrowser control , in testing did see same effect, steadily increasing private memory consumption while managed memory bytes stayed virtually same.

as none of posts provided definitive answer 1 option restart app indicates in 1 of answers here. on plus side, can implement command class...


Comments

Popular posts from this blog

qt - Using float or double for own QML classes -

Create Outlook appointment via C# .Net -

ios - Swift Array Resetting Itself -