5

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;
}
Peter
  • 435
  • 1
  • 3
  • 11
  • Did you try stepping through your code with a debugger? – Algirdas Preidžius Sep 03 '18 at 11:48
  • Not sure that this is the issue, but you create the event AFTER you already registered the `ServiceCtrlHandler` If it tries to set the event before the CreateEvent, you may have a problem – Shloim Sep 03 '18 at 11:48
  • @AlgirdasPreidžius I've tried. It doesn't help. When execution reaches `SetEvent(g_ServiceStopEvent)` in `ServiceCtrlHandler` nothing happens. But in fact stop signal should be sent to `ServiceWorkerThread`. – Peter Sep 03 '18 at 12:15
  • @Shloim I don't think this is the problem. Because this code works just fine without any additions. – Peter Sep 03 '18 at 12:17
  • @Peter 1) "_I've tried. It doesn't help._ It shouldn't help you automatically. You step through your program, step-by-step, while investigating every aspect of your code's execution, to find where it's doing something you don't expect. "2) By "Nothing happens", you mean, what, exactly? The debugger, will not, automatically jump to wherever you were waiting on object, you `SetEvent` on. Use breakpoints for that. 3) If you suspect that the problem is in your `ServiceWorkerThread`, why aren't you debugging it? – Algirdas Preidžius Sep 03 '18 at 12:25
  • 3
    The `ConnectNamedPipe()` and `ReadFile()` are blocking calls. So if nothing is written to pipe, your thread is stuck in these calls and won't receive stop event. Either send a "termination" message to the thread through the pipe or use asynchronous IO which can be cancelled. – zett42 Sep 03 '18 at 12:35
  • 1
    error that you blocked inside `ConnectNamedPipe` or `ReadFile` and not wait on `g_ServiceStopEvent`. also of course how named pipes work - does not depend from service this called or usual user process – RbMm Sep 03 '18 at 14:35
  • 1
    also create thread and than just wait for thread exit is senseless - you can direct call thread with the same effect - `HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL); WaitForSingleObject(hThread, INFINITE);` will be have the same effect as `ServiceWorkerThread(0);` – RbMm Sep 04 '18 at 00:31

1 Answers1

1

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;
}
Peter
  • 435
  • 1
  • 3
  • 11