窗口服务

时间:2018-04-26 12:26:56

标签: c++ winapi service

我写了一个简单的Windows服务,它将内存信息写入文本文件:

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

using namespace std;

#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void  ServiceMain(int argc, char** argv);
void  ControlHandler(DWORD request);
int InitService();

int WriteToLog(string file_name, string input)
{
    FILE *f;
    fopen_s(&f, file_name.c_str(), "a+");
    fprintf(f, "%s\n", input.c_str());
    fclose(f);
    return 0;
}

string N = "MemoryStatus";
LPWSTR Name = new wchar_t(N.size() + 1);

int main()
{
    SERVICE_TABLE_ENTRY ServiceTable[2];
    ServiceTable[0].lpServiceName = Name;
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;
    // Start the control dispatcher thread for our service
    StartServiceCtrlDispatcher(ServiceTable);
    return 0;
}


void ServiceMain(int argc, char** argv)
{
    int error;

    ServiceStatus.dwServiceType = SERVICE_WIN32;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;

    hStatus = RegisterServiceCtrlHandler(
        Name,
        (LPHANDLER_FUNCTION)ControlHandler);
    if (hStatus == (SERVICE_STATUS_HANDLE)0)
    {
        // Registering Control Handler failed
        return;
    }
    // Initialize Service 
    error = InitService();
    if (error)
    {
        // Initialization failed
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwWin32ExitCode = -1;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;
    }
    // We report the running status to SCM. 
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hStatus, &ServiceStatus);

    MEMORYSTATUS memory;
    // The worker loop of a service
    while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        char buffer[16];
        GlobalMemoryStatus(&memory);
        sprintf_s(buffer, "%d", memory.dwAvailPhys);
        int result = WriteToLog("TestFile.txt", buffer);
        if (result)
        {
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwWin32ExitCode = -1;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        }

        Sleep(SLEEP_TIME);
    }
    return;
}

// Service initialization
int InitService()
{
    int result;
    result = WriteToLog("TestFile.txt", "Monitoring started.");
    return(result);
}

// Control handler function
void ControlHandler(DWORD request)
{
    switch (request)
    {
    case SERVICE_CONTROL_STOP:
        WriteToLog("TestFile.txt", "Monitoring stopped.");

        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;

    case SERVICE_CONTROL_SHUTDOWN:
        WriteToLog("TestFile.txt", "Monitoring stopped.");

        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;

    default:
        break;
    }

    // Report current status
    SetServiceStatus(hStatus, &ServiceStatus);

    return;
}

我已成功安装,但任务管理器将其状态显示为已停止。当我右键单击并按下“开始”按钮时,出现系统无法找到指定文件的错误。

我从StackOverflow或其他任何地方跟随的几乎所有示例都遇到了同样的错误。

2 个答案:

答案 0 :(得分:0)

当一个函数没有指定一个调用约定(和你的一样)时,大多数C ++编译器默认使用__cdecl调用约定,除非配置了不同的默认值。但是,Win32 API使用__stdcall调用约定。您需要将该调用约定添加到您为服务API提供的函数声明中。您还需要确保这些相同的函数也使用正确的参数类型(您的不是)。

摆脱隐藏编译器错误的类型转换。编译器抱怨有一个原因,不要忽视它。

试试这个:

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string>
#include <fstream>

using namespace std;

#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"

SERVICE_STATUS ServiceStatus = {};
SERVICE_STATUS_HANDLE hStatus = NULL;

void WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
void WINAPI ControlHandler(DWORD request);
bool InitService();    

bool WriteToLog(const string &file_name, const string &input)
{
    ofstream f(file_name.c_str(), ios_base::app);
    if (f.is_open())
        f << input << "\n";
    return !f.fail();
}

LPCTSTR Name = TEXT("MemoryStatus");

int main()
{
    SERVICE_TABLE_ENTRY ServiceTable[2];
    ServiceTable[0].lpServiceName = Name;
    ServiceTable[0].lpServiceProc = &ServiceMain;

    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;

    // Start the control dispatcher thread for our service
    StartServiceCtrlDispatcher(ServiceTable);

    return 0;
}

void WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
    ServiceStatus.dwServiceType = SERVICE_WIN32;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;

    hStatus = RegisterServiceCtrlHandler(Name, &ControlHandler);
    if (!hStatus)
    {
        // Registering Control Handler failed
        return;
    }

    // Initialize Service 
    if (!InitService())
    {
        // Initialization failed
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwWin32ExitCode = -1;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;
    }

    // We report the running status to SCM. 
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hStatus, &ServiceStatus);

    MEMORYSTATUS memory;
    // The worker loop of a service
    while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        char buffer[16];
        GlobalMemoryStatus(&memory);
        sprintf_s(buffer, "%d", memory.dwAvailPhys);
        if (!WriteToLog("TestFile.txt", buffer))
        {
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwWin32ExitCode = -1;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        }

        Sleep(SLEEP_TIME);
    }
}

// Service initialization
bool InitService()
{
    return WriteToLog("TestFile.txt", "Monitoring started.");
}

// Control handler function
void WINAPI ControlHandler(DWORD request)
{
    switch (request)
    {
        case SERVICE_CONTROL_STOP:
            WriteToLog("TestFile.txt", "Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            break;

        case SERVICE_CONTROL_SHUTDOWN:
            WriteToLog("TestFile.txt", "Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            break;
        }

        // Report current status
        SetServiceStatus(hStatus, &ServiceStatus);    
    }
}

我建议您阅读Microsoft有关如何正确编写服务的文档:

Service Programs

Service Program Tasks

The Complete Service Example

话虽如此,您说您的服务无法启动,因为Windows声称它无法找到文件。这意味着您可能无法正确地将服务安装到SCM中。但是您没有显示传递给sc.exe的实际命令行。确保您传递的binPath值是服务EXE的正确路径。

如果你确定它是,那么另一种可能性是你的EXE依赖于不在Windows上的外部DLL&#39;搜索路径。这也可能导致您的EXE无法运行未找到&#39;文件&#34;错误。

我建议您在尝试启动服务时运行SysInternals Process Monitor。这将显示错误发生时Windows正在查找的文件。这将为您提供更好的线索,了解您需要对安装进行故障排除的位置。

另外,Windows内置了对日志记录的支持。您不需要手动将日志消息写入文本文件。考虑使用ReportEvent()EventWrite()等功能将消息记录到标准Windows事件查看器。

答案 1 :(得分:-2)

您可以尝试一些方法进行故障排除 -

1.以下注册表位置中的服务映像路径是什么 - HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ YourServiceName \ ImagePath 2.通过提供服务可执行文件的完全限定路径进行检查。