1

I am currently in the development of a windows forms application and I am having a little problem that is blocking me completely.

My problem: In the program.cs file I have the execution of a first form which allows me to check if the person is well connected to the Internet. Once the verification is done, I would like to close this form and open my application, I tried with Application.Run(new App());, but it tells me that I cannot do two starts in a thread.

// program.cs

using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Http;
using System.IO;
using Newtonsoft.Json;

namespace MyApp
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Loading());
        }

        public static bool CheckInternet()
        {
            try {
                using (var web = new WebClient())
                using (web.OpenRead("http://google.com"))
                    return true;
            } catch {
                return false;
            }
        }
    }
}
// loading.cs

using System;
using System.Windows.Forms;

namespace MyApp
{
    public partial class Loading : Form
    {
        public Loading()
        {
            InitializeComponent();

            this.text_version.Text = "0.0.0.1";

            if (Program.CheckInternet())
            {
                var timer = new System.Windows.Forms.Timer();
                timer.Interval = 2000;

                timer.Tick += (s, e) =>
                {
                    timer.Stop();
                    StartApp();
                };

                timer.Start();
            }
        }

        private void StartApp()
        {
            this.l_info.Text = "Starting app...";
            this.l_info.Location = new System.Drawing.Point(70, 270);

            Application.Exit();

            Application.Run(new App()); // Error here
        }
    }
}

I hope explain it well. If you have any questions do not hesitate.

Thanks to anyone who will have the time to help me.

Robostich
  • 41
  • 6
  • 2
    We will need a [mcve]. – mjwills Jun 08 '21 at 23:10
  • 1
    You can use `Someform.ShowDialog()` instead of `Application.Run(new SomeForm());`. Call `Application.Run(new MainForm());` after the Dialog is closed. If you really need a Form to check whether *the person is well connected to the Internet*, that is. I don't see why, but... – Jimi Jun 08 '21 at 23:13
  • go ahead and include the exception/error as well, and perhaps update your question' title with a sentiment like " is thrown when doing several Application.Run calls", that should improve this question – Brett Caswell Jun 08 '21 at 23:22
  • @Jimi I tried putting a `ShowDialog()` in front of `Application.run` but I still get the same error `System.InvalidOperationException` – Robostich Jun 08 '21 at 23:29
  • what have you tried? have you tried moving `Application.Run(new App());` out of `StartApp` scope and below `Application.Run(new Loading());` in `Main`? – Brett Caswell Jun 08 '21 at 23:32
  • `var loadForm = new Loading(); if (loadForm.ShowDialog() == DialogResult.OK) { Application.Run(new App()); }`. -- Move everything related to the Internet test to the `Loading` Form (WebClient stuff included) and make `Loading` return `DialogResult.OK` if the test succeeds. -- Better with a `using` statement, but the Form is disposed anyway. – Jimi Jun 08 '21 at 23:38
  • Ok, great @Jimi last comment works great. On the other hand my text which says that the application will launch this does not have time to be displayed I tried to make a sleep but it keeps the text defined by default in the form. Do you have an idea ? – Robostich Jun 08 '21 at 23:48
  • When the `CheckInternet()` method returns (hoping it returns, none of the WebClient async methods accept a CancellationToken, so you probably want to use HttpClient instead, to abort the test after a TimeOut), start a Timer and close the Form in the Tick handler. In the `FormClosed` handler, remove the Tick event handler and dispose of the Timer (if it's not in a Component you have added in the Designer). – Jimi Jun 08 '21 at 23:55
  • I'll say your issue is something worth noting, but what you're attempting to actually do here is covered in [How to build splash screen in windows forms application](https://stackoverflow.com/questions/7955663/how-to-build-splash-screen-in-windows-forms-application) – Brett Caswell Jun 09 '21 at 00:03

1 Answers1

1

There are various implementations you could do here for control flow (I think @jimi's comments on using DialogResult is a decent approach), but what's important is that you should not attempt to do a nested Application.Run call.

In this implementation, we'll close Loading Form which will continue the execution in Main. Note that doing Application.Exit also will continue execution in Main, but it won't actually Run any new forms. As such, you do have a scenario where we can add that behavior (whether or not you actually intended to have this behavior is up to your requirements).

[STAThread]
static void Main()
{
   Application.EnableVisualStyles();
   Application.SetCompatibleTextRenderingDefault(false);
   Application.Run(new Loading());
   Application.Run(new App());
}

Next,

we'll move Application.Exit() to the else-block of Program.CheckInternet conditional check.

we'll remove Application.Run from StartApp

we'll do Close() the Loading Form in the now improperly named StartApp function scope.

public Loading()
{
    InitializeComponent();
    this.text_version.Text = "0.0.0.1";

    if (Program.CheckInternet())
    {
        var timer = new System.Windows.Forms.Timer();
        timer.Interval = 2000;
        timer.Tick += (s, e) =>
        {
            timer.Stop();
            StartApp();
        };

        timer.Start();
    } else {
        Application.Exit();
    }

}

private void StartApp()
{
    this.l_info.Text = "Starting app...";
    this.l_info.Location = new System.Drawing.Point(70, 270);

    Close(); //this.Close for Form instance close, not Application.Exit
}
Brett Caswell
  • 1,486
  • 1
  • 13
  • 25
  • I modify it works correctly but still the same problem with the text which does not have time to change. – Robostich Jun 09 '21 at 00:20
  • Otherwise I just thought I increase the time eg to 5000 and I check to 2000 and change to new text. Do you think this is a good thing? – Robostich Jun 09 '21 at 00:24
  • Where as this answer is really intended to address the exception and demonstrate how you can successfully do Application.Run twice (running two separate form instances.. though not concurrently); the better implementation and approach for this splashscreen feature that relates to relating state/progress of starting the app, would be to use threading. – Brett Caswell Jun 09 '21 at 00:31
  • I look at the documentation : https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread?view=net-5.0 – Robostich Jun 09 '21 at 00:37
  • I try but I do not understand how the thread works as in the end it does not display the form until it is finished. – Robostich Jun 09 '21 at 01:04
  • I see you marked this as an answer that is helpful. I would like to comment that: though I do recognize this answer does not provide a working sample of what the OP is trying to (and yet to) achieve, I feel that what was observed and raised to question on does not necessitate a working example of that scope. To be clear, the requirements and implementations will vary. – Brett Caswell Jun 09 '21 at 19:59
  • Apart from this snippet demonstrating (sequential Application.Run calls - that don't run concurrently or share/manage state). there is threading as well as an `ApplicationContext` (encapsulating on Forms and managed states) implementations worth consideration. – Brett Caswell Jun 09 '21 at 20:00