Winlogon Notify的Vista移植
来源:互联网 发布:网络设计工程师要求 编辑:程序博客网 时间:2024/06/07 23:20
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, &id );
}
if (threadHandle==0)
return FALSE;
else
{
runningService = TRUE;
return TRUE;
}
}
// Log..
void Log(LPCTSTR msg)
{
#define LOGFILE_PATH TEXT("C:/Log.txt")
HANDLE h = CreateFile(LOGFILE_PATH,
GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
if (GetFileSize(h,NULL)==0)
{
byte b[2]={0xFF,0xFE};
DWORD cb = 2;
WriteFile(h, b, 2, &cb, 0);
}
if (INVALID_HANDLE_VALUE != h)
{
if (INVALID_SET_FILE_POINTER != SetFilePointer(h, 0, 0, FILE_END)) {
DWORD cb = lstrlen(msg) * sizeof *msg;
WriteFile(h, msg, cb, &cb, 0);
}
CloseHandle(h);
}
}
// Dispatches events received from the SCM
VOID HandlerEx(DWORD controlCode,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext)
{
DWORD currentState = 0;
BOOL success;
DWORD dwNum = 0;
CHAR *tszWrite = NULL;
switch(controlCode)
{
case SERVICE_CONTROL_STOP:
// Stop the service
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE;
success = SendStatusToSCM(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
runningService=FALSE;
SetEvent(terminateEvent);
return;
case SERVICE_CONTROL_PAUSE:
// Pause the service
if (runningService && !pauseService)
{
success = SendStatusToSCM(SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000);
pauseService = TRUE;
SuspendThread(threadHandle);
currentState = SERVICE_PAUSED;
}
break;
case SERVICE_CONTROL_CONTINUE:
// Resume from a pause
if (runningService && pauseService)
{
success = SendStatusToSCM(SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000);
pauseService=FALSE;
ResumeThread(threadHandle);
currentState = SERVICE_RUNNING;
}
break;
case SERVICE_CONTROL_SESSIONCHANGE:
switch(dwEventType)
{
case WTS_SESSION_LOGOFF:
// Logoff
Log("Logoff event happened!");
break;
default:
break;
}
break;
case SERVICE_CONTROL_SHUTDOWN:
// Do nothing in a shutdown. Could do cleanup
// here but it must be very quick.
return;
default:
break;
}
SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
//
// Handle an error and stop service.
//
VOID terminate(DWORD error)
{
// Close terminateEvent.
if (terminateEvent) CloseHandle(terminateEvent);
// Send a message to the SCM to stop service.
if (serviceStatusHandle)
SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0);
// Close thread.
if (threadHandle) CloseHandle(threadHandle);
}
//
#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, &id );
}
if (threadHandle==0)
return FALSE;
else
{
runningService = TRUE;
return TRUE;
}
}
// Log..
void Log(LPCTSTR msg)
{
#define LOGFILE_PATH TEXT("C:/Log.txt")
HANDLE h = CreateFile(LOGFILE_PATH,
GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
if (GetFileSize(h,NULL)==0)
{
byte b[2]={0xFF,0xFE};
DWORD cb = 2;
WriteFile(h, b, 2, &cb, 0);
}
if (INVALID_HANDLE_VALUE != h)
{
if (INVALID_SET_FILE_POINTER != SetFilePointer(h, 0, 0, FILE_END)) {
DWORD cb = lstrlen(msg) * sizeof *msg;
WriteFile(h, msg, cb, &cb, 0);
}
CloseHandle(h);
}
}
// Dispatches events received from the SCM
VOID HandlerEx(DWORD controlCode,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext)
{
DWORD currentState = 0;
BOOL success;
DWORD dwNum = 0;
CHAR *tszWrite = NULL;
switch(controlCode)
{
case SERVICE_CONTROL_STOP:
// Stop the service
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
| SERVICE_ACCEPT_PAUSE_CONTINUE;
success = SendStatusToSCM(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000);
runningService=FALSE;
SetEvent(terminateEvent);
return;
case SERVICE_CONTROL_PAUSE:
// Pause the service
if (runningService && !pauseService)
{
success = SendStatusToSCM(SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000);
pauseService = TRUE;
SuspendThread(threadHandle);
currentState = SERVICE_PAUSED;
}
break;
case SERVICE_CONTROL_CONTINUE:
// Resume from a pause
if (runningService && pauseService)
{
success = SendStatusToSCM(SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000);
pauseService=FALSE;
ResumeThread(threadHandle);
currentState = SERVICE_RUNNING;
}
break;
case SERVICE_CONTROL_SESSIONCHANGE:
switch(dwEventType)
{
case WTS_SESSION_LOGOFF:
// Logoff
Log("Logoff event happened!");
break;
default:
break;
}
break;
case SERVICE_CONTROL_SHUTDOWN:
// Do nothing in a shutdown. Could do cleanup
// here but it must be very quick.
return;
default:
break;
}
SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
//
// Handle an error and stop service.
//
VOID terminate(DWORD error)
{
// Close terminateEvent.
if (terminateEvent) CloseHandle(terminateEvent);
// Send a message to the SCM to stop service.
if (serviceStatusHandle)
SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0);
// Close thread.
if (threadHandle) CloseHandle(threadHandle);
}
另外在.net framework 1.1中,我们是没有办法支持OnSessionChange事件的,而在.net framework 2.0中的ServiceBase有了更好的扩展性,可以支持OnSessionChange。
使用Reflector观察.Net Framework 1.1.4322。打开%WINDIR%/Microsoft.NET/Framework/v1.1.4322/System.ServiceProcess.dll,查看System.ServiceProcess.ServiceBase类。它有以下两个私有成员函数,相当于C++ Service中的给RegisterServiceCtrlHandlerEx传递的回调函数(LPHANDLER_FUNCTION_EX)
private int ServiceCommandCallbackEx(int command, int eventType, IntPtr eventData, IntPtr eventContext)
...{
if (command != 13)
...{
this.ServiceCommandCallback(command);
}
else
...{
try
...{
PowerBroadcastStatus powerStatus = (PowerBroadcastStatus) eventType;
bool flag = this.OnPowerEvent(powerStatus);
this.WriteEventLogEntry(Res.GetString("PowerEventOK"));
if (!flag)
...{
return 0x424d5144;
}
}
catch (Exception exception)
...{
this.WriteEventLogEntry(Res.GetString("PowerEventFailed", new object[] ...{ exception.ToString() }), EventLogEntryType.Error);
}
}
return 0;
}
private unsafe void ServiceCommandCallback(int command)
...{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
...{
if (command == 4)
...{
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
}
else if (((this.status.currentState != 5) && (this.status.currentState != 2)) && ((this.status.currentState != 3) && (this.status.currentState != 6)))
...{
switch (command)
...{
case 1:
...{
int currentState = this.status.currentState;
if ((this.status.currentState == 7) || (this.status.currentState == 4))
...{
this.status.currentState = 3;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
this.status.currentState = currentState;
new DeferredHandlerDelegate(this.DeferredStop).BeginInvoke(null, null);
}
goto Label_0259;
}
case 2:
if (this.status.currentState == 4)
...{
this.status.currentState = 6;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
new DeferredHandlerDelegate(this.DeferredPause).BeginInvoke(null, null);
}
goto Label_0259;
case 3:
if (this.status.currentState == 7)
...{
this.status.currentState = 5;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
try
...{
this.OnContinue();
this.WriteEventLogEntry(Res.GetString("ContinueSuccessful"));
}
catch (Exception exception)
...{
this.WriteEventLogEntry(Res.GetString("ContinueFailed", new object[] ...{ exception.ToString() }), EventLogEntryType.Error);
this.status.currentState = 7;
goto Label_0259;
}
this.status.currentState = 4;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
}
goto Label_0259;
case 5:
try
...{
this.OnShutdown();
this.WriteEventLogEntry(Res.GetString("ShutdownOK"));
}
catch (Exception exception2)
...{
this.WriteEventLogEntry(Res.GetString("ShutdownFailed", new object[] ...{ exception2.ToString() }), EventLogEntryType.Error);
}
goto Label_0259;
}
try
...{
this.OnCustomCommand(command);
this.WriteEventLogEntry(Res.GetString("CommandSuccessful"));
}
catch (Exception exception3)
...{
this.WriteEventLogEntry(Res.GetString("CommandFailed", new object[] ...{ exception3.ToString() }), EventLogEntryType.Error);
}
}
}
Label_0259:;
}
...{
if (command != 13)
...{
this.ServiceCommandCallback(command);
}
else
...{
try
...{
PowerBroadcastStatus powerStatus = (PowerBroadcastStatus) eventType;
bool flag = this.OnPowerEvent(powerStatus);
this.WriteEventLogEntry(Res.GetString("PowerEventOK"));
if (!flag)
...{
return 0x424d5144;
}
}
catch (Exception exception)
...{
this.WriteEventLogEntry(Res.GetString("PowerEventFailed", new object[] ...{ exception.ToString() }), EventLogEntryType.Error);
}
}
return 0;
}
private unsafe void ServiceCommandCallback(int command)
...{
fixed (NativeMethods.SERVICE_STATUS* service_statusRef = &this.status)
...{
if (command == 4)
...{
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
}
else if (((this.status.currentState != 5) && (this.status.currentState != 2)) && ((this.status.currentState != 3) && (this.status.currentState != 6)))
...{
switch (command)
...{
case 1:
...{
int currentState = this.status.currentState;
if ((this.status.currentState == 7) || (this.status.currentState == 4))
...{
this.status.currentState = 3;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
this.status.currentState = currentState;
new DeferredHandlerDelegate(this.DeferredStop).BeginInvoke(null, null);
}
goto Label_0259;
}
case 2:
if (this.status.currentState == 4)
...{
this.status.currentState = 6;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
new DeferredHandlerDelegate(this.DeferredPause).BeginInvoke(null, null);
}
goto Label_0259;
case 3:
if (this.status.currentState == 7)
...{
this.status.currentState = 5;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
try
...{
this.OnContinue();
this.WriteEventLogEntry(Res.GetString("ContinueSuccessful"));
}
catch (Exception exception)
...{
this.WriteEventLogEntry(Res.GetString("ContinueFailed", new object[] ...{ exception.ToString() }), EventLogEntryType.Error);
this.status.currentState = 7;
goto Label_0259;
}
this.status.currentState = 4;
NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef);
}
goto Label_0259;
case 5:
try
...{
this.OnShutdown();
this.WriteEventLogEntry(Res.GetString("ShutdownOK"));
}
catch (Exception exception2)
...{
this.WriteEventLogEntry(Res.GetString("ShutdownFailed", new object[] ...{ exception2.ToString() }), EventLogEntryType.Error);
}
goto Label_0259;
}
try
...{
this.OnCustomCommand(command);
this.WriteEventLogEntry(Res.GetString("CommandSuccessful"));
}
catch (Exception exception3)
...{
this.WriteEventLogEntry(Res.GetString("CommandFailed", new object[] ...{ exception3.ToString() }), EventLogEntryType.Error);
}
}
}
Label_0259:;
}
从上面的代码可以知道,只有当服务控制码command不等于13的时候,我们才有机会用ServiceCommandCallback对其进行处理。而这种处理只能获得控制码command,对于SERVICE_CONTROL_SESSIONCHANGE(14)的消息来说,ServiceBase类没有办法获得到底是Logon,Logoff或是用户切换时候发生的。
因此对于以下C++代码是没有办法移植到Framework 1.1 上的:
VOID HandlerEx(DWORD controlCode,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext)
...{
...
case SERVICE_CONTROL_SESSIONCHANGE:
switch(dwEventType)
...{
case WTS_SESSION_LOGOFF:
// Logoff
ICSEventLogoff();
break;
default:
break;
}
break;
...
}
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext)
...{
...
case SERVICE_CONTROL_SESSIONCHANGE:
switch(dwEventType)
...{
case WTS_SESSION_LOGOFF:
// Logoff
ICSEventLogoff();
break;
default:
break;
}
break;
...
}
在.Net Framework 2.0的ServiceBase中,回调函数在接受服务控制命令command之外,还将所有其他参数都传给了用户可以自由扩展的回调函数,多了OnSessionChange等包装好的函数,因此就没有这个问题了。同样,我们不用将这个服务用于不支持sessionchange的Win2000上。以下是C#写的服务:
using System;
using System.ServiceProcess;
using System.Text;
using System.IO;
namespace ServiceTest
...{
public partial class Service1 : ServiceBase
...{
public Service1()
...{
InitializeComponent();
}
protected override void OnStart(string[] args)
...{
// TODO: Add code here to start your service.
this.CanHandleSessionChangeEvent = true;
}
protected override void OnStop()
...{
// TODO: Add code here to perform any tear-down necessary to stop your service.
this.CanHandleSessionChangeEvent = false;
System.IO.FileStream fs = new System.IO.FileStream("c:/mysvc.txt",
System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
UTF8Encoding asc = new UTF8Encoding();
String log = "stop ";
fs.Seek(0, System.IO.SeekOrigin.End);
fs.Write(asc.GetBytes(log), 0, asc.GetByteCount(log));
fs.Close();
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
...{
base.OnSessionChange(changeDescription);
FileStream fs = new FileStream("c:/Log.txt",
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
UTF8Encoding asc = new UTF8Encoding();
String log = "On Session Change ";
fs.Seek(0, System.IO.SeekOrigin.End);
fs.Write(asc.GetBytes(log), 0, asc.GetByteCount(log));
fs.Close();
}
}
}
using System.ServiceProcess;
using System.Text;
using System.IO;
namespace ServiceTest
...{
public partial class Service1 : ServiceBase
...{
public Service1()
...{
InitializeComponent();
}
protected override void OnStart(string[] args)
...{
// TODO: Add code here to start your service.
this.CanHandleSessionChangeEvent = true;
}
protected override void OnStop()
...{
// TODO: Add code here to perform any tear-down necessary to stop your service.
this.CanHandleSessionChangeEvent = false;
System.IO.FileStream fs = new System.IO.FileStream("c:/mysvc.txt",
System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
UTF8Encoding asc = new UTF8Encoding();
String log = "stop ";
fs.Seek(0, System.IO.SeekOrigin.End);
fs.Write(asc.GetBytes(log), 0, asc.GetByteCount(log));
fs.Close();
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
...{
base.OnSessionChange(changeDescription);
FileStream fs = new FileStream("c:/Log.txt",
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
UTF8Encoding asc = new UTF8Encoding();
String log = "On Session Change ";
fs.Seek(0, System.IO.SeekOrigin.End);
fs.Write(asc.GetBytes(log), 0, asc.GetByteCount(log));
fs.Close();
}
}
}
安装服务可以用一个叫SRVINSTW.EXE的小工具,很方便。微软的instalutil.exe命令行真难用,bs一下。
- Winlogon Notify的Vista移植
- Winlogon notify的Vista移植
- Winlogon notify的Vista移植
- Windows NT WinLogon Notify
- Windows NT WinLogon Notify(转载+修改版)
- Winlogon
- notify dll,Taking Advantage of the Winlogon Notification Package
- 对winlogon进程的hook
- 讨厌的0xc0150002 MS has not give friendly notify for Vista with VS2005
- winlogon进程100%cpu占用的解决办法
- Winlogon 的编译与深度研究
- 一个注入winlogon的程序的代码,学习API用
- 如何移植32位C#工程到64位的XP或者VISTA下?
- notify()和notify()All的区别:
- Object的wait()、notify、notify 侵立删
- 2月8日,GINA&WINLOGON的一点东西
- csrss.exe和winlogon.exe引起cpu居高不下的解决办法
- 注入winlogon.exe引起的蓝屏问题分析
- 数据结构算法
- Bapi_Quotation_Createfromdata2--VA21
- 源码:Richtextbox和Hyperlink的光标控制问题的解决。
- MIPS 汇编语言编程的艺术(龙芯适用)
- 用.net中的socket实现文件传输
- Winlogon Notify的Vista移植
- 精妙Sql语句(总结以前的所有精华)
- ADO,ASP Errors集合和Error对象
- WebBrowser知识
- 如何用asp.net向其他服务器post一条信息
- 输入网址但不被记录
- Java抽象类与接口人性化理解
- OpenCms7.0 RC 1_千寻汉化包v2.1.2 发布
- 第一次用bolg