Wiki Home

Win 32 Api Exit Windows Ex


Namespace: VFP
Before deciding to use this function, check out the following KB articles...

PRB: Exit Windows ExOffsite link to http://www.news2news.com/vfp/?function=55
with EWX_LOGOFF Doesn't Work Properly ID: Q149690 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q149690
BUG: Exit Windows ExOffsite link to http://www.news2news.com/vfp/?function=55
() May Not Logoff in a Workstation Locked State ID: Q229750 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q229750
INFO: Exit Windows ExOffsite link to http://www.news2news.com/vfp/?function=55
() Does Not Shut Down or Restart Win 95/98 ID: Q220706 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q220706
Exit Windows ExOffsite link to http://www.news2news.com/vfp/?function=55
Does Not Work With NEC Power Switch Service ID: Q159109 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q159109
PRB: Exit Windows ExOffsite link to http://www.news2news.com/vfp/?function=55
API Does Not Reboot Windows NT ID: Q176695 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q176695

How to use the ExitWindowsEx API function
MSDN Documentation:
ExitWindowsEx The ExitWindowsEx function either logs off the current user, shuts down the system, or shuts down and restarts the system. It sends the WM_QUERYENDSESSION message to all applications to determine if they can be terminated.
BOOL ExitWindowsEx(
  UINT uFlags,       // shutdown operation
  DWORD dwReserved   // reserved
);

Parameters
uFlags [in] Specifies the type of shutdown. This parameter must include one of the following values.
This parameter (uFlags) can optionally include the following values:
dwReserved [in] Reserved; this parameter is ignored.

Remarks:
Windows NT/2000: To shut down or restart the system, the calling process must use the AdjustTokenPrivileges function to enable the SE_SHUTDOWN_NAME privilege. For more information about security privileges, see Privileges.
To use this in VFP:

First, you have to declare the function:
declare integer ExitWindowsEx in Win32Api integer iFlags, integer

Then, just call it:
if 0=ExitWindowsEx(EWX_REBOOT,0)
  * It didn't work, or the user clicked "Cancel"
else
  * It worked!
endif


Having the values for the flags is useful, too:
#define EWX_LOGOFF           0
#define EWX_SHUTDOWN         0x01
#define EWX_REBOOT           0x02
#define EWX_FORCE            0x04
#define EWX_POWEROFF         0x08
#define EWX_FORCEIFHUNG      0x10

This starts to get tricky in Win NT / Win2000 (see the "Remarks" section above, and article Q168796 Offsite link to http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q168796 )

If you are in Win NT / Win2000, use this code instead:
( HELP: This code doesn't work... It seems like SetLastError(0) has no effect... GetLastError() ALWAYS returns 2 for me! I'm also pretty sure I don't have the pseudo-structures right )
#DEFINE mlngWindows95  0
#DEFINE mlngWindowsNT  1

#define EWX_LOGOFF           0
#define EWX_SHUTDOWN         0x01
#define EWX_REBOOT           0x02
#define EWX_FORCE            0x04
#define EWX_POWEROFF         0x08
#define EWX_FORCEIFHUNG      0x10

declare integer ExitWindowsEx in Win32Api integer iFlags, integer
*      The GetLastError function returns the calling thread's last-error
*      code value. The last-error code is maintained on a per-thread basis.
*      Multiple threads do not overwrite each other's last-error code.
Declare LONG GetLastError IN kernel32

*      ********************************************************************
*      * When the project starts, check the operating system used by
*      * calling the GetVersion function.
*      ********************************************************************
LOCAL lngVersion

DECLARE INTEGER GetVersion IN Win32api
lngVersion = GetVersion()
If (BitAnd(lngVersion,0x80000000) = 0)
  glngWhichWindows32 = mlngWindowsNT
  MessageBox("Running Windows NT or Windows 2000")
Else
  glngWhichWindows32 = mlngWindows95
  MessageBox("Running Windows 95 or 98")
EndIf


* Now, do one of the following:
*do cmdLogoff
*do cmdForceLogoff
do cmdShutdown       && This will first call AdjustToken in WinNT/2000
*do cmdForceShutdown  && This will first call AdjustToken in WinNT/2000


*      Private Type LUID
*         UsedPart As Long
*         IgnoredForNowHigh32BitPart As Long
*      End Type
*
*      Private Type LUID_AND_ATTRIBUTES
*         TheLuid As LUID
*         Attributes As Long
*      End Type
*
*      Private Type TOKEN_PRIVILEGES
*         PrivilegeCount As Long
*         TheLuid As LUID
*         Attributes As Long
*      End Type

PROCEDURE AdjustToken
*    ********************************************************************
*    * This procedure sets the proper privileges to allow a log off or a
*    * shut down to occur under Windows NT.
*    ********************************************************************

#DEFINE TOKEN_ADJUST_PRIVILEGES 0x20
#DEFINE TOKEN_QUERY             0x08
#DEFINE SE_PRIVILEGE_ENABLED    0x02

LOCAL hdlProcessHandle,;  && As Long
      hdlTokenHandle,;    && As Long
      tmpLuid,;           && As LUID
      tkp,;               && As TOKEN_PRIVILEGES
      tkpNewButIgnored,;  && As TOKEN_PRIVILEGES
      lBufferNeeded       && As Long

hdlProcessHandle = 0  && As Long
hdlTokenHandle   = 0  && As Long
tmpLuid          = 0  && As LUID
tkp              = Repl(Chr(0),16) && As TOKEN_PRIVILEGES (4 bytes per LONG, 8 bytes per LUID)
tkpNewButIgnored = Repl(Chr(0),16) && As TOKEN_PRIVILEGES
lBufferNeeded    = 0  && As Long

*         Set the error code of the last thread to zero using the
*         SetLast Error function. Do this so that the GetLastError
*         function does not return a value other than zero for no
*         apparent reason.
DECLARE SetLastError IN kernel32 LONG dwErrCode
SetLastError(0)

*         Use the GetCurrentProcess function to set the hdlProcessHandle
*         variable.
*      The GetCurrentProcess function returns a pseudohandle for the
*      current process.
DEBUG
SUSPEND

DECLARE DOUBLE GetCurrentProcess IN kernel32
hdlProcessHandle = GetCurrentProcess()


If GetLastError() <> 0
  MessageBox("GetCurrentProcess error==" + alltrim(Str(GetLastError())) )
EndIf

*      The OpenProcessToken function opens the access token associated with
*      a process.
DECLARE LONG OpenProcessToken IN advapi32 ;
  LONG ProcessHandle, ;
  LONG DesiredAccess, ;
  LONG TokenHandle
OpenProcessToken( hdlProcessHandle, BitOR(TOKEN_ADJUST_PRIVILEGES,TOKEN_QUERY), hdlTokenHandle )

If GetLastError() <> 0
  MessageBox("OpenProcessToken error==" + alltrim(str( GetLastError() )) )
EndIf

*      The LookupPrivilegeValue function retrieves the locally unique
*      identifier (LUID) used on a specified system to locally represent
*      the specified privilege name.
DECLARE LONG LookupPrivilegeValueA IN advapi32 ;
  STRING @ lpSystemName, ;
  STRING @ lpName, ;
  DOUBLE @ lpLuid
*         Get the LUID for shutdown privilege
LookupPrivilegeValueA( "", "SeShutdownPrivilege", tmpLuid )

If GetLastError() <> 0
  MessageBox("LookupPrivilegeValue error==" + alltrim(str( GetLastError() )) )
EndIf

tkp = chr(0)+Chr(0)+chr(0)+chr(1) ;
      +IntToBinStr( tmpLuid, 8 ) ;
      +chr(0)+chr(0)+chr(0)+chr(2)
*tkp.PrivilegeCount = 1    && One privilege to set
*tkp.TheLuid = tmpLuid
*tkp.Attributes = SE_PRIVILEGE_ENABLED

*      The AdjustTokenPrivileges function enables or disables privileges
*      in the specified access token. Enabling or disabling privileges
*      in an access token requires TOKEN_ADJUST_PRIVILEGES access.
DECLARE LONG AdjustTokenPrivileges IN advapi32 ;
  LONG TokenHandle, ;
  LONG DisableAllPrivileges, ;
  STRING @ NewState, ;      && As TOKEN_PRIVILEGES
  LONG BufferLength, ;
  STRING @ PreviousState, ; &&  As TOKEN_PRIVILEGES,
  LONG ReturnLength
*         Enable the shutdown privilege in the access token of this process
AdjustTokenPrivileges( hdlTokenHandle, .F., tkp, Len(tkpNewButIgnored), ;
                       tkpNewButIgnored, lBufferNeeded )

If GetLastError() <> 0
  MessageBox( "AdjustTokenPrivileges error==" + alltrim(str( GetLastError() )) )
EndIf

EndProc

********************************************************************************
PROCEDURE cmdLogoff
  ExitWindowsEx( EWX_LogOff, 0xFFFF )
  MessageBox( "ExitWindowsEx's GetLastError " + alltrim(str( GetLastError() )) )
EndProc

PROCEDURE cmdForceLogoff
  ExitWindowsEx( BitOR(EWX_LogOff,EWX_FORCE), 0xFFFF )
  MessageBox( "ExitWindowsEx's GetLastError " + alltrim(str( GetLastError() )) )
EndProc

PROCEDURE cmdShutdown
  If glngWhichWindows32 = mlngWindowsNT
    DO AdjustToken
    MessageBox("Post-AdjustToken GetLastError " + alltrim(str( GetLastError() )) )
  EndIf

  ExitWindowsEx( EWX_SHUTDOWN, 0xFFFF )
  MessageBox("ExitWindowsEx's GetLastError " + alltrim(str( GetLastError() )) )
EndProc

PROCEDURE cmdForceShutdown
  If glngWhichWindows32 = mlngWindowsNT Then
    DO AdjustToken
    MessageBox( "Post-AdjustToken GetLastError " + alltrim(str( GetLastError() )) )
  EndIf

  ExitWindowsEx( BitOR( EWX_SHUTDOWN, EWX_FORCE), 0xFFFF )
  MessageBox( "ExitWindowsEx's GetLastError " + alltrim(str( GetLastError() )) )
EndProc

PROCEDURE IntToBinStr( nVal, nLen )
LOCAL lnLeft, lcOut
lnLeft = nVal
lcOut = ''
do while len(lcOut)

Contributors: wgcs
Category Code Samples, Category Windows API
( Topic last updated: 2004.12.22 04:13:49 PM )