How to ensure an application always runs for standard users and cannot be stopped without admin rights?
Posted by Same-Target-3116@reddit | sysadmin | View on Reddit | 23 comments
Hi, I’m trying to understand how to properly enforce that a specific application always runs on a Windows machine, even when the user is a standard (non-admin) account.
My goal is:
- The application should run in the normal user session (visible, not as a background service in Session 0)
- The user should NOT be able to stop it, kill it, or remove it without admin privileges
- It should persist across reboots and always relaunch if closed
I’m not trying to do anything malicious — this is for a controlled environment (like a shared PC / restricted usage setup).
So far I’ve explored:
- Running it as a service (but then it runs in Session 0 and has no UI)
What would be the correct / standard way to design this in Windows?
I’d really appreciate guidance on the right architecture, not just a workaround.
Thanks!
ExceptionEX@reddit
Well honestly, windows has made this harder and shitty than it should be.
Are you in control of the app source code?
Is this a win32 or UWP/Browser based app?
Depending on your situation really would determine what is the "right way" Ideally windows kiosk mode would be what you want, but in windows 11 to force promote their shit platform they have limited this functionality to UWP and browser based.
If you want to use a win32 app, you can have a start up script, that will then launch the application as a different user that has limited admin rights (I recommend creating a admin user and narrowly scoping its permissions), this will prevent a standard user from closing it.
There are other methods, that can be used based on your situation, the nature of the environment, and the application requirements.
Same-Target-3116@reddit (OP)
Thanks, this is really helpful.
Yes, I do control the app source code, and it’s a Win32 application.
One important detail I probably should’ve mentioned is that the program needs to monitor user processes in the same session (including inspecting things like loaded DLLs and reacting to them). So running it under a completely separate user might limit what it can access.
At the same time, I’m trying to prevent a standard user from simply killing the process and bypassing the control entirely.
So I’m kind of stuck between:
I’m trying to figure out what the “correct” architecture would be rather than just patching around it.
Appreciate the insight.
ExceptionEX@reddit
If you control the source I would intercept any exit request (processExit and Application exit),
Then throw up a password prompt, or whatever method you want, you can even do a UAC elevation and see if that comes back valid, that way no need for anything but domain/local admin.
If they fail just exit the event.
Make sure in your handlers you check for system shutdown event is called, and exit if it is or you'll hang up system shutdowns.
This is all pretty straight forward in .net not sure what you are writing in but they should have the same functionality exposed.
Same-Target-3116@reddit (OP)
That makes sense, and I agree it’s a good layer to have.
Intercepting exit events and requiring some kind of authorization would definitely prevent casual or accidental closure from the UI side.
My main concern is more about external termination (like Task Manager or other tools), where the process can be killed without going through the application’s exit handlers.
So I’m trying to figure out what the best approach is to combine both:
Appreciate the suggestion. I’ll likely implement that as an extra safeguard.
ExceptionEX@reddit
ProcessExit method is called anytime the OS sends a kill signal it doesn't matter how its called, so it will call no matter internal or external, that is why you have to put the condition to check for OS shutdown, because if you don't the application will refuse to close when you try to reboot the system.
Same-Target-3116@reddit (OP)
That makes sense, and I agree ProcessExit is useful for handling normal shutdown paths.
From what I’ve tested though, it doesn’t seem to trigger in all cases (for example forceful termination like taskkill /f or similar), so I’m trying to handle those scenarios as well.
ExceptionEX@reddit
those are running at required levels that a standard user shouldn't be able to use anyway?
Same-Target-3116@reddit (OP)
Just to share what ended up working for me in case it helps someone else:
I stopped trying to force everything from a single process and instead split the logic into two parts:
myapp.exe)The key detail is that the service does nothing at startup. It waits until the program has been detected running at least once. This avoids boot loops or false triggers when the user session is just starting.
After that point, the service continuously checks if the process is running. If the process is ever closed (no matter how), it immediately forces a system reboot.
So the logic becomes:
This doesn’t technically prevent the app from being killed, but it achieves the goal anyway:
👉 If the process is terminated, the session becomes unusable.
In my case, that was enough — the user can’t meaningfully bypass the restriction because killing the app just kicks them out.
Appreciate all the suggestions here, they helped point me in the right direction.
MomentsInTruth@reddit
Hey, I'm with you on Microsoft generally making things worse, but the last time we worked on this in our org we used PowerShell wrapping the Microsoft Kiosk Shell Launcher to maximize our customizability (and since we weren't in Intune). I believe we still have copies of Kiosk Shell Launcher 1.0 and I vaguely recall a 2.0 coming out later, but looking now on a fresh Google the code looks simpler, maybe this is the 2.0 version, but the example code shows launching the Win32/non-UWP version of Edge:
https://learn.microsoft.com/en-us/windows/configuration/shell-launcher/quickstart-kiosk?tabs=ps
https://mobile-jon.com/2025/01/28/deep-dive-into-windows-11-kiosks-part-2-advanced/
Is this what you're referring to? Wouldn't this work to launch any Win32 app? That's the vibe I'm getting on a quick scan but I haven't looked at this code in a good long while.
Same-Target-3116@reddit (OP)
That’s actually what I’m trying to figure out. I don’t necessarily want to fully lock down the system like a traditional kiosk.
My goal is to keep the normal Windows experience (so the user can still browse, search, use apps, etc.), but at the same time ensure that a specific Win32 application is always running and not easily bypassed.
So I was wondering: with Shell Launcher, is it possible to use it in a way where it doesn’t completely replace explorer.exe, or maybe have my app act as the shell but still allow the normal desktop environment to load (for example, launching explorer from within it)?
Also, from your experience, does Shell Launcher work reliably with arbitrary Win32 executables (not just Edge or UWP apps)?
I’m trying to understand if this could fit a “monitoring + persistence” scenario without breaking normal usability.
Appreciate any insight!
MomentsInTruth@reddit
Hey, I'm tied up right now but quick reply -
Same-Target-3116@reddit (OP)
Got it, that makes sense. thanks for the clarification.
The “multi-app kiosk” and restricted mode sound closer to what I’m trying to achieve. I don’t really need a single locked-down app, but more like a controlled environment where the user can still use the system normally to some extent.
My main goal is to ensure that a specific background/monitoring Win32 app is always running in the user session, while still allowing things like browsing and general usage.
From your experience, would multi-app kiosk allow something like that? For example:
Or is kiosk mode generally not designed for that kind of “persistent background app” scenario?
Appreciate the pointers, this is helping me understand the direction much better.
Educational_Item5124@reddit
Kiosk modes are generally intended for POS and industrial set ups. Program/app is force launched on boot, and cannot be exited (without password/knowledge/remote access etc). Multi app kiosks exist, but I've yet to find one a like, or that works exactly how you want. You'd be locked between the two/three apps, without further access.
It sounds like you want something more like an MDM solution. I don't know of any that force programs or apps to always run though, auto-boot is as far as they usually go in terms of making something run. If it's not the program itself but an underlying service that needs to always run, then you could possibly use powershell and task planner to achieve that aspect in Windows.
Same-Target-3116@reddit (OP)
Just to share what ended up working for me in case it helps someone else:
I stopped trying to force everything from a single process and instead split the logic into two parts:
framewrk.exe)The key detail is that the service does nothing at startup. It waits until the program has been detected running at least once. This avoids boot loops or false triggers when the user session is just starting.
After that point, the service continuously checks if the process is running. If the process is ever closed (no matter how), it immediately forces a system reboot.
So the logic becomes:
This doesn’t technically prevent the app from being killed, but it achieves the goal anyway:
👉 If the process is terminated, the session becomes unusable.
In my case, that was enough — the user can’t meaningfully bypass the restriction because killing the app just kicks them out.
Appreciate all the suggestions here, they helped point me in the right direction.
Same-Target-3116@reddit (OP)
Just to share what ended up working for me in case it helps someone else:
I stopped trying to force everything from a single process and instead split the logic into two parts:
framewrk.exe)The key detail is that the service does nothing at startup. It waits until the program has been detected running at least once. This avoids boot loops or false triggers when the user session is just starting.
After that point, the service continuously checks if the process is running. If the process is ever closed (no matter how), it immediately forces a system reboot.
So the logic becomes:
This doesn’t technically prevent the app from being killed, but it achieves the goal anyway:
👉 If the process is terminated, the session becomes unusable.
In my case, that was enough — the user can’t meaningfully bypass the restriction because killing the app just kicks them out.
Appreciate all the suggestions here, they helped point me in the right direction.
Same-Target-3116@reddit (OP)
Just to share what ended up working for me in case it helps someone else:
I stopped trying to force everything from a single process and instead split the logic into two parts:
myapp.exe)The key detail is that the service does nothing at startup. It waits until the program has been detected running at least once. This avoids boot loops or false triggers when the user session is just starting.
After that point, the service continuously checks if the process is running. If the process is ever closed (no matter how), it immediately forces a system reboot.
So the logic becomes:
This doesn’t technically prevent the app from being killed, but it achieves the goal anyway:
If the process is terminated, the session becomes unusable.
In my case, that was enough — the user can’t meaningfully bypass the restriction because killing the app just kicks them out.
Appreciate all the suggestions here, they helped point me in the right direction.
Same-Target-3116@reddit (OP)
Just to share what ended up working for me in case it helps someone else:
I stopped trying to force everything from a single process and instead split the logic into two parts:
myapp.exe)The key detail is that the service does nothing at startup. It waits until the program has been detected running at least once. This avoids boot loops or false triggers when the user session is just starting.
After that point, the service continuously checks if the process is running. If the process is ever closed (no matter how), it immediately forces a system reboot.
So the logic becomes:
This doesn’t technically prevent the app from being killed, but it achieves the goal anyway:
👉 If the process is terminated, the session becomes unusable.
In my case, that was enough — the user can’t meaningfully bypass the restriction because killing the app just kicks them out.
Appreciate all the suggestions here, they helped point me in the right direction.
UnexpectedDragon651@reddit
Does the app itself need to be running in the users context? If not, I’d suggest splitting your app into a service and a ui process, and make them talk using your preferred ipc method. Though it almost sounds like you’re trying to create something akin to an edr, in which case you might want to look into creating a software driver, as they have access to PsSetCreateProcessNotifyRoutine and similar functions.
Same-Target-3116@reddit (OP)
Just wanted to follow up and say — your suggestion about splitting the app into a service + user-level process ended up being exactly the right direction.
I moved away from trying to directly force execution in the user session and instead used a service as a watchdog, with a scheduled task to launch the UI process in the correct session. The service now monitors and relaunches it almost instantly if it gets terminated.
This solved both sides of the problem:
Also your point about this resembling an EDR design was spot on — I didn’t realize I was basically heading in that direction.
Really appreciate the insight, that helped a lot.
smarthomepursuits@reddit
I use NinjaOne to customize the system tray icon, and one of the items opens an automation that runs as SYSTEM.
boli99@reddit
run it as a service and have a client app that sits in the user session and talks to the service.
schedule a job that restarts the user app whenever its closed or quit.
man__i__love__frogs@reddit
Honestly it would be to redesign the app so it's designed to work how it needs to in Windows.
sryan2k1@reddit
You run a system service that does the work, and a UI that runs in userland.
Or you have a scheduled task that runs as system that will restart it in user land if it's killed.