Unable to (somewhat) gracefully terminate some processes
Hey! Newbie here with very limited skills trying to learn.
For context: I currently have a batch script that uses the command-line tool TASKKILL to end a bunch if processes when needed. I'm looking to implement this in my current C# app in a somewhat more "proper" way.
I've been testing this by running PowerToys minimized and trying to end that process as gracefully as possible. The TASKKILL command seems to be able to end PowerToys without the use of the /F (force kill process) parameter.
I found the System.Diagnostic.Process class which seems to be the recommended way for ending tasks via C#, but have been unable to match the functionality of TASKKILL via the Process object created from the class.
* Process.Kill() seems to be able to Kill tasks in the same way TASKKILL /F does.
* Process.CloseMainWindow() is unable to close the PowerToys process, whereas TASKKILL command (without /F) succeeds. Regardless of PowerToys being minimized or having an open window, CloseMainWindow() just does not work.
So TASKKILL seems to do something that is "in-between" the C# Process object's CloseMainWindow() and Kill(), seeing as it is able to close PowerToys without the /F parameter. I don't know exactly how graceful that is, but there seems to be "a step above" using /F. But as far as I know we don't have any source code for TASKKILL to look at how it's non-forced task ending works.
I know I could in theory just have the app launch TASKKILL itself, but I'm trying to find a code-based solution for it. Does anyone have any pointers in the right direction as to how I can replicate the TASKKILL non-forced behaviour?
If needed I'll add my prototype code, but seeing as Process.Kill() does work it would imply that the Process object successfully found the PowerToys process.
4 Replies
I believe taskkill sends a WM_CLOSE Windows message when you don't specify
/f
.
I'm not sure if C# has a direct way to do that in the BCL. You could P/Invoke the PostMessage Win32 API though.@Kresjah here is an example program I just whipped up:
https://github.com/Treit/scratch/tree/main/CloseProcessExample
GitHub
scratch/CloseProcessExample at main · Treit/scratch
Misc code, snippets, etc. Contribute to Treit/scratch development by creating an account on GitHub.
I was under the same impression; that taskkill sends WM_CLOSE. My understanding is that Process.CloseMainWindow() should be doing the same thing (although I may be wrong there)?
What you said about BCL and P/Invoke is honestly above my level of understanding. I'm aware I'm punching well above my weight class here so to speak.
I did try to search a lot, and sending a WM_CLOSE message seems to be the recommended way (although I do see a rare few mentions of WM_QUIT). I did experiment with some code for doing that which, of course, may be wrong.
Ooh, I see you posted the example while I was typing. Very much appreciate it! I'll quickly check it out first, then if I can't get it working from there I'll post the code I did for experimenting with sending WM_CLOSE/WM_QUIT messages.
So took a look at the example. Educational for sure on alternate routes to doing stuff. Again, I really appreciate that you've put together the example.
Your code does seem to end up with the same results as my code in the end though. It can terminate mspaint for instance with no problem, but it can't terminate PowerToys, which is what stumps me. I can still do Process.Kill() as a last resort, but seeing as taskkill manages to close it without /F, whereas both of our codes fail to do so makes me curious as to what exactly is going on there.
I'll post my current code attempts too. Just let me clean it up a bit to reduce the size of the code block here.
Attempt 1: Using CloseMainWindow. Can kill mspaint, but can't kill PowerToys.
Attempt 2: Following tips from random posts online and using WM_CLOSE. Looking at it now it seems like I'm going a roundabout way to access the same functionality your code does (going via user32.dll rather than Windows.Win32 ... your way probably being the better alternative). Same as above, can kill mspaint, can't kill PowerToys.
Looking more into it, PowerToys returns a 0 for the window handle, so taskkill must have an alternative way of doing it when something does not return a window handle.
No idea how many hours I've spent searching online, but finally stumbled upon a solution. I'll add the code in case anyone stumbles into here in the future looking for a solution. Thank you mtreit.
There are some finer details of the newly added code I don't 100% understand (yet... I'll figure it out soon enough), but I understand more than enough of it to get the gist of what it is doing. Some of this code could probably be written better, i.e. potentially using Windows.Win32.PInvoke as per mtreit's example code or other alternative methods instead of importing SendMessage from user32.dll and such things, adding async checking for the process so it doesn't freeze the app during the wait time, but it works and is a functional starting point for further development.
That seems to indicate PowerToys has its WM_CLOSE handler on some window other than the main window, and you need to iterate all its window handles to notify your request to close. You might want to review its source code https://github.com/microsoft/PowerToys and see if that explains all the details to you.