Winlogon notify的Vista移植
By MikeFeng
大家知道,在Windows XP和2000中,有个Winlogon notify的方法来接收logon,logoff事件。如果有些事情需要在登录注销时去做,那么使用notify技术可以很好的解决。但是,出于安全考虑,在vista下,原来的winlogon notify的功能被微软取消了。现在只能通过系统服务的方法来代替logon, logoff, sessionchange事件的接受与处理。在大部分情况下,这种方法可以当作把winlogon notify移植到vista的方法,并且这种方法是和原来的xp兼容的。但是有两个注意点:win2000是不支持sessionchange事件的,如果要将这个服务应用于win2000,将会出现莫名奇妙的问题。另外就是毕竟服务是在winlogon notify之后才起来的,如果这个logon/logoff/sessionchange事件必须在服务启动之前发生,那么就不能用这个方法了。
下面是一个用c++写的服务,它在logoff的时候写日志。这个服务可以用在xp,2000 SP4,vista中。但是处理SessionChange事件的服务只能用在xp,vista中。
//
SimService.cpp
//
#pragma comment (lib,"Secur32")
#define _WIN32_WINNT 0x6000
#include < windows.h >
#include < iostream >
#include < winuser.h >
using namespace std;
#define SERVICE_NAME "Vista Service For Logoff Event"
HANDLE terminateEvent = NULL;
SERVICE_STATUS_HANDLE serviceStatusHandle;
SERVICE_STATUS MyServiceStatus;
HANDLE threadHandle = NULL;
BOOL pauseService = FALSE;
BOOL runningService = FALSE;
BOOL InitService();
VOID terminate(DWORD error);
VOID ICSEventLogoff();
VOID ServiceMain(DWORD argc, LPTSTR * argv);
BOOL SendStatusToSCM(DWORD , DWORD , DWORD , DWORD , DWORD );
VOID HandlerEx(DWORD controlCode, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);
void main( int argc, char * argv[])
{
BOOL success;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
//
// Register with the SCM
//
success = StartServiceCtrlDispatcher(serviceTable);
if ( ! success)
return ;
}
// ServiceMain is called when the SCM wants to
// start the service. When it returns, the service
// has stopped. It therefore waits on an event
// just before the end of the function, and
// that event gets set when it is time to stop.
// It also returns on any error because the
// service cannot start if there is an error.
VOID ServiceMain(DWORD argc, LPTSTR * argv)
{
BOOL success;
DWORD dwNum = 0 ;
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
serviceStatusHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME,
(LPHANDLER_FUNCTION_EX)HandlerEx, NULL);
if ( ! serviceStatusHandle) {terminate(GetLastError()); return ;}
// create the termination event
terminateEvent = CreateEvent ( 0 , TRUE, FALSE, 0 );
if ( ! terminateEvent) {terminate(GetLastError()); return ;}
// Start the service itself
success = InitService();
if ( ! success) {terminate(GetLastError()); return ;}
// The service is now running.
// Notify SCM of progress
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE;
success = SendStatusToSCM(SERVICE_RUNNING, 0 , 0 , 0 , 0 );
if ( ! success) {terminate(GetLastError()); return ;}
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE
| SERVICE_ACCEPT_SESSIONCHANGE;
success = SendStatusToSCM(SERVICE_RUNNING, 0 , 0 , 0 , 0 );
// Wait for stop signal, and then terminate
WaitForSingleObject (terminateEvent, INFINITE);
terminate( 0 );
}
// This function consolidates the activities of
// updating the service status with SetServiceStatus.
BOOL SendStatusToSCM(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL success;
DWORD dwNum = 0 ;
// Fill in all of the SERVICE_STATUS fields
MyServiceStatus.dwServiceType = SERVICE_WIN32;
MyServiceStatus.dwCurrentState = dwCurrentState;
// Set the control codes the service can receive from SCM.
// Make sure that the code contains SERVICE_ACCEPT_SESSIONCHANGE.
// So service will can accept winlogon message.
/* if (dwCurrentState == SERVICE_START_PENDING)
MyServiceStatus.dwControlsAccepted = 0;
else
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN|
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SESSIONCHANGE; */
// if a specific exit code is defined, set up
// the win32 exit code properly
if (dwServiceSpecificExitCode == 0 )
MyServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
MyServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
MyServiceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
MyServiceStatus.dwCheckPoint = dwCheckPoint;
MyServiceStatus.dwWaitHint = dwWaitHint;
// Pass the status record to the SCM
success = SetServiceStatus (serviceStatusHandle, & MyServiceStatus);
return success;
}
DWORD ServiceThread(LPDWORD param)
{
while ( 1 )
{
Sleep( 1000 );
}
return 0 ;
}
// Initializes the service. Start a new thread
BOOL InitService()
{
DWORD id = 0 ;
int iTime = 0 ;
// Start the service's thread
while (NULL == threadHandle && iTime ++ < 10 )
{
threadHandle = CreateThread( 0 , 0 ,
(LPTHREAD_START_ROUTINE) ServiceThread, // callback function.
0 , 0
//
#pragma comment (lib,"Secur32")
#define _WIN32_WINNT 0x6000
#include < windows.h >
#include < iostream >
#include < winuser.h >
using namespace std;
#define SERVICE_NAME "Vista Service For Logoff Event"
HANDLE terminateEvent = NULL;
SERVICE_STATUS_HANDLE serviceStatusHandle;
SERVICE_STATUS MyServiceStatus;
HANDLE threadHandle = NULL;
BOOL pauseService = FALSE;
BOOL runningService = FALSE;
BOOL InitService();
VOID terminate(DWORD error);
VOID ICSEventLogoff();
VOID ServiceMain(DWORD argc, LPTSTR * argv);
BOOL SendStatusToSCM(DWORD , DWORD , DWORD , DWORD , DWORD );
VOID HandlerEx(DWORD controlCode, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);
void main( int argc, char * argv[])
{
BOOL success;
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL }
};
//
// Register with the SCM
//
success = StartServiceCtrlDispatcher(serviceTable);
if ( ! success)
return ;
}
// ServiceMain is called when the SCM wants to
// start the service. When it returns, the service
// has stopped. It therefore waits on an event
// just before the end of the function, and
// that event gets set when it is time to stop.
// It also returns on any error because the
// service cannot start if there is an error.
VOID ServiceMain(DWORD argc, LPTSTR * argv)
{
BOOL success;
DWORD dwNum = 0 ;
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
serviceStatusHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME,
(LPHANDLER_FUNCTION_EX)HandlerEx, NULL);
if ( ! serviceStatusHandle) {terminate(GetLastError()); return ;}
// create the termination event
terminateEvent = CreateEvent ( 0 , TRUE, FALSE, 0 );
if ( ! terminateEvent) {terminate(GetLastError()); return ;}
// Start the service itself
success = InitService();
if ( ! success) {terminate(GetLastError()); return ;}
// The service is now running.
// Notify SCM of progress
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE;
success = SendStatusToSCM(SERVICE_RUNNING, 0 , 0 , 0 , 0 );
if ( ! success) {terminate(GetLastError()); return ;}
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE
| SERVICE_ACCEPT_SESSIONCHANGE;
success = SendStatusToSCM(SERVICE_RUNNING, 0 , 0 , 0 , 0 );
// Wait for stop signal, and then terminate
WaitForSingleObject (terminateEvent, INFINITE);
terminate( 0 );
}
// This function consolidates the activities of
// updating the service status with SetServiceStatus.
BOOL SendStatusToSCM(DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL success;
DWORD dwNum = 0 ;
// Fill in all of the SERVICE_STATUS fields
MyServiceStatus.dwServiceType = SERVICE_WIN32;
MyServiceStatus.dwCurrentState = dwCurrentState;
// Set the control codes the service can receive from SCM.
// Make sure that the code contains SERVICE_ACCEPT_SESSIONCHANGE.
// So service will can accept winlogon message.
/* if (dwCurrentState == SERVICE_START_PENDING)
MyServiceStatus.dwControlsAccepted = 0;
else
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN|
SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SESSIONCHANGE; */
// if a specific exit code is defined, set up
// the win32 exit code properly
if (dwServiceSpecificExitCode == 0 )
MyServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
MyServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
MyServiceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
MyServiceStatus.dwCheckPoint = dwCheckPoint;
MyServiceStatus.dwWaitHint = dwWaitHint;
// Pass the status record to the SCM
success = SetServiceStatus (serviceStatusHandle, & MyServiceStatus);
return success;
}
DWORD ServiceThread(LPDWORD param)
{
while ( 1 )
{
Sleep( 1000 );
}
return 0 ;
}
// Initializes the service. Start a new thread
BOOL InitService()
{
DWORD id = 0 ;
int iTime = 0 ;
// Start the service's thread
while (NULL == threadHandle && iTime ++ < 10 )
{
threadHandle = CreateThread( 0 , 0 ,
(LPTHREAD_START_ROUTINE) ServiceThread, // callback function.
0 , 0