65.9K
CodeProject 正在变化。 阅读更多。
Home

将可执行文件作为 Windows 服务运行

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.65/5 (5投票s)

2008年7月9日

CPOL
viewsIcon

21296

将可执行文件作为 Windows 服务运行的快速方法。

引言

这是一篇关于将可执行文件作为 Windows 服务运行的参考资料。 代码中提供了以下思路,以及关于从服务中运行 exe 的参考。

  1. 编写 Windows 服务
  2. 安装 Windows 服务
  3. 卸载 Windows 服务
  4. 调试 Windows 服务

使用代码

服务代码中使用的 API 调用在 MSDN 中有很好的文档记录。 我们需要使用以下代码创建一个项目来开始使用它。

#include "windows.h"
#include "tchar.h"

#define _MAX_PATH   260

TCHAR* serviceName = ""; 
TCHAR* exePath = "";
STARTUPINFO si;
PROCESS_INFORMATION pi;
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;
DWORD exitCode = -1;

void StartExe()
{
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    // Start the child process. 
    CreateProcess( NULL, // No module name (use command line). 
        exePath, // Command line. 
        NULL,             // Process handle not inheritable. 
        NULL,             // Thread handle not inheritable. 
        FALSE,            // Set handle inheritance to FALSE. 
        0,                // No creation flags. 
        NULL,             // Use parent's environment block. 
        NULL,             // Use parent's starting directory. 
        &si,              // Pointer to STARTUPINFO structure.
        &pi );            // Pointer to PROCESS_INFORMATION structure.
}

void WINAPI ServiceControlHandler( DWORD controlCode )
{
    switch ( controlCode )
    {
        case SERVICE_CONTROL_INTERROGATE:
            break;

        case SERVICE_CONTROL_SHUTDOWN:
        case SERVICE_CONTROL_STOP:

            TerminateProcess(pi.hProcess, 
               GetExitCodeProcess(pi.hProcess,&exitCode));

            serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
            SetServiceStatus( serviceStatusHandle, &serviceStatus );

            SetEvent( stopServiceEvent );
            return;

        case SERVICE_CONTROL_PAUSE:
            break;

        case SERVICE_CONTROL_CONTINUE:
            break;

        default:
            if ( controlCode >= 128 && controlCode <= 255 )
                // user defined control code
                break;
            else
                // unrecognised control code
                break;
    }

    SetServiceStatus( serviceStatusHandle, &serviceStatus );
}

void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
    // initialise service status
    serviceStatus.dwServiceType = SERVICE_WIN32;
    serviceStatus.dwCurrentState = SERVICE_STOPPED;
    serviceStatus.dwControlsAccepted = 0;
    serviceStatus.dwWin32ExitCode = NO_ERROR;
    serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;

    serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, 
                                          ServiceControlHandler );

    if ( serviceStatusHandle )
    {
        // service is starting
        serviceStatus.dwCurrentState = SERVICE_START_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        // do initialisation here
        stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );

        // running
        serviceStatus.dwControlsAccepted |= 
               (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
        serviceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        StartExe();
        WaitForSingleObject( stopServiceEvent, INFINITE ); 

        // service was stopped
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        // do cleanup here
        CloseHandle( stopServiceEvent );
        stopServiceEvent = 0;

        // service is now stopped
        serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );
    }
}

void RunService()
{
    SERVICE_TABLE_ENTRY serviceTable[] =
    {
        { serviceName, ServiceMain },
        { 0, 0 }
    };

    StartServiceCtrlDispatcher( serviceTable );
}

void InstallService()
{
    SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );

    if ( serviceControlManager )
    {
        TCHAR path[ _MAX_PATH + 1 ];
        if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 )
        {
            SC_HANDLE service = CreateService( serviceControlManager,
                            serviceName, serviceName,
                            SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                            SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
                            0, 0, 0, 0, 0 );
            if ( service )
                CloseServiceHandle( service );
        }

        CloseServiceHandle( serviceControlManager );
    }
}

void UninstallService()
{
    SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );

    if ( serviceControlManager )
    {
        SC_HANDLE service = OpenService( serviceControlManager,
            serviceName, SERVICE_QUERY_STATUS | DELETE );
        if ( service )
        {
            SERVICE_STATUS serviceStatus;
            if ( QueryServiceStatus( service, &serviceStatus ) )
            {
                if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
                    DeleteService( service );
            }

            CloseServiceHandle( service );
        }

        CloseServiceHandle( serviceControlManager );
    }
}

int _tmain( int argc, TCHAR* argv[] )
{

    if ( argc > 2 && lstrcmpi( argv[1], TEXT("install") ) == 0 )
    {
        serviceName = argv[2];
        InstallService();
    }
    else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 )
    {
        UninstallService();
    }
    else if( argc > 2 && lstrcmpi( argv[1], TEXT("start") ) == 0 )
    {
        exePath = argv[2];
        RunService();
    }
    else
    {
        //show error message
    }

    return 0;
}

为了调试代码,在 _tmain 函数的开头编写以下代码

__asm
{
  int 3
}

这将会在服务运行时创建一个断点。

历史

如有任何更新或疑问,请通过 somenshaw at gmail.com 与我联系。

© . All rights reserved.