My apologies if this question has been asked and already answered, I have spent the best part of three days experimenting with WndProc() in WinPE (for Windows 10).
How do I "Catch" Messages through WndProc() (or a Handler Routine) in WinPE (Windows 10)?
I have a custom application(written in C# .Net 4.5.2) that is launched by WinPEShl.exe, on boot of WinPE. This is currently an Application that provides access to other applications to enable Windows deployment or Image Capture.
While this Application may not always be the current Windows Form, there a one or two routines that need to be completed before Windows PE has shutdown. I would like this to happen on either the WM_QUERYENDSESSION/WM_ENDSESSION or WTS_SESSION_CHANGE notifications through the overriden WndProc() function.
Currently this is my WndProc() function:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
// Listen for operating system messages.
if (m.Msg == WM_QUERYENDSESSION)
{
Program.WriteLogFile(4, 1, "WM_QUERYENDSISSION: received. Return Bool=True");
}
if (m.Msg == WTS.WM_WTSSESSION_CHANGE)
{
int wValue = m.WParam.ToInt32();
if (wValue == WTS.WTS_SESSION_LOGOFF)
{
//Write my darn Log file!
Program.WriteLogFile(4, 1, "WM_WTS_SESSION_LOGOFF: received. Return Bool=True");
}
if (wValue == WTS.WTS_CONSOLE_DISCONNECT)
{
Program.WriteLogFile(4, 1, "WM_WTS_SESSION_DISCONNECT: received. Return Bool=True");
}
}
base.WndProc(ref m);
}
Program.WriteLogFile() is a Log File writer (as it says!) that will be used to report that the system is shutting down.
From this Link I am aware that as WinPE is a Stream lined version of Windows, with only a small number of API's being available for usage. I have looked through both of the MinCore.lib sets for both Windows API Sets mentioned - resulting in nill success for finding any function in relation to the WndProc() holder function. I have evensearched OneCore.lib aswell.
I did however find the WTSRegiSessionNotifications() functions. Again even though they register fine in WinPE, my Shell App doesn't receive the messages if another application shuts down the system (such as Windows Setup on completion of the first phase).
Testing in Windows provides both results in the associated application log file.
Should I be using a hidden console app, to capture the CTRL_LOGOFF_EVENT/CTRL-SHUTDOWN_EVENT, or should i be using a service (and have all log writing routed through it)?
Log files from Windows 10 (working as should be) and WinPE available on request.
Thanks for any and all help in this matter.
Kind regards
Richie
I'm not sure why you're not using the regular winforms form lifetime events. I'm guessing you're somewhat more used to C-oriented ways of doing things?
If you've built a winpeshl.ini that starts your program, and it's the only program listed (beyond winpeinit.exe), then when it shuts down, PE shuts down. This is true even if you spawn other applications from your main program (via System.Diagnostics.Process).
So, don't list a lot of programs in winpeshl.ini. Let your one-and-only main program start the rest.
FWIW, I've got a similar app...does image capture, deploy and periodic maintenance. We put our winPE in it's own partition and do a lot of active BCD management to make it boot when we want. I've closed off all the "normal" shutdown routes. Yes, I suppose a persistent user can circumvent...but if they're determined, so be it...but we haven't had an issue with that.
Related
Keep in mind, this is a Console App running .NET with C#. Also, I might later convert this into a service, but for now, I need to handle the shutdown while my Console App is running.
My system sometimes gets shutdown automagically by Windows Updates or by Kace Updates. This is irritating when my program is in the middle of doing long tests in the middle of the night and I'm not there to see it happen. I would like to be able to detect the system shutting down and delay it long enough for my tests to finish gracefully and allow me to gracefully exit my program keeping all my test data intact.
I've looked at various threads and can't seem to find a working solution. I've tried both of these:
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
Application.ApplicationExit += OnApplicationExit;
But neither of these events seem to get fired. I basically want to send an e-mail notification to myself when the shutdown is detected and pause the shutdown. Or at least log a message somehow...
I can simulate this behavior using (of all things) 'notepad.exe'. I just open notepad.exe, type some junk, and then attempt to ShutDown my computer. The computer tries to shutdown, and then shows a message that says it cannot shut down because 'notepad' is blocking it.
I would like MY program to block the shutdown (or at least delay it) the same way as 'notepad.exe' does it.
Any information, events, or other ideas that you may have would be very useful. Thanks in advance for your assistance.
This can help you:
https://www.meziantou.net/prevent-windows-shutdown-or-session-ending-in-dotnet.htm
When the user logs off or when he stops his machine, Windows quits all open applications. Some applications want to execute code before ending in order not to lose the current state (a document being written for example).
For this Windows sends a message to all applications:
WM_QUERYENDSESSION when closing the session
WM_ENDSESSION when shutting down the machine
Windows provides 3 methods:
ShutdownBlockReasonCreate
Indicates that the system cannot be shut down and sets a reason string to be displayed to the user if system shutdown is initiated ("I want to live" in the screenshot)
ShutdownBlockReasonDestroy
Indicates that the system can be shut down
SetProcessShutdownParameters
This function sets a shutdown order for a process relative to the other processes in the system
The final method determines the order in which Windows sends the WM_ENDSESSION and WM_QUERYENDSESSION messages to the applications. This is used in the case where an application A depends on an application B. The application A wishes to be informed before the application B that the machine is stopping to potentially block this stop. Thus application B remains alive as application A blocks the shutdown.
The priority is defined with an integer between 0 and 0x4FF. 0x4FF is the highest priority (called first), 0 is the lowest priority (last called). By default, applications have priority 280.
In the link there is also a code for WinForm that can be adapted.
Something like:
private const int WM_QUERYENDSESSION=0x0011;
private bool isShuttingDown=false;
protected override void WndProc(ref Message m)
{
if(m.Msg==WM_QUERYENDSESSION)
{
isShuttingDown=true;
}
base.WndProc(ref m);
}
As part of some work I need to get done for Windows 10, I have written a code in C# that essentially detects every minute whether a PC is in screen saver mode or not, and it writes to a table in MySQL the relevant status ("PC in use" if the screen saver is off, "available PC" if the screen saver is on).
I did this using (full link if required - https://www.codeproject.com/Articles/17067/Controlling-The-Screen-Saver-With-C):
// Returns TRUE if the screen saver is actually running
public static bool GetScreenSaverRunning( )
{
bool isRunning = false;
SystemParametersInfo( SPI_GETSCREENSAVERRUNNING, 0,
ref isRunning, 0 );
return isRunning;
}
The code works flawlessly in console application mode (I made a loop to test it out over a minute with a check up on screen save status every 10 seconds), this means in MySQL the status was set correctly every time, depending on the screen save status at the moment of the check up.
The problem occurs when I use this code for a windows service. The service is installed correctly, the log on tab is set on Local System (I also tried with the logged in user instead, same results) and I allow the service to interact with the desktop, just in case, but the difference here is that no matter if the PC enters screen save or not, it always returns false on GetScreenSaverRunning(), thus setting the status of the PC in MySQL as "PC in use", even if the screen saver is on at the moment of check up.
I get the sense that the problem isn't in the code itself, since it works without any issues as a console application, but perhaps something behind the scenes. I tried to search here and on many other websites, haven't found anything related to such a problem.
Does anyone have any idea at all what might be the issue? Any help and/or suggestions will be greatly appreciated!
Thank you in advance.
(I could post the code if required, but it is pretty much straight forward and the main part of it, controlling the screen save detection, is taken from the website mentioned above, afterwards it's a simple if (GetScreenSaverRunning() == true) )
Ever since Vista, Services are barred from a Interactive Session. Even if they run under the same rights, they do not get a interactive Session. I would guess that is getting in the way here.
While you can overwrite this behavior in the Service settings, this is not adviseable for new code. Consider making this a Background Task started by the Task Sheduler instead.
Because the windows service runs in different session then the windows logon. You can't interact with the desktop related services unless you run the windows service in win logon session. There used to be an option in Windows service manager where you can set the properties to "Interact with desktop session" but I don't think that ever worked.
There's a work around to run the windows service using the win logo session.
See this helper class that can get the current logged on user session and interact with the desktop services. https://github.com/murrayju/CreateProcessAsUser/blob/master/ProcessExtensions/ProcessExtensions.cs
Similar to this question which invokes the Windows 10 store to allow a user to write a review or rate an app, I'd also like to be able to invoke the Windows 10 Feedback app and allow users to provide feedback there.
I cannot seem to find much information on:
How this works in general. Can any old app use this service? (I
notice it just kind of shows whatever apps I have running)
How to invoke the Windows Feedback app with my package id
In short - not that I can see.
Other apps are invoked via protocol activation. I haven't seen this documented for the feedback app though so I have to err on the side of 'we haven't made this available yet' (I'm still checking though)
Here's an overall guide to the process http://blog.jerrynixon.com/2012/10/walkthrough-using-windows-8-custom.html?m=1
When I look in the registry under HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Protocol I see (shortened a tad)
[HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Protocol\PackageId\Microsoft.WindowsFeedback...\ActivatableClassId\App.AppX7eaybq6p4x7d4jgd6w6jk7r5dg6yhmbf.mca\CustomProperties]
"Name"="windows-feedback"
So - give that a try via launching windows-feedback
If I do Windows Key-R (run): windows-feedback://
it works fine so this should work:
var uri = new Uri(#"windows-feedback://");
var success = await Windows.System.Launcher.LaunchUriAsync(uri);
if (success)
{
// URI launched
}
else
{
// URI launch failed
}
Update
I've done some searching and it seems the magic parameter there is
windows-feedback:?contextid=522
That launches the NFL feedback for example. This is a predetermined number - I'm not sure how one gets on this list though.
In my windows phone7(Silverlight) Application I have to display a message box asking, the user to confirm(Yes/No) before exit from the application [on device back button click].
The problem is I have to use a custom messagebox(using a popup) to get user confirmation, and I have no way to get exit from the application.(No method found which will exit the application like dispose() or close()).
if I didn't have to use a custom messagebox, the on the Device back key press event "OnBackKeyPress" I would have use the following logic and done my work
MessageBoxResult res = MessageBox.Show("Do you want to Exit?", "Exit", MessageBoxButton.OKCancel);
if (res == MessageBoxResult.OK)
{
if (NavigationService.CanGoBack)
{
while (NavigationService.RemoveBackEntry() != null)
{
NavigationService.RemoveBackEntry();
}
}
}
else
{
e.Cancel = true;
}
The problem is I need to use the custom messagebox and done this work. Same problem arise if need to implement a button to exit the application with out using the device back button.
I found in several posts suggesting to throw an exception and make this done. Following are some of them
http://mobile.dzone.com/articles/windows-phone-mango-sample-3
http://imaginativeuniversal.com/blog/post/2010/08/22/How-to-Quit-a-WP7-Silverlight-Application.aspx
I don't think that this is a good practice and also not sure if the windows market place will certify this way. Would like to hear the thoughts of once who have experienced this issue, and any suggestion to Achieve this(Terminate the application). Thanks inadvance....!!!!
If you want to submit to the Marketplace you've got a couple of problems because of the following certification requirements:
5.2.4.2 Pressing the Back button from the first screen of an application must close the application.
5.2.2 A Windows Phone application is closed and terminated by the OS when the user navigates away from the application. When an application is started after being closed, its launch time must meet the requirements in Section 5.2.1 – Launch Time
5.2.3 A Windows Phone application is deactivated when the user presses the Start button or if the device timeout causes the lock screen to engage. A Windows Phone application is also deactivated with it invokes a Launcher or Chooser API.
This is a couple instances where you simply can't display a message box.
And technically using an exception to termniate the app is a violation:
5.1.2 The application must handle exceptions raised by the .NET Framework and not close unexpectedly. During the certification process, the application is monitored for unexpected closure. An application that closes unexpectedly fails certification. The application must continue to run and remain responsive to user input after the exception is handled.
As a developer, part of your job is communicating to users about requirements that are unrealistic or unreasonable.
Sorry, no way to do this. Before mango update you could Clear the back stack, then programmatically trigger the Back button. but as of SDK 7.1 (wp7.5) we can no longer do this.
My recommendation is to create a custom Exception type ApplicationXExitException and throw that to exit the app. The reason for the custom type is so that when you pull your exception logs from the marketplace, you'll know the ones that were indeed unintended exceptions crashing the app, vs your exception to intentionally exit the app.
You can hook an event raising after your custom messagebox closes. Event arguments will keep information about user's choice. Depending on that you will decide whether to exit app or not.
currently I’m creating 2 applications (app A and B) for Windows Mobile 5.0 and using Compact Framework 2.0. App A is the main application and B is the sub application.
Below is the flow:
Start app A.
App A will start app B.
App B will do some process.
App B will kill app A.
App B will patch/upgrade app A. (ala update manager)
App B will restart app A.
App B will exit.
Now I’m stuck in killing app A. I did tried using OpenNETCF ProcessEntry Kill() function. When calling Kill(), it made the device crash.
I did tried using the SendMessage(hWnd, WM_CLOSE, 0, 0) funct where WM_CLOSE will have the ProcessEntry.ProcessID value and I didn’t assigned any value to hWnd variable. But it didn’t terminate app A. Did I assign the wrong value?
I also did tried using
Process.GetProcessById(processEntry.ProcessID).CloseMainWindow()
, but failed as GetProcessById only accepts int32 value. Note that processEntry.ProcessID value is larger than int32 value and GetProcessByName() is not supported in Compact Framework.
Could you help me in killing app A through app B?
Thanks.
You may try native code, using the TerminateProcess function:
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
success = TerminateProcess(processHandle, 0);
The above code is from a Task Manager at Code Project.
However if you are writing the code for both the applications, it will be better if you designed a communication mechanism between the two applications. In this way you will send a message from app B to app A and app A will kill itself.
Stormenet, I hardcoded the application's name. Then I generate an object to get all the available process using OpenNETCF.ToolHelp.ProcessEntry[ ] = ProcessEntry.GetProcesses();
then in a foreach loop, if the ProcessEntry object eg: processEntry.ExeFile matches with the "applicationName", i shall use processEntry.Kill().
I think you can get the OpenNETCF.ToolHelp dll from the OpenNETCF site.
Note that if the application you are trying to kill is holding open ports or other system resources then it might hang on exiting. Ensure everything is effectively disposed when the form closes.
This can be acheived by putting stuff in the:
public void Dispose(bool disposing)
{
}
block of code in the designer of your main form, or if you've chosen a less Windows Form centric architecture then just run your dispose calls following Application.Run(new YourForm()) and it will execute after the application has closed.
If you're feeling really lazy then just setup some destructors (otherwise known as finalizers ~) but be careful about navigating through relationships between managed objects at "destruct" time if you do this as there is no guarantee as to which order objects will be destroyed.
ctacke, I think app A crashes due to some of the running threads are not closed properly or still running at the background as app A will run multiple threads during app B executing the Kill( ) function.
If I use the SendMessage(hWnd, WM_CLOSE, 0, 0) function, it will not crash the device (which is a good thing)... it only closes the form. (app A contains multiple forms eg: frmLogin and frmMainMenu). hmmm maybe I need to point hWnd to the right form...
Now I'm taking a different route.
After downloading the patch and put it in a temp folder, I'll do a soft reset using OpenNETCF.WindowsCE.PowerManagement.SoftReset().
App B will be launched upon startup, then it will scan the temp folder and replace app A with the new version.