c++ - 在 Windows 服务中使用命名管道时出现问题

标签 c++ windows winapi visual-c++ service

我正在为 Windows 10 创建服务。我已按照本教程进行操作。 https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus

但是当我将我的代码添加到这个示例中时,发生了一些事情,我无法从服务列表中停止服务。我只能从任务管理器中停止它。但是,如果我评论我的代码,服务会正常停止。

如有任何建议,我将不胜感激。代码如下。

#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;
}

最佳答案

肮脏的方式,但它的工作。我创建了处理与管道相关的所有工作的函数。我在新线程中运行了这个函数。当服务收到停止信号时,我向管道发送停止消息并停止循环。

#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;
}

关于c++ - 在 Windows 服务中使用命名管道时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52149153/

相关文章:

windows - 如果我使用 LoadIconWithScaleDown 实现,是否需要调用 DestroyIcon?

c++ - 返回 unique_ptr 变量返回错误

windows - 为什么 LLVM JIT 生成的入口点在 64 位 Windows 下如此冗长?

python - 使用Python拍照

javascript - 重新激活到新页面 - Windows 8 应用程序

Python 作为 Windows 看门狗

c++ - winapi - 获取和/或更改我的钩子(Hook)的顺序和优先级

c++ - 什么应该是一个随机变量总是生成 5

c# - 短按和长按处理

c++ - 如何发送 POST 请求并接收 GET 响应