Windows Mobile 的服务浏览器应用程序






4.42/5 (10投票s)
用于管理在 Windows Mobile 平台 Services.exe 进程下运行的服务的一个辅助应用程序。

目录
引言
我最近的一个项目要求在智能手机设备上运行一个后台进程。Windows Mobile SDK 提供了编写运行在 Services.exe 进程中的服务的编程模型。我决定为我的项目使用这个模型。在服务开发/调试期间,我需要一种方法来启动/停止我的服务,同时 Visual Studio 已连接到目标设备上的 Services.exe 进程。本文介绍如何创建一个应用程序来管理在智能手机设备上运行的服务。
背景
Service Browser 是使用 Visual Studio 2005、Windows Mobile 6 SDK Standard 和 Windows Template Library (WTL) 8.0 开发的。该应用程序已在 MOTO Q 9h 智能手机和 Visual Studio 2005 模拟器上进行了测试。
Using the Code
下载 CAB 文件并将其安装到您的设备上,或者使用提供的解决方案来构建应用程序。请确保您已安装 SDK 和 WTL。
Services.exe
SDK 文档中的 Services.exe 参考说明了创建和注册服务所需的所有步骤。我们需要知道服务是如何从 Services.exe 进程中注册、加载和卸载的。
注册是通过在注册表 HKEY_LOCAL_MACHINE\Services 下写入一个任意的键来完成的。该键必须具有以下描述的特定值。
值 | 类型 | 描述 |
Order | REG_DWORD |
Services.exe 加载每个服务的顺序。顺序值越小的服务,越先加载。 |
Dll | REG_SZ |
要加载的动态链接库 (DLL) 文件。仅文件名,带扩展名。此 DLL 应位于 \Windows 文件夹中。 |
Keep | REG_DWORD |
对于应在后台运行的服务,此值必须为 1。如果 Keep = 0 ,则 DLL 将在初始化后立即卸载。 |
前缀 | REG_SZ |
服务 DLL 中导出的服务函数的名称前缀。必须至少包含 3 个符号。 |
目录 | REG_DWORD |
服务索引 |
DisplayName | REG_SZ |
显示服务名称 |
描述 | REG_SZ |
显示服务的描述 |
背景 | REG_DWORD |
传递给初始化例程的值 |
注意:关于哪些值是可选的,哪些值是必需的,没有提供信息。根据我的经验,Order、Dll、Keep、Prefix、Index 和 Context 必须提供,其他都可以是可选的。我还看到,某些服务在注册表中具有其他不同的值,这些值是服务特定的。
为了将服务加载到 Services.exe 进程中,应用程序必须调用 ActivateService
函数。
HANDLE ActivateService(
LPCWSTR lpszDevKey,
DWORD dwClientInfo
);
此函数的第一参数是服务在注册表中注册的键。第二个参数应设置为 0。此函数加载服务并返回服务的句柄。
要卸载服务,应用程序可以使用 ActivateService
返回的句柄并调用 DeregisterService
函数。
BOOL DeregisterService(
HANDLE hDevice
);
如果应用程序没有服务句柄,可以使用 GetServiceHandle
来获取句柄。
HANDLE GetServiceHandle(
LPWSTR szPrefix,
LPWSTR szDllName,
DWORD pdwDllBuf
);
第一个参数是最重要的参数。它以 XXXN: 的形式指定服务的键前缀,其中 XXX 是服务在注册表中指定的 Prefix,而 N 也是注册表中的 Index。
注意: 我见过一些服务使用长度超过 3 个字符的前缀,但系统只使用前 3 个字符。文档指出 GetServiceHandle
是一个已弃用的函数,应该使用 CreateFile
替代,但当句柄是为了注销服务而获取时除外。
获取服务句柄的另一种方法是使用 EnumServices
函数枚举所有已加载的服务。
BOOL EnumServices(
PBYTE pBuffer,
DWORD * pdwServiceEntries,
DWORD * pdwBufferLen
);
为了枚举所有正在运行的服务,必须调用两次 EnumServices
。第一次调用时,将 pBuffer 设置为 NULL
,将 pdwBufferLen 设置为 0
。该函数将返回 pBuffer 所需的大小和正在运行的服务数量。第二次调用 EnumServices
时,使用正确大小的有效 pBufffer
,将返回有关正在运行服务的信息。根据文档,返回的缓冲区由两部分组成。我们将使用第一部分,即总共 pdwServiceEntries 个 ServiceEnumInfo
结构。
Typedef struct_ServiceEnumInfo {
WCHAR szPrefix[6];
WCHAR szDllName;
HANDLE hServiceHandle;
DWORD dwServiceState;
} ServiceEnumInfo;
现在我们有足够的信息来构建应用程序了。
应用程序
请在“其他语言/Visual C++/Smart Device”下使用 WTL Mobile Application Wizard。在此应用程序中,我使用了“Dialog base”应用程序类型,并勾选了“Orientation aware”。有关使用该向导的详细分步说明,请参阅我的另一篇文章:GPS and Web Service using C++ ATL/WTL (Windows Mobile 6, Standard)。
应用程序的核心是可用服务列表,该列表通过读取注册表 HKEY_LOCAL_MACHINE\Services 下的所有键来生成。EnumServices
用于标记哪些服务是“正在运行”(已加载)。通过使用“Start”和“Stop”菜单项来加载和卸载列表中选定的服务。
VOID CServiceBrowserDialog::RefreshServices()
{
ATLTRACE(_T("CServiceBrowserDialog::RefreshServices\n"));
//Get services information from the registry
CRegKey rkServices;
LONG lRes = rkServices.Open(HKEY_LOCAL_MACHINE, _T("Services"));
//Registry Key/Value name
DWORD dwIndex = 0;
DWORD dwNameLength = 64;
TCHAR szBuffer[64] = {0};
//Registry Values we are interested in
CString strPrefx(_T("Prefix"));
CString strDll(_T("Dll"));
CString strDisplayName(_T("DisplayName"));
CString strDescription(_T("Description"));
CString strIndex(_T("Index"));
CString strOrder(_T("Order"));
CString strKeep(_T("Keep"));
CString strContext(_T("Context"));
//Registry Value data and type
DWORD dwType = 0;
DWORD dwValueIndex = 0;
BYTE pData[256] = {0};
DWORD dwDataLength = 256;
//Enumerate through services subkeys
while( rkServices.EnumKey(dwIndex++,szBuffer,&dwNameLength) == ERROR_SUCCESS )
{
ATLTRACE(_T("%s\n"),szBuffer);
//Open each Service key
CRegKey rkService;
if( rkService.Open(rkServices,szBuffer) == ERROR_SUCCESS )
{
CServiceInfo * pServiceInfo = new CServiceInfo();
if( !pServiceInfo )
continue;
pServiceInfo->m_strName = szBuffer;
//We will reuse buffer for name
szBuffer[0] = 0;
dwNameLength = 64;
dwType = 0;
dwValueIndex = 0;
pData[0] = 0;
dwDataLength = 256;
//Get all values for each registered service
while( RegEnumValue(rkService,dwValueIndex++,szBuffer,
&dwNameLength,NULL,&dwType, pData, &dwDataLength) == ERROR_SUCCESS )
{
switch( dwType )
{
case REG_SZ:
ATLTRACE(_T("\t%s - %s\n"),szBuffer, pData);
if( strPrefx.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_strPrefix = (TCHAR*)pData;
else if( strDll.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_strDll = (TCHAR*)pData;
else if( strDescription.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_strDescription = (TCHAR*)pData;
else if( strDisplayName.CompareNoCase( szBuffer ) == 0 )
pServiceInfo->m_strDisplayName = (TCHAR*)pData;
break;
case REG_DWORD:
{
DWORD dwValue = *((LPDWORD)pData);
ATLTRACE(_T("\t%s - %d\n"),szBuffer, dwValue);
if( strIndex.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_dwIndex = dwValue;
else if( strKeep.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_dwKeep = dwValue;
else if( strOrder.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_dwOrder = dwValue;
else if( strContext.CompareNoCase(szBuffer) == 0 )
pServiceInfo->m_dwContext = dwValue;
}
break;
default:
ATLTRACE(_T("\t%s\n"),szBuffer);
break;
}
//Reset everything for next iteration
szBuffer[0] = 0;
dwNameLength = 64;
pData[0] = 0;
dwDataLength = 256;
}
//If there is no "Prefix" it is not a valid service key
if( pServiceInfo->m_strPrefix.IsEmpty() )
{
delete pServiceInfo;
pServiceInfo = NULL;
}else
{
pServiceInfo->m_strKey.Format(_T("%s%d:"),pServiceInfo->m_strPrefix,
pServiceInfo->m_dwIndex == -1 ? 0:
pServiceInfo->m_dwIndex);
m_services.insert( InfoPair(pServiceInfo->m_strKey, pServiceInfo) );
}
}
szBuffer[0] = 0;
dwNameLength = 64;
}
//Let's get information about running services
DWORD nServices = 0;
DWORD nBufferSize = 0;
PBYTE pBuffer = NULL;
ServiceEnumInfo * pInfo = NULL;
EnumServices(NULL, &nServices, &nBufferSize);
if( nBufferSize != NULL && nServices != 0 )
{
pBuffer = (PBYTE)malloc(nBufferSize);
nServices = 0;
if( EnumServices(pBuffer,&nServices, &nBufferSize) && nServices != 0 )
pInfo = (ServiceEnumInfo *)pBuffer;
}
if( pInfo )
{
InfoMap::iterator serviceIter;
for( int i = 0; i < nServices; i++ )
{
serviceIter = m_services.find( pInfo[i].szPrefix );
if( serviceIter != m_services.end() )
{
//Save service handle
serviceIter->second->m_hHandle = pInfo[i].hServiceHandle;
}else
{
ATLASSERT( serviceIter != m_services.end() );
continue;
}
}
}
if( pBuffer )
free((void*)pBuffer);
}
VOID CServiceBrowserDialog::CleanUp()
{
m_list.DeleteAllItems();
InfoMap::iterator serviceIter = m_services.begin();
while( serviceIter != m_services.end() )
{
CServiceInfo * pServiceInfo = serviceIter->second;
delete pServiceInfo;
serviceIter++;
}
m_services.clear();
}
LRESULT CServiceBrowserDialog::OnStart(WORD /*wNotifyCode*/,
WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
INT nSelected = m_list.GetSelectedIndex();
if( nSelected != -1 )
{
CServiceInfo * pInfo = (CServiceInfo*)m_list.GetItemData(nSelected);
HANDLE h = ActivateService(pInfo->m_strName, 0);
UpdateList();
}
return 0;
}
LRESULT CServiceBrowserDialog::OnStop(WORD /*wNotifyCode*/,
WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
INT nSelected = m_list.GetSelectedIndex();
if( nSelected != -1 )
{
CServiceInfo * pInfo = (CServiceInfo*)m_list.GetItemData(nSelected);
HANDLE h = GetServiceHandle(CT2W(pInfo->m_strKey), NULL,0);
BOOL bRet = DeregisterService(h);
UpdateList();
}
return 0;
}
资源
历史
- 2008 年 2 月 11 日 - 初始版本