【问题标题】:Create a windows service from a compiled app从已编译的应用程序创建 Windows 服务
【发布时间】:2017-12-01 11:56:03
【问题描述】:

我想使用 Microsoft Visual Studio 2015 从我编译的 C/C++ 代码创建一个服务。我编译了我的代码并使用来自应用程序可执行文件的sc.exe 创建了一个服务。我的代码包含一个无限循环,它在时间间隔内将一个简单的字符串写入静态文件。创建服务并重新启动后,服务似乎自动停止。可执行文件直接运行时工作正常。

代码如下:

#include "stdafx.h"
#include <string>
#include <cstdio>
#include <windows.h>


using namespace std;

void write_txt_file(string file_name, string input) {
    /*
    write a string to a specific txt file
    */
    FILE *f = fopen(file_name.c_str(), "a+");
    fprintf(f, "%s\n", input.c_str());
    fclose(f);
}

int main(int argc, char** argv)
{
    int i = 0;
    while (true) {
        write_txt_file("C:\\...\\Desktop\\out.txt", "Writing...#" + to_string(i));
        Sleep(5000);
        i++;
    }
}

这是我用来创建服务的命令:

sc.exe create My_service binPath= "<path to executable>" start= auto

out.txt 文件的输出为:

Writing...#0
Writing...#1
Writing...#2
Writing...#3
Writing...#4
Writing...#5
Writing...#6
Writing...#7
Writing...#8

【问题讨论】:

    标签: c++ c windows service


    【解决方案1】:

    在@Aconcagua 和this reference 的帮助下,我找到了一个简单的代码来使我的代码与Windows 服务兼容。在函数ServiceWorkerThread 中,您只需将循环放在while 块中。服务可以安全停止。

    #include <Windows.h>
    #include <tchar.h>
    #include <string>
    #include <cstdio>
    
    
    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")
    
    
    using namespace std;
    
    void write_txt_file(string file_name, string input) {
        /*
        write a string to a specific txt file
        */
        FILE *f = fopen(file_name.c_str(), "a+");
        fprintf(f, "%s\n", input.c_str());
        fclose(f);
    }
    
    
    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"));
        int i = 0;
        //  Periodically check if the service has been requested to stop
        while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
        {        
            /* 
             * Perform main service function here
             */
    
            //while (true) {
                write_txt_file("C:\\...\\out.txt", "Writing...#" + to_string(i));
                Sleep(5000);
                i++;
        //  }
            //  Simulate some work by sleeping
            Sleep(3000);
        }
    
        OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Exit"));
    
        return ERROR_SUCCESS;
    }
    

    【讨论】:

      【解决方案2】:

      Windows 服务不仅仅是像您编写的那样简单的可执行文件,还需要遵循特定的模式与操作系统进行通信。您可以在microsoft 网站上找到所有必要的信息,此外,任何好的搜索引擎都会提供无数的例子来说明如何编写一个(例如here)。

      或者,您可以使用 Windows 服务包装器(您可以尝试 this one - 不过需要说明的是,我没有评估它 - 或搜索另一个合适的包装器)。

      旁注:为什么您的可执行文件停止执行?好吧,因为它没有遵循上述模式,我。 e.没有适当地通知操作系统它的状态(见链接),因此被操作系统再次关闭(杀死),因为它认为你的“服务”无法运行......

      【讨论】:

      • 谢谢。我找不到任何简单的示例来使代码服务兼容。这些示例尝试从代码本身创建服务,但我不想在运行我的可执行文件时提供服务,我只想让它与 Windows 服务兼容。
      • @MasoudR。很抱歉必须告诉您:如果不将其实现为 Windows 服务,您将无法获得“与 Windows 服务兼容”的可执行文件...如果您不想这样做,请尝试使用服务包装器替代方案。包装器就像 Windows 所要求的那样充当服务,并会在时间到来时启动您的可执行文件。当服务停止时,它会杀死你的应用程序并为你做正常的服务关闭。会怎样杀人?不同的选项,因此请查看您选择的包装器的文档...
      • Here 的另一个服务包装器。我觉得这个名字很熟悉,可能很久很久以前用过一次...
      • 谢谢,我正在使用nssm,它也是一个包装器。我希望有人在我的代码上放一个简单的例子,或者我自己把它:D.
      • @MasoudR。看起来也不错...根据 nssm 的 documentation,您现在需要做的就是从命令行运行 nssm install &lt;name for your service&gt; &lt;your exe&gt; &lt;optionally: some start parameters&gt;。如果您想查看服务是如何实际启动的,请查看 Windows 注册表 (regedit) 并查找 HOT_KEY_LOCAL_MACHINE/System/CurrentControlSet/services/&lt;name you gave before&gt;/ImagePath...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-17
      • 1970-01-01
      • 2018-05-29
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      相关资源
      最近更新 更多