0

I've made a simple desktop application (Winform, in Visual Studio 2015, .NET version 4.5.2) which listens for incoming pusher.com notifications, and does various things depending on what the notification contains. However, there are some issues regarding the internet connectivity - if there is no connection on startup, the application crashes, because of an unhandled exception:

An unhandled exception of type System.Reflection.TargetInvocationException occurred in in mscorlib.dll

The InnerException is No such host is known, and it's understandable, since there's no internet connection.

The StartPushing() bit of the code will continue trying to reconnect if the connection is interrupted while the app is running, and unstable connection is not (the biggest) problem. It's not having the connection before the application is run.

I've also removed StartPushing() from public Form1()1 and placed it within Form1_Shown(...) (thinking that maybe it was called before everything had been setup), but that did not solve the problem - the application kept crashing on startup.

This is my code.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using Newtonsoft.Json;
using PusherClient;


namespace PusherWebApps
{
    public partial class Form1 : Form
    {
        private static Pusher _pusher;
        public string channel, key;
        public string event1, event2;
        public string iniName = "PusherWebApps.ini";
        public string type;
        WebClient client = new WebClient();

        public Form1()
        {
            InitializeComponent();
            // https://stackoverflow.com/a/39308022
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
            StartPushing();
        }

        void _pusher_ConnectionStateChanged(object sender, PusherClient.ConnectionState state)
        {
            fileLogging("Connection state: " + state.ToString());
        }

        void _pusher_Error(object sender, PusherException error)
        {
            fileLogging("Pusher Channels Error: " + error.ToString());
        }

        // Start
        public async void StartPushing()
        {
            iniParams();
            _pusher = new Pusher(key);
            _pusher.ConnectionStateChanged += _pusher_ConnectionStateChanged;
            _pusher.Error += _pusher_Error;
            PusherClient.ConnectionState connectionState = await _pusher.ConnectAsync();

            Channel _myChannel = await _pusher.SubscribeAsync(channel);

            _myChannel.Bind("myevent", (dynamic incoming) =>
            {
                fileLogging(incoming.ToString());

                dynamic tmp = JsonConvert.DeserializeObject(incoming.ToString());
                dynamic data = JsonConvert.DeserializeObject(tmp.data.ToString());
                dynamic message = JsonConvert.DeserializeObject(data.message.ToString());
                dynamic header = JsonConvert.DeserializeObject(message.header.ToString());

                type = header.type.ToString();

                if (type.ToLower() == event1)
                {
                    // do stuff
                }

                if (type.ToLower() == event2)
                {
                    // do stuff 2
                }
            });
        }

        void _myChannel_Subscribed(object sender)
        {
            fileLogging("Subscribed!");
        }

        public void iniParams()
        {
            bool checkIni = File.Exists(Application.StartupPath + "\\" + iniName);

            if(!checkIni)
            {
                fileLogging("INI missing!");
                MessageBox.Show("INI missing!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(0);
            }

            var MyIniFile = new IniFile(Application.StartupPath + "\\" + iniName);

            channel = MyIniFile.Read("channel", "Main").ToString();
            key = MyIniFile.Read("key", "Main").ToString();
            event1 = MyIniFile.Read("event1", "Main").ToString();
            event2 = MyIniFile.Read("event2", "Main").ToString();
        }

/************ BEGIN logging **********/
        public void fileLogging(string text)
        {
            // do logging stuff
        }
/********** END logging *********/
    }
}

Finally, my question.

How can I handle this error?

I know that I could probably inform the user that there's no internet connection at startup, and then exit the application, but I'd like to keep it running, and let it keep trying to connect in the background. I would also like to avoid having to use a separate thread and a timer to check for internet connectivity in the background.

EDIT 1

This is the part to which the debugger points - instead of pointing somewhere inside Form1.cs, it highlights this part of Program.cs

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1()); // <== this is the one which the debugger highlights as the error
    }
}
FiddlingAway
  • 1,598
  • 3
  • 14
  • 30
  • For well expected cases (no internet connection is likely to be one) you simply have to check for it in advance and don't let anything what rely on it to run. For unexpected (but possible and unwanted) things, you can put the code inside `try/catch` block. – Sinatr Jul 13 '21 at 14:41
  • If this is your main form the code flow must allow the form to be shown or the application ends. So this is at least on my side expected behaviour. To help you what is your expectation of what should happen? – Ralf Jul 13 '21 at 14:51
  • SSL, TLS 1.0, and TLS 1.1 are obsolete and may cause issue. Use only TLS 1.2 or TLS 1.3. To prevent application from crashing I would put the code into a Try/Catch block : try{ ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;} catch{;} – jdweng Jul 13 '21 at 14:53
  • Is `Pusher.ConnectAsync()` (pseudo) `async` and `void` as `async void StartPushing()`? Do you have other `async void` stuff around? Are you handing exceptions somehow, somewhere? – Jimi Jul 13 '21 at 15:07
  • @Ralf This is indeed the main form. The thing is, if there's no internet connection, the `StartPushing()` bit should fail gracefully (alert the user, and leave it at that), and it should continue running and trying to reconnect (something that it already does on its own if there's an interruption in the connection midway, while it's running - the external libraries are set up like that out of the box). – FiddlingAway Jul 14 '21 at 08:20
  • @jdweng Will keep that in mind. I've used this on occassion, for other projects: https://www.medo64.com/2020/05/using-tls-1-3-from-net-4-0-application/ (last bit of code), and it seemed to be working well. It starts from TLS 1.3, and keeps going back. Might end up using it, if problems occur on this end. – FiddlingAway Jul 14 '21 at 08:21
  • @Jimi Exceptions are handled in other parts of the code, for things unrelated to internet connection. My biggest problem is handling exceptions in `StartPushing()`. Adding `try/catch` as the first thing (try everything in `StartPushing()`, catch the error and inform the user), will not return to `StartPushing()` once there is an internet connection. I know that I could add another thread with a timer (500ms) to keep returning to `StartPushing()`, and to keep `try`ing, but I was wondering if there's another way of handling it. – FiddlingAway Jul 14 '21 at 08:22
  • I don't believe the link. With Net versions below 4.7 the TLS is performed in Net (not operating system). And Net did not do all the encryption modes for TLS. So depending on the encryption mode code may or may not work. See Wiki : (library support). TLS doesn't work with encryption modes that are vulnerable https://en.wikipedia.org/wiki/Transport_Layer_Security?force_isolation=true – jdweng Jul 14 '21 at 17:04

0 Answers0