C# - Manipulation of controls in event triggered by a multithreaded method of another class

Asked

Viewed 61 times

0

Good morning, you guys.

I’m developing a C# library using Visual Studio for software authentication/licensing. It’s something simple. It basically consists of a method that executes a continuous loop by checking an xml file containing the license expiration date, the date of the last update of the file and compares this information with the date and time of the pc.

Three situations can be identified in this method:

  1. Authenticated licence
  2. Unpaid leave
  3. Date/time of the backdated pc

Each such situation will trigger an event containing this authentication information that should be dealt with within the software that I will develop in the future, such as the common events of controls and other Windows libraries. (In the case of this library of mine, it is something like the timer control, which triggers the tick event every time period)

So far so good. The problem occurs when I will use this authentication library on another system to display the authentication information in the form controls for the user. Error occurs: 'Invalid thread operation: accessed control of a thread that is not the one in which it was created.'

I found some methods that can get around this error, but they are implemented within the programs and I wanted something that could be encapsulated within that mine of the authentication library.

This is how events are triggered:

public delegate void MyDelegate(object sender, AuthenticationEventArgs e);
public event MyDelegate Authenticated;

public EsLicense(string FilePath, string Key)
{
    //Carregamento de outras propriedades
    
    this.t = new Thread(new ThreadStart(OnAuthentication));
    
}

public void StartTimer(int interval)
{
    _interval = interval;
    _running = true;

    Load();

    if (!t.IsAlive)
    {
        Load();

        t.IsBackground = true;
        t.Start();
    }

}
    
protected virtual void OnAuthentication()
{
    while (_running)
    {

        // Cria um novo objeto do tipo AuthenticationEventArgs
        AuthenticationEventArgs e = new AuthenticationEventArgs();


        if (DateTime.Now <= this.LastUpdate) //Verifica o relógio
        {
            //EXECUTA QUANDO O RELÓGIO DO SISTEMA ESTIVER INCORRETO


            if (TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now)) //Verifica horário de verão
            {
                //Código para tratar erro quando o horário de verão acabar

                
            }

            // Verifica se existem métodos inscritos no eventos
            if (Authenticated != null)
            {

                // Define os argumentos do evento
                e.Status = AuthenticationStatus.SystemClockError;
                e.Remaining = this.Remaining;
                e.Exception = new Exception("O relógio do computador está desconfigurado. Configure a hora certa para poder continuar.");

                // Dispara o evento
                Authenticated.Invoke(this, e);
                //Authenticated(this, e);

            }

        }
        else
        {
            //EXECUTA ENQUANTO O RELÓGIO DO SISTEMA ESTIVER CORRETO


            this.Update(); //Atualiza o último acesso


            if (DateTime.Now < this.ExpirationDate) //Verifica a data de expiração da autenticação
            {
                //LICENÇA VÁLIDA

                //Define a quantidade de dias que faltam até o vencimento da licença
                this.Remaining = this.ExpirationDate.Subtract(DateTime.Now);

                // Verifica se existem métodos inscritos no eventos
                if (Authenticated != null)
                {

                    // Define os argumentos do evento
                    e.Status = AuthenticationStatus.Validated;
                    e.Remaining = this.Remaining;
                    e.Exception = null;

                    // Dispara o evento
                    Authenticated.Invoke(this, e);
                    //Authenticated(this, e);


                }

            }
            else
            {
                /*
                 * LICENÇA EXPIRADA
                 * 
                 */

                //Define a quantidade de dias que faltam até o vencimento da licença
                this.Remaining = new TimeSpan(0);

                // Verifica se existem métodos inscritos no eventos
                if (Authenticated != null)
                {

                    // Define os argumentos do evento
                    e.Status = AuthenticationStatus.Expired;
                    e.Remaining = new TimeSpan();
                    e.Exception = null;

                    // Dispara o evento
                    Authenticated.Invoke(this, e);
                    //Authenticated(this, e);

                }

            }

        }

        Thread.Sleep(_interval);
    }
}

Example of method that will handle the event:

private void Form1_Load(object sender, EventArgs e)
{
    Licença = new EsLicense(@"C:\licença.xml", key);
    Licença.Authenticated += new EsLicense.MyDelegate(Licença_Authenticated);
    Licença.StartTimer(1000);
}

private void Licença_Authenticated(object sender, AuthenticationEventArgs e)
{

    switch (e.Status)
    {
        case AuthenticationStatus.Validated:
            textBox1.Text = e.Remaining.ToString(); // Linha onde o erro é disparado
            break;

        case AuthenticationStatus.Expired:
            textBox1.Text = "Licença expirada"; 
            break;

        case AuthenticationStatus.SystemClockError:
            MessageBox.Show("Erro de autenticação.\n\nO relógio do computador foi desatualizado", "Autenticação", MessageBoxButtons.OK, MessageBoxIcon.Error);
            Application.Exit();
            break;

        default:
            break;
    }


}

I’m a beginner in C# and I hope I was clear enough.

  • you don’t need to declare a eventand a delegate, in essence they do basically the same thing, have technical differences, but in your example just a event to solve your problem. Remove the delegate and invoke directly from event. Now about the thread, explain better pq uses this, is pq the program can monitor multiple applications, and each runs in a thread?

  • @Ricardo Punctual, thank you for the comment. I did what you said, I removed the delegate and declared the event as public event EventHandler<AuthenticationEventArgs> Authenticated, but the thread problem continues. I need to use thread because of the while in the method OnAuthentication(). This method updates and monitors the authentication xml file at each time interval. If I don’t use a different thread the application simply hangs. In the example above I put the class instance EsLicense at the event click button, but actually it will be in the event Load mainform of the application

  • only the Timer is not enough? it itself is already a thread, if you just want every X times it looks at the file that is enough :)

  • Yes, that’s the way I used this library, but I would like to learn more about the operation of events triggered by multithreaded methods of other classes and the manipulation of these events using the controls. I think it would even be a more elegant way to work without using extra components and encapsulating as much code as possible for reuse.

  • 1

    in this case study more about your code, debug.. will realize that your code code create a thread and a timer, which makes no sense, only one of the two would solve this, good luck

  • Precisely. I can use only timer, but would like to use only thread.

Show 1 more comment
No answers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.