jump to navigation

Thread Synchronization Between Worker Threads April 15, 2021

Posted by codinglifestyle in Architecture, C#, CodeProject, Parallelism.
Tags: , , , ,
add a comment

Many moons ago I wrote the last Windows service I’d ever need. It has a pluggable architecture and today there are over 20 modules doing all manner of business tasks such as sending emails, generating reports, performing automation, etc. Inside each plug-in module is a thread daemon which listens to events to orchestrate the operations of the other supporting module threads. This primarily consists of a thread which provides incoming data (what?), a thread which is the scheduler (when?), and a thread pool of worker threads which process said data (how?).

Recently a new plug-in was created to enforce global trade compliance. A bug crept up in the new worker thread initialization which surprised me because it caused the incoming data queue thread to die even though that code hadn’t changed in years. The reason was that the incoming queue fired the event and therefore it was the thread which executed the delegate (aka function pointer). This function is defined in the thread daemon class which takes care of initializing and starting a worker thread. From an error handling point of view this struck me as less than ideal. So the question was asked:

How do I get the thread daemon to be the thread which executes it’s own management functions which are triggered by events?

This got me thinking about WinForm apps whereby a worker thread must marshal execution of a delegate to the main UI thread to update the UI like changing the status bar. The pattern is to check if an invoke is required (am I the UI thread?) and if not then you use the built in SynchronizationObject to invoke the delegate in the UI thread.

        public delegate void UpdateStatusEvent(string message, params object[] args);

        public void UpdateStatus(string message, params object[] args)
        {
            if (_Status.InvokeRequired)
            {
                UpdateStatusEvent amc = new UpdateStatusEvent(UpdateStatus);
                this.Invoke(amc, new object[] { message, args });
            }
            else
            {
                //Now we are the UI thread we can actually update the status bar
                _LabelStatus.Text = string.Format(message, args);
                _Status.Refresh();
            }
        }

With this pattern in my head I thought I’d be able to implement this quite easily between my background worker threads. How wrong I was and hence this article! WinForms and WPF provide a synchronization context but there isn’t something in the framework you get for free in my case. From what I understand they both implement a classic windows message pump which is used to marshal the execution from the worker thread to the UI thread. This will become important shortly, but suffice it to say after a lot of searching a pre-baked, easy solution wasn’t on offer.

As I continued down this path I searched for ISynchronizeInvoke and IAsyncResult which primarily returned questions and examples of the above pattern. I tried in vein to find an equivalent implementation except for worker threads. Surely it wasn’t that complicated to get an event initiated in Thread A to execute in Thread B? I tried using the SynchronizationContext class but quickly discovered it’s just a base class and didn’t do the work of marshalling the execution from Thread A to Thread B. So while I went through the motions the code still executed in the wrong thread context (so why isn’t this class abstract if you must extend it to get it to work?). BTW, the way I was testing to see which thread was running was to refer to Thread.CurrentThread.ManagedThreadId.

So now I had wasted a lot of time trying to find an easy answer and had to accept some work on my part would be necessary in this scenario. What struck me was the notion of the classic windows pump being crucial for the synchronization context. Basically that it has a message queue running so a thread can enqueue a message and that message then be executed by another thread when it is able. So in the thread daemon I defined a queue and in the OnDoWork created my rudimentary “message pump”.

        private Queue<WaitCallback> _qWorkItems;
        private object _oLock;
      
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            LogSvc.Debug(this, "Running");

            while (!CancellationPending)
            {
                //Critical section
                lock (_oLock)
                {
                    //This is the message pump allowing for the thread synchronization context
                    while (_qWorkItems.Count > 0)
                    {
                        LogSvc.Debug(this, "Dequeue and invoke work item on thread {0}", Thread.CurrentThread.ManagedThreadId);

                        //Dequeue next work item
                        var workItem = _qWorkItems.Dequeue();
                        
                        //Execute work item in thread daemon context
                        workItem.Invoke(null);
                    }

                    //Wait for new work items to process
                    Monitor.Wait(_oLock);
                }
            }
        }

        /// <summary>
        /// Queues a method for execution.  The method executes when the thread daemon is available.
        /// </summary>
        /// <param name="callback">The callback.</param>
        public void QueueWorkItem(WaitCallback callback)
        {
            LogSvc.Debug(this, "Enqueuing work item from event caller thread {0}", Thread.CurrentThread.ManagedThreadId);

            //Critical section
            lock (_oLock)
            {
                _qWorkItems.Enqueue(callback);
                Monitor.Pulse(_oLock);
            }
        }

I started with a Queue<Action> which allowed me to accomplish asynchronous execution. However I wanted to be able to support synchronous execution as well to support getting the return value from the delegate. So I looked at what ThreadPool.EnqueueUserWorkItem used and settled on WaitCallback.

Now we have our thread daemon setup to queue and execute operations in it’s thread context. What we need next is a synchronization context to allow the worker threads to marshal the delegate and data from their thread to the thread daemon thread. We’ll implement both ISynchronizeInvoke and IAsyncResult classes to nicely encapsulate this functionality. This will offer a test to see if an invoke is required and support both asynchronous and synchronous execution of the event delegate.

    /// <summary>
    /// Internally used by ThreadSynchronization to represent asynchronous operations
    /// </summary>
    /// <seealso cref="System.IAsyncResult" />
    class ThreadAsyncResult : IAsyncResult
    {
        /// <summary>
        /// Gets a value that indicates whether the asynchronous operation has completed.
        /// </summary>
        public bool IsCompleted { get; set; }

        /// <summary>
        /// Gets a <see cref="T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
        /// </summary>
        public WaitHandle AsyncWaitHandle { get; internal set; }

        object _state;
        /// <summary>
        /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
        /// </summary>
        public object AsyncState
        {
            get
            {
                if (Exception != null)
                {
                    throw Exception;
                }
                return _state;
            }
            internal set
            {
                _state = value;
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the asynchronous operation completed synchronously.
        /// </summary>
        public bool CompletedSynchronously { get { return IsCompleted; } }

        /// <summary>
        /// Gets or sets the exception.
        /// </summary>
        /// <value>
        /// The exception.
        /// </value>
        internal Exception Exception { get; set; }
    }

    /// <summary>
    /// Thread synchronization context to marshal delegate and data to ThreadManager thread
    /// </summary>
    /// <seealso cref="System.ComponentModel.ISynchronizeInvoke" />
    class ThreadSynchronization : ISynchronizeInvoke
    {
        public readonly int _nExecutingContextID = 0;
        private ThreadManager _manager;
        
        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadSynchronization"/> class.
        /// </summary>
        /// <param name="manager">The thread manager object</param>
        public ThreadSynchronization(ThreadManager manager)
        {
            _nExecutingContextID = Thread.CurrentThread.ManagedThreadId;
            _manager             = manager;

            Log.Debug("Synchronization context created for thread {0}", _nExecutingContextID);
        }

        /// <summary>
        /// Gets a value indicating whether the caller must call <see cref="M:System.ComponentModel.ISynchronizeInvoke.Invoke(System.Delegate,System.Object[])" /> when calling an object that implements this interface.
        /// </summary>
        public bool InvokeRequired => Thread.CurrentThread.ManagedThreadId != _nExecutingContextID;

        /// <summary>
        /// Asynchronously executes the delegate on the thread that created this object.
        /// </summary>
        /// <param name="method">A <see cref="T:System.Delegate" /> to a method that takes parameters of the same number and type that are contained in <paramref name="args" />.</param>
        /// <param name="args">An array of type <see cref="T:System.Object" /> to pass as arguments to the given method. This can be <see langword="null" /> if no arguments are needed.</param>
        /// <returns>
        /// An <see cref="T:System.IAsyncResult" /> interface that represents the asynchronous operation started by calling this method.
        /// </returns>
        public IAsyncResult BeginInvoke(Delegate method, object[] args)
        {
            var result                 = new ThreadAsyncResult();
            var manualResetEvent       = new ManualResetEvent(false);
            result.AsyncWaitHandle     = manualResetEvent;

            _manager.QueueWorkItem(delegate
            {
                try
                {
                    //Invoke the delegate and capture the return value
                    result.AsyncState  = method.DynamicInvoke(args);
                }
                catch (Exception ex)
                {
                    Log.Err(ex);
                    //Capture the exception
                    result.Exception   = ex;
                }
                finally
                {
                    //Mark complete
                    result.IsCompleted = true;
                    //Set event for anyone waiting
                    manualResetEvent.Set();
                }
            });

            return result;
        }

        /// <summary>
        /// Waits until the process started by calling <see cref="M:System.ComponentModel.ISynchronizeInvoke.BeginInvoke(System.Delegate,System.Object[])" /> completes, and then returns the value generated by the process.
        /// </summary>
        /// <param name="result">An <see cref="T:System.IAsyncResult" /> interface that represents the asynchronous operation started by calling <see cref="M:System.ComponentModel.ISynchronizeInvoke.BeginInvoke(System.Delegate,System.Object[])" />.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that represents the return value generated by the asynchronous operation.
        /// </returns>
        public object EndInvoke(IAsyncResult result)
        {
            //If not complete then wait until done
            if (!result.IsCompleted)
            {
                result.AsyncWaitHandle.WaitOne();
            }

            //The return value of the delegate
            return result.AsyncState;
        }

        /// <summary>
        /// Synchronously executes the delegate on the thread that created this object and marshals the call to the creating thread.
        /// </summary>
        /// <param name="method">A <see cref="T:System.Delegate" /> that contains a method to call, in the context of the thread for the control.</param>
        /// <param name="args">An array of type <see cref="T:System.Object" /> that represents the arguments to pass to the given method. This can be <see langword="null" /> if no arguments are needed.</param>
        /// <returns>
        /// An <see cref="T:System.Object" /> that represents the return value from the delegate being invoked, or <see langword="null" /> if the delegate has no return value.
        /// </returns>
        public object Invoke(Delegate method, object[] args)
        {
            //Get IAsyncResult operation
            var result = BeginInvoke(method, args);
            //Wait for asynchronous operation to complete
            EndInvoke(result);
            //The return value of the delegate
            return result.AsyncState;
        }
    }

So notice that ThreadSynchronization is tied to our thread daemon object which implements QueueWortItem. You could expose access to QueueWorkItem a different way if you wish. So, at long last, we have everything setup so we’re ready to alter the events themselves. These events, located within the thread daemon class, would have executed in another worker thread’s execution context. By instantiating the ThreadSynchronization object we can test if an invoke is required and enqueue the work to execute on the thread daemon thread and even get the return result of the event.

        bool Incoming_Dequeued(object oData)
        {
            bool bReturn = false;

            //If the event is called by a thread other than thread daemon...
            if (Sync.InvokeRequired)
            {
                //Marshal delegate call and data to thread daemon context
                var result = Sync.Invoke(new Func<object, bool>(Incoming_Dequeued), new object[] { oData });
                bReturn    = TypeParser.ParseBool(result, false);

                return bReturn;
            }

            //Execute this code in the context of the thread daemon

            return bReturn;
        }

        void ThreadPool_ThreadComplete(IModuleThread sender, object oResult)
        {
            //If the event is called by a thread other than thread daemon...
            if (Sync.InvokeRequired)
            {
                //Marshal delegate call and data to thread daemon context
                Sync.Invoke(new Action<IModuleThread, object>(ThreadPool_ThreadComplete), new object[] { sender, oResult });
                return;
            }

            //Execute this code in the context of the thread daemon
        }

At last here is the pattern I was looking for all too familiar to anyone who has worked on WinForms or WPF. Now we can easily see if we’re the correct thread and if not do an asynchronous or synchronous invoke of the delegate and data. When invoking it’s easy to use an Action<TIn1, TIn2> or Func<TIn1, Tin2, TOut>, as required, to generate your delegate.

In conclusion, you can see why Microsoft didn’t have a prepackaged solution to this as they couldn’t presume the executing thread would implement a message pump in a strict fashion. They did provide ISynchronizeInvoke which also needs IAsyncResult. Just creating these objects and implementing their interfaces lays bare what you need to do. While I love how it encapsulates this functionality in a familiar manner, it’s not strictly necessary. Really just the implementation of the message pump in our executing thread along with a composite object containing the delegate, data, and a lock would be enough to marshal the required pieces across and signal the thread who fired the event when execution is complete. However, if like me, you are hunting for a best practice implementation I’m very happy with how neatly the above solution turned out in the end.

Understanding BackgroundWorker and Encapsulating your own Thread Class February 22, 2011

Posted by codinglifestyle in C#, CodeProject, Parallelism.
Tags: , , , , , , ,
add a comment

You may have come across this page if you were searching for any of the following:

  • BackgroundWorker events not firing
  • BackgroundWorker RunWorkerCompleted event not firing
  • BackgroundWorker threads frozen
  • Encapsulate thread class

Yesterday my web page was launching several worker threads and waiting for them to return to amalgamate the results in to a single data set to bind to a grid. Launching several worker threads and waiting for a join is a common pattern. To nicely encapsulate the thread itself I derived a class from BackgroundWorker. BackgroundWorker has many advantages such as using an event model, thread pool, and all the thread plumbing built right in. All you have to do is override OnDoWork and off you go. The RunWorkerCompleted event was used, in conjunction with a lock, to collect the data once the worker thread finished.

Everything looked good but for some reason the event never fired. The problem was that I had gotten myself in to a deadlock scenario. The expectation is that when the event fires the delegate method will run in the context of the thread which fired it. If this were true, this would have allowed my synchronization logic to operate normally and not deadlock. The reality is that BackgroundWorker goes out of its way to run this event in the calling thread’s identity. It did this so when using BackgroundWorker in conjunction with the UI no invoke will be required (an exception will be thrown if a thread tries to touch the UI’s controls requiring you to check InvokeRequired).

When in doubt, use something like this to check the identity of the thread executing the code:

Trace.WriteLine(string.Format(“Thread id {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));

Once putting the above trace in the code I would clearly see that the identity of my main thread was identical to the thread identity in the RunWorkerCompleted event. Once the code tried to aquire the lock it was all over.

So the solution in my case was not to use the RunWorkerCompleted event. I added an alternative event to my thread class and called that at the end of OnDoWork. The event executed in the context of the thread, as expected, and my synchronization logic worked fine. But I couldn’t help feeling it was a bit of a kludge and pondered whether I should be deriving from BackgroundWorker at all. However, what’s the alternative? There really aren’t other alternatives to BackgroundWorker built in to the framework but it is easy to create your own.  See below:

 /// <summary>
 /// Abstract base class which performs some work and stores it in a data property
 /// </summary>
 /// <typeparam name="T">The type of data this thread will procure</typeparam>
 public abstract class ThreadBase<T>
 {
 #region Public methods
 /// <summary>
 /// Does the work asynchronously and fires the OnComplete event
 /// </summary>
 public void DoWorkAsync()
 {
     DoWorkAsync(null);
 }

 /// <summary>
 /// Does the work asynchronously and fires the OnComplete event
 /// </summary>
 /// <param name="arguement">The arguement object</param>
 public void DoWorkAsync(object arguement)
 {
 ThreadPool.QueueUserWorkItem(DoWorkHelper, arguement);
 }

 /// <summary>
 /// Does the work and populates the Data property
 /// </summary>
 public void DoWork()
 {
 DoWork(null);
 }

 /// <summary>
 /// Does the work and populates the Data property
 /// </summary>
 /// <param name="arguement">The arguement object</param>
 /// <remarks>
 /// Can be called to run syncronously, which doesn't fire the OnComplete event
 /// </remarks>
 public abstract void DoWork(object arguement);
 #endregion

#region Private methods
 private void DoWorkHelper(object arguement)
 {
 DoWork(arguement);
 if (OnComplete != null)
     OnComplete.Invoke(this, Data);
 }
 #endregion

#region Properties
 public T Data { get; protected set; }
 #endregion

#region Events

 /// <summary>
 /// Delegate which is invoked when the thread has completed
 /// </summary>
 /// <param name="thread">The thread.</param>
 /// <param name="data">The data.</param>
 public delegate void ThreadComplete(ThreadBase<T> thread, T data);

 /// <summary>
 /// Occurs when the thread completes.
 /// </summary>
 /// <remarks>This event operates in the context of the thread</remarks>
 public event ThreadComplete OnComplete;
 #endregion
 }

My generic ThreadBase class is a lightweight baseclass substitute for BackgroundWorker providing the flexibility to call it synchronously or asynchronously, a generically typed Data property, and an OnComplete event. The OnComplete will execute in the thread’s context so synchronization of several threads won’t be a problem. Take a look at it in action:

 public class MyThread : ThreadBase<DateTime>
 {
 public override void DoWork(object arguement)
 {
 Trace.WriteLine(string.Format("MyThread thread id {0}", System.Threading.Thread.CurrentThread.ManagedThreadId));

Data = DateTime.Now;
 }
 }

What a nicely encapsulated thread! Below we can see how cleanly a MyThread can be used:

 MyThread thread = new MyThread();
 thread.OnComplete += new ThreadBase<DateTime>.ThreadComplete(thread_OnComplete);
 thread.DoWorkAsync();

 void thread_OnComplete(ThreadBase<DateTime> thread, DateTime data)
 {
 Trace.WriteLine(string.Format("Complete thread id {0}: {1}", Thread.CurrentThread.ManagedThreadId, data));
 }

Then I got to thinking what if I wanted the best of both worlds? Thanks to reflector I found out how BackgroundWorker’s RunWorkerCompleted event executes in the context of the calling thread. My generic ThreadBaseEx class offers two events: OnCompleteByThreadContext and OnCompleteByCallerContext.

 /// <summary>
 /// Abstract base class which performs some work and stores it in a data property
 /// </summary>
 /// <typeparam name="T">The type of data this thread will procure</typeparam>
 public abstract class ThreadBaseEx<T>
 {
 #region Private variables
 private AsyncOperation _asyncOperation;
 private readonly SendOrPostCallback _operationCompleted;
 #endregion

#region Ctor
 public ThreadBaseEx()
 {
 _operationCompleted = new SendOrPostCallback(AsyncOperationCompleted);
 }
 #endregion

#region Public methods
 /// <summary>
 /// Does the work asynchronously and fires the OnComplete event
 /// </summary>
 public void DoWorkAsync()
 {
 DoWorkAsync(null);
 }

 /// <summary>
 /// Does the work asynchronously and fires the OnComplete event
 /// </summary>
 /// <param name="arguement">The arguement object</param>
 public void DoWorkAsync(object arguement)
 {
 _asyncOperation = AsyncOperationManager.CreateOperation(null);
 ThreadPool.QueueUserWorkItem(DoWorkHelper, arguement);
 }

 /// <summary>
 /// Does the work and populates the Data property
 /// </summary>
 public void DoWork()
 {
 DoWork(null);
 }

 /// <summary>
 /// Does the work and populates the Data property
 /// </summary>
 /// <param name="arguement">The arguement object</param>
 /// <remarks>
 /// Can be called to run syncronously, which doesn't fire the OnComplete event
 /// </remarks>
 public abstract void DoWork(object arguement);
 #endregion

#region Private methods
 private void DoWorkHelper(object arguement)
 {
 DoWork(arguement);
 if (OnCompleteByThreadContext != null)
 OnCompleteByThreadContext.Invoke(this, Data);
 _asyncOperation.PostOperationCompleted(this._operationCompleted, arguement);
 }

private void AsyncOperationCompleted(object arg)
 {
 OnCompleteByCallerContext(this, Data);
 }
 #endregion

#region Properties
 public T Data { get; protected set; }
 #endregion

#region Events
 /// <summary>
 /// Delegate which is invoked when the thread has completed
 /// </summary>
 /// <param name="thread">The thread.</param>
 /// <param name="data">The data.</param>
 public delegate void ThreadComplete(ThreadBaseEx<T> thread, T data);

 /// <summary>
 /// Occurs when the thread completes.
 /// </summary>
 /// <remarks>This event operates in the context of the worker thread</remarks>
 public event ThreadComplete OnCompleteByThreadContext;

 /// <summary>
 /// Occurs when the thread completes.
 /// </summary>
 /// <remarks>This event operates in the context of the calling thread</remarks>
public event ThreadComplete OnCompleteByCallerContext;
 #endregion
 }

Your encapsulated thread will be the same as above, but now with two events allowing either scenario, depending on what suits.

Late binding DataGridView data with BackgroundWorker threads October 18, 2010

Posted by codinglifestyle in C#, Parallelism, Uncategorized, Winform.
Tags: , , ,
1 comment so far

In an increasingly multicore world you may notice your WinForm app never pushes your machine to 100%.  Great, right?  Erm, no.  Winforms are traditionally single threaded applications meaning we typically only tap into 1/2, 1/4, or even 1/8th of our processor’s potential.

I’ve recently been working on a utility containing a DataGridView for displaying work item data from TFS. Some of the column data had to be calculated so when displaying 500+ records the whole app was slowing down considerably. What I wanted was a delayed binding such that the cell would be initially blank, launch a thread, and update itself when the thread returned with the value. It turned out this was pretty easy.

First, use a data entity. You probably don’t have to, but I find having this layer offers an obvious place to add the changes I’ll cover below. The ideal place is in the bound property’s getter. Here you can see that the value is not yet calculated, launch a thread, and return blank or another default value in the meantime.

private int _nWeight = -1;

public int Weight

{

get

{

if (_nWeight < 0)

{

Tfs.GetWeight(Tag, GetWeightComplete);

_nWeight = 0;

}

return _nWeight;

}

}

private void GetWeightComplete(object sender, RunWorkerCompletedEventArgs e)

{

_nWeight = (int)e.Result;

if (Row < FormMain.Instance.Grid.Rows.Count)

FormMain.Instance.Grid.InvalidateRow(Row);

}

The property above represents the weight of the entity to be used in sorting or graphing by importance. Calculating the weight is a time intensive operation and is a great candidate for calculating in a worker thread. Notice a backing store, _nWeight, is used to check if the value has been calculated and also to cache the value. If _nWeight is uncached (-1) we launch the thread and return a default weight of 0 while the thread calculates the actual value. Notice when we call Tfs.GetWeight we pass the delegate, GetWeightComplete, as an argument. This function will ultimately be called when the thread returns.

public static void GetWeight(WorkItem wi, RunWorkerCompletedEventHandler onCompleteEvent)

{

BackgroundWorker worker    = new BackgroundWorker();

worker.DoWork             += new DoWorkEventHandler(GetWeightDoWork);

worker.RunWorkerCompleted += onCompleteEvent;

worker.RunWorkerAsync(wi);

}

private static void GetWeightDoWork(object sender, DoWorkEventArgs e)

{

WorkItem wi = (WorkItem)e.Argument;

int result = 0;

foreach (Revision rev in wi.Revisions)

{

if (rev.Fields[CoreField.ChangedBy].Value.ToString() == wi.Store.TeamFoundationServer.AuthenticatedUserDisplayName)

result += 1;

}

result = Math.Min(result, 10);

e.Result = result;

}

When a call is made to GetWeight you can see it uses the standard System.CompnentModel.BackgroundWorker class to manage the work. This has two main advantages: 1) an easy to use asynchronous event based pattern and 2) uses the thread pool for optimal thread management. Notice the two events, DoWork and RunWorkerCompleted are set before the thread is launched and that we can pass an arguement via RunWorkerAsync. GetWeightDoWork is called when the thread is launched and sets the result to the event arguments.  When we leave this function the RunWorkerCompleted event is called.

Finally, back in the data entity, GetWeightComplete is called when the thread has calculated the value.  The result is taken from the RunWorkercompletedEventArgs and set to the backing store. The form uses the singleton pattern and exposes the grid as a property (see here). This allows the data entity to easily invalidate the row which redraws the row taking the Weight value into account (in my case the weight determined the intensity of a yellow highlight drawn over the row indicating how often the authenticated user appeared in the work item’s revision history).

The user experience when using the above method is truely fantastic.  You get a responsive UI which immediately displays information with calculated information quickly coming in a few seconds later.  The standard binding method of the DataGridView further enhances this experience by only binding the data currently shown to the user.  So if only the first 25 rows are displayed, only those values will be calculated.  As we scroll down to show more rows the DataGridView will calculate only the newly bound rows (saving loads of time for potentially 1000’s of rows never shown).  Good luck unlocking your other cores for a better UI experience.

Start a WinForm Hidden June 18, 2009

Posted by codinglifestyle in CodeProject, Winform.
Tags: , , , ,
add a comment
This is a short one, there may be better ways but this was fast and simple.  Works for me!
The case here is I have a WinForm application and want the Main window to start hidden.  From the designer, you can’t set Visisble = false.  Also, setting this.Visible=false in the constructor or Load event has no effect.  Sure you can set it later (like in Paint) but the last thing you want is your window to flash and disappear every time your application starts.
So, here is all you have to do:
  1. In the designer, set your window Opacity to 0%
  2. In the constructor, pass in a boolean to indicate if the window is displayed.
  3. In the function body, add this line:
    Opacity = bVisible ? 100 : 0;
Simple, fast, done… moving on to other things in life.

Well okay… I’m not really one for leaving well enough alone.  The fact is, there is a better way but you’re not going to like it!  I imagine your main form is doing something like sitting in the notification tray and/or monitoring something like a windows service.  That functionality, whatever it is your application does, should be abstracted out of the form altogether.  You’re really not going to like what’s next.  This object, the meat of your program, can then exist in a worker thread and provide the relevant events to your UI so it may update itself if the main form is shown.
It’s not as bad as it sounds.  Go on, you know separating the logic from the UI layer is the right design!  We’ll abstract all our real functionality into a class called MyFunctionality.  We have BackgroundWorker in our .NET toolbox MyFunctionality will inherit from.  Then override OnDoWork where we’ll put the code we originally had in the main form’s constructor or Load event.  We will handle the case of exiting the thread in case the user logs off.  Last, we just need optional events for the UI to receive an update from our functionality object.  An example of this is below, with our DisplayChanged event and DoDisplayChanged function.  Use a custom event to pass more information.
class MyFunctionality : System.ComponentModel.BackgroundWorker
{
public event EventHandler DisplayChanged = null;
protected override void OnDoWork(DoWorkEventArgs e)
{
//The meat of my program…
Microsoft.Win32.SystemEvents.SessionEnded += new Microsoft.Win32.SessionEndedEventHandler(SystemEvents_SessionEnded);
base.OnDoWork(e);
}
void SystemEvents_SessionEnded(object sender, Microsoft.Win32.SessionEndedEventArgs e)
{
this.CancelAsync();
}
void DoDisplayChanged()
{
if (DisplayChanged != null)
DisplayChanged.Invoke(this, EventArgs.Empty);
}
}
Your form will create the thread and add the relevant events, in this case DisplayChanged.
private void Main_Load(object sender, EventArgs e)
{
MyFunctionality func = new MyFunctionality();
func.DisplayChanged += new EventHandler(Func_DisplayChanged);
func.RunWorkerAsync();
}
Lastly, we just need to make a small change in Program.cs to either (A) instantiate our main form, which in turn launches the thread hosting our functionality, or (B) directly instantiate the thread without ever using our main form.
if (bDisplay)
Application.Run(new Main());
else
{
MyFunctionality func = new MyFunctionality ();
func.RunWorkerAsync();
Application.Run();
}
See, I told you you weren’t going to like it!