Native Windows Service Example

Hi Community,

This blog post describes and explains the steps required to build a native Windows Service. One of the projects I’m currently working on is about a metadata filesystem for Windows (Similar to what WinFS was supposed to be but different in many aspects, mainly because users will have a greater level of customization and it will be template driven). One of the artefacts required (besides Minifilter driver and managed .NET assemblies it’s in the form of a Windows Service).

Building Windows Service in .NET is pretty straightforward, mainly because the project template in Visual Studio does pretty much all of the scaffolding for developers, however, in native code using Visual C++ that template is completely missing and it’s up to developers to do it by themselves.

In my use scenario, the rationale for having a Windows Service is to interact with Minifilter driver. Please bear in mind, most of the stuff we developers build run in User mode and drivers and such do it in Kernel mode, thus the need of having a Windows Service. User and Kernel mode are isolated one from the other, but there are ways to exchange information between the two (if required).

image

The source code for Windows Service is below (please disregard InitializeClr method).

1 #include "stdafx.h" 2 3 // Global variables 4 SERVICE_STATUS g_SvcStatus = {0}; 5 SERVICE_STATUS_HANDLE g_svcHandle = NULL; 6 HANDLE g_filterEventHandle = INVALID_HANDLE_VALUE; 7 HANDLE g_svcStopNotification = INVALID_HANDLE_VALUE; 8 9 10 11 12 /// <summary> 13 /// _tmains the specified argc. 14 /// </summary> 15 /// <param name="argc">The argc.</param> 16 /// <param name="argv">The argv.</param> 17 /// <returns>int.</returns> 18 int _tmain(int argc, _TCHAR* argv[]) { 19 auto retval = 0; 20 21 InitializeClr(); 22 23 SERVICE_TABLE_ENTRY ServiceTable[] = {{SERVICE_NAME, ServiceMain}}; 24 25 if (!StartServiceCtrlDispatcher(ServiceTable)) { 26 WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to start MetaFSAgent Service.")); 27 retval = GetLastError(); 28 } 29 30 return retval; 31 } 32 33 34 /// <summary> 35 /// Services the controller. 36 /// </summary> 37 /// <param name="ctlCode">The control code.</param> 38 VOID WINAPI ServiceController(DWORD ctlCode) { 39 switch (ctlCode) { 40 case SERVICE_CONTROL_STOP: 41 if (g_SvcStatus.dwCurrentState == SERVICE_RUNNING) { 42 ConfigureService(ConfigOption::STOP_SERVICE); 43 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 44 SetEvent(g_filterEventHandle); 45 SetEvent(g_svcStopNotification); 46 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"MetaFS Agent Service stopped.\nVersion 1.0.0.0.\n\nChanges made to metadata on existing filesystem objects will not be monitored")); 47 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Stop Pending).")); 48 } 49 break; 50 } 51 } 52 53 /// <summary> 54 /// Services the main. 55 /// </summary> 56 /// <param name="argc">The argc.</param> 57 /// <param name="argv">The argv.</param> 58 VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) { 59 HANDLE hThread; 60 61 if ((g_svcHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceController)) != NULL) { 62 ConfigureService(ConfigOption::START_SERVICE); 63 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 64 if ((g_svcStopNotification = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL) { 65 ConfigureService(ConfigOption::RUNNING_SERVICE); 66 if (SetServiceStatus(g_svcHandle, &g_SvcStatus)) { 67 InitializeCommunicationWithDriver(); 68 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"MetaFS Agent Service started.\nVersion 1.0.0.0")); 69 hThread = CreateThread(NULL, NULL, AsyncFilterWorker, NULL, NULL, NULL); 70 WaitForSingleObject(hThread, INFINITE); 71 CloseHandle(g_svcStopNotification); 72 ConfigureService(ConfigOption::STOPPED_SERVICE); 73 SetServiceStatus(g_svcHandle, &g_SvcStatus); 74 CloseHandle(hThread); 75 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Running).")); 76 } else { 77 WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to CreateEvent(g_ServiceStopEvent) for MetaFSAgent Service.")); 78 ConfigureService(ConfigOption::STOP_SERVICE); 79 SetServiceStatus(g_svcHandle, &g_SvcStatus); 80 } 81 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to change status of MetaFSAgent Service (Started).")); 82 } else WriteEvent(EventInformation(EventType::ERROR_TYPE, L"Unable to start MetaFSAgent Service.")); 83 } 84 85 /// <summary> 86 /// Asynchronouses the filter worker. 87 /// </summary> 88 /// <param name="lpParam">The lp parameter.</param> 89 /// <returns>DWORD.</returns> 90 DWORD WINAPI AsyncFilterWorker(LPVOID lpParam) { 91 while (WaitForSingleObject(g_svcStopNotification, INFINITE) != WAIT_OBJECT_0) { 92 // Has there been changes to the filesystem? 93 if (WaitForSingleObject(g_filterEventHandle, INFINITE) != WAIT_OBJECT_0) { 94 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"Notification from driver...")); 95 SetEvent(g_filterEventHandle); 96 } 97 } 98 return ERROR_SUCCESS; 99 } 100 101 /// <summary> 102 /// Configures the service. 103 /// </summary> 104 /// <param name="option">The option.</param> 105 VOID ConfigureService(ConfigOption option) { 106 switch (option) { 107 case ConfigOption::START_SERVICE: 108 ZeroMemory(&g_SvcStatus, sizeof(g_SvcStatus)); 109 g_SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 110 g_SvcStatus.dwControlsAccepted = 0; 111 g_SvcStatus.dwCurrentState = SERVICE_START_PENDING; 112 g_SvcStatus.dwWin32ExitCode = 0; 113 g_SvcStatus.dwServiceSpecificExitCode = 0; 114 g_SvcStatus.dwCheckPoint = 0; 115 break; 116 117 case ConfigOption::STOP_SERVICE: 118 g_SvcStatus.dwControlsAccepted = 0; 119 g_SvcStatus.dwCurrentState = SERVICE_STOPPED; 120 g_SvcStatus.dwWin32ExitCode = GetLastError(); 121 g_SvcStatus.dwCheckPoint = 1; 122 break; 123 124 case ConfigOption::RUNNING_SERVICE: 125 g_SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 126 g_SvcStatus.dwCurrentState = SERVICE_RUNNING; 127 g_SvcStatus.dwWin32ExitCode = 0; 128 g_SvcStatus.dwCheckPoint = 0; 129 break; 130 131 case ConfigOption::STOPPED_SERVICE: 132 g_SvcStatus.dwControlsAccepted = 0; 133 g_SvcStatus.dwCurrentState = SERVICE_STOPPED; 134 g_SvcStatus.dwWin32ExitCode = 0; 135 g_SvcStatus.dwCheckPoint = 3; 136 } 137 } 138 139 /// <summary> 140 /// Initializes the CLR. 141 /// </summary> 142 VOID InitializeClr() { 143 HINSTANCE hInstance; 144 145 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); 146 147 if ((hInstance = LoadLibraryEx(INTEROP_LIBRARY, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) != NULL) { 148 ptrInitializeClr funcPtr = (ptrInitializeClr)GetProcAddress(hInstance, "InitializeClr"); 149 funcPtr(); 150 } 151 152 SetErrorMode(NULL); 153 154 FreeLibrary(hInstance); 155 } 156 157 /// <summary> 158 /// Initializes communication with driver 159 /// </summary> 160 VOID InitializeCommunicationWithDriver() { 161 HANDLE hFile; 162 DWORD cbSize; 163 164 if ((hFile = CreateFile(TARGET_DRIVER, GENERIC_READ | GENERIC_WRITE, 165 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) { 166 if ((g_filterEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL)) != NULL) { 167 if (DeviceIoControl(hFile, IOCTL_REGISTER_EVENT, &g_filterEventHandle, sizeof(g_filterEventHandle), NULL, NULL, &cbSize, NULL)) 168 WriteEvent(EventInformation(EventType::INFORMATION_TYPE, L"Event successfully created....")); 169 } 170 CloseHandle(hFile); 171 } 172 } 173 174 /// <summary> 175 /// Writes the event. 176 /// </summary> 177 /// <param name="ei">The ei.</param> 178 VOID WriteEvent(const EventInformation& ei) { 179 HANDLE hEventLog; 180 LPWSTR messages[1] = {(WCHAR*)ei.Message.c_str()}; 181 182 if ((hEventLog = OpenEventLog(NULL, L"Application")) != NULL) { 183 ReportEvent(hEventLog, ei.Type, 0x1, 0x1, NULL, 1, NULL, (LPCWSTR*)messages, NULL); 184 CloseEventLog(hEventLog); 185 } 186 }

Regards,

Angel

Leave a Reply

Your email address will not be published. Required fields are marked *