views:

419

answers:

3

I have an application that is launched from within a service using a local administrator account. This application is accessible by a web browser and the host PC can be shut down through this interface.

If a user is logged into the host PC and I browse to it and shut it down, the application exits and shuts down the PC as I would expect - using ExitWindowsEx() (with the shutdown priviledge enabled).

If, however, the PC is logged off, I browse to it - the application still running within the service, and attempt a shutdown using ExitWindowsEx(), it returns successful and the appears to be no problem but the PC never shuts down.

I have also tried InitiateSystemShutdown() which bizzarely fails and returns error 2! (The system cannot find the file specified).

It doesn't seem to matter what account I use to launch the application with.

Any help would be greatly appreciated!

Thanks.

A: 

If I http://www.google.ca/search?hl=en&q=exitwindowsex+service then the frst thing I find for example is http://www.eggheadcafe.com/software/aspnet/29901267/lockworkstation-and-exitw.aspx which suggests there's a problem if your service has the "interact with desktop" flag (which is deprecated) enabled.

People then suggest fixes in various messages which reply to the above topic, such as http://www.eggheadcafe.com/conversation.aspx?messageid=29901274&threadid=29901267 ... something like that might help you.

An alternative, which is certainly a kludge but which avoids using the suggested magic, might be to have a second service running which doesn't interact with the desktop: have the second service invoke ExitWindowsEx ... and use any IPC (or a service-specific API) to trigger the second service from the first service (or perhaps from the application).

ChrisW
A: 

Try this code and tell us what it does:

GrabPrivilege (SE_SHUTDOWN_NAME);
ExitWindowsEx (EWX_REBOOT|EWX_FORCE, 0); // or whatever EWX flags you want

Helper function:

BOOL  GrabPrivilege (LPCTSTR lpctPrivName) 
{
   TOKEN_PRIVILEGES newtkp;
   HANDLE hToken;
   BOOL   bRetVal = FALSE;

   if (OpenProcessToken (GetCurrentProcess(), 
                         TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                         &hToken)) 
   {
      LookupPrivilegeValue (NULL, 
                            lpctPrivName, 
                            &newtkp.Privileges[0].Luid); 
      newtkp.PrivilegeCount = 1;  // one privilege to set
      newtkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 

      if (AdjustTokenPrivileges (hToken, 
                                 FALSE, 
                                 &newtkp, 
                                 0, 
                                 (PTOKEN_PRIVILEGES) NULL, 
                                 0))
      {
         DWORD dwRet = GetLastError();
         if (dwRet == ERROR_SUCCESS) bRetVal = TRUE;
      }
   }
   CloseHandle (hToken);
   return bRetVal;
}
Bob Moore
Thanks for your suggestion, unfortunatley that is the exact same code I currently have in place that doesn't work!
Adam Cobb
Oops, that's what I get for not reading the question carefully enough. The single commonest problem is not enabling the shutdown priv. A thought occurs - SE_SHUTDOWN_NAME assumes a login session, but you don't have one in your failure case. Have you tried enabling SE_REMOTE_SHUTDOWN_NAME ?
Bob Moore
Hmm yeah I will give that a try and see if it makes any difference
Adam Cobb
Unfortunately that appeared to have no effect either, still returned success from ExitWindowsEx() call but nothing happened.
Adam Cobb
Too long for comment, added another answer.
Bob Moore
+1  A: 

Sadly, cannot repro. I have a pre-existing service which exposes a mailslot, so I added the code:

void RebootThisMachine ()
{
   if (GrabPrivilege (SE_SHUTDOWN_NAME))
   {
      if (!InitiateSystemShutdown (NULL,NULL,0,TRUE,TRUE))
      {
         wsprintf (g_szDebug, TEXT("RebootMachine - ISS failed, error %d"),
                   GetLastError()) ;
         DebugMessage (DEBUG_ERROR, g_szDebug) ;
      }
   }
   else
   {
      wsprintf (g_szDebug, TEXT("RebootMachine - cannot grab priv, error %d"),
                GetLastError()) ;
      DebugMessage (DEBUG_ERROR, g_szDebug) ;
   }
}

and called it when I received a mailslot message from a little command-line utility I wrote. InitiateSystemShutdown is the right API for a service, and this reboots the machine running the service whether it's logged in or not. The shutdown does take a while if my (vista) machine is not logged in, but it eventually works (after 30-40s of saying "shutting down"). My service executes under LocalSystem. GrabPrivilege is the same code as I posted before.

So you can take heart from the fact that what you're trying to do is possible. I see you are using an administrator account to run your service. Have you tried running your service under LocalSystem for the purposes of a shutdown test? Perhaps the privs of your administrator don't quite match those of LocalSystem...

Bob Moore
Thanks for your help, i'll give these a try again and see if I have any more luck.
Adam Cobb