I am currently working on some parental control software. This software is supposed to log out a user and then lock the account so that they cannot log back in unless the parent/admin has specified that they can.
I have tried several things so far such as setting flags on the user account saying that it is disabled. This completely removes it from the login screen. From what I have found out is that if the user account is logged in, it doesn't apply the ADS_Disable flag. I have also tried looking for resources on logging out another account but I can only seem to find information on logging out the account that is running the logout command. Such as Pinvoke, or directly calling the LOGOUT.EXE program.
I found a resource on LSAUser and found that there might be something there. I am doing this project for school and I am needing a little guidance. Since there is such a sparse amount of information on doing this, is there a better way of doing what I want to do? Or is there a reason why I shouldn't do this? Any alternatives?
Use the WTSDisconnectSession() Windows API. See article here.
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
class Program
{
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSDisconnectSession(IntPtr hServer, int sessionId, bool bWait);
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int WTSGetActiveConsoleSessionId();
const int WTS_CURRENT_SESSION = -1;
static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
static void Main(string[] args)
{
if (!WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE,
WTS_CURRENT_SESSION, false))
throw new Win32Exception();
}
}
Even without remote desktop, it will disconnect the current user and go to the login screen. The processes will still run in the background. After manually login in again, the running programs will appear as they were before the disconnect.
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern bool WTSDisconnectSession(IntPtr hServer, int sessionId, bool bWait);
When you use WTSDisconnectSession in remote desktop is equivalent to 'Close' the remote desktop windows. It is disconnect your Windows session, but hold the connection.
The advantage is you can reconnect back the session later by remote log in again.
The disadvantage is the Windows may not be able log in by other user when the remote desktop connection is full.
To simulate Windows 'Log off' should use ExitWindowsEx under user32.dll
[DllImport("user32.dll", SetLastError = true)]
static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
public static bool WindowsLogOff() {
return ExitWindowsEx(0, 0);
}
if you want to Force the user to log off you need to add the EWX_FORCE flag like this:
ExitWindowsEx(0 | 0x00000004, 0);
More details on the function here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa376868(v=vs.85).aspx
piggybacking off Leng Weh Seng's answer (since I can't comment), if you want to Force the user to log off you need to add the EWX_FORCE flag like this:
ExitWindowsEx(0 | 0x00000004, 0);
More details on the function here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa376868(v=vs.85).aspx
Related
What's the best way to programmatically cause a Windows XP (or above) machine to wake up at a specific time. (Ideally a lot like how Media Center can start up automatically to record a particular TV program)
I've got a Windows service (written in C#) and I'd like this service to be able to cause the machine it is hosted on to start up at predetermined times.
Are there any BIOS settings or prerequisites (eg. ACPI) that need to be configured for this to work correctly?
This machine would be using dialup or 3G wireless modem, so unfortunately it can't rely on Wake on LAN.
You can use waitable timers to wake from a suspend or hibernate state. From what I can find, it is not possible to programmatically wake from normal shut down mode (soft off/S5), in that case, you need to specify a WakeOnRTC alarm in BIOS. To use waitable timers from C#, you need pInvoke. The import declarations are:
public delegate void TimerCompleteDelegate();
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long pDueTime, int lPeriod, TimerCompleteDelegate pfnCompletionRoutine, IntPtr pArgToCompletionRoutine, bool fResume);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CancelWaitableTimer(IntPtr hTimer);
You can use those functions in the following way:
public static IntPtr SetWakeAt(DateTime dt)
{
TimerCompleteDelegate timerComplete = null;
// read the manual for SetWaitableTimer to understand how this number is interpreted.
long interval = dt.ToFileTimeUtc();
IntPtr handle = CreateWaitableTimer(IntPtr.Zero, true, "WaitableTimer");
SetWaitableTimer(handle, ref interval, 0, timerComplete, IntPtr.Zero, true);
return handle;
}
You can then cancel the waitable timer with CancelWaitableTimer, using the returned handle as an argument.
Your program can hibernate and sleep using pInvoke:
[DllImport("powrprof.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
public static bool Hibernate()
{
return SetSuspendState(true, false, false);
}
public static bool Sleep()
{
return SetSuspendState(false, false, false);
}
Your system may not allow programs to let the computer enter hibernation. You can call the following method to allow hibernation:
public static bool EnableHibernate()
{
Process p = new Process();
p.StartInfo.FileName = "powercfg.exe";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.Arguments = "/hibernate on"; // this might be different in other locales
return p.Start();
}
The task scheduler program in Win7, taskschd.msc (and I beleive XP as well) can be set to wake the system on different triggers. Those triggers can be schedule, time, event, etc.
In at least Win7, you need to set "Allow Wake Timers" to 'Enabled' for this to work. This setting is found under...
--> Control Panel\Hardware and Sound\Power Options
click - "Edit Plan Settings"
click - "Change advanced power setting"
expand - "Sleep"
Expand - "Allow Wake timers"
Your best bet is using Wake on LAN capability. This will require another machine to send a packet of a special kind to wake your machine up.
This will not be helpful if your machine is not connected to the network or you for some reason don't wasnt to move this logic onto another machine. But it's useful for some configurations where you have multiple machines and want to wake them up programmatically.
Some machines have a BIOS alarm clock that can be set to wake up the computer at a certain hour. It should be possible to program this clock, but I don't know the specific details.
Edit: I found this program that should let you set the time. It's in C, under Linux, but maybe it can give you some hints.
A warning though: before trying anything that changes the BIOS settings directly be sure to write down every setting from BIOS screens, because in case of an error the BIOS might revert to factory default and you might need to set it up again as it was.
This question already has answers here:
Is there any way to prevent console application to close?
(2 answers)
Prevent the application from exiting when the Console is closed
(3 answers)
How to prevent app from closing before finishing a task?
(1 answer)
Closed 2 years ago.
I have searched far and wide and still found nothing.
What I'm trying to accomplish is preventing/stopping the console from exiting/terminating, for example clicking X on the console or having a different program closing it (I know for a fact that it is not possible to bypass Task Managers "Kill Task").
What I have been using is the following:
private delegate bool ConsoleCtrlHandlerDelegate(int sig);
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add);
static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;
//...
_consoleCtrlHandler += s => {/* I have a while true loop right here with some extra code doing stuffs*/};
SetConsoleCtrlHandler(_consoleCtrlHandler, true);
//...
That does work...for about 5 seconds, then closes on it's own.
Please help.
Also, DO NOT SAY CTRL+F5, as it will not accomplish my goal. My goal is beyond debugging tools.
If you want an application to be working non-stop, you should run this as a Windows service rather than a console application.
With a little research, you can convert your application to a Windows Service and set appropriate user rights for starting and stopping the service.
You can't stop someone from killing a task, if they have the admin rights to kill your task. The best you can do is to create a user with admin privileges on the machine, then run the application under that user. That will prevent any task, other than a task with admin privileges from killing your app.
Now, as far as disabling the close button on your console app, you can use the Win32 DeleteMenu API to disable the X button. Here is an example:
public const int ScClose = 0xF060;
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags);
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
static void Main(string[] args)
{
// Get a pointer to the console window
IntPtr consoleWindow = GetConsoleWindow();
// Get a pointer to the system menu inside the console window
IntPtr systemMenu = GetSystemMenu(consoleWindow, false);
// Delete the close menu item
DeleteMenu(systemMenu, ScClose, 0);
}
I need to find out if the current user is a RDP Session, that is simple but I need to know if the session is initiated with the '/admin' flag.
In the past I use the Session Id, that was 0 but now on newer Windows Versions its not 0 anymore.
Perhaps, this is what you need:
[DllImport("Kernel32.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.U4)]
public static extern int WTSGetActiveConsoleSessionId ( );
Project:
I currently devellop an application to warn the user if he leaves (lockscreen or shutdown) his workplace and has left his smartcard in the reader.
I was able to detect through the use of the WinAPI (WinSCard.dll) if the smarcard is currently in the reader.
Problem:
I read (correct me if this is wrong) it is not possible for an application to delay the lockscreen so I currently focus on delaying the shutdown.
The problem I am having now is that I need to delay the ongoing shutdown to warn the user that he left his smartcard.
I try to use ShutdownBlockReasonCreate to delay the shutdown for at least the 5 seconds Windows so generously lets me. The idea was that if the user removes his smartcard my application calls ShutdownBlockReasonDestroy to continue the shutdown.
I implemented the two methods like so:
[DllImport("User32.dll", EntryPoint = "ShutdownBlockReasonCreate",
CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool ShutdownBlockReasonCreate(
[In()]IntPtr wndHandle,
[In()]string reason);
[DllImport("User32.dll", EntryPoint = "ShutdownBlockReasonDestroy",
CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool ShutdownBlockReasonDestroy(
[In()]IntPtr wndHandle);
furthermore I use GetLastError to check for errors which I implemented this way:
[DllImport("Kernel32.dll", EntryPoint = "GetLastError",
CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int GetLastError();
Now the strange thing is that if I create the reason
WinAPIMethoden.ShutdownBlockReasonCreate(
new WindowInteropHelper(Application.Current.MainWindow).Handle,
"Smartcard still in reader!");
and then display the error
MessageBox.Show(WinAPIMethoden.GetLastError().ToString("X"));
it shows 0 which stands for ERROR_SUCCESS.
So far everything seems great.
Now if I try to shut the PC down there is no sign whatsoever that my application has requested that the PC should not be shut down right now.
Question:
What am I doing wrong so that ShutdownBlockReasonCreate does not work as intended?
Or is there maybe a better way to prevent the user from shutting the PC down if his smartcard is still in like preventing him from initiating the shutdown while his card is in or something like that?
tl;dr:
I try to prevent shutdown while user has his smartcard in the reader.
I use ShutdownBlockReasonCreate but it doesnt seem to work although there is no error.
Solution:
The accepted answer lead me to the solution of the problem.
You have to create the cancle reason AND subscribe a handler to SystemEvents.SessionEnding.
This handler then has to set e.Cancel to true
void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
if (mgr.state == SmartcardState.Inserted)
{
e.Cancel = true;
}
}
To execute the programm on shutdown I addet it to the shutdown scripts using gpendit.msc.
The programm then gets executed AFTER all programs have been terminated. Looks really weird but does the job.
Please take a look at these posts that could help you with your situation:
How to abort shutdown in Windows (XP|Vista) programatically?
Aborting computer shutdown from windows service
Prevent windows Shutdown with CANCEL option C#
Is there a way in c# to detect a Windows shutdown/logoff and cancel that action (after asking the user)
Hi all and thanks for taking the time to read this. I have a c# application where I wish to override the default WinInet cookie settings. The goal is that even when the system WinInet cookie privacy settings are set to Block All, within my process, cookies will still be accepted calls. Reading the documentation, it looked straightforward enough. Here's a cleaned up version of what I have:
private unsafe void SuppressWininetBehavior()
{
int option = (int)WinInet.SuppressBehaviorFlags.INTERNET_SUPPRESS_COOKIE_POLICY;
int* optionPtr = &option;
bool success = WinInet.InternetSetOption(IntPtr.Zero, WinInet.InternetOption.INTERNET_OPTION_SUPPRESS_BEHAVIOR, new IntPtr(optionPtr), sizeof(int));
if (!success)
{
_log.Warn("Failed in WinInet.InternetSetOption call with INTERNET_OPTION_SUPPRESS_BEHAVIOR, INTERNET_SUPPRESS_COOKIE_POLICY");
}
}
Where WinInet.InternetSetOption is defined as:
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
And the constants are:
WinInet.InternetOption.INTERNET_OPTION_SUPPRESS_BEHAVIOR = 81
WinInet.SuppressBehaviorFlags.INTERNET_SUPPRESS_COOKIE_POLICY = 1
The InternetSetOption call succeeds - no error.
I have also tried passing in a global internet handle returned by InternetOpen as the first parameter to the InternetSetOption call, and it makes no difference. Cookies continue to be blocked within my process.
The Reason I need to do this is that I have an embedded Flash Player ActiveX instance which makes web requests. I have successfully used other InternetSetOption calls to modify the proxy settings that Flash uses in my process. I'm testing this on Windows 7.
Try InternetSetPerSiteCookieDecision(). You'll have to sink DWebBrowserEvents2::OnBeforeNavigate and call it for each domain, but it should work.
Also, you're using the wrong flag. If you want to disable the cookie policy, use INTERNET_SUPPRESS_COOKIE_POLICY. By using the RESET flag, you're enabling the default policy.