#include "stdafx.h" CService::CService() { m_hDevNotify = NULL; m_evTerminate = NULL; m_bRunningService = FALSE; m_StatusHandle = NULL; m_pDev = NULL; m_Status = SERVICE_STOPPED; } CService::~CService() { m_evTerminate = NULL; m_bRunningService = FALSE; m_StatusHandle = NULL; m_pDev = NULL; m_Status = SERVICE_STOPPED; } DWORD __stdcall CService::HandlerExThunk(CService *service, DWORD ctlcode, DWORD evtype, PVOID evdata) { switch (ctlcode) { case SERVICE_CONTROL_DEVICEEVENT: case SERVICE_CONTROL_HARDWAREPROFILECHANGE: return service->ServiceHandleDeviceChange(evtype); case SERVICE_CONTROL_POWEREVENT: return service->ServiceHandlePowerEvent(evtype, (DWORD)((DWORD_PTR)evdata)); default: service->ServiceCtrlHandler(ctlcode); return NO_ERROR; } } void __stdcall CService::ServiceMainThunk(CService *service, DWORD argc, TCHAR *argv[]) { service->ServiceMain(argc, argv); } BOOL CService::InitService() { m_bRunningService = TRUE; m_Status = SERVICE_RUNNING; return TRUE; } BOOL CService::SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint) { BOOL res; SERVICE_STATUS serviceStatus; serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; serviceStatus.dwCurrentState = dwCurrentState; if (dwCurrentState == SERVICE_START_PENDING) { serviceStatus.dwControlsAccepted = 0; } else { serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; } if (dwServiceSpecificExitCode == 0) { serviceStatus.dwWin32ExitCode = dwWin32ExitCode; } else { serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; } serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode; serviceStatus.dwCheckPoint = dwCheckPoint; serviceStatus.dwWaitHint = dwWaitHint; res = SetServiceStatus(m_StatusHandle, &serviceStatus); if (!res) { StopService(); } return res; } void CService::StopService() { if (m_bRunningService && m_pDev) { m_pDev->Stop(); m_bRunningService = FALSE; m_Status = SERVICE_STOPPED; } SetEvent(m_evTerminate); } void CService::terminate(DWORD error) { UnregisterNotification(m_hDevNotify); if (m_evTerminate) { CloseHandle(m_evTerminate); m_evTerminate = NULL; } if (m_StatusHandle) { SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0); } delete m_pDev; } void CService::ServiceCtrlHandler(DWORD controlCode) { switch (controlCode) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: m_Status = SERVICE_STOP_PENDING; SendStatusToSCM(m_Status, NO_ERROR, 0, 1, 5000); StopService(); return; case SERVICE_CONTROL_INTERROGATE: break; default: break; } SendStatusToSCM(m_Status, NO_ERROR, 0, 0, 0); } DWORD CService::ServiceHandleDeviceChange(DWORD evtype) { switch (evtype) { case DBT_DEVICEARRIVAL: case DBT_DEVICEQUERYREMOVEFAILED: m_pDev->Start(); break; case DBT_DEVICEQUERYREMOVE: case DBT_DEVICEREMOVECOMPLETE: m_pDev->Stop(); break; default: break; } return NO_ERROR; } DWORD CService::ServiceHandlePowerEvent(DWORD evtype, DWORD flags) { PrintMessage("ServiceHandlePowerEvent"); return NO_ERROR; } void CService::ServiceMain(DWORD argc, LPTSTR *argv) { BOOL res; if (!m_StatusHandle) { terminate(GetLastError()); return; } res = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000); if (!res) { terminate(GetLastError()); return; } m_pDev = new CDevice(); if (!m_pDev || !m_pDev->Init(this) || !m_pDev->Start()) { terminate(GetLastError()); return; } m_hDevNotify = RegisterDeviceInterfaceNotification(); if (m_hDevNotify == NULL) { terminate(GetLastError()); return; } m_evTerminate = CreateEvent(NULL, TRUE, FALSE, NULL); if (!m_evTerminate) { terminate(GetLastError()); return; } res = InitService(); if (!res) { terminate(GetLastError()); return; } res = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0, 0, 0); if (!res) { terminate(GetLastError()); return; } WaitForSingleObject(m_evTerminate, INFINITE); terminate(0); } void CService::GetStatus(SC_HANDLE service) { SERVICE_STATUS status; DWORD CurrentState; if (!QueryServiceStatus(service, &status)) { printf("Failed to get service status.\n"); return; } switch (status.dwCurrentState) { case SERVICE_RUNNING: CurrentState = SERVICE_RUNNING; printf("Service RUNNING.\n"); break; case SERVICE_STOPPED: CurrentState = SERVICE_STOPPED; printf("Service STOPPED.\n"); break; case SERVICE_CONTINUE_PENDING: CurrentState = SERVICE_CONTINUE_PENDING; printf("Service is resuming...\n"); break; case SERVICE_START_PENDING: CurrentState = SERVICE_START_PENDING; printf("Service is starting...\n"); break; case SERVICE_STOP_PENDING: CurrentState = SERVICE_STOP_PENDING; printf("Service is stopping...\n"); break; default: return; } SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0); } DWORD WINAPI CService::DeviceNotificationCallback(HCMNOTIFICATION Notify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize) { CService *pThis = reinterpret_cast(Context); DWORD event = 0; switch (Action) { case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: event = DBT_DEVICEARRIVAL; break; case CM_NOTIFY_ACTION_DEVICEQUERYREMOVE: event = DBT_DEVICEQUERYREMOVE; break; case CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED: event = DBT_DEVICEQUERYREMOVEFAILED; break; case CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE: event = DBT_DEVICEREMOVECOMPLETE; break; default: break; } if (event > 0) { pThis->ServiceHandleDeviceChange(event); } return ERROR_SUCCESS; } VOID WINAPI UnregisterNotificationWork(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work) { HCMNOTIFICATION Handle = static_cast(Context); CM_Unregister_Notification(Handle); CloseThreadpoolWork(Work); } HCMNOTIFICATION CService::RegisterDeviceInterfaceNotification() { HCMNOTIFICATION handle = NULL; CM_NOTIFY_FILTER filter; CONFIGRET cr; ::ZeroMemory(&filter, sizeof(filter)); filter.cbSize = sizeof(filter); filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_BALLOON; cr = CM_Register_Notification(&filter, this, CService::DeviceNotificationCallback, &handle); if (cr != CR_SUCCESS) { SetLastError(CM_MapCrToWin32Err(cr, ERROR_NOT_SUPPORTED)); } return handle; } HCMNOTIFICATION CService::RegisterDeviceHandleNotification(HANDLE DeviceHandle) { HCMNOTIFICATION handle = NULL; CM_NOTIFY_FILTER filter; CONFIGRET cr; ::ZeroMemory(&filter, sizeof(filter)); filter.cbSize = sizeof(filter); filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE; filter.u.DeviceHandle.hTarget = DeviceHandle; cr = CM_Register_Notification(&filter, this, CService::DeviceNotificationCallback, &handle); if (cr != CR_SUCCESS) { SetLastError(CM_MapCrToWin32Err(cr, ERROR_NOT_SUPPORTED)); } return handle; } BOOL CService::UnregisterNotification(HCMNOTIFICATION Handle) { BOOL ret = FALSE; if (Handle) { PTP_WORK work; work = CreateThreadpoolWork(UnregisterNotificationWork, Handle, NULL); if (work != NULL) { SubmitThreadpoolWork(work); } ret = TRUE; } return ret; }