使用场景:
调试输出:OutPutDebugString
步骤:
服务有scm(系统的一个组件),用来管理所有的服务:比如通知服务启动,暂停,停止,继续;
每当scm要管理服务的时候,就调他一个回调函数;
服务主要体现在回调函数,想干什么,在回调函数中写自己的逻辑即可;
创建一个简单的Windows服务需要涉及以下API:
OpenSCManager() 打开SCM:服务控制管理器
LPCSTR lpMachineName, 计算机名,本地计算机给NULL
LPCSTR lpDatabaseName, 服务的数据库,一般给NULL
DWORD dwDesiredAccess,SCM的具体访问权限,一般给 SC_MANAGER_ALL_ACCESS
CreateService(); 安装/创建服务
SC_HANDLE hSCManager:SCM句柄,由系统函数OpenSCManager 返回
LPCTSTR lpServiceName:以NULL 结尾的服务名,用于创建登记数据库中的关键字
LPCTSTR lpDisplayName:以NULL 结尾的服务名,用于用户界面标识服务
DWORD dwDesiredAccess:指定服务返回类型
DWORD dwServiceType:指定服务类型,三环程序需要使用给定类型SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS
DWORD dwStartType:指定何时启动服务
DWORD dwErrorControl:指定服务启动失败的严重程度
LPCTSTR lpBinaryPathName:指定服务程序二进制文件的全路径
LPCTSTR lpLoadOrderGroup:指定顺序装入的服务组名,给NULL或0
LPDWORD lpdwTagId:忽略,给NULL或0
LPCTSTR lpDependencies:指定启动该服务前必须先启动的服务或服务组,给NULL或0
LPCTSTR lpServiceStartName:以NULL 结尾的字符串,指定服务帐号。如是NULL,则表示使用LocalSystem帐号
LPCTSTR lpPassword:以NULL结尾的字符串,指定对应的口令。为NULL表示无口令。但使用LocalSystem时填NULL
服务程序实现CODE:需设为Release版本,使用OutputDebugString
服务程序里的main()函数的唯一作用:使用StartServiceCtrlDispatcher()r告诉SCM,ServiceMain()的位置
代码测试
#include
#include
#define SERVICE_NAME (LPSTR)"MyService"
template<typename T>
void Output(T szFmt) {}
template<typename T,typename... ARGS>
void Output(T szFmt,ARGS... args)
{
char szBuff[MAXBYTE] = {};
wsprintf(szBuff,szFmt,args...);
OutputDebugString(szBuff);
}
VOID WINAPI Handler(DWORD fdwControl)
{
switch (fdwControl)
{
case SERVICE_CONTROL_CONTINUE:
Output("[Service]:服务开始运行");
break;
case SERVICE_CONTROL_PAUSE:
Output("[Service]:服务暂停");
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
Output("[Service]:服务停止");
break;
default:
break;
}
}
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
//注册控制状态回调函数
SERVICE_STATUS_HANDLE hservice= RegisterServiceCtrlHandler(SERVICE_NAME, Handler);
if (hservice == NULL)
{
//输出错误信息
Output("[Service]:RegisterServiceCtrlHandler failed %d",GetLastError());
return;
}
}
// servicemain 数组,以全0为结尾
SERVICE_TABLE_ENTRY g_DisPatcher[] =
{
{SERVICE_NAME,ServiceMain},
{NULL,NULL}
};
int main()
{
//注册ServiceMain
BOOL bRet = StartServiceCtrlDispatcher(g_DisPatcher);
if (!bRet)
{
Output("[Service]:StartServiceCtrlDispatcher failed %d",GetLastError());
}
}
在win7环境中使用sc 去启动该服务
相关命令
sc create 服务名 binPath= 程序路径
sc create MyService binPath= C:\Users\Onlyxiu\Desktop\41期\第二阶段\14-服务\ServicesTest.exe DisplayName= "服务测试" start= demand
删除服务
sc delete Myservice
使用services.msc
命令打开服务管理 工具
运行会出现该错误
使用View Dependencies工具查看,你会发现该程序加载如下DLL,若你没有配好相关的环境,就会报错。
解决方法:
打开项目的属性 C/C++ ,选择代码生成,将运行库改为多线程(MT)
再次使用View Dependencies工具查看
再次运行查看调试输出的信息,显示报错
// ServicesTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
#define SERVICE_NAME (LPSTR)"MyService" // 定义服务名称为"MyService"
SERVICE_STATUS_HANDLE g_hservice = NULL; // 服务状态句柄
template<typename T>
void Output(T szFmt) { OutputDebugString(szFmt); } // 定义输出调试信息的函数
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv); // 定义服务主函数
template<typename T, typename... ARGS>
void Output(T szFmt, ARGS... args)
{
char szBuff[MAXBYTE] = {};
wsprintf(szBuff, szFmt, args...); // 格式化输出调试信息
OutputDebugString(szBuff);
}
// servicemain 数组,以全0为结尾
SERVICE_TABLE_ENTRY g_DisPatcher[] =
{
{SERVICE_NAME,ServiceMain}, // 服务调度表中第一项为服务名称,第二项为服务主函数
{NULL,NULL} // 服务调度表以全零结尾
};
VOID WINAPI Handler(DWORD fdwControl) // 定义处理控制状态的函数
{
DWORD dwStatus = SERVICE_RUNNING;
switch (fdwControl)
{
case SERVICE_CONTROL_CONTINUE:
Output("[Service]:服务开始运行"); // 若控制状态为SERVICE_CONTROL_CONTINUE,输出服务开始运行的调试信息
break;
case SERVICE_CONTROL_PAUSE:
Output("[Service]:服务暂停"); // 若控制状态为SERVICE_CONTROL_PAUSE,输出服务暂停的调试信息
dwStatus = SERVICE_PAUSED; // 设置服务状态为暂停
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
Output("[Service]:服务停止"); // 若控制状态为SERVICE_CONTROL_STOP或SERVICE_CONTROL_SHUTDOWN,输出服务停止的调试信息
dwStatus = SERVICE_STOPPED; // 设置服务状态为停止
break;
default:
break;
}
//通知 SCM ,初始化成功
SERVICE_STATUS ss = {
SERVICE_WIN32_OWN_PROCESS, // 3环的exe程序
dwStatus,//服务状态
SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE,//接受的控制
NO_ERROR,//没有发生错误
};
if (!SetServiceStatus(g_hservice, &ss)) // 通过服务状态句柄通知SCM服务状态已更改
{
Output("[Service]:SetServiceStatus failed %d", GetLastError()); // 若通知失败,输出错误信息
}
}
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) // 定义服务主函数
{
OutputDebugString("[Service]: ServiceMain Begin"); // 输出服务开始的调试信息
//注册控制状态回调函数
g_hservice = RegisterServiceCtrlHandler(SERVICE_NAME, Handler); // 注册处理控制状态的函数
if (g_hservice == NULL)
{
//输出错误信息
Output("[Service]:RegisterServiceCtrlHandler failed %d", GetLastError());
return;
}
//初始化...
//通知 SCM ,初始化成功
SERVICE_STATUS ss = {
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS, // 3环的exe程序
ss.dwCurrentState = SERVICE_RUNNING,//服务开始运行
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE,//接受的控制
ss.dwWin32ExitCode = NO_ERROR,//没有发生错误
};
if (!SetServiceStatus(g_hservice, &ss))
{
Output("[Service]:SetServiceStatus failed %d", GetLastError());
}
}
int main()
{
OutputDebugString("[Service]: Hello World!");
//注册ServiceMain
BOOL bRet = StartServiceCtrlDispatcher(g_DisPatcher);
if (!bRet)
{
Output("[Service]:StartServiceCtrlDispatcher failed %d", GetLastError());
}
}
安装服务的时候可以写注册表
创建服务对象并将其添加到指定的服务控制管理器数据库中。
#include
#define SERVICE_NAME (LPSTR)"MyService" // 定义服务名为"MyService"
void CHookTestDlg::OnBnClickedInstall()
{
//获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS); // 获取SCM管理器的句柄
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 打开SCM管理器失败
return;
}
//安装
SC_HANDLE hService = CreateService(
hSCM,
SERVICE_NAME, // 指定服务名
"服务测试", // 指定服务描述
SERVICE_ALL_ACCESS, // 指定访问权限
SERVICE_WIN32_OWN_PROCESS, // 指定服务类型
SERVICE_DEMAND_START, // 指定启动类型
SERVICE_ERROR_NORMAL,
"C:\\Users\\Onlyxiu\\Desktop\\41期\\第二阶段\\14-服务\\ServicesTest.exe", // 指定服务路径
NULL,
NULL,
NULL,
NULL,
NULL
);
if (hService == NULL)
{
AfxMessageBox("CreateService failed"); // 创建服务失败
}
//关闭句柄
CloseServiceHandle(hService); // 关闭服务句柄
CloseServiceHandle(hSCM); // 关闭SCM管理器句柄
}
void CHookTestDlg::OnBnClickedUninstall()
{
//获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); // 获取SCM管理器的句柄
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 打开SCM管理器失败
return;
}
//获取服务句柄
SC_HANDLE hService = OpenService(
hSCM,
SERVICE_NAME, // 指定服务名
SERVICE_ALL_ACCESS // 指定访问权限
);
if (hService == NULL)
{
AfxMessageBox("OpenService failed"); // 打开服务失败
return;
}
//卸载服务
if (!DeleteService(hService)) // 删除服务
{
AfxMessageBox("卸载服务失败"); // 卸载服务失败
}
//关闭句柄
CloseServiceHandle(hService); // 关闭服务句柄
CloseServiceHandle(hSCM); // 关闭SCM管理器句柄
}
void CHookTestDlg::OnBnClickedStart()
{
// 获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 打开SCM失败
return;
}
// 获取服务句柄
SC_HANDLE hService = OpenService(
hSCM,
SERVICE_NAME, // 定义服务名的宏
SERVICE_ALL_ACCESS
);
if (hService == NULL)
{
AfxMessageBox("OpenService failed"); // 打开服务失败
return;
}
// 启动服务
if (!StartService(hService, NULL, NULL))
{
AfxMessageBox("启动服务失败"); // 启动服务失败
}
// 关闭句柄
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
}
void CHookTestDlg::OnBnClickedPause()
{
// 获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 打开SCM失败
return;
}
// 获取服务句柄
SC_HANDLE hService = OpenService(
hSCM,
SERVICE_NAME, // 定义服务名的宏
SERVICE_ALL_ACCESS
);
if (hService == NULL)
{
AfxMessageBox("OpenService failed"); // 打开服务失败
return;
}
SERVICE_STATUS ss = {};
// 暂停服务
if (!ControlService(hService, SERVICE_CONTROL_PAUSE, &ss))
{
AfxMessageBox("暂停服务失败"); // 暂停服务失败
}
// 关闭句柄
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
}
void CHookTestDlg::OnBnClickedResume()
{
// 获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 如果无法打开SCM,则显示错误消息
return;
}
// 获取服务句柄
SC_HANDLE hService = OpenService(
hSCM,
SERVICE_NAME,
SERVICE_ALL_ACCESS
);
if (hService == NULL)
{
AfxMessageBox("OpenService failed"); // 如果无法打开服务,则显示错误消息
return;
}
SERVICE_STATUS ss = {};
// 恢复服务
if (!ControlService(hService, SERVICE_CONTROL_CONTINUE, &ss))
{
AfxMessageBox("恢复服务失败"); // 如果无法恢复服务,则显示错误消息
}
// 关闭句柄
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
}
void CHookTestDlg::OnBnClickedStop()
{
// 获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
AfxMessageBox("OpenSCManager failed"); // 如果无法打开SCM,则显示错误消息
return;
}
// 获取服务句柄
SC_HANDLE hService = OpenService(
hSCM,
SERVICE_NAME,
SERVICE_ALL_ACCESS
);
if (hService == NULL)
{
AfxMessageBox("OpenService failed"); // 如果无法打开服务,则显示错误消息
return;
}
SERVICE_STATUS ss = {};
// 停止服务
if (!ControlService(hService, SERVICE_CONTROL_STOP, &ss))
{
AfxMessageBox("停止服务失败"); // 如果无法停止服务,则显示错误消息
}
// 关闭句柄
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
}
#include
#include
using namespace std;
int main()
{
//获取SCM
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); // 获取服务控制管理器句柄,用于之后操作系统中的服务
if (hSCM == NULL) // 判断句柄获取是否成功
{
cout << "OpenSCManager failed" << endl; // 若失败则输出错误信息
return 0;
}
// 枚举系统中的服务信息
ENUM_SERVICE_STATUS ess[0x500] = {}; // 定义一个ENUM_SERVICE_STATUS类型的数组来存储服务信息
DWORD dwNeed = 0;
DWORD dwServicesReturned = 0;
DWORD dwHandleNext = 0;
if(!EnumServicesStatus(
hSCM, // 服务控制管理器句柄
SERVICE_WIN32, // 要枚举的服务类型(这里选择了SERVICE_WIN32)
SERVICE_STATE_ALL, // 要获取的服务状态(这里选择了SERVICE_STATE_ALL,表示获取所有服务状态)
ess, // 存放服务信息的缓冲区
sizeof(ess), // 缓冲区大小
&dwNeed, // 需要的缓冲区大小
&dwServicesReturned, // 返回的服务个数
&dwHandleNext // 用于遍历服务的句柄
))
{
cout << "EnumServicesStatus failed" << endl; // 若获取服务信息失败,则输出错误信息
}
// 输出服务信息
for (size_t i = 0; i < dwServicesReturned; i++) // 遍历获取到的服务信息
{
cout.setf(std::ios::left); // 设置输出方式为左对齐
cout.width(43); // 设置输出宽度为43个字符
cout << ess[i].lpServiceName << " : "; // 输出服务名称
cout.width(20); // 设置输出宽度为20个字符
cout << ess[i].lpDisplayName << endl; // 输出服务显示名称
}
CloseServiceHandle(hSCM); // 关闭服务控制管理器句柄
system("pause"); // 暂停程序的执行,以便用户查看输出结果
return 0;
}