From 2d8d6facc32154471f70bb75f7f1f4ec4ca166ed Mon Sep 17 00:00:00 2001 From: mutouyun Date: Sat, 23 Sep 2023 14:03:13 +0800 Subject: [PATCH] Windows services can communicate with common processes. --- CMakeLists.txt | 4 + demo/win_service/client/CMakeLists.txt | 8 + demo/win_service/client/main.cpp | 28 ++++ demo/win_service/service/CMakeLists.txt | 8 + demo/win_service/service/main.cpp | 189 ++++++++++++++++++++++++ src/libipc/platform/win/mutex.h | 2 +- src/libipc/platform/win/semaphore.h | 2 +- src/libipc/platform/win/shm_win.cpp | 2 +- 8 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 demo/win_service/client/CMakeLists.txt create mode 100644 demo/win_service/client/main.cpp create mode 100644 demo/win_service/service/CMakeLists.txt create mode 100644 demo/win_service/service/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d9b2a5..12cebd4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,10 @@ if (LIBIPC_BUILD_DEMOS) add_subdirectory(demo/chat) add_subdirectory(demo/msg_que) add_subdirectory(demo/send_recv) + if (MSVC) + add_subdirectory(demo/win_service/service) + add_subdirectory(demo/win_service/client) + endif() endif() install( diff --git a/demo/win_service/client/CMakeLists.txt b/demo/win_service/client/CMakeLists.txt new file mode 100644 index 0000000..2dc03d5 --- /dev/null +++ b/demo/win_service/client/CMakeLists.txt @@ -0,0 +1,8 @@ +project(win_client) + +file(GLOB SRC_FILES ./*.cpp) +file(GLOB HEAD_FILES ./*.h) + +add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) + +target_link_libraries(${PROJECT_NAME} ipc) diff --git a/demo/win_service/client/main.cpp b/demo/win_service/client/main.cpp new file mode 100644 index 0000000..fea348e --- /dev/null +++ b/demo/win_service/client/main.cpp @@ -0,0 +1,28 @@ +/// \brief To create a basic Windows command line program. + +#include +#include +#include + +#include "libipc/ipc.h" + +int _tmain (int argc, TCHAR *argv[]) { + _tprintf(_T("My Sample Client: Entry\n")); + ipc::channel ipc_r{"service ipc r", ipc::receiver}; + ipc::channel ipc_w{"service ipc w", ipc::sender}; + while (1) { + auto msg = ipc_r.recv(); + if (msg.empty()) { + _tprintf(_T("My Sample Client: message recv error\n")); + return -1; + } + printf("My Sample Client: message recv: [%s]\n", (char const *)msg.data()); + while (!ipc_w.send("Copy.")) { + _tprintf(_T("My Sample Client: message send error\n")); + Sleep(1000); + } + _tprintf(_T("My Sample Client: message send [Copy]\n")); + } + _tprintf(_T("My Sample Client: Exit\n")); + return 0; +} diff --git a/demo/win_service/service/CMakeLists.txt b/demo/win_service/service/CMakeLists.txt new file mode 100644 index 0000000..b7bb357 --- /dev/null +++ b/demo/win_service/service/CMakeLists.txt @@ -0,0 +1,8 @@ +project(win_service) + +file(GLOB SRC_FILES ./*.cpp) +file(GLOB HEAD_FILES ./*.h) + +add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEAD_FILES}) + +target_link_libraries(${PROJECT_NAME} ipc) diff --git a/demo/win_service/service/main.cpp b/demo/win_service/service/main.cpp new file mode 100644 index 0000000..109c560 --- /dev/null +++ b/demo/win_service/service/main.cpp @@ -0,0 +1,189 @@ +/// \brief To create a basic Windows Service in C++. +/// \see https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus + +#include +#include +#include + +#include "libipc/ipc.h" + +SERVICE_STATUS g_ServiceStatus = {0}; +SERVICE_STATUS_HANDLE g_StatusHandle = NULL; +HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE; + +VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv); +VOID WINAPI ServiceCtrlHandler (DWORD); +DWORD WINAPI ServiceWorkerThread (LPVOID lpParam); + +#define SERVICE_NAME _T("My Sample Service") + +int _tmain (int argc, TCHAR *argv[]) { + OutputDebugString(_T("My Sample Service: Main: Entry")); + + SERVICE_TABLE_ENTRY ServiceTable[] = { + {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, + {NULL, NULL} + }; + + if (StartServiceCtrlDispatcher (ServiceTable) == FALSE) { + OutputDebugString(_T("My Sample Service: Main: StartServiceCtrlDispatcher returned error")); + return GetLastError (); + } + + OutputDebugString(_T("My Sample Service: Main: Exit")); + return 0; +} + +VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv) { + DWORD Status = E_FAIL; + + OutputDebugString(_T("My Sample Service: ServiceMain: Entry")); + + g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler); + + if (g_StatusHandle == NULL) { + OutputDebugString(_T("My Sample Service: ServiceMain: RegisterServiceCtrlHandler returned error")); + goto EXIT; + } + + // Tell the service controller we are starting + ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus)); + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwServiceSpecificExitCode = 0; + g_ServiceStatus.dwCheckPoint = 0; + + if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) { + OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); + } + + /* + * Perform tasks neccesary to start the service here + */ + OutputDebugString(_T("My Sample Service: ServiceMain: Performing Service Start Operations")); + + // Create stop event to wait on later. + g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + if (g_ServiceStopEvent == NULL) { + OutputDebugString(_T("My Sample Service: ServiceMain: CreateEvent(g_ServiceStopEvent) returned error")); + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = GetLastError(); + g_ServiceStatus.dwCheckPoint = 1; + + if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) { + OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); + } + goto EXIT; + } + + // Tell the service controller we are started + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 0; + + if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) { + OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); + } + + // Start the thread that will perform the main task of the service + HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL); + + OutputDebugString(_T("My Sample Service: ServiceMain: Waiting for Worker Thread to complete")); + + // Wait until our worker thread exits effectively signaling that the service needs to stop + WaitForSingleObject (hThread, INFINITE); + + OutputDebugString(_T("My Sample Service: ServiceMain: Worker Thread Stop Event signaled")); + + + /* + * Perform any cleanup tasks + */ + OutputDebugString(_T("My Sample Service: ServiceMain: Performing Cleanup Operations")); + + CloseHandle (g_ServiceStopEvent); + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 3; + + if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) { + OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error")); + } + +EXIT: + OutputDebugString(_T("My Sample Service: ServiceMain: Exit")); + return; +} + +VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode) { + OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Entry")); + + switch (CtrlCode) { + case SERVICE_CONTROL_STOP : + + OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SERVICE_CONTROL_STOP Request")); + + if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) + break; + + /* + * Perform tasks neccesary to stop the service here + */ + + g_ServiceStatus.dwControlsAccepted = 0; + g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwCheckPoint = 4; + + if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) { + OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error")); + } + + // This will signal the worker thread to start shutting down + SetEvent (g_ServiceStopEvent); + + break; + + default: + break; + } + + OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Exit")); +} + +DWORD WINAPI ServiceWorkerThread (LPVOID lpParam) { + OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Entry")); + ipc::channel ipc_r{"service ipc r", ipc::sender}; + ipc::channel ipc_w{"service ipc w", ipc::receiver}; + + // Periodically check if the service has been requested to stop + while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) { + /* + * Perform main service function here + */ + if (!ipc_r.send("Hello, World!")) { + OutputDebugString(_T("My Sample Service: send failed.")); + } + else { + OutputDebugString(_T("My Sample Service: send [Hello, World!]")); + auto msg = ipc_w.recv(1000); + if (msg.empty()) { + OutputDebugString(_T("My Sample Service: recv error")); + } else { + OutputDebugStringA((std::string{"My Sample Service: recv ["} + msg.get() + "]").c_str()); + } + } + Sleep(3000); + } + + OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Exit")); + + return ERROR_SUCCESS; +} diff --git a/src/libipc/platform/win/mutex.h b/src/libipc/platform/win/mutex.h index 8ecaf47..97f4a64 100644 --- a/src/libipc/platform/win/mutex.h +++ b/src/libipc/platform/win/mutex.h @@ -33,7 +33,7 @@ public: bool open(char const *name) noexcept { close(); - h_ = ::CreateMutex(detail::get_sa(), FALSE, ipc::detail::to_tchar(name).c_str()); + h_ = ::CreateMutex(detail::get_sa(), FALSE, ipc::detail::to_tchar(ipc::string{"Global\\"} + name).c_str()); if (h_ == NULL) { ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name); return false; diff --git a/src/libipc/platform/win/semaphore.h b/src/libipc/platform/win/semaphore.h index 47fd45d..b979ba3 100644 --- a/src/libipc/platform/win/semaphore.h +++ b/src/libipc/platform/win/semaphore.h @@ -32,7 +32,7 @@ public: close(); h_ = ::CreateSemaphore(detail::get_sa(), static_cast(count), LONG_MAX, - ipc::detail::to_tchar(name).c_str()); + ipc::detail::to_tchar(ipc::string{"Global\\"} + name).c_str()); if (h_ == NULL) { ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name); return false; diff --git a/src/libipc/platform/win/shm_win.cpp b/src/libipc/platform/win/shm_win.cpp index 366e8bd..be3fd45 100755 --- a/src/libipc/platform/win/shm_win.cpp +++ b/src/libipc/platform/win/shm_win.cpp @@ -33,7 +33,7 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) { return nullptr; } HANDLE h; - auto fmt_name = ipc::detail::to_tchar(ipc::string{"__IPC_SHM__"} + name); + auto fmt_name = ipc::detail::to_tchar(ipc::string{"Global\\__IPC_SHM__"} + name); // Opens a named file mapping object. if (mode == open) { h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());