windows服务程序的编写

本文介绍如何在Windows系统中编写、安装及管理服务程序。包括通过命令行参数实现服务安装与卸载的方法,以及使用服务控制管理器进行状态监控的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        windows服务程序可以设置为开机运行,具有特权。一个服务程序编写完成后要进行安装,然后通过本地服务的管理工具打开,当然如果设置为开机自动运行就不用手动开启了。服务程序不应该双击运行。

        windows服务程序需要进行安装,,也可以在服务程序的入口函数接收传入的参数,然后进行判断是否进行安装,判断接收的参数是什么字符串,然后再程序内进行安装,这个时候,服务还没有被注册,也没有被安装,程序只能算是一个普通应用程序。安装之后就不应该双击运行了,要通过本地服务管理工具打开或者开机启动。

#define SERVER_NAME                  "MyTestServer"



//内部变量
bool                        bDebugServer=false;
SERVICE_STATUS              ssStatus;
SERVICE_STATUS_HANDLE       hService;
DWORD                       dwErr=0;
TCHAR                       szErr[256];



int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);

    if((argc>1) && ((argv[1][0] == _T('-')) || (argv[1][0] == _T('/'))))
    {
        if(_tcsicmp(_T("install"), argv[1]+1)==0)
        {
            // 不想一个服务程序加一个安装程序,所以把安装的功能放入服务程序中,通过命令行安装程序,命令行运行该程序时,发现携带的参数,只会走到安装流程,不会触发服务
            installService();
        }
        else if(_tcsicmp(_T("remove"), argv[1]+1)==0)
        {
            removeService();
        }
        else if(_tcsicmp(_T("debug"), argv[1]+1)==0)
        {
            //bDebugServer=true;
            //debugService(argc, argv);
        }


        return 0;
    }


    SERVICE_TABLE_ENTRY st[] =
    {
        { _T(SERVER_NAME), Service_Main },
        { NULL, NULL }
    };

    StartServiceCtrlDispatcher(st);//通过指定的Service_Main 程序会跳转到Service_Main 去执行,如果失败,可能是因为程序不是用过windows服务管理器打开的。

    //安装的时候通过CreateService可以设置开机自动启动。


    CoUninitialize();


    return 0;
}

//安装服务程序

void installService()
{
    SC_HANDLE schService;
    SC_HANDLE schSCManager;
    TCHAR szPath[512];


    //得到程序磁盘文件的路径
    GetModuleFileName(NULL,szPath,512);


    AppendDbgMsg("install service \r\n");//AppendDbgMsg是我自己写的写文件操作的函数,以免在没有界面的情况下不知道具体执行情况。


    //打开服务管理数据库
    schSCManager=OpenSCManager(
                    NULL,   //本地计算机
                    NULL,   //默认的数据库
                    SC_MANAGER_ALL_ACCESS //要求所有的访问权
                    );


    if(schSCManager)
    {
         //登记服务程序
        schService=CreateService(
            schSCManager,               //服务管理数据库句柄
            TEXT(SERVER_NAME),          //服务名
            TEXT(SERVER_NAME),          //用于显示服务的标识
            SERVICE_ALL_ACCESS,         //响应所有的访问请求
            SERVICE_WIN32_OWN_PROCESS,  //服务类型
            SERVICE_AUTO_START,         //启动类型                     //设置为Auto就会开机自动启动
            SERVICE_ERROR_NORMAL,       //错误控制类型
            szPath,                     //服务程序磁盘文件的路径
            NULL,                       //服务不属于任何组
            NULL,                       //没有tag标识符
            NULL,                       //启动服务所依赖的服务或服务组,这里仅仅是一个空字符串
            NULL,                       //LocalSystem 帐号
            NULL);


        if(schService)
        {
            AppendDbgMsg("%s installed. \n", SERVER_NAME);
            CloseServiceHandle(schService);
        }
        else
        {
            AppendDbgMsg("CreateService failed - %d \n", GetLastError());
        }
        CloseServiceHandle(schSCManager);
    }
    else
    {
        AppendDbgMsg("OpenSCManager failed - %d \n", GetLastError());
    }
}

//停止和删除已安装的服务程序

void removeService()
{
    SC_HANDLE schService;
    SC_HANDLE schSCManager;


    AppendDbgMsg("remove service \r\n");


    //打开服务管理数据库
    schSCManager=OpenSCManager(
                    NULL,  //本地计算机
                    NULL,  //默认的数据库
                    SC_MANAGER_ALL_ACCESS //要求所有的访问权
                    );


    if(schSCManager)
    {
        //获取服务程序句柄
        schService=OpenService(
             schSCManager,         //服务管理数据库句柄
             TEXT(SERVER_NAME),  //服务名
             SERVICE_ALL_ACCESS     //响应所有的访问请求
             );


        if(schService)
        {
            //试图停止服务
            if(ControlService(
                schService,                 //服务程序句柄
                SERVICE_CONTROL_STOP, //停止服务请求码
                &ssStatus             //接收最后的服务状态信息
                ))
            {
                AppendDbgMsg("Stopping %s.", SERVER_NAME);
                Sleep(1000);
                //等待服务停止
                //
                while(QueryServiceStatus(schService,&ssStatus))
                {
                    if(SERVICE_STOP_PENDING==ssStatus.dwCurrentState)
                    {
                            AppendDbgMsg(".");
                            Sleep(1000);
                    }
                    else
                        break;
                }
                if(SERVICE_STOPPED==ssStatus.dwCurrentState)
                {
                    AppendDbgMsg("\n %s stopped. \n", SERVER_NAME);
                }
                else
                {
                    AppendDbgMsg("\n %s failed to stopp. \n", SERVER_NAME);
                }
            }
             //删除已安装的服务程序安装
            if(DeleteService(schService))
            {
                AppendDbgMsg("%s removed. \n", SERVER_NAME);
            }
            else
            {
                AppendDbgMsg("DeleteService failed - %d. \n", GetLastError());
            }


             CloseServiceHandle(schService);
        }
        else
        {
            AppendDbgMsg("OpenService failed - %d \n", GetLastError());
        }


        CloseServiceHandle(schSCManager);
    }
    else
    {
        AppendDbgMsg("OpenSCManager failed - %d \n", GetLastError());
    }
}



//服务的入口

void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
{
    AppendDbgMsg("in server main gonna to register server control handle \r\n");
    //注册服务控制处理函数
    hService = RegisterServiceCtrlHandler(TEXT(SERVER_NAME), Service_Ctrl);//消息会通过Service_Ctrl传入,然后再函数中处理


    //如果注册失败
    if(!hService)
    {
        AppendDbgMsg("register failed \r\n");
        goto cleanup;
        return;
    } 
    else
    {
        AppendDbgMsg("success to register \r\n");
    }


    //初始化 SERVICE_STATUS 结构中的成员
    ssStatus.dwCurrentState            = SERVICE_RUNNING;
    ssStatus.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0; 


    //更新服务状态
    if(!ReportStatusToSCMgr(
        SERVICE_RUNNING,     //服务状态,The service is starting.
        NO_ERROR,            //退出码        
        3000))               //等待时间
    {
        goto cleanup;      //更新服务状态失败则转向 cleanup
    }


    ServiceStart(dwArgc, lpszArgv);/服务启动之后执行的函数,不一定要设置为函数。可以直接写代码
    return;


cleanup:
    //把服务状态更新为 SERVICE_STOPPED,并退出。
    if(hService)
    {
        (void)ReportStatusToSCMgr(SERVICE_STOPPED, dwErr, 0);
    }
}

//消息处理函数

void WINAPI Service_Ctrl(DWORD dwCtrlCode)
{
    AppendDbgMsg("server control\r\n");
    //处理控制请求码
    switch(dwCtrlCode)
    {
    //先更新服务状态为 SERVICDE_STOP_PENDING,再停止服务。
    case SERVICE_CONTROL_STOP:
    {
        ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 500);
        AppendDbgMsg("SERVICE_CONTROL_STOP");
        //ServiceStop();     //由具体的服务程序实现
        ssStatus.dwCurrentState=SERVICE_STOPPED;
        break;
    }
    //暂停服务
    case SERVICE_CONTROL_PAUSE:
    {
        ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 500);
        AppendDbgMsg("SERVICE_CONTROL_PAUSE");
        // ServicePause();    //由具体的服务程序实现
        ssStatus.dwCurrentState=SERVICE_PAUSED;
        break;
    }
    //继续服务
    case SERVICE_CONTROL_CONTINUE:
    {
        ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 500);
        AppendDbgMsg("SERVICE_CONTROL_CONTINUE");
        // ServiceContinue(); //由具体的服务程序实现
        ssStatus.dwCurrentState=SERVICE_RUNNING;
        break;
    }
    //更新服务状态
    case SERVICE_CONTROL_INTERROGATE:
    {
        AppendDbgMsg("SERVICE_CONTROL_INTERROGATE");
        break;
    }
    //无效控制码
    default:
    {
        AppendDbgMsg("default");
        break;
    }
    }


    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}

// main中指定的启动函数

void WINAPI ServiceStart(DWORD dwArgc,LPTSTR *lpArgv)
{
    HANDLE hThread;
 
    
    //创建服务线程   服务完成的功能在这里调用
    hThread = CreateThread(NULL,
                    0,
                    Service,
                    NULL,
                    0,
                    NULL);
    if(hThread == NULL)
    {
        AppendDbgMsg("failed to create service thread \r\n");
        return;
    }


    return;
}

//服务线程函数,其实什么都没做

DWORD WINAPI Service(LPVOID lpvThread)
{
    while(1)
    {
        AppendDbgMsg("service thread \r\n");//这里可以用命名管道与普通应用程序进行通信,也可以通过读写文件进行通信,然后完成自己的任务
        Sleep(30000);
    }

    return 0;
}

//初始化SERVICE_STATUS ,为了方便设置

void InitServiceState(SERVICE_STATUS *pStatus)
{
    (*pStatus).dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    (*pStatus).dwCurrentState = SERVICE_STOPPED;
    (*pStatus).dwControlsAccepted = SERVICE_ACCEPT_STOP;
    (*pStatus).dwWin32ExitCode = 0;
    (*pStatus).dwServiceSpecificExitCode = 0;
    (*pStatus).dwCheckPoint = 0;
    (*pStatus).dwWaitHint = 0;
}



//设置状态和返回值、延时

BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
    InitServiceState(&ssStatus);


    ssStatus.dwCurrentState  = dwCurrentState;
    ssStatus.dwWin32ExitCode = dwWin32ExitCode;
    ssStatus.dwWaitHint      = dwWaitHint;


    return SetServiceStatus(hService, &ssStatus);
}

服务程序中的主函数中有一段用来识别传入参数的模块,如果参数是install就进行安装,所以最好写一个批处理,然后用管理员权限运行,就能安装了,代码如下:

set filedir=%~dp0
set file=%filedir%MyServer
start %file% -install
pause

安装完之后就可以在本地服务管理器中看到对应的名字,按win+R,输入services.msc就可以看到,任务管理器中也可以看到,但最好通过services.msc进行开启、停止。

命令行控制服务

服务MyTestServer

net命令:
启动服务MyTestServer
net start MyTestServer
关闭服务MyTestServer
net stop MyTestServer
查看所有已经启动的服务
net start
**命令行net start后面不带参数会显示所有已经启动的服务**

sc命令:
启动服务MyTestServer
sc start MyTestServer
关闭服务
sc stop MyTestServer
查看服务信息
sc qc MyTestServer
sc query MyTestServer
qc可以看到服务程序的路径
查看所有服务
sc query
查看所有驱动类型发服务
sc query type=driver
查看所有服务类型的服务
sc query type=service
sc还可以做config等更多其他的操作

wmic命令:
wmic service get name,pathname
wmic path win32_service get name,pathname

参考Sc.exe query | Microsoft Docs

因为设置的是开机启动,所以立即重启会看到调试文件中的信息(调试文件的写入函数要自己提供,可以不写)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值