Windows服务调用ControlService返回1061错误,无法接受停止控制
Windows服务停止时返回ERROR_SERVICE_CANNOT_ACCEPT_CTRL(1061)的解决方案
核心问题分析
你的服务出现1061错误且控制处理程序未被调用,主要原因是服务控制处理函数的签名错误,同时存在服务状态管理和线程逻辑的不合理之处:
- 控制处理函数签名不匹配,导致系统无法正确触发回调
- 服务状态字段维护不规范,系统无法识别服务可接受的控制指令
- 主线程轮询逻辑占用资源,影响控制消息分发
具体修复步骤
1. 修正服务控制处理函数的签名
Windows服务要求控制处理函数必须遵循DWORD WINAPI(DWORD)的签名,替换你当前错误的定义:
HANDLE gServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // 全局或通过上下文传递服务状态变量 SERVICE_STATUS g_ServiceStatus = {0}; SERVICE_STATUS_HANDLE g_StatusHandle = NULL; DWORD WINAPI ServiceHandler(DWORD dwCtrl) { switch (dwCtrl) { case SERVICE_CONTROL_STOP: // 先报告停止待处理状态,告知系统服务正在响应停止请求 ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000, g_ServiceStatus, g_StatusHandle); SetEvent(gServiceStopEvent); break; case SERVICE_CONTROL_INTERROGATE: // 响应系统询问,重新报告当前状态 ReportSvcStatus(g_ServiceStatus.dwCurrentState, NO_ERROR, 0, g_ServiceStatus, g_StatusHandle); break; default: return ERROR_CALL_NOT_IMPLEMENTED; } return NO_ERROR; }
2. 规范服务初始化与状态注册流程
在ServiceMain中,确保注册控制处理程序后正确初始化并报告服务状态:
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv) { // 初始化服务状态结构 g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_ServiceStatus.dwWin32ExitCode = NO_ERROR; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 0; // 注册控制处理程序 g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_INTERNAL_NAME, ServiceHandler); if (g_StatusHandle == NULL) { // 错误处理逻辑 return; } // 报告启动待处理状态 ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 1000, g_ServiceStatus, g_StatusHandle); // 初始化停止事件 if (gServiceStopEvent == NULL) { ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0, g_ServiceStatus, g_StatusHandle); return; } // 设置服务可接受STOP控制,并报告运行状态 g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0, g_ServiceStatus, g_StatusHandle); // 启动服务工作逻辑 ServiceWork(g_ServiceStatus, g_StatusHandle); }
3. 优化服务工作线程的等待逻辑
将轮询改为阻塞等待,避免占用主线程资源,确保系统能正常分发控制消息:
void ServiceWork(SERVICE_STATUS &ServiceStatus, SERVICE_STATUS_HANDLE &StatusHandle) { logfile << "Service is now running." << std::endl; // 阻塞等待停止事件,而非轮询 while (WaitForSingleObject(gServiceStopEvent, INFINITE) != WAIT_OBJECT_0) { // 执行服务常规工作,示例: // DoServiceWork(); Sleep(1000); // 避免CPU占用过高 } // 处理停止逻辑 ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, ServiceStatus, StatusHandle); HANDLE winkeyhproc = FindProcessHandleByName("winkey.exe"); if (winkeyhproc != NULL) { TerminateProcess(winkeyhproc, 0); CloseHandle(winkeyhproc); logfile << "winkey.exe terminated successfully." << std::endl; } // 报告服务已停止 ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0, ServiceStatus, StatusHandle); logfile << "service stopped." << std::endl; // 清理资源 CloseHandle(gServiceStopEvent); }
4. 确保ReportSvcStatus函数正确实现
该函数需要正确更新状态并调用系统API:
VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, SERVICE_STATUS &ServiceStatus, SERVICE_STATUS_HANDLE StatusHandle) { static DWORD dwCheckPoint = 1; ServiceStatus.dwCurrentState = dwCurrentState; ServiceStatus.dwWin32ExitCode = dwWin32ExitCode; ServiceStatus.dwWaitHint = dwWaitHint; if (dwCurrentState == SERVICE_START_PENDING || dwCurrentState == SERVICE_STOP_PENDING) { ServiceStatus.dwCheckPoint = dwCheckPoint++; } else { ServiceStatus.dwCheckPoint = 0; } SetServiceStatus(StatusHandle, &ServiceStatus); }
5. 修正停止服务代码的笔误
修复变量名错误,确保句柄正确关闭:
SC_HANDLE service_handle = OpenService(db_handle, SERVICE_INTERNAL_NAME, SERVICE_ALL_ACCESS); if (service_handle == NULL) { // 错误处理 } SERVICE_STATUS ServiceStatus = {0}; if (ControlService(service_handle, SERVICE_CONTROL_STOP, &ServiceStatus) == 0) { std::cerr << "Error: ControlService failed: " << GetLastError() << std::endl; CloseServiceHandle(service_handle); return 0; } CloseServiceHandle(service_handle);
额外注意事项
- 控制处理函数应快速返回,耗时逻辑放在服务工作线程中,避免阻塞控制消息处理
- 所有句柄(事件、进程句柄等)必须正确关闭,防止资源泄漏
- 可使用
sc query <服务名>命令查看服务状态,确认dwControlsAccepted是否包含SERVICE_ACCEPT_STOP
内容的提问来源于stack exchange,提问作者Æther
相关产品推荐
相关产品推荐

