远程 NT 服务控制 COM 组件和 WPF 客户端应用程序
演示各种 MS 技术的示例应用程序。
引言
几年前,我在为一家知名软件公司准备面试。面试的先决条件之一是编写一个用于远程Windows NT服务控制的示例应用程序。该应用程序必须包含两个组件:一个用C++编写的COM组件,允许远程控制服务(启动、停止、暂停、恢复和重启),以及一个演示该组件功能的VB客户端应用程序。这一切都发生在VS 6.0 – VS 2003的时代。
这种面试要求的背后思想是筛选出那些真正对获得该职位感兴趣的候选人。此外,候选人还可以展示他们的技术技能:如你所知,有时代码比言语更能说明问题。
不久前,我决定使用WPF更新此应用程序,并与其他需要开发类似应用程序的人分享,以提供帮助。
背景
了解Windows NT如何控制其服务最佳的地方是MSDN。我们感兴趣的是一些API函数
- OpenSCManager
- OpenService
- GetServiceDisplayName
- QueryServiceConfig
- QueryServiceConfig2
- EnumServicesStatus
- StartService
- ControlService
- CloseServiceHandle
开发COM组件
从一开始,我就决定将实际功能封装在一个C++类层(模型层)中,并使用ATL通过COM(外观层)公开此功能。

模型层实现使用智能服务句柄来自动化句柄管理,并使用C++异常向外观层报告错误。所有系统错误消息都通过CAutoThrowError
抛出,组件错误通过CMessageError
抛出。错误消息使用消息编译器(MC.exe)编译。
服务操作(启动、停止等)可能需要一段时间才能完成。因此,对于通过CService
公开的所有操作,您可以定义操作完成的等待间隔。
try
{
// Open Service Control Manager (SCM)
CServiceControllerPtr scm(new CServiceController(CComBSTR(_T("TargetMachineName"))));
// Open service by its Key/Display name
CServicePtr service = scm->OpenServiceByKeyName(CComBSTR(_T("Browser")));
// Access service properties
DWORD state = service->CurrentState();
// Execute operations
DWORD waitMSec = 2000;
service->Start(waitMSec);
}
catch (CError& err)
{
// You can get error details here:
HRESULT hr = err.GetId();
CComBSTR description = err.GetDescription();
}
外观层支持“Both”公寓模型,并使用Free-Threaded Marshaler(FTM),因此这些组件可以在多线程环境中安全使用。
该组件支持侧边运行(SxS),因此无需注册即可部署。
公共接口与内部接口非常相似,可从任何支持COM的语言或平台(如.NET、VB等)使用。
try
{
// Open Service Control Manager (SCM)
IServiceController scm = new ServiceController();
scm.Connect(Environment.MachineName);
// Open service by its Key/Display name
IService service = scm.FindServiceByKeyName("Browser");
// Access service properties
CurrentState state = service.CurrentState;
// Execute operations
int waitMSec = 2000;
service.Start(waitMSec);
}
catch (COMException e)
{
int id = e.ErrorCode;
string description = e.Message;
}
开发客户端应用程序
在项目升级过程中,我决定使用WPF重新实现客户端应用程序。Microsoft的Services MMC Snap-in是UI的灵感来源。
它包含3个基本视图和一些服务命令。为了保持条理清晰,我使用了MVC模式,并带有Mediator(用于控制器之间的通信)。
所有服务调用都以异步方式实现,以使UI对用户操作更具响应性(通过System.Threading.Task
)。
我故意避免使用任何第三方工具包(如MVVM等),以使此程序尽可能简洁易懂。
客户端应用程序将COM组件用作隔离的COM。换句话说,您无需注册COM组件即可运行它,只需将其与清单文件一起保存在客户端的同一目录中。
已知问题
在将此解决方案升级到VS 2010后,有时在解决方案构建过程中会遇到以下错误
Error 1 general error c101008d: Failed to write the updated manifest to the
resource of file ".\Debug\ServiceControlLib.dll".
The process cannot access the file because it is being used by another process. mt.exe
Microsoft尚未为此问题提供任何解决方案。对我而言最有效的解决方法是将构建文件夹排除在防病毒监控之外。
特别感谢
- Alex Fr,感谢他提供的精美的模板智能句柄示例
- Marlon Grech,感谢他关于MVC+M的文章
- Isak Savo,感谢他提供的TwoColumnGrid控件
- Christian Mosers,感谢他提供的WPF Tutorials.net
- Daniel Lohmann,感谢他关于MC.exe的文章
- 我可能忘记提及的所有其他人
历史
- 版本 1.0. 首次发布
- 版本 1.1. 添加了代码示例
- 版本 1.2. 对源代码和演示进行了小更新