Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Troubles when using named pipes in Windows service

I'm creating service for Windows 10. I've followed this tutorial. https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus

But when I've added my code to this example, something happened and I can't stop service from Services list. I can stop it only from task manager. But if I comment my code, service stopping properly.

I would be grateful for any advice. Code listed below.

#include <Windows.h>
#include <tchar.h>
#include <string>
#include <fstream>

SERVICE_STATUS        g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;

std::wofstream output(L"C:\\Users\\0x0\\source\\Service\\output.txt");

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);

#define SERVICE_NAME _T("TestService")

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    auto dwFlags = FILE_ATTRIBUTE_NORMAL;
    STARTUPINFOW si;
    GetStartupInfoW(&si);
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    ZeroMemory(&pi, sizeof(pi));

    HANDLE service_pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\ServicePipe"),
        PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
        128,
        16 * 1024,
        16 * 1024,
        0,
        nullptr);

    if (service_pipe == INVALID_HANDLE_VALUE)
    {
        return 1;
    }

    TCHAR inbox_buffer[1024];
    DWORD read, write;

    while (1)
    {
        if (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
        {
            //If I comment this 'if block', service stopping properly. But I don't see any errors in this part of code.
            if (ConnectNamedPipe(service_pipe, nullptr))
            {
                if (ReadFile(service_pipe, inbox_buffer, 1024 * sizeof(TCHAR), &read, nullptr))
                {
                    std::wstring args = inbox_buffer;

                    std::wstring command = L"\"C:\\Program Files (x86)\\Unility\\utility.exe\" " + args;

                    if (!CreateProcessW(NULL, (LPWSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
                    {
                        output << "CreateProcessW Error = " << GetLastError() << std::endl;
                    }
                    WaitForSingleObject(pi.hProcess, INFINITE);
                    CloseHandle(pi.hProcess);
                    CloseHandle(pi.hThread);

                    bool success = false;
                    do
                    {
                        success = WriteFile(service_pipe, L"executed", sizeof(L"executed"), &write, nullptr);
                    } while (!success);
                }
                DisconnectNamedPipe(service_pipe);
            }
        }
        else
        {
            output << "WaitForSingleObject(g_ServiceStopEvent, 0)" << std::endl;
            break;
        }
    }

    output << "CloseHandle(service_pipe)_1" << std::endl;
    CloseHandle(service_pipe);
    output << "CloseHandle(service_pipe)_2" << std::endl;
    return ERROR_SUCCESS;
}

VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
    switch (CtrlCode)
    {
    case SERVICE_CONTROL_STOP:

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
            break;

        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)
        {
            output << "TestService: ServiceCtrlHandler: SetServiceStatus returned error" << std::endl;
        }
        SetEvent(g_ServiceStopEvent);

        output << "SetEvent(g_ServiceStopEvent)_1" << std::endl;
        break;

    default:
        break;
    }
    output << "SetEvent(g_ServiceStopEvent)_2" << std::endl;
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;

    g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);

    if (g_StatusHandle == NULL)
    {
        return;
    }

    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)
    {
            output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
    }

    g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
        }
        return;
    }

    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)
    {
        output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
    }

    HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);

    output << "CloseHandle(g_ServiceStopEvent)_1" << std::endl;
    CloseHandle(g_ServiceStopEvent);
    output << "CloseHandle(g_ServiceStopEvent)_2" << std::endl;

    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
    }

    return;
}

int _tmain(int argc, TCHAR *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        {(LPWSTR)"TestService", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
        {NULL, NULL}
    };

    if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        return GetLastError();
    }

    return 0;
}
like image 741
Peter Avatar asked Nov 08 '22 02:11

Peter


1 Answers

Dirty way, but it's working. I've created function which handles all job related to pipes. I've runned this function in new thread. When the service receives a stop signal, I send a stop message to the pipe and it stops the loop.

#include <Windows.h>
#include <tchar.h>
#include <string>
#include <thread>
#include <fstream>

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("TestService")

void pipe_server_function() {
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    auto dwFlags = FILE_ATTRIBUTE_NORMAL;
    STARTUPINFOW si;
    GetStartupInfoW(&si);
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
    ZeroMemory(&pi, sizeof(pi));

    HANDLE service_pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\ServicePipe"),
        PIPE_ACCESS_DUPLEX,
        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
        128,
        16 * 1024,
        16 * 1024,
        0,
        nullptr);

    if (service_pipe == INVALID_HANDLE_VALUE)
    {
        return;
    }

    TCHAR inbox_buffer[1024];
    std::fill(inbox_buffer, inbox_buffer + 1024, '\0');
    DWORD read, write;

    while (true)
    {

        if (ConnectNamedPipe(service_pipe, nullptr))
        {
            if (ReadFile(service_pipe, inbox_buffer, 1024 * sizeof(TCHAR), &read, nullptr))
            {
                std::wstring args = inbox_buffer;

                if (args.find("stop_signal") != std::wstring::npos)
                {
                    DisconnectNamedPipe(service_pipe);
                    break;
                }

                std::wstring command = L"\"C:\\Program Files (x86)\\Unility\\utility.exe\" " + args;

                if (!CreateProcessW(NULL, (LPWSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
                {
                    //CreateProcessW failed. You should log this!
                }
                WaitForSingleObject(pi.hProcess, INFINITE);
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);

                bool success = false;
                do
                {
                    success = WriteFile(service_pipe, L"executed", sizeof(L"executed"), &write, nullptr);
                } while (!success);
            }
            DisconnectNamedPipe(service_pipe);
            std::fill(inbox_buffer, inbox_buffer + 1024, '\0');
        }
    }
    CloseHandle(service_pipe);
}

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
    if (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) {
        Sleep(1000);
    }

    service::handle gsprint_pipe = CreateFile(TEXT("\\\\.\\pipe\\ServicePipe"),
        GENERIC_READ | GENERIC_WRITE,
        0,
        nullptr,
        OPEN_EXISTING,
        0,
        nullptr);
    bool succeess = false;
    DWORD read;
    do
    {
        succeess = WriteFile(gsprint_pipe, L"stop_signal", sizeof(L"stop_signal"), &read, nullptr);
    } while (!succeess);
    return ERROR_SUCCESS;
}

VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
    switch (CtrlCode)
    {
    case SERVICE_CONTROL_STOP:

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
            break;

        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)
        {
            //SetServiceStatus failed. You should log this!
        }
        SetEvent(g_ServiceStopEvent);

        break;

    default:
        break;
    }
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;

    g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);

    if (g_StatusHandle == NULL)
    {
        return;
    }

    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)
    {
        //SetServiceStatus failed. You should log this!
    }

    g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            //SetServiceStatus failed. You should log this!
        }
        return;
    }

    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)
    {
        //SetServiceStatus failed. You should log this!
    }

    std::thread pipe_server(pipe_server_function);

    HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);

    pipe_server.join();

    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)
    {
        //SetServiceStatus failed. You should log this!
    }

    return;
}

int _tmain(int argc, TCHAR *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        {(LPWSTR)"TestService", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
        {NULL, NULL}
    };

    if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        return GetLastError();
    }

    return 0;
}
like image 58
Peter Avatar answered Nov 15 '22 06:11

Peter