Take the 2-minute tour ×

I have several variants to start the screensaver. I favourite is

[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr GetDesktopWindow();

private void startScreensaver()
    UInt32 WM_SYSCOMMAND = 0x112;
    IntPtr SC_SCREENSAVE = new IntPtr(0xf140);
    IntPtr hWnd = GetDesktopWindow();
    SendMessage(hWnd, WM_SYSCOMMAND, SC_SCREENSAVE, new IntPtr(0));

My problem is that i want to start the screensaver out of a system service. If i e.g. want to start the screensaver as soon as the session is locked (just for prove of concept), i could try

protected override void OnSessionChange(SessionChangeDescription changeDescription)
    if (changeDescription.Reason == SessionChangeReason.SessionLock)

This doesn't work and i think the reason is that the service is installed with the

ServiceProcessInstaller.Account = ServiceAccount.LocalSystem;

which does not have access to the User's session. I could implement a small program that runs in the user session, which is triggered by the service to trigger the screensaver... but that's not the nice way.

Any suggestions? Thanks.

edited: obviously the problem is related to the GetDesktopWindow(); call, still i don't know how to fix that


According to Erics suggestion, i do now iterate all window station (using OpenWindowStation), then for all of those i iterate all desktops (using EnumDesktops). I then open the desktops using OpenDesktop and store the handle to the desktop. My standard Windows installation yields to the following list of windowStation:Desktop:dskHandle

  • WinSta0:Default:732
  • WinSta0:Disconnect:760
  • WinSta0:Winlogon:784
  • msswindowstation:mssrestricteddesk:0

I do now start a new Thread in which i

static extern bool SetThreadDesktop(IntPtr hDesktop);

and then invoke the startScreensaver() method above. The IntPtr hWnd = GetDesktopWindow() does return reasonable results, still the screensaver is not started. In the

static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);

i use GENERIC_ALL = 0x10000000 as the dwDesiredAccess. And as Farzin noted, i checked the

Allow service to interact with desktop

I am not a win32 or pInvoke pro, so i am totally lost now. Can sb explain how all the stuff works together? Does sb has a better suggestion? All I want to do is to invoke the screensaver from a system service.

share|improve this question

3 Answers 3

OK. I formatted this posting with links hoping to help and then when clicked Post got this:

Oops! Your answer couldn't be submitted because: •we're sorry, but as a spam prevention mechanism, new users can only post a maximum of two hyperlinks. Earn more than 10 reputation to post more hyperlinks.

So, here you go, totally unreadable posting....

Go here for this messages with hyperlinks: http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/89485c95-61e5-46ea-84c7-5d8e03081c61

Erik, Farzin Zaker, and the OP, do not use SERVICE_INTERACTIVE_PROCESS flag, nor rely on Interactive Services techniques. This approach has been phased out since Windows Vista. [More here]:

From Microsoft's own words:

Important Services cannot directly interact with a user as of Windows Vista. Therefore, the techniques mentioned in the section titled Using an Interactive Service should not be used in new code.

So even if any of the mentioned approaches above work they would not be anything than a "hack" and may stop working in any new version or even an update to Windows.

Your best bet to do what you want is through what you mentioned yourself, "I could implement a small program that runs in the user session, which is triggered by the service to trigger the screensaver".

Trust me, I spent countless hours trying to do what you want (the wrong way) and I failed. Here's how Microsoft do it themselves in their software and how you need to do it:

  1. In your system service create a global named auto-reset event, set its state to non-sginaled. Make sure to adjust the security descriptor for this event to be read and synchronized by "Everyone". More [here] and [here] and [here] on creating a security descriptor. This step is important if you don't want to deal with ERROR_ACCESS_DENIED later.

  2. Make a small Win32 GUI program that has a hidden window. Upon the start it opens the global event created by the service above. If I was to write it in C++ it'd look like this: OpenEvent(READ_CONTROL | SYNCHRONIZE, FALSE, _T("Global\\Whatever_name_you_use")); Then create a worker thread that simply waits for this event to become signaled using one of the WaitFor*Object APIs from the [synchronization functions]. Of course, make sure that worker thread handles the situation when this small GUI program closes.

  3. From the worker thread run the following code when the global named auto-reset event becomes signaled. Send the WM_SYSCOMMAND notification to its own window in the main GUI thread with wParam = SC_SCREENSAVE, and lParam = 0, or do it via a call to DefWindowProc() API from the main GUI thread. This should start the currently set up screen saver for the user where the GUI program is running.

  4. In case you want to start a specific screensaver, then you can simply run it using ShellExecute with the /s parameter from your GUI program. (Of course, do it from the worker thread when the global named auto-reset event is signaled.) All screensavers are normally placed into the "%WINDIR%\System32" folder. They have the .scr extension.

  5. OK, now how to activate it from the system service.

  6. When you need to run your screensaver you need to ensure that your small GUI program is running in a user session that is currently active. The active part is important. There are two apporaches here. First. You can do it every time a user session becomes active (of cource by closing a copy of this GUI program for a session that stops being active. You can close it by issuing a command via a global named event. And you can track the user session changes from your system service and the ServiceHandlerEx() by trapping SERVICE_CONTROL_SESSIONCHANGE notifications.) You can also run this GUI program right when you need to activate the screensaver and then close it immediately. I'll leave it up to you which approach you choose. The main point is that you have to somehow run your GUI program in an active user session and use global named events to communicate with it. (Of cource, you can incorporate any [other means of the IPC]. In my book global events are simplest to convey a boolean, or "yes and no" type command.) I need to tell you right off the bat that starting a process in another user session is the most labor intensive part here, is poorly documented and is hard to debug. In a nutshell, you need to use the CreateProcessAsUser() API from your system service, but the hard part is prepping for the call to that API. Unfortunately there's no clear-cut consensus on how to call it and there's a [bunch of advice available on the web] that is all somewhat different. The steps that worked for me are as follows:

    • Place your GUI program into a commonly accessible place (even for the least privileged users). Since it's a part of the system service, you can use "%WINDIR%\System32" but make sure to remove it from there when it is no longer needed!

    • Get the current active session by calling WTSEnumerateSessions() and look a session with the WTSActive state.

    • WTSQueryUserToken() to get the active user session token

    • DuplicateTokenEx(, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &);

    • Create environment strings block with a call to CreateEnvironmentBlock()

    • Load user profile by calling LoadUserProfile(). You can collect all necessary info before with the following APIs: NetUserGetInfo() for the profile path, and WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, , WTSUserName, &, &) to get a session user name.

    • And impersonate that user with a call to ImpersonateLoggedOnUser()

    • At this point call CreateProcessAsUser() on the location of your GUI program where you placed it. Let me repeat that you must run it from the location accessible for the user that you've just impersonated! The common mistake here is running it from a location something like this: "C:\Users\SomeUserName\AppData\Roaming". This call may look like this: CreateProcessAsUser(hToken2, NULL, pNonConstOrStaticBufferWithPathToGUIProgram, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, pEnvironmentBlock, NULL, &pSTARTUPINFO, &pPROCESS_INFORMATION);

    • Always revert imporsonation: RevertToSelf();

    • WaitForInputIdle() to make sure your GUI process started and reached the message pump.

    • Clean up by calling UnloadUserProfile(), DestroyEnvironmentBlock(), WTSFreeMemory(), CloseHandle(), etc.

    • Now you can set your global named auto-reset event by calling SetEvent() to signal your GUI process to start the screensaver. And you're done! You may also want to enable some sort of backwards feedback from a GUI program to ensure that the screensaver is actually started, but I'll leave it up to you. Again refer to the [means of the IPC] for the ways to do it.

As a conclusion, let me say, that the above approach has been collected through a countless forum postings and by gleaning off multiple web searches. And, yes, I understand how bulky and cumbersome this approach is, but, hey, that's what Windows is, isn't it :) If you want simplicity, go OS X or iOS. That's what I eventually did...


PS. Who in the world came up with the formatting rules on this forum? It's the hardest thing to type into and get something readable....

share|improve this answer
great post, however the detour "createProcessAsUser" is way to complicated for my purposes. I now simply assume that my "deamon" is running in the user session. This little program exposes a public interface via Windows Remoting. From there i can just fire the screensaver. I may write a blog post to wrap it up later... –  Martin Booka Weser Apr 21 '11 at 8:28

go to your services, right click on service and in the LogOn tab set the item bellow to true :

Allow service to interact with desktop

if you want to do this on install :

public WindowsServiceInstaller()
  // This call is required by the Designer.

  ServiceInstaller si = new ServiceInstaller();
  si.ServiceName = "WindowsService1"; 
  si.DisplayName = "WindowsService1";
  si.StartType = ServiceStartMode.Manual;
  ServiceProcessInstaller spi = new ServiceProcessInstaller();
  spi.Account = System.ServiceProcess.ServiceAccount.LocalSystem; 
  spi.Password = null;
  spi.Username = null;


  // Grab the subkey to our service

  RegistryKey ckey = Registry.LocalMachine.OpenSubKey(
  // Good to always do error checking!

  if(ckey != null)

    //and then do our bitwise operation on it.

    if(ckey.GetValue("Type") != null)

Reference : http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx

share|improve this answer
The point is correct, i need to "Allow service to interact with desktop", however i still need to figure the logged on users desktop. Therefore i now explore the solution provided by Erik –  Martin Booka Weser Apr 20 '11 at 9:53

It may work to allow the service to interact with the desktop when installing (Pass SERVICE_INTERACTIVE_PROCESS to CreateService). Otherwise (there may be problems with access - I haven't tried this) you'll need to start with Window Station and Desktop Functions.

What you need to do is to find the logged on users window station (EnumWindowStations, OpenWindowStation), the Desktop (EnumDesktops, OpenDesktop), create a thread and SetThreadDesktop, then finally use GetDesktopWindow.

share|improve this answer
hmm, sounds like a reasonable approach. i can do EnumWindowStations with pInvoke, but i can't figure how to OpenWindowStation. Could you please elaborate more on that? Maybe you find time to give some code snippeds?! –  Martin Booka Weser Apr 20 '11 at 10:39
@Martin: Sorry, I don't really do .NET, no idea. This is just how I'd approach it from a language-neutral point of view –  Erik Apr 20 '11 at 11:09

Your Answer


