none
How to start a screensaver from a service

    Question

  • Hello:

    Can someone suggest how to start a customized screensaver from a service application?

    PS. If I simply run it as "scrname.scr /s" it doesn't show up.

    Tuesday, September 21, 2010 10:28 PM

All replies

  •  

    Hi,

     

    I can find out a easy way is create a windows script application and run it in windows Task Scheduler(it's a easy way), also you can run it in windows application.

     

    You can create script like this:

    1.open Notepad

    2.copy these code into it "RUNDLL32.EXE PowrProf.dll,SetSuspendState"

    3.save it as Hibernate.bat

     

    Then you can create service in Visual Studio2010 like this:

    http://msdn.microsoft.com/en-us/library/zt39148a.aspx

     

    In you service application you should run Hibernate.bat.

     

    If you still have any doubts and concerns about this issue, please let me know.

    If I misunderstood you, please kindly elaborate your question.

     

    Regards!

    Jesse


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Thursday, September 23, 2010 6:31 AM
  • Thank you for response, but I think you misunderstood me. Your example will send system into a sleep mode. On the other hand I need to start a screensaver.
    Thursday, September 23, 2010 9:12 AM
  •  

    Hi,

     

    OK, I get it, if so you can copy "C:\Windows\System32\Bubbles.scr /s" in Notepad and save it as .bat and run in services.

     

    >PS. If I simply run it as "scrname.scr /s" it doesn't show up.

     

    May be should run in C:\Windows\System32\ folder.

     

    If you still have any doubts and concerns about this issue, please let me know.

     

    Regards!

    Jesse


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Friday, September 24, 2010 7:43 AM
  •  

    Hi,

     

    OK, I get it, if so you can copy "C:\Windows\System32\Bubbles.scr /s" in Notepad and save it as .bat and run in services.

     

    Thanks, but it is not that easy. If you simply run a user-mode program (like a screensaver) from a service it will be executing in a non interactive desktop under session 0, so no one will actually see it. (You can check that it's running by using the Task Manager. Moreover it will continue running since non interactive desktop does not receive user input by definition, to cancel the screensaver.)

     

    So far I have not come up with a solution to my question...

    Tuesday, September 28, 2010 5:48 AM
  • BUMP!

    I have exactly the same problem. I asked here

    http://stackoverflow.com/questions/5727977/how-to-start-screensaver-from-system-service

    already, but still no answer.

    I think i am quite far, however the last bit is the hardest. Please help, if someone has an idea.

    Thx

    Wednesday, April 20, 2011 3:50 PM
  • Hi Booka,

    Is this helpful? - How to Start the ScreenSaver Programmatically?

    Best Regards,
    Jijo.

     


    http://weseetips.com[^] Visual C++ tips and tricks. Completely revamped!
    Wednesday, April 20, 2011 4:44 PM
  • OK. To decry the approach suggested on the stackoverflow forum, do not use SERVICE_INTERACTIVE_PROCESS flag, nor rely on Interactive Services techniques. This approach has been phased out since Windows Vista. More here [1]

    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 in the stackoverflow forum 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][2] and [here][3] and [here][4] on creating a security descriptor. This step is important if you don't want to deal with ERROR_ACCESS_DENIED errors later.

    2. Make a small Win32 GUI program that has a hidden window. Upon the start it should open 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][5]. Of course, make sure that the 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 start your GUI program 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 to it via a global named event. And you can track the user session changes from your system service and the ServiceHandlerEx() handler 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][6]. 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][7]() 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][8] 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 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][9] 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...

    Cheers.


    PS. Links used above in case they didn't get added as hyperlinks:

      [1]: http://msdn.microsoft.com/en-us/library/ms683502%28v=vs.85%29.aspx
      [2]: http://msdn.microsoft.com/en-us/library/ms682396%28VS.85%29.aspx
      [3]: http://msdn.microsoft.com/en-us/library/ms707085%28v=vs.85%29.aspx
      [4]: http://msdn.microsoft.com/en-us/library/aa446595%28v=vs.85%29.aspx
      [5]: http://msdn.microsoft.com/en-us/library/ms686360%28v=VS.85%29.aspx
      [6]: http://msdn.microsoft.com/en-us/library/aa365574%28v=vs.85%29.aspx
      [7]: http://msdn.microsoft.com/en-us/library/ms682429%28VS.85%29.aspx
      [8]: http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/31bfa13d-982b-4b1a-bff3-2761ade5214f
      [9]: http://msdn.microsoft.com/en-us/library/aa365574%28v=vs.85%29.aspx

     


    • Edited by ahmd0 Wednesday, April 20, 2011 11:00 PM Stupid formatting rules
    Wednesday, April 20, 2011 10:58 PM
  • not really. I am aware of this method to start the screensaver, but the point is the "Session 0 Isolation" which does not permits interaction from the system service and the user session...

    Thx anyway.

    Thursday, April 21, 2011 8:24 AM
  • great post, however the detour "createProcessAsUser" is way to complicated for my purposes. I now assume that my "deamon" is running in the user session and exposes a public interface via Windows Remoting. From there i can just fire the screensaver.
    Thursday, April 21, 2011 8:26 AM