Windows services can communicate with common processes.

This commit is contained in:
mutouyun 2023-09-23 14:03:13 +08:00
parent 6e635934c1
commit 2d8d6facc3
8 changed files with 240 additions and 3 deletions

View File

@ -59,6 +59,10 @@ if (LIBIPC_BUILD_DEMOS)
add_subdirectory(demo/chat) add_subdirectory(demo/chat)
add_subdirectory(demo/msg_que) add_subdirectory(demo/msg_que)
add_subdirectory(demo/send_recv) add_subdirectory(demo/send_recv)
if (MSVC)
add_subdirectory(demo/win_service/service)
add_subdirectory(demo/win_service/client)
endif()
endif() endif()
install( install(

View File

@ -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)

View File

@ -0,0 +1,28 @@
/// \brief To create a basic Windows command line program.
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#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;
}

View File

@ -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)

View File

@ -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 <Windows.h>
#include <tchar.h>
#include <string>
#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<char const *>() + "]").c_str());
}
}
Sleep(3000);
}
OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Exit"));
return ERROR_SUCCESS;
}

View File

@ -33,7 +33,7 @@ public:
bool open(char const *name) noexcept { bool open(char const *name) noexcept {
close(); 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) { if (h_ == NULL) {
ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name); ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name);
return false; return false;

View File

@ -32,7 +32,7 @@ public:
close(); close();
h_ = ::CreateSemaphore(detail::get_sa(), h_ = ::CreateSemaphore(detail::get_sa(),
static_cast<LONG>(count), LONG_MAX, static_cast<LONG>(count), LONG_MAX,
ipc::detail::to_tchar(name).c_str()); ipc::detail::to_tchar(ipc::string{"Global\\"} + name).c_str());
if (h_ == NULL) { if (h_ == NULL) {
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name); ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name);
return false; return false;

View File

@ -33,7 +33,7 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
return nullptr; return nullptr;
} }
HANDLE h; 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. // Opens a named file mapping object.
if (mode == open) { if (mode == open) {
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str()); h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());