Very simply, I have created a simple shutdown timer with C# Forms that runs a shutdown command through the command prompt with an associated timer. I have noticed that Windows displays a notification indicating the computer is about to be shutdown just prior to the event (I think it appears no sooner than 15 min or so).
My question is this: Is there a way to stop that popup notification from appearing without interacting with the registry or making intrusive changes in Windows 7/8/10?
I haven't been able to find a good resource on interacting with native Windows (OS) elements like popups and notifications handed out by the OS for these kinds of events. Anyway to catch that event and modify it (like to not display the notification) would be great. But a more permanent solution such as disabling it through the registry or something is also fine. But I'm more interested in a programmatic solution.
Note: If there is no intrusive way of handling this, then I am really open to anything. I'm also trying to re-learn Forms so the exercise is worth it.
Edit: The code is on GitHub and pretty much completed but I would like to add this functionality to it.
Based upon what McNets said in his comment:
Why do not use the std. windows shutdown command? Add a scheduled task that shutdown the system immediately
I decided to leverage the Window's Task Scheduler. I decided to use Task Scheduler but there are others out there that do similar things.
Even though this approach is a bit more involved than just opening up a command prompt, it does everything I need including a circumvention of the shutdown banner that I noted in my question. Note, I can still give the user the option of running it with a banner like I did before but I have yet to implement that functionality. It is pretty trivial though (especially with the base code below). Simply add the timer to the shutdown args instead of /t 1 and set the task to run immediately.
This probably isn't the only solution but it is the least invasive that I could find. Thanks for all the help everyone. I appreciate it.
Full Source on GitHub.
Globals:
public const string DEFAULT_TASK_NAME = "ScheduledShutdownTimer";
private const string SHUTDOWN_COMMAND_ARGS = "/s /c \"Scheduled Computer shutdown via " +
"the Windows Shutdown Timer App\" /t 1";
Create a task:
using (TaskService ts = new TaskService())
{
// If the task doesn't exist, create it.
if (TimerExists(DEFAULT_TASK_NAME))
throw new TimerExists("The timer already exists in the task scheduler. You " +
"must modify it instead of attempting to create it!");
else
{
try
{
TaskDefinition td = ts.NewTask();
td.RegistrationInfo.Date = _currentTime; // DateTime.Now
td.RegistrationInfo.Source = "Windows Shutdown Timer";
td.RegistrationInfo.Description = "Shutdown Timer initiated Windows " +
"Shutdown Timer";
td.Settings.Enabled = true;
td.Triggers.Add(new TimeTrigger(_shutdownTime));
td.Actions.Add(new ExecAction("shutdown", SHUTDOWN_COMMAND_ARGS, null));
TaskService.Instance.RootFolder
.RegisterTaskDefinition(DEFAULT_TASK_NAME,td);
Properties.Settings.Default.ShutdownTimer = _shutdownTime;
Properties.Settings.Default.Save();
StartLocalTimer();
}
catch(Exception)
{
DialogResult alert = MessageBox.Show("The timer couldn't be set. ",
"Error - Couldn't Set Timer!", MessageBoxButtons.RetryCancel,
MessageBoxIcon.Error);
if (alert == DialogResult.Retry)
CreateShutdownTimer(numSeconds);
}
}
}
Modify a Task:
using (TaskService ts = new TaskService())
{
// If the task exists, update the trigger.
if (TimerExists(DEFAULT_TASK_NAME))
{
Task task = ts.GetTask(DEFAULT_TASK_NAME);
if (task.Definition.Triggers.Count == 1)
task.Definition.Triggers.RemoveAt(0);
else if (task.Definition.Triggers.Count > 1)
{
for (int index = 0; index < task.Definition.Triggers.Count - 1; index++)
{
task.Definition.Triggers.RemoveAt(index);
}
}
// Add the new trigger after making sure it is the only one.
task.Definition.Triggers.Add(new TimeTrigger(_shutdownTime));
if (task.Definition.Actions.Count == 1)
task.Definition.Actions.RemoveAt(0);
else if (task.Definition.Actions.Count > 1)
{
for (int index = 0; index < task.Definition.Actions.Count - 1; index++)
{
task.Definition.Actions.RemoveAt(index);
}
}
// Add the new action after making sure it is the only one.
task.Definition.Actions.Add(new ExecAction("shutdown", SHUTDOWN_COMMAND_ARGS,
null));
// Reset the status in case it was set as anything but "Ready"
task.Definition.Settings.Enabled = true;
task.RegisterChanges();
Properties.Settings.Default.ShutdownTimer = _shutdownTime;
Properties.Settings.Default.Save();
// Starts the timer display and enables/disables buttons.
StartLocalTimer();
}
else
throw new NoTimerExists("The timer doesn't exist in the task scheduler. You " +
"must create it instead of attempting to modify it!");
}
Stop a task:
using (TaskService ts = new TaskService())
{
// If the task exists, remove the trigger.
// Note: the included Stop() method doesn't work.
if (TimerExists(DEFAULT_TASK_NAME))
{
Task task = ts.GetTask(DEFAULT_TASK_NAME);
task.Definition.Triggers.RemoveAt(0);
task.RegisterChanges();
StopLocalTimer(); // Resets display timers in program
}
else
throw new NoTimerExists("The timer doesn't exist in the task scheduler. " +
"You must create it instead of attempting to modify it!");
}
Related
I have thef ollowing background worker in my app which is meant to start a user's session automatically if there is not already one available.
This is done on a backgroundworker (backgroundInit) on initialisation. As you can see below, I have a while loop which continues to run as long as the var checker remains false:
var checker = false;
var i = 0;
while (checker == false)
{
_session = funcs.GetSession(_servers, _name);
_sessID = _session[0].Trim();
_servName = _session[1];
checker = funcs.CheckRunning("lync.exe");
i++;
if (i > 200)
{
break;
}
}
The CheckRunning method just checks if a specified program (in this case, "lync") is currently running and returns either true or false accordingly (This is done via a CMD command).
When I run the app in an empty session however, the while loop only iterates one time before breaking out, even though "Lync" is definitely not running.
Is there any reason why running a process or too many processes from within a Backgroundworker may cause it to exit?
As the comments mentioned, this was not an issue with the BackgroundWorker, but rather an exception occurring at _sessID = session[0].Trim(); where the session had not yet started, so there is no ID.
To resolve this, I simply placed a Try/Catch block around this assignment, and let the program silently ignore the exception:
try
{
_sessID = _session[0].Trim();
_servName = _session[1];
}
catch (Exception exp)
{
// MessageBox.Show(exp.Message);
}
This works for me, as the loop will continue checking until the counter i reaches the 200 limit, at which stage the program will accept failure.
I'm building a c# console application that automatically finds updates, downloads them and installs them.
I'm using this method for the installation part:
public static void InstallUpdates(UpdateCollection DownloadedUpdates)
{
UpdateSession UpdateSession = new UpdateSession();
UpdateInstaller InstallAgent = UpdateSession.CreateUpdateInstaller() as UpdateInstaller;
InstallAgent.Updates = DownloadedUpdates;
//Starts a synchronous installation of the updates.
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa386491(v=VS.85).aspx#methods
IInstallationResult InstallResult = InstallAgent.Install();
}
According to this link, I can check whether or not a reboot is required. What I want to achieve is a system reboot that is done immediately when the RebootRequiredBeforeInstallation changes to true.
I thought of doing this, but that won't work because I can't use an else statement:
while (!InstallAgent.RebootRequiredBeforeInstallation)
{
} else
{
// Reboot
}
What would be the correct approach to this?
How about this:
while (!InstallAgent.RebootRequiredBeforeInstallation)
{
// Install things
// If the installer sets the RebootRequiredBeforeInstallation flag to
// true, the while loop will terminate and you can reboot.
}
if (InstallAgent.RebootRequiredBeforeInstallation)
{
// Reboot
}
I have a WPF project that uses background workers to interfaces with some external hardware (Test & Measure equipment, etc), write to local files, and insert data into a database as it runs. Program flow is basically sequential, the background workers in use are to keep the GUI accessible for the user, and were chosen because I haven't had issues with using them before. We take measurements, do some stuff, log, then repeat. There is a status log on the GUI that displays messages as we go.
All of this works beautifully for hours on end, however, eventually, without fail, it appears that the background worker used to write to the database never calls DoWork.
BackgroundWorker DbLogWorker = new BackgroundWorker();
...
DbLogWorker.DoWork +=
new DoWorkEventHandler(DbLogWorker_DoWork);
DbLogWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
DbLogWorker_RunWorkerCompleted);
ProcessScanData is called when data has been retrieved from one of the pieces of hardware, setting a property and firing a generic property changed event
(disclaimer: some extra code everywhere as I was investigating to see whats going on):
private void Data_DataSetChanged(object pSender, EventArgs pArgs)
{
this.Test.ProcessScanData(OsaVm.Osa.Data.DataSet);
}
...
public void ProcessScanData(SortedList<double,double> pData)
{
...
RaiseLogEvent(MessageType.SystemGeneral, "Logging to database...");
DbLogWorker.RunWorkerAsync(new AsyncDbLogArgs(CurrentChannel, tempdate,
loss1, loss2, loss3,
CurrentTemperature, CurrentPressure, CurrentRoomTemp));
}
private void DbLogWorker_DoWork(object pSender, DoWorkEventArgs pArgs)
{
AsyncDbLogArgs args = (AsyncDbLogArgs)pArgs.Argument;
string filename = string.Empty;
try
{
long datakey = Db.LogScan(CurrentChannel, args.Time,
args.Temperature, args.Pressure, args.RoomTemperature,
args.Loss1, args.Loss2, args.Loss3);
filename = args.Time.ToString(FOLDER_DATETIME_FORMAT) + "_[" + datakey.ToString() + "]";
}
catch (Exception ex)
{
filename = args.Time.ToString(FOLDER_DATETIME_FORMAT) + "_{" + (fileindex++) + "}";
}
pArgs.Result = new Tuple<AsyncDbLogArgs, string>(args, filename);
}
Symptoms:
Everything works fine for anywhere between 1 hour and ~16 hours until eventually we get to a point where we see "Logging to database..." and nothing else ever happens. No more messages, no exceptions (release build on target machine), no database entry...etc. This happens consistently.
I've been scratching my head on this for a while. Any leads will help, I have some workarounds in mind but I'd really like to know whats going on so I can avoid this in the future.
Thanks
Edited back to original code...thought the most recent would help avoid some "how do you know its not firing" questions
When I try to start my c# service it says:"starting" for a second and it turns back to being "stopped"
What can be the problem? I had the same code before, and it worked but made some changes in the code now and it stopped working. Here is what I added to my code:
App Config:
<add key="cut-copy" value="copy"/>
Normal code:
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
String cut_copy = ConfigurationManager.AppSettings[#"cut-copy"];
if (cut_copy == "copy")
{
cut = false;
}
else
{
cut = true;
}
if (WaitForFileAvailable(e.FullPath, TimeSpan.FromSeconds(10)))
{
var file = Path.Combine(source, e.Name);
var copy_file = Path.Combine(target, e.Name);
var destination = Path.Combine(target, Path.ChangeExtension(source, Path.GetExtension(source)));
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Copy(e.FullPath, Path.Combine(target, e.Name));
}
else// If it doesn't, just copy the file.
{
if (cut == true)
{
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Move(Path.Combine(e.FullPath, e.Name), target);
}
}
else
{
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Copy(e.FullPath, Path.Combine(target, e.Name));
}
}
//under this is more code that didn't change
}
EDIT: ONSTART:
protected override void OnStart(string[] args)
{
base.OnStart(args);
this.fileSystemWatcher1 = new System.IO.FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).BeginInit();
fileSystemWatcher1.Path = source;
fileSystemWatcher1.Changed += new FileSystemEventHandler(fileSystemWatcher1_Changed);
fileSystemWatcher1.Created += new FileSystemEventHandler(fileSystemWatcher1_Created);
fileSystemWatcher1.Deleted += new FileSystemEventHandler(fileSystemWatcher1_Deleted);
fileSystemWatcher1.Renamed += new RenamedEventHandler(fileSystemWatcher1_Renamed);
this.fileSystemWatcher1.EnableRaisingEvents = true;
this.fileSystemWatcher1.IncludeSubdirectories = true;
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).EndInit();
logger("Service started "+ DateTime.Now);
}
What am I doing wrong?
Usually this behavior indicates that the OnStart method ends without leaving any threads running. I have experienced that if an unhandled exception is thrown starting the service, the service doesn't go into Stopped state, but the service manager shows the "Starting service" window for 30 seconds and then fails saying it can't determine the service's state.
I'm not sure whether the FileSystemWatcher actually spawns a new thread that keeps running. You could (just to try it), also create a System.Timers.Timer that fires every 30 seconds in OnStart and stop that timer in OnStop. If the service keeps running then, you have to spawn a new thread for the FileSystemWatcher.
Usually, in OnStart you'd spawn a separate thread that does the service's work. Be it waiting for TCP connections, a timer that does things on a regular basis or any other kind of thread. If you don't do that, the service just stops as soon as there are no more threads active.
For the code, the only answer that anyone could give you are just "guessing". You better debug it yourself.
The easiest way to hit a break-point in a Windows Service is to put this line of code at the beginning of the OnStart method:
Debugger.Break();
Compile your service in Debug mode, so you can have all the necessary symbols in your executable.
Install your service
Start it from the service list.
You will get a prompt for debugging the "yourservicename.exe" program.
Say Yes-debug, Choose debugger.
Choose the correct Visual Studio version as your debugger.
Now you will be in the Debugger.Break line
Have fun
I am developing a .net application, where I am using FileSystemWatcher class and attached its Created event on a folder. I have to do action on this event (i.e. copy file to some other location). When I am putting a large size into the attached watch folder the event raised immediately even the file copy process still not completed. I don’t want to check this by file.open method.
Is there any way get notify that my file copy process into the watch folder has been completed and then my event get fire.
It is indeed a bummer that FileSystemWatcher (and the underlying ReadDirectoryChangesW API) provide no way to get notified when a new file has been fully created.
The best and safest way around this that I've come across so far (and that doesn't rely on timers) goes like this:
Upon receiving the Created event, start a thread that, in a loop, checks whether the file is still locked (using an appropriate retry interval and maximum retry count). The only way to check if a file is locked is by trying to open it with exclusive access: If it succeeds (not throwing an IOException), then the File is done copying, and your thread can raise an appropriate event (e.g. FileCopyCompleted).
I have had the exact same problem, and solved it this way:
Set FileSystemWatcher to notify when files are created and when they are modified.
When a notification comes in:
a. If there is no timer set for this filename (see below), set a timer to expire in a suitable interval (I commonly use 1 second).
b. If there is a timer set for this filename, cancel the timer and set a new one to expire in the same interval.
When a timer expires, you know that the associated file has been created or modified and has been untouched for the time interval. This means that the copy/modify is probably done and you can now process it.
You could listen for the modified event, and start a timer. If the modified event is raised again, reset the timer. When the timer has reached a certain value without the modify event being raised you can try to perform the copy.
I subscribe to the Changed- and Renamed-event and try to rename the file on every Changed-event catching the IOExceptions. If the rename succeeds, the copy has finished and the Rename-event is fired only once.
Three issues with FileSystemWatcher, the first is that it can send out duplicate creation events so you check for that with something like:
this.watcher.Created += (s, e) =>
{
if (!this.seen.ContainsKey(e.FullPath)
|| (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
{
this.seen[e.FullPath] = DateTime.Now;
ThreadPool.QueueUserWorkItem(
this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
}
};
where this.seen is a Dictionary<string, DateTime> and this.seenInterval is a TimeSpan.
Next, you have to wait around for the file creator to finish writing it (the issue raised in the question). And, third, you must be careful because sometimes the file creation event gets thrown before the file can be opened without giving you a FileNotFoundException but it can also be removed before you can get a hold of it which also gives a FileNotFoundException.
private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
{
// Make sure the just-found file is done being
// written by repeatedly attempting to open it
// for exclusive access.
var path = (string)threadContext;
DateTime started = DateTime.Now;
DateTime lastLengthChange = DateTime.Now;
long lastLength = 0;
var noGrowthLimit = new TimeSpan(0, 5, 0);
var notFoundLimit = new TimeSpan(0, 0, 1);
for (int tries = 0;; ++tries)
{
try
{
using (var fileStream = new FileStream(
path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
// Do Stuff
}
break;
}
catch (FileNotFoundException)
{
// Sometimes the file appears before it is there.
if (DateTime.Now - started > notFoundLimit)
{
// Should be there by now
break;
}
}
catch (IOException ex)
{
// mask in severity, customer, and code
var hr = (int)(ex.HResult & 0xA000FFFF);
if (hr != 0x80000020 && hr != 0x80000021)
{
// not a share violation or a lock violation
throw;
}
}
try
{
var fi = new FileInfo(path);
if (fi.Length > lastLength)
{
lastLength = fi.Length;
lastLengthChange = DateTime.Now;
}
}
catch (Exception ex)
{
}
// still locked
if (DateTime.Now - lastLengthChange > noGrowthLimit)
{
// 5 minutes, still locked, no growth.
break;
}
Thread.Sleep(111);
}
You can, of course, set your own timeouts. This code leaves enough time for a 5 minute hang. Real code would also have a flag to exit the thread if requested.
This answer is a bit late, but if possible I'd get the source process to copy a small marker file after the large file or files and use the FileWatcher on that.
Try to set filters
myWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;