伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下。

将Windows服务转变为控制台程序

由于默认的Windows服务程序,编译后为Win32的窗口程序。我们在程序启动或运行过程中,如果想看到一些调试信息,那么就只能通过DebugView或者输出到日志的方式了。因为如果我们通过printf或者std::cout输出调试信息的话,Win32窗口程序是无法显示的。

此时,我们是多么怀念我们的经典的控制台程序啊,它可以很方便的将我们的调试信息输出出来,简直是太方便了。既然如此,那我们就让它一秒钟变格格吧,额,应该是一秒钟变控制台。

下面分享一下我的实现代码

#ifdef _DEBUG
//Debug版本,直接输出到控制台
#define OUT(s) printf_s(s);
#define OUT_LN(s) printf_s(s##"\r\n");
#else
//非Debug版本,则输出到调试器,一般使用DebugView
#define OUT(s) OutputDebugString(s);
#define OUT_LN(s) OutputDebugString(s);
#endifclass CServicesModule : public ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >
{
public :DECLARE_LIBID(LIBID_ServicesLib)DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SERVICES, "{0794CF96-5CC5-432E-8C1D-52B980ACBE0F}")HRESULT InitializeSecurity() throw(){return S_OK;}//服务启动
    HRESULT Load();//服务停止
    HRESULT UnLoad();HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw(){HRESULT hr = S_OK;OUT_LN("准备启动服务");hr = Load();if(hr){OUT_LN("启动服务失败");return hr;}OUT_LN("Services服务已启动");hr = ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >::Run(nShowCmd);hr = UnLoad();OUT_LN("Services服务正常退出");return hr;}
};CServicesModule _AtlModule;//
#ifndef _DEBUG
//非Debug版本,编译为Win32程序
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int nShowCmd)
{return _AtlModule.WinMain(nShowCmd);
}
#else
//Debug版本,编译为控制台程序
int _tmain(int argc, _TCHAR* argv[])
{return _AtlModule.WinMain(SW_SHOW);
}
#endifHRESULT CServicesModule::Load()
{OUT_LN("服务正在启动");return 0;
}HRESULT CServicesModule::UnLoad()
{OUT_LN("服务正在停止");return 0;
}

通过_DEBUG宏来区分是否编译成控制台程序。

当指定编译Debug版本时,可以将程序编译为控制台程序,通过RegServer注册服务,然后直接运行服务exe程序,这样通过printf输出的信息,就可以在控制台上显示了,如下图。

当指定编译Release版本时,将程序编译为Win32程序,通过Service注册服务,通过服务管理器管理服务的运行和停止。

当然,这还不是全部,还有一个地方需要设置,下面分别是Debug和Release下的设置

当然,还有一种更简单的方法,可以将Debug和Release模式下的“子系统”项修改为“未设置”。这样编译器在编译链接时,会根据代码中的入口函数,自动将代码链接为对应的程序。如图

注册服务为自动启动服务

注册服务的流程已经在前面的博客玩转Windows服务系列——Debug、Release版本的注册和卸载,及其原理中有介绍,这里就不再多说。重点说一下创建服务的Windows API CreateService。

如下是MSDN中的API声明

SC_HANDLE WINAPI CreateService(_In_       SC_HANDLE hSCManager,_In_       LPCTSTR lpServiceName,_In_opt_   LPCTSTR lpDisplayName,_In_       DWORD dwDesiredAccess,_In_       DWORD dwServiceType,_In_       DWORD dwStartType,_In_       DWORD dwErrorControl,_In_opt_   LPCTSTR lpBinaryPathName,_In_opt_   LPCTSTR lpLoadOrderGroup,_Out_opt_  LPDWORD lpdwTagId,_In_opt_   LPCTSTR lpDependencies,_In_opt_   LPCTSTR lpServiceStartName,_In_opt_   LPCTSTR lpPassword
);

参数太多,不一一介绍,详细介绍可以查看MSDN。

其中第六个参数,代表启动方式

dwStartType [in]
The service start options. This parameter can be one of the following values.Value Meaning
SERVICE_AUTO_START
0x00000002 A service started automatically by the service control manager during system startup. For more information, see Automatically Starting Services.SERVICE_BOOT_START
0x00000000 A device driver started by the system loader. This value is valid only for driver services.SERVICE_DEMAND_START
0x00000003 A service started by the service control manager when a process calls the StartService function. For more information, see Starting Services on Demand.SERVICE_DISABLED
0x00000004 A service that cannot be started. Attempts to start the service result in the error code ERROR_SERVICE_DISABLED.SERVICE_SYSTEM_START
0x00000001 A device driver started by the IoInitSystem function. This value is valid only for driver services.

SERVICE_AUTO_START 表示自动启动,这个参数就是我们想要的。

SERVICE_BOOT_START 也属于自动启动,但是只能用于内核服务。

SERVICE_DEMAND_START 手动启动,这是目前服务的默认启动方式。

SERVICE_DISABLED 禁止启动。

SERVICE_SYSTEM_START 属于自动启动,但只能用于内核服务。

所以,我们只需要在调用CreateService方法时,设置dwStartType参数为SERVICE_AUTO_START即可实现服务自动启动,而CreateService的其他参数,则参考现在的调用参数

::CreateService(hSCM, m_szServiceName, m_szServiceName,SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);

接下来,我们只需要重载命令行解析方法,添加参数用于确定是否自动启动即可。

注册服务时设置服务的依赖关系

设置服务的依赖关系仍然要看CreateService服务,这次我们看倒数第三个参数lpDependencies

lpDependencies [in, optional]
A pointer to a double null-terminated array of null-separated names of services or load ordering groups that the system must start before this service. Specify NULL or an empty string if the service has no dependencies. Dependency on a group means that this service can run if at least one member of the group is running after an attempt to start all members of the group.You must prefix group names with SC_GROUP_IDENTIFIER so that they can be distinguished from a service name, because services and service groups share the same name space.

lpDependencies是一个指针,指针指向了一个以’\0\0’结尾,并且以’\0’分割开的字符串,由’\0’分割开的字符串为依赖服务的名字。

比如,如果设置当前服务依赖RPCSS 和DependTest服务的,则可以这样调用CreateService方法

::CreateService(hSCM, m_szServiceName, m_szServiceName,SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,szFilePath, NULL, NULL, _T("RPCSS\0DependTest\0"), NULL, NULL);

由于字符串自身会以’\0’结尾,所以字符串内容中的结尾处只需写一个’\0’。

接下来,我们只需要重载命令行解析方法,添加参数用于确定是否自动启动即可

添加自定义命令行参数

添加自定义命令行参数 Auto, 用来设置启动方式为自动启动, 并且给Service参数添加依赖项,实现代码如下

// Services.cpp : WinMain 的实现
#include "stdafx.h"
#include "resource.h"
#include "Services_i.h"using namespace ATL;#include <stdio.h>#ifdef _DEBUG
#define OUT(s) printf_s(s);
#define OUT_LN(s) printf_s(s##"\r\n");
#else
#define OUT(s) OutputDebugString(s);
#define OUT_LN(s) OutputDebugString(s);
#endifclass CServicesModule : public ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >
{
public :DECLARE_LIBID(LIBID_ServicesLib)DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SERVICES, "{0794CF96-5CC5-432E-8C1D-52B980ACBE0F}")HRESULT InitializeSecurity() throw(){// TODO : 调用 CoInitializeSecurity 并为服务提供适当的安全设置// 建议 - PKT 级别的身份验证、// RPC_C_IMP_LEVEL_IDENTIFY 的模拟级别// 以及适当的非 NULL 安全描述符。return S_OK;}//服务启动
    HRESULT Load();//服务停止
    HRESULT UnLoad();// Parses the command line and registers/unregisters the rgs file if necessarybool ParseCommandLine(_In_z_ LPCTSTR lpCmdLine,_Out_ HRESULT* pnRetCode) throw();//注册服务BOOL Install() throw();//重写此方法,主要是为了调用重写的Install方法inline HRESULT RegisterAppId(_In_ bool bService = false) throw(){if (!Uninstall())return E_FAIL;CServicesModule::UpdateRegistryAppId(TRUE);CRegKey keyAppID;keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);CRegKey key;key.Create(keyAppID, CServicesModule::GetAppIdT());key.DeleteValue(_T("LocalService"));key.SetStringValue(_T("LocalService"), m_szServiceName);// Create serviceif (!Install())return E_FAIL;return S_OK;}HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw(){HRESULT hr = S_OK;OUT_LN("准备启动服务");hr = Load();if(hr){OUT_LN("启动服务失败");return hr;}OUT_LN("Services服务已启动");hr = ATL::CAtlServiceModuleT< CServicesModule, IDS_SERVICENAME >::Run(nShowCmd);hr = UnLoad();OUT_LN("Services服务正常退出");return hr;}private:_TCHAR dependServices[256];DWORD size;DWORD dwStartType;
};CServicesModule _AtlModule;//
#ifndef _DEBUG
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int nShowCmd)
{return _AtlModule.WinMain(nShowCmd);
}
#else
int _tmain(int argc, _TCHAR* argv[])
{return _AtlModule.WinMain(SW_SHOW);
}
#endifHRESULT CServicesModule::Load()
{memset(dependServices, 0, sizeof(dependServices));dwStartType = SERVICE_DEMAND_START;OUT_LN("服务正在启动");return 0;
}HRESULT CServicesModule::UnLoad()
{OUT_LN("服务正在停止");return 0;
}// Parses the command line and registers/unregisters the rgs file if necessary
bool CServicesModule::ParseCommandLine(_In_z_ LPCTSTR lpCmdLine,_Out_ HRESULT* pnRetCode) throw()
{if (!CAtlExeModuleT<CServicesModule>::ParseCommandLine(lpCmdLine, pnRetCode))return false;TCHAR szTokens[] = _T("-/");*pnRetCode = S_OK;LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);while (lpszToken != NULL){if (WordCmpI(lpszToken, _T("Service"))==0){lpszToken += _tcslen(_T("Service"));//循环读取依赖项while (true){LPCTSTR lpszTokenBegin = lpszToken;while(isprint(*lpszToken) && *lpszToken != _T(' ') && *lpszToken != _T('-') && *lpszToken != _T('/')){lpszToken++;}DWORD tokenSize = (lpszToken - lpszTokenBegin);memcpy_s(dependServices + size, sizeof(dependServices) - size, lpszTokenBegin, tokenSize * sizeof(_TCHAR));size += tokenSize;dependServices[size] = _T('\0');if(tokenSize){size++;}while(isprint(*lpszToken) && *lpszToken == ' '){lpszToken++;}if((*lpszToken == _T('\0') || *lpszToken == _T('-') || *lpszToken == _T('/'))){dependServices[size + 1] = _T('\0');break;}}*pnRetCode = this->RegisterAppId(true);if (SUCCEEDED(*pnRetCode))*pnRetCode = this->RegisterServer(TRUE);return false;}//设置启动类型if (WordCmpI(lpszToken, _T("Auto"))==0){dwStartType = SERVICE_AUTO_START;}lpszToken = FindOneOf(lpszToken, szTokens);}return true;
}BOOL CServicesModule::Install() throw()
{if (IsInstalled())return TRUE;// Get the executable file pathTCHAR szFilePath[MAX_PATH + _ATL_QUOTES_SPACE];DWORD dwFLen = ::GetModuleFileName(NULL, szFilePath + 1, MAX_PATH);if( dwFLen == 0 || dwFLen == MAX_PATH )return FALSE;// Quote the FilePath before calling CreateServiceszFilePath[0] = _T('\"');szFilePath[dwFLen + 1] = _T('\"');szFilePath[dwFLen + 2] = 0;SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);//创建服务、根据命令行设置启动方式,设置依赖关系SC_HANDLE hService = ::CreateService(hSCM, m_szServiceName, m_szServiceName,SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,dwStartType, SERVICE_ERROR_NORMAL,szFilePath, NULL, NULL, dependServices, NULL, NULL);if (hService == NULL){::CloseServiceHandle(hSCM);return FALSE;}::CloseServiceHandle(hService);::CloseServiceHandle(hSCM);return TRUE;
}

自定义命令行参数演示

注册服务时使用如下命令行

Services.exe -Auto -service CryptSvc RPCSS DcomLaunch

注册后,效果如下

系列链接

玩转Windows服务系列——创建Windows服务

玩转Windows服务系列——Debug、Release版本的注册和卸载,及其原理

玩转Windows服务系列——无COM接口Windows服务启动失败原因及解决方案

玩转Windows服务系列——服务运行、停止流程浅析

玩转Windows服务系列——Windows服务小技巧

玩转Windows服务系列——命令行管理Windows服务

玩转Windows服务系列——Windows服务启动超时时间

玩转Windows服务系列——使用Boost.Application快速构建Windows服务

玩转Windows服务系列——给Windows服务添加COM接口

转载于:https://www.cnblogs.com/hbccdf/p/3491641.html

玩转Windows服务系列——Windows服务小技巧相关推荐

  1. 计算机水平考试win操作,计算机等级考试综合指导:Windows系统“运行”中的小技巧...

    计算机等级考试综合指导:Windows系统"运行"中的小技巧 1.用"."和""打开指定文件夹 在"运行"中输入&quo ...

  2. 暴力破解sshd服务的密码的小技巧

    暴力破解sshd服务的密码的小技巧 准备环境   1:在虚拟机上准备一台XP系统   2:开启两台

  3. 微服务系列:服务注册与发现的实现原理、及实现优劣势比较

    服务注册与发现的来源 首先,服务注册与发现是来自于微服务架构的产物. 在传统的服务架构中,服务的规模处于运维人员的可控范围内.当部署服务的多个节点时,一般使用静态配置的方式实现服务信息的设定.而在微服 ...

  4. springcloud微服务系列之服务注册与发现组件Eureka

    一.Eurake的简介 二.使用Eureka进行服务的注册消费 1.创建一个服务注册中心 2.创建服务的提供者 3.创建服务的消费者 总结 一.Eurake的简介 今天我们来介绍下springclou ...

  5. 值得玩味儿的14个Python编程小技巧

    全文共394个字,1张图,预计阅读时间5分钟. 最近的工作中经常使用Python编写一些基本解决一些NLP的小问题,在自己的工作日记里面也记录了不少的python编程中常用的小"Trick& ...

  6. Windows Mobile开发的一些小技巧(持续更新)

    1.如何真正的退出程序,而不是隐藏窗体? Windows Mobile中当你点击X的时候,并不是真正的关闭窗体,而是隐藏窗体,需要在应用程序的每个Form中调用一个函数才可以.         pri ...

  7. c#开发windows应用程序几个小技巧

    最近,我在用.net做一个c/s的项目,把我做的情况给大家说说. datagrid是用的c1控件的c1FlexGrid,功能很多. 自定义分组和outlook形式的列头拖拽. textbox,comb ...

  8. Windows Server 2008 R2 小技巧 (转)

    一些 Windows Server 2008 R2 的小技巧,包括启用「God Mode (上帝模式)」.添加「快速启动」工具栏.启用桌面「个性化」服务.停用「密碼複雜性」要求,对老程序员熟悉新版的 ...

  9. 微服务系列:Dubbo与SpringCloud的Ribbon、Hystrix、Feign的优劣势比较

    在微服务架构中,分布式通信.分布式事务.分布式锁等问题是亟待解决的几个重要问题. Spring Cloud是一套完整的微服务解决方案,基于 Spring Boot 框架.确切的说,Spring Clo ...

最新文章

  1. 【ZooKeeper Notes 3】ZooKeeper Java API 使用样例
  2. 这里天刚黑,而家里都已经后半夜了
  3. dplyr和data.table让你的数据分析事半功倍
  4. 跟我一起写 Makefile(七)
  5. GitLab 在多分支中的一个push
  6. Python基础小结
  7. Python字典(Dictionary)的setdefault()方法的详解,字典中的赋值技巧
  8. 自学python好找工作么-自学Python那个方向好找工作?
  9. 火爆的大数据被什么拖了后腿
  10. 一步一步打造基于TICK的工业级系统监控平台
  11. centos mpeg acc 解码器安装
  12. 嵌入式软件工程师学习路线图
  13. ESP32实现Wave(.wav)音频文件输出
  14. 新浪微博架构和FEED架构分析--人人架构
  15. php面试常考函数,PHP面试常见算法、函数总结
  16. n96 微信 服务器繁忙,网友称塞班手机已无法登陆QQ/微信
  17. 【Day3.7】建兴海鲜酒家,味道还很不错
  18. python画正方形并涂色_这种图片怎么用python画出来,每一个数字代表一种颜色?...
  19. vue路由传参—params—query
  20. PTA乙级 1108 String复读机——20分

热门文章

  1. 鼠标点击响应双击问题修复
  2. 小米兔跳格子 (dfs BFS/ 贪心)
  3. WIN10下CANdb++编辑器的下载和安装
  4. 移动阅读APP开发功能有哪些?
  5. 一位台湾码农的心路历程
  6. LED线性恒流DC-DC降压恒流IC
  7. 【数据结构与算法】之深入解析“点菜展示表”的求解思路与算法示例
  8. eNSP-小型网络拓扑(DNS、DHCP、网站服务器、无线路由器)
  9. 在以num为首址的存储区中存有n个带符号的字节类型的数据,从中找出最大数并送入max单元【汇编语言】
  10. vue中引入字体文件