I'm writing a small app that will be the endpoint for NLog Network targets (Sending debug messages over TCP) The app uses Sockets to create a server and accept connections. This application is windowless and starts up in the System Tray using NotifyIcon and ApplicationContext. The application listens on a port, when it receives it's first message from a unique endpoint it will create a new window and display it (These windows will contain the actual debug messages) I've been able to get the window to display but it's displaying as if it's hung, I'm guessing it's because it is getting created from on of the invisible threads created by the Sockets.
How can I properly create a new Windows.Form from the test_ClientConnected event?
Here is the ApplicationContext Code
public NLApplicationContext()
{
NLServer test = new NLServer();
test.ClientConnected += test_ClientConnected;
test.Start();
}
void test_ClientConnected(object sender)
{
Form2 newForm = new Form2((NLClient)sender);
newForm.Invoke(new MethodInvoker(() => {newForm = new Form2((NLClient)sender);}));
newForm.Invoke(new MethodInvoker(() => { newForm.Show(); }));
Console.WriteLine("Connected");
/*if (((NLClient)sender).testy.InvokeRequired)
{
((NLClient)sender).testy.BeginInvoke(new MethodInvoker(((NLClient)sender).testy.Show()));
return;
}*/
}
Here is the programs entry point
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new NLApplicationContext());
}
}
You can delegate the UI work to a separate thread like this :
void test_ClientConnected(object sender)
{
Thread displayFormThread = new Thread(ParameterizedThreadStart(DisplayForm));
displayFormThread.Start(sender);
}
private void DisplayForm(object sender)
{
Form2 newForm = new Form2((NLClient)sender);
newForm.Show();
}
You had the right idea, instead of creating the form in the socket thread, move the code to create the form and show it into a method, then Dispatcher.Invoke the method to execute it on the UI thread.
Finally figured out a different way that allowed me to create the form in the main UI thread.
NLApplicationContext
class NLApplicationContext : ApplicationContext
{
List<Form2> _connections; // Temp storage for now
SynchronizationContext testS;
public NLApplicationContext()
{
testS = SynchronizationContext.Current;
_connections = new List<Form2>();
NLServer test = new NLServer();
test.ClientConnected += test_ClientConnected;
test.Start();
}
void test_ClientConnected(object sender)
{
testS.Post(DisplayForm, sender);
}
private void DisplayForm(object sender)
{
Form2 newForm = new Form2((NLClient)sender);
newForm.Show();
_connections.Add(newForm); //Find better storage/sorting
}
}
Using a SynchronizationContext allows me to post back to the thread that it was created on.
Related
This is an incredibly simple task tray app - using ApplicationContext and a few guides I found online.
The purpose of the app is to query a small REST API and show a message box to the user on a given result. I need to essentially have the API query in a background loop, running every 10 seconds or something similar. This is to report on data that I've made accessible via another service.
I've done some reading and it seems a BackgroundWorker and Timer is an appropriate option, but I'm lost on where to go next. How exactly can I achieve this? I initially tried adding a while(true) loop to the TaskTrayApplicationContext but it just created an infinite loop whereby you couldn't do anything else with the app.
namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
NotifyIcon notifyIcon = new NotifyIcon();
Configuration configWindow = new Configuration();
public TaskTrayApplicationContext()
{
MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
notifyIcon.DoubleClick += new EventHandler(ShowMessage);
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
void ShowMessage(object sender, EventArgs e)
{
// Only show the message if the settings say we can.
if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
MessageBox.Show("This is the Serenity TaskTray Agent.");
}
void ShowConfig(object sender, EventArgs e)
{
// If we are already showing the window meerly focus it.
if (configWindow.Visible)
configWindow.Focus();
else
configWindow.ShowDialog();
}
void Exit(object sender, EventArgs e)
{
// We must manually tidy up and remove the icon before we exit.
// Otherwise it will be left behind until the user mouses over.
notifyIcon.Visible = false;
Application.Exit();
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TaskTrayApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Instead of running a form, we run an ApplicationContext.
Application.Run(new TaskTrayApplicationContext());
}
}
}
Threading is hard, concurrency is hard. Background worker and System.Timers are both constructs that run in their own thread.
winforms won't allow for interaction between threads that own a control (read: that created a control) and threads that don't. This is a whole subject apart i wont get into now - theres good stuff to read out there why this is and how to go about it: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx
There are tools to help, one is the dispatchertimer:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1
This is a special timer that instead of its own thread, schedules tasks on the main thread. The main thread in a winforms application handles the drawing of controls, showing of the different windows etc. e.g. this 'owns' all controls.
A sample can be seen on msdn, i adopted it here to show you what you could do:
public class TaskTrayApplicationContext : ApplicationContext
{
...
DispatcherTimer dispatcherTimer;
public TaskTrayApplicationContext()
{
...
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Fetch your data via a rest api
var myData = MyDataFunction();
// check and show dialog if the data is not okay
if(myData.Result.Value = 'NOT_OKAY!')
ShowMessage(this, myData.Result); // or something.
}
...
Now since this does not utilize a second thread, this means the main ui thread could be blocked from drawing the windows, reacting to user input etc. because its busy doing work in the timer_tick function. This would for example happen if your rest call takes a long time.
This will make your application freeze and irresponsive. This could be a problem but most likely wont, so lets burn that bridge when we get to it.
I have a WinForms application that creates a multiple forms, each in their own GUI thread, (not the main GUI thread). I would like to handle the Unhandled Exception event (Application.ThreadException) for all these forms to handle any errors. I would also like to handle exceptions from worker threads - this bit seems to be working correctly, but I'm having trouble with exceptions from GUI threads still:
Program.cs:
[STAThread]
static void Main()
{
AttachExceptionHandlers();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static void AttachExceptionHandlers()
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Dispatcher.CurrentDispatcher.UnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(CurrentDispatcher_UnhandledException);
}
Form1.cs:
//GUI Thread Crash
private void button1_Click(object sender, EventArgs e)
{
object a = null;
a.ToString();
}
//Worker Thread Crash
private void button2_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(() =>
{
object a = null;
a.ToString();
});
myThread.Start();
myThread.Join();
}
//New Thread, New Gui Crash
private void button3_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(() =>
{
using (CrashingForm form = new CrashingForm()) //Crashing Form Crashes in it's FormLoad event.
{
Application.Run(form);
}
});
myThread.Start();
myThread.Join();
}
This code will call my exception handler in the first 2 instances (GUI Thread Crash and Worker Thread Crash) but does not handle the third instance where a new GUI thread is created. I have found that if I call Program.AttachExceptionHandlers(); before the Application.Run(form) line, all is OK, but this is undesirable as I would have to implement some logic to make sure the call to Program.AttachExceptionHandlers() is made before we call a form is created on each thread (the call to Application.SetUnhandledExceptionMode fails if called after creating a form on the thread).
This example is part of a bigger bit of code which would ideally give the user of my code a simple API to call at the start of their application (like in Program.cs) to attach exception handlers. The exception handler then does some magic to record details about the exception being thrown before the application dies. So telling the user they have to track down each time they create a new GUI thread (worker threads don't seem to be effected by this issue) and reattach the Application.ThreadException Handler is not such a clean solution.
Is there another way to achieve this, without having to re registerer for the Application.ThreadException event each time a new GUI thread is created?
Is there another way to achieve this, without having to re registerer
for the Application.ThreadException event each time a new GUI thread
is created?
I'm not aware of any, and my teammate and I have spent a good amount of time looking into it. .NET WinForms doesn't appear to be very opinionated when it comes to how to create / manage / destroy multiple message pumps.
We use framework methods similar to the one below, in addition to Retlang's WinFormsFiber.
using System;
using System.Threading;
using System.Windows.Forms;
internal static class Program
{
[STAThread]
private static void Main()
{
CreateFormAndStartMessagePump(() => CreateForm("first"), OnException, OnException, false, "pumpThread1");
CreateFormAndStartMessagePump(() => CreateForm("second"), OnException, OnException, false, "pumpThread2");
// note app shutdown not handled here
}
private static T CreateFormAndStartMessagePump<T>(
Func<T> createForm,
ThreadExceptionEventHandler onThreadException,
UnhandledExceptionEventHandler onDomainException,
bool isBackground,
string name) where T : Form
{
var latch = new ManualResetEvent(false);
T form = null;
var thread = new Thread(ts =>
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += onThreadException;
AppDomain.CurrentDomain.UnhandledException += onDomainException;
form = createForm();
latch.Set();
Application.Run();
})
{
IsBackground = isBackground,
Name = name
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
latch.WaitOne();
return form;
}
private static Form CreateForm(string name)
{
var form = new Form();
form.Text = name;
form.Show();
return form;
}
private static void OnException(object sender, UnhandledExceptionEventArgs e)
{
// ...
}
private static void OnException(object sender, ThreadExceptionEventArgs e)
{
// ...
}
}
While using forms in C# for a project, i have my main Form (mainForm). This form, when clicking the button1, it creates a new thread for the second Form (actionForm). This one, does the same as the main, when i click button1, it creates a new thread for the thid Form (registerForm). This third Form, when i close it, it must recreate the second form.
The problem is that, the threads keep running. The forms, were closed. But when i click the "X" in the third form, it loops, creating new actionsForms.
How can i stop the threads when creating new ones?
Is there a better way to use the Forms?
Code:
namespace Lector
{
public partial class register : Form
{
public register()
{
InitializeComponent();
}
//New thread for Form2
public static void ThreadProc()
{
//New Form
Application.Run(new Form2());
}
//Close Form
private void Registro_FormClosing(Object sender, FormClosingEventArgs e)
{
regresoForma();
}
private void regresoForma()
{
//New thread
System.Threading.Thread nuevoRegistro2 = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));
//Start thread
nuevoRegistro2.Start();
//Close this form
this.Close();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
}
I suggest you use this instead and you don't need multi-threading at all:
private void regresoForma()
{
//Hide this form
this.Visible=false;
//Start Form2 but as a dialog
//i.e. this thread will be blocked til Form2 instance closed
(new Form2()).ShowDialog();
//Reshow this form
this.Visible=true;
}
Unless you need to have each form establish itself as an entirely new process, I would recommend using BackgroundWorker to display the new forms if you need them to all be asynchronous. If you're using WinForms. If you are using WPF, You'll need to use Dispatcher to create the new forms.
It really depends on the flow of your forms.
I personally try to avoid creating new threads unless it is absolutely one hundred percent necessary, and I use one of the above mentioned methods to do so unless I'm calling a completely brand new application.
I'm trying to run an ATM Simulation in C# with Windows Forms that can have more than one instance of an ATM machine transacting with a bank account simultaneously.
The idea is to use semaphores/locking to block critical code that may lead to race conditions.
My question is this:
How can I run two Forms simultaneously on separate threads? In particular, how does all of this fit in with the Application.Run() that's already there?
Here's my main class:
public class Bank
{
private Account[] ac = new Account[3];
private ATM atm;
public Bank()
{
ac[0] = new Account(300, 1111, 111111);
ac[1] = new Account(750, 2222, 222222);
ac[2] = new Account(3000, 3333, 333333);
Application.Run(new ATM(ac));
}
static void Main(string[] args)
{
new Bank();
}
}
...that I want to run two of these forms on separate threads...
public partial class ATM : Form
{
//local reference to the array of accounts
private Account[] ac;
//this is a reference to the account that is being used
private Account activeAccount = null;
private static int stepCount = 0;
private string buffer = "";
// the ATM constructor takes an array of account objects as a reference
public ATM(Account[] ac)
{
InitializeComponent(); //Sets up Form ATM GUI in ATM.Designer.cs
this.ac = ac;
}
...
I've tried using
Thread ATM2 = new Thread(new ThreadStart(/*What goes in here?*/));
But what method do I put in the ThreadStart constructor, since the ATM form is event-driven and there's no one method controlling it?
EDIT:
I've tried replacing Application.Run(new ATM(ac)); with
ATM atm1 = new ATM(ac);
ATM atm2 = new ATM(ac);
Thread ATM2_T = new Thread(new ThreadStart(atm1.Show));
Thread ATM1_T = new Thread(new ThreadStart(atm2.Show));
ATM1_T.Start();
ATM2_T.Start();
in the Bank constructor. Nothing is displayed and the program drops-off the end of the Main() function.
Here's what I think you need to do:
Thread ATM2 = new Thread(new ThreadStart(ThreadProc));
ATM2.Start();
It calls this method:
private void ThreadProc()
{
var frm = new ATM();
frm.ShowDialog();
}
The above is unsafe code
Please find the threadsafe code:
Thread ATM2 = new Thread(new ThreadStart(ThreadProc));
ATM2.Start();
It calls this method:
private void ThreadProc()
{
if(InvokeRequired)
{
this.Invoke(new Action(() => CreateAndShowForm()));
return;
}
CreateAndShowForm();
}
private void CreateAndShowForm()
{
var frm = new ATM();
frm.ShowDialog();
}
In Bank.Main(), try relpacing Application.Run(new ATM(acc)) with new ATM(acc).Show(). You can use the Form.Show() method as many times as you want. If I recall correctly, the application will close when all forms are closed (although I could be mistaken--try this with the VS debugger)
I don't know why this is happening, but when I create a new form inside an EventHandler, it disappears as soon as the method is finished.
Here's my code. I've edited it for clarity, but logically, it is exactly the same.
static void Main()
{
myEventHandler = new EventHandler(launchForm);
// Code that creates a thread which calls
// someThreadedFunction() when finished.
}
private void someThreadedFunction()
{
//Do stuff
//Launch eventhandler
EventHandler handler = myEventHandler;
if (handler != null)
{
handler(null, null);
myEventHandler = null;
}
}
private void launchForm(object sender, EventArgs e)
{
mf = new myForm();
mf.Show();
MessageBox.Show("Do you see the form?");
}
private myForm mf;
private EventHandler myEventHandler;
The new form displays as long as the MessageBox "Do you see the form?" is there. As soon as I click OK on it, the form disappears.
What am I missing? I thought that by assigning the new form to a class variable, it would stay alive after the method finished. Apparently, this is not the case.
I believe the problem is that you are executing the code within the handler from your custom thread, and not the UI thread, which is required because it operates the Windows message pump. You want to use the Invoke method here to insure that the form gets and shown on the UI thread.
private void launchForm(object sender, EventArgs e)
{
formThatAlreadyExists.Invoke(new MethodInvoker(() =>
{
mf = new myForm();
mf.Show();
MessageBox.Show("Do you see the form?");
}));
}
Note that this assumes you already have a WinForms object (called formThatAlreadyExists) that you have run using Application.Run. Also, there may be a better place to put the Invoke call in your code, but this is at least an example of it can be used.
I think if you create a form on a thread, the form is owned by that thread. When creating any UI elements, it should always be done on the main (UI) thread.
this looks as if you are not on the form sta thread so once you show the form it is gone and the thread finishes it's job it kills it self since there is nothing referenceing the thread. Its not the best solution out there for this but you ca use a showdialog() rather than a show to accomplish it keeping state if you need a code example i use this exact same process for a "loading...." form
public class Loading
{
public delegate void EmptyDelegate();
private frmLoadingForm _frmLoadingForm;
private readonly Thread _newthread;
public Loading()
{
Console.WriteLine("enteredFrmLoading on thread: " + Thread.CurrentThread.ManagedThreadId);
_newthread = new Thread(new ThreadStart(Load));
_newthread.SetApartmentState(ApartmentState.STA);
_newthread.Start();
}
public void Load()
{
Console.WriteLine("enteredFrmLoading.Load on thread: " + Thread.CurrentThread.ManagedThreadId);
_frmLoadingForm = new frmLoadingForm();
if(_frmLoadingForm.ShowDialog()==DialogResult.OK)
{
}
}
/// <summary>
/// Closes this instance.
/// </summary>
public void Close()
{
Console.WriteLine("enteredFrmLoading.Close on thread: " + Thread.CurrentThread.ManagedThreadId);
if (_frmLoadingForm != null)
{
if (_frmLoadingForm.InvokeRequired)
{
_frmLoadingForm.Invoke(new EmptyDelegate(_frmLoadingForm.Close));
}
else
{
_frmLoadingForm.Close();
}
}
_newthread.Abort();
}
}
public partial class frmLoadingForm : Form
{
public frmLoadingForm()
{
InitializeComponent();
}
}
Is
dbf.Show();
a typo? Is it supposed to be this instead?
mf.Show();
Is it possible that there is another form that you are showing other than the one you intend to show?
You created a window on a non UI thread. When the thread aborts it will take your window along with it. End of story.
Perform invoke on the main form passing a delegate which will execute the method that creates the messagebox on the UI thread.
Since the MessageBox is a modal window, if dont want the launchForm method to block the background thread, create a custom form with the required UI and call show() on it, not ShowDialog().