//*************************************************************** // From the book "Win32 System Services: The Heart of Windows 98 // and Windows 2000" // by Marshall Brain // Published by Prentice Hall // // Copyright 1995, by Prentice Hall. // // This code implements an automatic logon announcement service by // placing two independent services into a single executable. // One part of the service detects logins and sends out mailslot // messages on the network. The other part announces the logins in // a message box. //*************************************************************** // logonann.cpp #include #include #include #include // Global variables // Event used to hold ServiceMain from completing HANDLE terminateAnnounceEvent = 0; HANDLE terminateSenseEvent = 0; // Handle used to communicate status info with // the SCM. Created by RegisterServiceCtrlHandler SERVICE_STATUS_HANDLE senseServiceStatusHandle; SERVICE_STATUS_HANDLE announceServiceStatusHandle; // Flags holding current state of service BOOL pauseSenseService = FALSE; BOOL runningSenseService = FALSE; BOOL pauseAnnounceService = FALSE; BOOL runningAnnounceService = FALSE; // Thread for the actual work HANDLE senseThreadHandle = 0; HANDLE announceThreadHandle = 0; // global mailslot HANDLE mailslot = 0; // Holds on to current user so it can detect change char currentUser[100]; void ErrorHandler(char *s, DWORD err) { cout << s << endl; cout << "Error number: " << err << endl; ExitProcess(err); } DWORD AnnounceThread(LPDWORD param) { char buffer[2000]; DWORD numRead; DWORD maxMsg, nextMsg, numMsg, timeout; while (1) { GetMailslotInfo(mailslot, &maxMsg, &nextMsg, &numMsg, &timeout); if (numMsg > 0) { ReadFile(mailslot, buffer, nextMsg, &numRead, 0); buffer[numRead]='\0'; MessageBox(0, buffer, "Logon Announcement", MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY); } else Sleep(1000); } return 0; } BOOL CALLBACK EnumWindowsProc(HANDLE win, LPARAM param) { char buffer[100]; DWORD numWrite; GetWindowText(win, buffer, 100); if (strncmp(buffer, "Program Manager", 15)==0 && strcmp((buffer+18),currentUser) != 0) { HANDLE writeMailslot = CreateFile("\\\\*\\mailslot\\logonann", GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); WriteFile(writeMailslot, (buffer+18), strlen(buffer+18), &numWrite, 0); CloseHandle(writeMailslot); strcpy(currentUser, (buffer+18)); return FALSE; } else return TRUE; } DWORD SenseThread(LPDWORD param) { BOOL success; while (1) { success = EnumWindows((WNDENUMPROC) EnumWindowsProc, 0); Sleep(10000); } return 0; } // Initializes the service by starting its thread BOOL InitSenseService() { DWORD id; senseThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) SenseThread, 0, 0, &id); if (senseThreadHandle==0) return FALSE; else { runningSenseService = TRUE; return TRUE; } } // Initializes the service by starting its thread BOOL InitAnnounceService() { DWORD id; announceThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) AnnounceThread, 0, 0, &id); if (announceThreadHandle==0) return FALSE; else { runningAnnounceService = TRUE; return TRUE; } } // Resumes a paused service VOID ResumeSenseService() { pauseSenseService=FALSE; ResumeThread(senseThreadHandle); } // Pauses the service VOID PauseSenseService() { pauseSenseService = TRUE; SuspendThread(senseThreadHandle); } // Resumes a paused service VOID ResumeAnnounceService() { pauseAnnounceService=FALSE; ResumeThread(announceThreadHandle); } // Pauses the service VOID PauseAnnounceService() { pauseAnnounceService = TRUE; SuspendThread(announceThreadHandle); } // This function consolidates the activities of // updating the service status with SetServiceStatus BOOL SendStatusToSCM (SERVICE_STATUS_HANDLE s, DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint) { BOOL success; SERVICE_STATUS serviceStatus; // Fill in all of the SERVICE_STATUS fields serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; //*** serviceStatus.dwCurrentState = dwCurrentState; // If in the process of starting, then accept // no control events, else accept anything if (dwCurrentState == SERVICE_START_PENDING) serviceStatus.dwControlsAccepted = 0; else serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; // if a specific exit code is defines, set up the // win32 exit code properly if (dwServiceSpecificExitCode == 0) serviceStatus.dwWin32ExitCode = dwWin32ExitCode; else serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode; serviceStatus.dwCheckPoint = dwCheckPoint; serviceStatus.dwWaitHint = dwWaitHint; success = SetServiceStatus (s, &serviceStatus); return success; } // Dispatches events received from the service control // manager VOID AnnounceServiceCtrlHandler (DWORD controlCode) { DWORD currentState = 0; BOOL success; switch(controlCode) { // There is no START option because // ServiceMain gets called on a start // Stop the service case SERVICE_CONTROL_STOP: currentState = SERVICE_STOP_PENDING; // Tell the SCM what's happening success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000); // Not much to do if not successful // Allow ServiceMain to return SetEvent(terminateAnnounceEvent); return; // Pause the service case SERVICE_CONTROL_PAUSE: if (runningAnnounceService && !pauseAnnounceService) { // Tell the SCM what's happening success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000); PauseAnnounceService(); currentState = SERVICE_PAUSED; } break; // Resume from a pause case SERVICE_CONTROL_CONTINUE: if (runningAnnounceService && pauseAnnounceService) { ResumeAnnounceService(); // Tell the SCM what's happening success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000); currentState = SERVICE_RUNNING; } break; // Update current status case SERVICE_CONTROL_INTERROGATE: // it will fall to bottom and send status break; // Do nothing in a shutdown. Could do cleanup here, // but it must be very quick. case SERVICE_CONTROL_SHUTDOWN: // Do nothing on shutdown return; default: break; } SendStatusToSCM(announceServiceStatusHandle,currentState, NO_ERROR, 0, 0, 0); } // Dispatches events received from the service control // manager VOID SenseServiceCtrlHandler (DWORD controlCode) { DWORD currentState = 0; BOOL success; switch(controlCode) { // There is no START option because // ServiceMain gets called on a start // Stop the service case SERVICE_CONTROL_STOP: currentState = SERVICE_STOP_PENDING; // Tell the SCM what's happening success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000); // Not much to do if not successful // Allow ServiceMain to return SetEvent(terminateSenseEvent); return; // Pause the service case SERVICE_CONTROL_PAUSE: if (runningSenseService && !pauseSenseService) { // Tell the SCM what's happening success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000); PauseSenseService(); currentState = SERVICE_PAUSED; } break; // Resume from a pause case SERVICE_CONTROL_CONTINUE: if (runningSenseService && pauseSenseService) { ResumeSenseService(); // Tell the SCM what's happening success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000); currentState = SERVICE_RUNNING; } break; // Update current status case SERVICE_CONTROL_INTERROGATE: // it will fall to bottom and send status break; // Do nothing in a shutdown. Could do cleanup here, // but it must be very quick. case SERVICE_CONTROL_SHUTDOWN: // Do nothing on shutdown return; default: break; } SendStatusToSCM(senseServiceStatusHandle,currentState, NO_ERROR, 0, 0, 0); } // Handle an error from ServiceMain by cleaning up and // telling SCM that it didn't start. VOID terminate(SERVICE_STATUS_HANDLE s, DWORD error) { // if terminateEvent has been created, close it. if (s==senseServiceStatusHandle) if (terminateSenseEvent) CloseHandle(terminateSenseEvent); else if (terminateAnnounceEvent) CloseHandle(terminateAnnounceEvent); // Send a message to the scm to tell about stopage SendStatusToSCM(s, SERVICE_STOPPED, error, 0, 0, 0); // If the thread has started kill it off if (s==senseServiceStatusHandle) if (senseThreadHandle) CloseHandle(senseThreadHandle); else if (announceThreadHandle) CloseHandle(announceThreadHandle); // Do not need to close serviceStatusHandle } // 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 eror. VOID SenseServiceMain(DWORD argc, LPTSTR *argv) { BOOL success; // immediately call Registration function senseServiceStatusHandle = RegisterServiceCtrlHandler( "LogonSense", (LPHANDLER_FUNCTION)SenseServiceCtrlHandler); if (!senseServiceStatusHandle) { terminate(senseServiceStatusHandle,GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000); if (!success) { terminate(senseServiceStatusHandle,GetLastError()); return; } // create the termination event terminateSenseEvent = CreateEvent (0, TRUE, FALSE, 0); if (!terminateSenseEvent) { terminate(senseServiceStatusHandle,GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_START_PENDING, NO_ERROR, 0, 2, 5000); if (!success) { terminate(senseServiceStatusHandle,GetLastError()); return; } // Start the service itself success = InitSenseService(); if (!success) { terminate(senseServiceStatusHandle,GetLastError()); return; } // The service is now running. // Notify SCM of progress success = SendStatusToSCM(senseServiceStatusHandle, SERVICE_RUNNING, NO_ERROR, 0, 0, 0); if (!success) { terminate(senseServiceStatusHandle,GetLastError()); return; } // Wait for stop signal, and then terminate WaitForSingleObject (terminateSenseEvent, INFINITE); terminate(senseServiceStatusHandle,0); } // 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 eror. VOID AnnounceServiceMain(DWORD argc, LPTSTR *argv) { BOOL success; // immediately call Registration function announceServiceStatusHandle = RegisterServiceCtrlHandler( "LogonAnnounce", (LPHANDLER_FUNCTION)AnnounceServiceCtrlHandler); if (!announceServiceStatusHandle) { terminate(announceServiceStatusHandle,GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000); if (!success) { terminate(announceServiceStatusHandle,GetLastError()); return; } // create the termination event terminateAnnounceEvent = CreateEvent (0, TRUE, FALSE, 0); if (!terminateAnnounceEvent) { terminate(announceServiceStatusHandle,GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_START_PENDING, NO_ERROR, 0, 2, 5000); if (!success) { terminate(announceServiceStatusHandle,GetLastError()); return; } // Start the service itself success = InitAnnounceService(); if (!success) { terminate(announceServiceStatusHandle,GetLastError()); return; } // The service is now running. // Notify SCM of progress success = SendStatusToSCM(announceServiceStatusHandle, SERVICE_RUNNING, NO_ERROR, 0, 0, 0); if (!success) { terminate(announceServiceStatusHandle,GetLastError()); return; } // Wait for stop signal, and then terminate WaitForSingleObject (terminateAnnounceEvent, INFINITE); terminate(announceServiceStatusHandle,0); } //VOID main(VOID) int WINAPI WinMain(HANDLE ghInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { SERVICE_TABLE_ENTRY serviceTable[] = { { "LogonSense", (LPSERVICE_MAIN_FUNCTION) SenseServiceMain}, { "LogonAnnounce", (LPSERVICE_MAIN_FUNCTION) AnnounceServiceMain}, { NULL, NULL } }; BOOL success; // Put it here so it's initialized for both. mailslot = CreateMailslot("\\\\.\\mailslot\\logonann", 200, MAILSLOT_WAIT_FOREVER, 0); success = StartServiceCtrlDispatcher(serviceTable); if (!success) ErrorHandler("In StartServiceCtrlDispatcher", GetLastError()); return 0; }