Blocking windows shutdown from a form on a different thread

P

Paul

Hi,

In my application I have a separate DLL which handles encrypting data
in the application (including saving it to disk, and loading it).

The main application calls for example "EncryptToDisk(object obj,
string fileName)".
This method then opens a new form (internal to the DLL assembly) with
a progress bar on it which is updated to show how things are going.
In order for the new form be displayed and update correctly I had to
put it on it's own thread, otherwise the Encrypt function would take
all the CPU time from the form.
This is all working fine.

However when the progress bar form is open I want it to block the
Windows shutdown, because shutting down during this period will cause
the data to be lost.
The process only takes a few seconds, but as soon as it receives the
command to shutdown from windows, it kills everything.

I tried adding the following to the new form:
protected override void WndProc(ref Message aMessage)
{
const int WM_QUERYENDSESSION = 0x0011;
const int WM_ENDSESSION = 0x0016;

if (aMessage.Msg == WM_QUERYENDSESSION || aMessage.Msg ==
WM_ENDSESSION)
return;

base.WndProc(ref aMessage);
}

This doesn't do anything though, I can see WndProc is passing other
messages through (I can't debug the endsession messages), but the form
and application are still killed.
 
W

Willy Denoyette [MVP]

Paul said:
Hi,

In my application I have a separate DLL which handles encrypting data
in the application (including saving it to disk, and loading it).

The main application calls for example "EncryptToDisk(object obj,
string fileName)".
This method then opens a new form (internal to the DLL assembly) with
a progress bar on it which is updated to show how things are going.
In order for the new form be displayed and update correctly I had to
put it on it's own thread, otherwise the Encrypt function would take
all the CPU time from the form.
This is all working fine.

However when the progress bar form is open I want it to block the
Windows shutdown, because shutting down during this period will cause
the data to be lost.
The process only takes a few seconds, but as soon as it receives the
command to shutdown from windows, it kills everything.

I tried adding the following to the new form:
protected override void WndProc(ref Message aMessage)
{
const int WM_QUERYENDSESSION = 0x0011;
const int WM_ENDSESSION = 0x0016;

if (aMessage.Msg == WM_QUERYENDSESSION || aMessage.Msg ==
WM_ENDSESSION)
return;

base.WndProc(ref aMessage);
}

This doesn't do anything though, I can see WndProc is passing other
messages through (I can't debug the endsession messages), but the form
and application are still killed.



You don't need to catch the WM_QUERYENDSESSION, all you need to do is give
the thread some time to terminate in you FormClosing event handler.
Something similar like the following will do:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(myThread.Join(waitTime))
e.Cancel = false;
else // thread has not terminated yet, adjust your waitTime
e.Cancel = true; // alert user, block shutdown
}
 
P

Paul

You don't need to catch the WM_QUERYENDSESSION, all you need to do is give
the thread some time to terminate in you FormClosing event handler.
Something similar like the following will do:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if(myThread.Join(waitTime))
e.Cancel = false;
else // thread has not terminated yet, adjust your waitTime
e.Cancel = true; // alert user, block shutdown

}

Hi,

Unfortunately that doesn't work either.
I think it's because the parent thread is being killed by windows so
it just closes the thread the progress bar is running on without
caring what it does.
The problem is that the bulk of the work done in my assembly is not
done in a form, is there a way to halt/pause the shutdown a class
which isn't based on a form?
 
W

Willy Denoyette [MVP]

Paul said:
Hi,

Unfortunately that doesn't work either.
I think it's because the parent thread is being killed by windows so
it just closes the thread the progress bar is running on without
caring what it does.
The problem is that the bulk of the work done in my assembly is not
done in a form, is there a way to halt/pause the shutdown a class
which isn't based on a form?



It should work, windows does not directly kill the threads in case of a
shutdown, it gives the application a fair chance to do some orderly shutdown
(it posts a WM_ENDSESSION followed by a WM_CLOSE to all top level windows).
If you check the FormClosingEventArgs you will see that:
e.CloseReason == CloseReason.WindowsShutDown. However, there is no need to
check this, the event handler will get called whenever the TOP level window
receives a WM_CLOSE message and you'll have to set e.Cancel = true when you
don't want the program to terminate.
Could you please post a complete sample that illustrates the issue?

Willy.
 
W

Willy Denoyette [MVP]

Willy Denoyette said:
It should work, windows does not directly kill the threads in case of a
shutdown, it gives the application a fair chance to do some orderly
shutdown (it posts a WM_ENDSESSION followed by a WM_CLOSE to all top level
windows).
If you check the FormClosingEventArgs you will see that:
e.CloseReason == CloseReason.WindowsShutDown. However, there is no need to
check this, the event handler will get called whenever the TOP level
window receives a WM_CLOSE message and you'll have to set e.Cancel = true
when you don't want the program to terminate.
Could you please post a complete sample that illustrates the issue?

Willy.

Following illustrates how to wait for a max. of 5 seconds for thread to
finish.
When the thread did not finish before time-out, you should adapt the
time-out value.

Thread th = ...


private void Form1_FormClosing(object sender, FormClosingEventArgs
e)
{
if (th != null)
{
if (th.Join(5000))
e.Cancel = false; // ok thread done.
else // time-out, thread still running
{
e.Cancel = true; // prevent program from closing,
system will alert user after ~30 seconds.
return;
}
}

Willy.
 
P

Paul

Following illustrates how to wait for a max. of 5 seconds for thread to
finish.
When the thread did not finish before time-out, you should adapt the
time-out value.

Thread th = ...

private void Form1_FormClosing(object sender, FormClosingEventArgs
e)
{
if (th != null)
{
if (th.Join(5000))
e.Cancel = false; // ok thread done.
else // time-out, thread still running
{
e.Cancel = true; // prevent program from closing,
system will alert user after ~30 seconds.
return;
}
}

Willy.

Hi,

Code is as follows:
Main application/form calls the following which is in a separate .DLL
file:
public EncryptedObject Encrypt(object obj)
{
ProgressBarCreate();
EncryptedObject returnedObj = null;
try
{
returnedObj = EncryptInternal(obj);
}
catch
{
ProgressBarUpdateStatus(ProgressBar.Status.ERROR);
}
finally
{
ProgressBarDestroy();
}
return returnedObj;
}

EncryptInternal then does the actual work, and periodically calls
ProgressBarIncrement(); / ProgressBarUpdateStatus to update the
ProgressBar form.
I made the following methods to handle interacting with the
progressbar form:
private void ProgressBarCreate()
{
this.progressBar = new ProgressBar();
this.progressBarThread = new Thread(new
ThreadStart(this.progressBar.Start));
this.progressBarThread.Start();
// The following is required to make sure the form has
loaded before we try to modify it
while (!this.progressBar.InvokeRequired) ;
}
private void ProgressBarUpdateStatus(ProgressBar.Status
status)
{
this.progressBar.Invoke((MethodInvoker)delegate
{ this.progressBar.UpdateStatus(status); });
}
private void ProgressBarIncrement()
{
this.progressBar.Invoke((MethodInvoker)delegate
{ this.progressBar.IncrementBar(); });
}
private void ProgressBarDestroy()
{
Thread.Sleep(1500);
this.progressBar.Invoke((MethodInvoker)delegate
{ this.progressBar.Stop(); });
this.progressBarThread.Join();
this.progressBarThread = null;
this.progressBar = null;
}

The progressbar itself is as follows:
internal partial class ProgressBar : Form
{
// List of possible status text's which can be set
public enum Status
{
/* various status types here */
ERROR
}

public ProgressBar()
{
InitializeComponent();
}
public void Start()
{
ShowDialog();
}
public void Stop()
{
Close();
}
// Add a new line of status text to the label
private bool updatingStatus = true;
public void UpdateStatus(Status status)
{
if (status == Status.ERROR)
{
uxLabel.Text += " ERROR\nERROR: An error has been
detected";
updatingStatus = false;
}
if (updatingStatus)
{
uxLabel.Text += " Done\n";
switch (status)
{
/* various status updates here */
}
}
}
// Progress the bar
public void IncrementBar()
{
uxProgressBar.PerformStep();
}


Setting e.Cancel to true in the progressbar form doesn't stop the
shutdown, nor does returning from the WndProc functions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top