65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (10投票s)

2008 年 2 月 12 日

CPOL

4分钟阅读

viewsIcon

56514

downloadIcon

418

用于管理在 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,将返回有关正在运行服务的​​信息。根据文档,返回的缓冲区由两部分组成。我们将使用第一部分,即总共 pdwServiceEntriesServiceEnumInfo 结构。

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 日 - 初始版本
© . All rights reserved.