diff --git a/.gitignore b/.gitignore index 9bca0610..b8573713 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ ui_*.h *.jsc Makefile* *build-* +*build_* # Qt unit tests target_wrapper.* diff --git a/3rdparty/gtest/src/gtest-death-test.cc b/3rdparty/gtest/src/gtest-death-test.cc index da09a1cf..7419ba2c 100644 --- a/3rdparty/gtest/src/gtest-death-test.cc +++ b/3rdparty/gtest/src/gtest-death-test.cc @@ -1296,8 +1296,8 @@ static void StackLowerThanAddress(const void* ptr, bool* result) { GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ static bool StackGrowsDown() { - int dummy; - bool result; + int dummy {}; + bool result {}; StackLowerThanAddress(&dummy, &result); return result; } diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d9b2a57..7e07dd3c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,13 @@ 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) + else() + add_subdirectory(demo/linux_service/service) + add_subdirectory(demo/linux_service/client) + endif() endif() install( diff --git a/demo/linux_service/client/CMakeLists.txt b/demo/linux_service/client/CMakeLists.txt new file mode 100644 index 00000000..b7d83920 --- /dev/null +++ b/demo/linux_service/client/CMakeLists.txt @@ -0,0 +1,8 @@ +project(linux_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/linux_service/client/main.cpp b/demo/linux_service/client/main.cpp new file mode 100644 index 00000000..9839be92 --- /dev/null +++ b/demo/linux_service/client/main.cpp @@ -0,0 +1,28 @@ +/// \brief To create a basic command line program. + +#include +#include +#include + +#include "libipc/ipc.h" + +int main(int argc, char *argv[]) { + printf("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()) { + printf("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.")) { + printf("My Sample Client: message send error\n"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + printf("My Sample Client: message send [Copy]\n"); + } + printf("My Sample Client: Exit\n"); + return 0; +} diff --git a/demo/linux_service/service/CMakeLists.txt b/demo/linux_service/service/CMakeLists.txt new file mode 100644 index 00000000..a8773377 --- /dev/null +++ b/demo/linux_service/service/CMakeLists.txt @@ -0,0 +1,8 @@ +project(linux_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/linux_service/service/main.cpp b/demo/linux_service/service/main.cpp new file mode 100644 index 00000000..cf8dec37 --- /dev/null +++ b/demo/linux_service/service/main.cpp @@ -0,0 +1,34 @@ +/// \brief To create a basic command line program. + +#include +#include +#include +#include + +#include "libipc/ipc.h" + +int main(int argc, char *argv[]) { + printf("My Sample Service: Main: Entry\n"); + + ipc::channel ipc_r{"service ipc r", ipc::sender}; + ipc::channel ipc_w{"service ipc w", ipc::receiver}; + + while (1) { + if (!ipc_r.send("Hello, World!")) { + printf("My Sample Service: send failed.\n"); + } + else { + printf("My Sample Service: send [Hello, World!]\n"); + auto msg = ipc_w.recv(1000); + if (msg.empty()) { + printf("My Sample Service: recv error\n"); + } else { + printf("%s\n", (std::string{"My Sample Service: recv ["} + msg.get() + "]").c_str()); + } + } + std::this_thread::sleep_for(std::chrono::seconds(3)); + } + + printf("My Sample Service: Main: Exit\n"); + return 0; +} diff --git a/demo/win_service/client/CMakeLists.txt b/demo/win_service/client/CMakeLists.txt new file mode 100644 index 00000000..2dc03d5a --- /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 00000000..44a9ba62 --- /dev/null +++ b/demo/win_service/client/main.cpp @@ -0,0 +1,41 @@ +/// \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{ipc::prefix{"Global\\"}, "service ipc r", ipc::receiver}; + ipc::channel ipc_w{ipc::prefix{"Global\\"}, "service ipc w", ipc::sender}; + while (1) { + if (!ipc_r.reconnect(ipc::receiver)) { + Sleep(1000); + continue; + } + auto msg = ipc_r.recv(); + if (msg.empty()) { + _tprintf(_T("My Sample Client: message recv error\n")); + ipc_r.disconnect(); + continue; + } + printf("My Sample Client: message recv: [%s]\n", (char const *)msg.data()); + for (;;) { + if (!ipc_w.reconnect(ipc::sender)) { + Sleep(1000); + continue; + } + if (ipc_w.send("Copy.")) { + break; + } + _tprintf(_T("My Sample Client: message send error\n")); + ipc_w.disconnect(); + 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 00000000..b7bb3574 --- /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 00000000..ce522280 --- /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{ipc::prefix{"Global\\"}, "service ipc r", ipc::sender}; + ipc::channel ipc_w{ipc::prefix{"Global\\"}, "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/include/libipc/def.h b/include/libipc/def.h index 8c1a72ba..45cd7801 100755 --- a/include/libipc/def.h +++ b/include/libipc/def.h @@ -65,4 +65,9 @@ struct relat_trait> { template