通过编译应用程序创建Windows服务

Create a windows service from a compiled app

本文关键字:Windows 服务 创建 应用程序 编译      更新时间:2023-10-16

我想用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, "%sn", 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

在@aconcagua的帮助下,我找到了一个简单的代码,可以使我的代码与Windows Service兼容。在函数ServiceWorkerThread中,您只需将循环放入时块即可。该服务可以安全停止。

#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, "%sn", 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
         */
        write_txt_file("C:\...\out.txt", "Writing...#" + to_string(i));
        //  Simulate some work by sleeping
        Sleep(3000);
        i++;
    }
    OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Exit"));
    return ERROR_SUCCESS;
}

Windows Services不仅像您写的那样简单的可执行文件,而且需要遵循特定模式如何与操作系统通信。您可以在Microsoft网站上找到所有必要的信息,此外,任何良好的搜索引擎都会揭示无数的示例(例如,此处)。

另外,您可以使用Windows Service包装器(您可以尝试一下,但是,我没有对其进行评估或搜索另一个合适的版本)。

>

旁注:为什么您的可执行执行权?好吧,因为它没有遵循上述模式,i。e。没有适当地将其状态告知OS(请参阅链接),因此再次被OS关闭(杀死),因为它认为您的"服务"无法使用...