Windows服务(应用与驱动)编写实例(C语言)

References:

1.Windows服务基础知识 https://blog.csdn.net/Fei_Liu/article/details/72615202
2.Windows服务编程之服务程序 https://blog.csdn.net/Fei_Liu/article/details/73395233
3.编写Windows服务程序 https://www.jianshu.com/p/c9e8ca38510d

一、服务管理器

SCM(Service Control Manager)服务管理器是 Windows 操作系统中的一个关键组件,负责管理系统服务的启动、停止和配置。
服务是一种在后台运行的应用程序,可以在系统启动时自动启动,也可以由用户或其他应用程序手动启动。

win + r = 运行下 services.msc 来启动服务控制面板

 

二、服务程序

服务程序通常编写成控制台类型的应用程序,总的来说,一个遵守服务控制管理程序接口要求的程序
包含下面三个函数:
1.服务程序主(线程)函数(main):调用系统函数 StartServiceCtrlDispatcher 连接程序主线程到服务控制管理程序。

2.服务主(入口点)函数(ServiceMain):执行服务初始化任务,RegisterServiceCtrlHandler注册"服务控制函数",并获取当前程序的状态信息句柄。

3.服务控制消息处理函数(Handler):在服务程序收到控制请求时由控制分发线程引用。(此处是Service_Ctrl)

2.1  主线程

// 1.服务程序主(线程)函数(main):调用系统函数 StartServiceCtrlDispatcher 连接程序主线程到服务控制管理程序。
int main( int argc, char **argv )
{
    FILE *log;
    int homedir_temp_counter = 0;
    int homedir_temp_counter_2 = 0;
    char *homedir_temp;
    char argv_copy[MAX_LINE];

    if( strlen( argv[0] ) >= MAX_LINE )
    {
        log = fopen( LOGFILE, "a+" );
        if( log != NULL )
        {
            fprintf( log, "Command line argument too long, quitting...\n" );
            fclose( log );
            exit( -1 );
        }
    }

    strncpy( argv_copy, argv[0], strlen(argv[0]) );
    homedir_temp = strtok( argv[0], "\\" );
    while( homedir_temp != NULL )
    {
        homedir_temp_counter++;
        homedir_temp = strtok( NULL, "\\" );
    }

    homedir_temp = strtok( argv_copy, "\\" );
    while( homedir_temp_counter_2 < (homedir_temp_counter - 1) )
    {
        strcat( homedir, homedir_temp );
        strcat( homedir, "\\" );
        homedir_temp = strtok( NULL, "\\" );
        homedir_temp_counter_2++;
    }

    SetPriorityClass( GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS );

    SetCurrentDirectory( homedir );
    log = fopen( LOGFILE, "a+" );
    if( log != NULL )
    {
        fprintf( log, "SetCurrentDirectory = %s\n", homedir );
        fclose( log );
    }
    //SERVICE_TABLE_ENTRY  "服务入口点"列表,每个入口点对应该进程中的一个"服务",每个入口点由其所对应的lpServiceName服务名称来标识
    SERVICE_TABLE_ENTRY ServiceTable[2];
    ServiceTable[0].lpServiceName = "MemoryStatus";                        // 服务名字
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;  // 服务主函数
    
    // 注意需要加上下面两行,因为StartServiceCtrlDispatcher规定最后一个元素必须为NULL
    ServiceTable[1].lpServiceName = NULL;
    ServiceTable[1].lpServiceProc = NULL;
    // Start the control dispatcher thread for our service 
    // 连接程序主线程, 到SCM服务控制服务管理器
    StartServiceCtrlDispatcher(ServiceTable);
    return 1;
}

 

2.2  主服务

// 全局变量
// 程序最近的状态信息
SERVICE_STATUS ServiceStatus;
// 当前程序的状态信息句柄
SERVICE_STATUS_HANDLE hStatus;
/* meat and potatoes */
2.服务主(入口点)函数(ServiceMain):执行服务初始化任务,RegisterServiceCtrlHandler注册"服务控制函数",并获取当前程序的状态信息句柄。
void ServiceMain(int argc, char** argv) 
{ 
    int error;
    FILE* log;
    // 下面填充当前服务的基本信息    
    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( "MemoryStatus", (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.
    // 将状态更改为running,即代表程序可以接收SCM发送的控制信息
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    // 反馈状态信息给SCM
    SetServiceStatus (hStatus, &ServiceStatus);

    // The worker loop of a service
    while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        status = -1;
        log = fopen( LOGFILE, "a+" );
        if( log != NULL )
        {
            fprintf( log, "Status = %i\n", status );
            fclose( log );
        }

        // status is -1 at start, so every time this runs, force it to read config and status file
        if( status < 0 || status > 3 )
        {
            init_scan();
        }

        // perform recursive directory listing
        if( status == 0 )
        {
            recurse_all_dirs();
        }

        // whitelist files based on policy, sum file sizes and number of files
        if( status == 1 )
        {
            whitelist();
        }

        // grep for stuff
        if( status == 2 )
        {
            search();
        }

        // we are all done
        if( status == 3 )
        {
            int upload_attempt = 1;
            WriteToLog( "Done with everything. Sending request to web server to uninstall us.\n" );

            while( upload_stuff( status ) != 0 )
            {
                upload_attempt++;
                Sleep( time_wait * 1000 );
                log = fopen( LOGFILE, "a+" );
                if( log != NULL )
                {
                    fprintf( log, "Done with everything. Sending request #%i to web server to uninstall us.\n", upload_attempt );
                    fclose( log );
                }
            }
        }
    }
    return;
}

 

2.3  主监控

3.服务监控(消息处理)函数(Handler):在服务程序收到控制请求时由控制分发线程引用。(此处是Service_Ctrl)
void ControlHandler(DWORD request)
{
    switch(request)
    {
        case SERVICE_CONTROL_STOP:     // 服务停止
            WriteToLog("OpenDLP service stopped.\n");

            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            SetServiceStatus (hStatus, &ServiceStatus);
            return;
 
        case SERVICE_CONTROL_SHUTDOWN:  // 系统关机
            WriteToLog("Monitoring service stopped.\n");

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

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

    return;
}

 

三、 安装/卸载服务:
Refer:sc.exe create | Microsoft Learn

C:\Users\Administrator> sc create "DLP Agent" binpath=E:\workForClion\DLP_Agent\cmake-build-debug\DLP_Agent.exe
C:\Users\Administrator> sc delete "DLP Agent"
 

 

posted @ 2024-08-19 20:15  suntroop  阅读(24)  评论(0)    收藏  举报