程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Windows 7開發:後台服務(動手實驗)

Windows 7開發:後台服務(動手實驗)

編輯:關於.NET

Windows 7 和 Windows Vista 在後台處理方面經過了很多改進。如今實現有效後台處理 的挑戰包括:

•   性能——啟動延遲,登錄延遲,關閉延遲;後台 處理干擾前台處理

•  耗電

•  安全——攻擊面增多

Windows 7 後台服務和計劃任務采用各種機制最大程度地減小耗電量,減少系統攻擊 面,並提高應用程序和系統性能。這些機制包括:

•   服務請求的安全權限

•   服務 SID

•   延遲自動啟動服務

•   觸發器 啟動服務

•  計劃任務條件和設置

由於如今的 Windows 中存在大量服 務,後台處理必須滿足安全性、性能和耗電要求。

目標

在本實驗中,您將學 習如何:

•   設計和實現觸發器啟動服務

•   最大程度地減少 服務請求的權限量

系統要求

要完成本實驗,您必須擁有以下軟件:

•  Microsoft Visual Studio 2008

•  Windows 7

•  Windows 7 SDK

•  Windows Sysinternals Process Explorer

設置

本實驗要求 Windows 7 SDK 與 Visual Studio 2008 正確集成。可以按照以下步驟 實現:

1. 單擊“開始”菜單,轉到所有程序 | Microsoft Windows SDK v7.0 | Visual Studio Registration ,然後單擊 Windows SDK Configuration Tool 。在 Windows SDK Configuration Tool 對話框上,確認選擇了 v7.0 版本,然後單擊 Make Current 。

圖 1

Windows SDK 配置工具

2. 還需要更新 Visual C++ 目錄,使之指向 Windows 7 SDK 頭文件、庫和工具。路徑應該與 Windows 7 SDK 本地安裝路徑匹配。要做到 這一點,在 Microsoft Visual Studio 2008 菜單中選擇 Tools | Options 打開 Options 對話框。展開左側窗格中的 Projects and Solutions 節點,選擇 VC++ Directories 並按 照以下步驟操作:

a. 從右側窗格右上角的組合框中選擇 Executable files 。然後 選擇    按鈕,浏覽到 %Windows7SDKInstallDir%\v7.0\Bin 文件夾並單擊 Select Folder 。

圖 2

配置 VC++ 目錄

注意: Windows 7 SDK 默認安裝在 C:\Program Files\Microsoft SDKs\Windows 文件夾。

b. 重復上一步,對 Include files 選項 添加 %Windows7SDKInstallDir%\v7.0\Include 文件夾,然後對 Library Files 選項添加 %Windows7SDKInstallDir%\v7.0\Lib 文件夾。

c. 最後,單擊 OK 保存設置。

此外,本實驗需要特定的“硬編碼”文件夾,需要創建:

1. 在本 地 C 驅動器上,需要創建 C:\FromUSB 文件夾, USB 鍵上的所有映像都將復制到該文件夾 。

2. 在通用 USB 鍵上,需要創建 ToCopy 文件夾,所有的映像都從該文件夾復制。

注意: 本練習中演示的服務不是針對 Windows 背景處理而設計的。它只是一個示例 應用程序,演示如何使用 Windows 7 中的 Trigger Start Services 。有關設計有效背景處 理的更多信息,請參考 Microsoft 白皮書“ Designing Efficient Background Processes ”。

練習 1 :觸發器 - 啟動服務

在本練習 中,您將修改現有服務,使之成為更適合操作系統的服務。需要更改服務的請求安全權限, 使之在最低的權限下運行;您將更改服務配置,使之在需要時自動啟動,而不是在後台運行 並輪詢系統。

使用的示例服務是一個 USB 文件復制服務,它從插入系統的 USB 存儲設備的特定目錄中 復制文件。本練習中使用的技術適用於滿足特定標准的觸發器 - 啟動服務。

任務 1 – 實現服務配置更改

要使用編程方式修改服務配置,必須使用相應的參數調用 Win32 API 函數 ChangeServiceConfig2 。

1. 打開 %TrainingKitInstallDir%\BackgroundServices\Ex1- TriggerStartService\Begin 文件夾下的初始解決方案 Begin.sln ,並選擇要使用的語言( C# 或 VB )。

注意: Visual Studio 2008 必須以提升的模式運行。要做到這一點 ,右鍵單擊 Visual Studio 2008 圖標並選擇 Run as Administrator 。

2. 在 ServiceControlInterop 項目中,在 Source Files 文件夾下找到 ServiceControlInterop.cpp 文件,並在 RemoveAllPrivilegesFromService 方法下添加代 碼指定該服務不需要任何權限:

a. 分配 SERVICE_REQUIRED_PRIVILEGES_INFO 結構並將其 pmszRequiredPrivileges 方 法設置為 SE_CHANGE_NOTIFY_NAME L"\0" 。

b. 在 SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO 信息級別調用 ChangeServiceConfig2 方法, 並傳遞上一個步驟中分配的結構的地址。

完整示例見以下代碼(用粗體顯示):

C++

void ServiceControl::RemoveAllPrivilegesFromService (System::String^ serviceName)
{
    ....
    if  (hService == NULL)
    {
        DWORD dwLastError =  GetLastError();
        CloseServiceHandle(hSCManager);
         throw Marshal::GetExceptionForHR(dwLastError);
    }

    //This effectively strips out all other privileges.
     SERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivileges = {0};
     requiredPrivileges.pmszRequiredPrivileges = SE_CHANGE_NOTIFY_NAME L"\0";

    if (ChangeServiceConfig2(hService,  SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO, &requiredPrivileges) == FALSE)
    {
        DWORD dwLastError = GetLastError();
         CloseServiceHandle(hService);
         CloseServiceHandle(hSCManager);
        throw  Marshal::GetExceptionForHR(dwLastError);
    }

     CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
}

3. 在 SetServiceTriggerStartOnUSBArrival 方法中添加代碼,在通用 USB 磁盤可用時 將服務設置為觸發器 - 啟動服務:

a. 分配 SERVICE_TRIGGER_SPECIFIC_DATA_ITEM 結構。要實現這一點:

將其 dwDataType 成員設置為 SERVICE_TRIGGER_DATA_TYPE_STRING 。

將其 cbData 成員設置為字符串 L"USBSTOR\\GenDisk" 的字節長度。

將其 pData 成員設置為該字符串。

b. 分配 SERVICE_TRIGGER 結構。要實現這一點:

將其 dwTriggerType 成員設置為 SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL 。

將其 dwAction 成員設置為 SERVICE_TRIGGER_ACTION_SERVICE_START 。

將其 pTriggerSubtype 成員設置為 GUID_USBDevice GUID 的地址。

將其 cDataItems 成員設置為 1 ,將其 pDataItems 成員設置為上一步分配的結構的地址。

c. 分配 SERVICE_TRIGGER_INFO 結構。要實 現這一點:

將其 cTriggers 成員設置為 1 ,將其 pTriggers 成員設置為上一步中 分配的結構的地址。

d. 在 SERVICE_CONFIG_TRIGGER_INFO 信息級別調用 ChangeServiceConfig2 函數,並傳遞上一步中分配的結構的地址。

完整示例見以下 代碼(用粗體顯示):

C++

void  ServiceControl::SetServiceTriggerStartOnUSBArrival(System::String^ serviceName)
{
    ...
    if (hService == NULL)
         {
        DWORD dwLastError = GetLastError();
         CloseServiceHandle(hSCManager);
        throw  Marshal::GetExceptionForHR(dwLastError);
    }

     LPCWSTR lpszDeviceString = L"USBSTOR\\GenDisk";

     SERVICE_TRIGGER_SPECIFIC_DATA_ITEM deviceData = {0};
     deviceData.dwDataType = SERVICE_TRIGGER_DATA_TYPE_STRING;
     deviceData.cbData = (wcslen(lpszDeviceString)+1) * sizeof(WCHAR);
     deviceData.pData = (PBYTE)lpszDeviceString;

     SERVICE_TRIGGER serviceTrigger = {0};
     serviceTrigger.dwTriggerType =  SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL;
     serviceTrigger.dwAction = SERVICE_TRIGGER_ACTION_SERVICE_START;
     serviceTrigger.pTriggerSubtype = (GUID*)&GUID_USBDevice;
     serviceTrigger.cDataItems = 1;
    serviceTrigger.pDataItems =  &deviceData;

    SERVICE_TRIGGER_INFO serviceTriggerInfo =  {0};
    serviceTriggerInfo.cTriggers = 1;
     serviceTriggerInfo.pTriggers = &serviceTrigger;

    if  (ChangeServiceConfig2(hService, SERVICE_CONFIG_TRIGGER_INFO,  &serviceTriggerInfo) == FALSE)
    {
        DWORD  dwLastError = GetLastError();
        CloseServiceHandle (hService);
        CloseServiceHandle(hSCManager);
         throw Marshal::GetExceptionForHR(dwLastError);
    }

    CloseServiceHandle(hService);
    CloseServiceHandle (hSCManager);
}

這就是 .NET 與觸發器 - 啟動 API 交互所需 C++/CLI 包裝器的完整代碼。

任務 2 – 添加代碼,將服務注冊為觸發器 - 啟動服務

使用 sc.exe 命令行實用工 具(在提升的命令 Shell 中使用 sc triggerinfo 命令)或 ChangeServiceConfig2 API 編 程(上一個任務的一部分)可以將服務注冊為觸發器 - 啟動服務。

1. 在 RegisterService 項目中,找到 RegisterServiceForm.cs (C#) 或 RegisterServiceForm.vb (Visual Basic) 代碼文件,並在 btnRegisterTriggerStart_Click 方法中添加代碼,使用 C++/CLI 包裝器將服務配置更改為 觸發器 - 啟動服務,如下所示(用粗體顯示):

C#

private void  btnRegisterTriggerStart_Click(object sender, EventArgs e)
{
     AddService();

     ServiceControl.SetServiceTriggerStartOnUSBArrival(ServiceName);

     StopLogReaderTimer();
    StartLogReaderTimer();
}

Visual Basic

Private Sub  btnRegisterTriggerStart_Click() Handles btnRegisterTriggerStart.Click
     AddService()

     ServiceControl.SetServiceTriggerStartOnUSBArrival(ServiceName)

     StopLogReaderTimer()
    StartLogReaderTimer()
End  Sub

2. 在 btnRemovePrivileges_Click 方法中,使用 C++/CLI 包裝器添加 代碼更改服務配置,使其請求最小的權限,如下所示:

C#

private  void btnRemovePrivileges_Click(object sender, EventArgs e)
{
     ServiceControl.RemoveAllPrivilegesFromService(ServiceName);
}

Visual Basic

Private Sub btnRemovePrivileges_Click() Handles  btnRemovePrivileges.Click
     ServiceControl.RemoveAllPrivilegesFromService(ServiceName)
End  Sub

完成對服務注冊 UI 應用程序的更改後,服務可以使用最少的權限注冊為 觸發器 - 啟動服務。

任務 3 – 為觸發器 - 啟動服務啟用 UsbCopyService

與使用周期性計時器輪詢感興趣事件的自動啟動服務不同,觸發器 - 啟動服務在觸發器啟動它們時活動,在執行完任務之後停用(停止)。

1. 在 UsbCopyService 項目中,找到並打開 USBService.cs (C#) 或 USBService.vb (Visual Basic) 代碼文件。

2. (僅用於 Visual Basic 用戶)添加 ProcessTriggerStart 方法,在服務被配置為觸發器 - 啟動時將調用該方法。

Visual Basic

Private Sub ProcessTriggerStart()
    DoWork()
End Sub

3. 修改 OnStart 方法,使用 ServiceControl.IsServiceTriggerStart 方法檢查服務是否配置為觸發器 - 啟動服務(傳 遞從 ServiceBase 類繼承的 ServiceName 屬性作為參數)。為此,使用以下代碼替換方法 實現:

C#

protected override void OnStart(string[] args)
{
    if (ServiceControl.IsServiceTriggerStart(ServiceName))
     {
    }
    else
    {
         _timer = new Timer(delegate
        {
             DoWork();
        });
        _timer.Change(0,  5000);
    }
}

Visual Basic

Protected Overrides Sub OnStart(ByVal args() As  String)
    If ServiceControl.IsServiceTriggerStart(ServiceName)  Then

    Else
        _timer = New Timer(AddressOf  ProcessTimerEvent)
        _timer.Change(0, 5000)
     End If
End Sub

4. 現在,如果將服務配置為觸發器 - 啟動服務而不 是創建周期性的計時器,該方法將按順序排列所有僅執行一次的工作。為此,添加以下代碼 (用粗體顯示):

C#

protected override void OnStart(string[]  args)
{
    if (ServiceControl.IsServiceTriggerStart (ServiceName))
    {
        ThreadPool.QueueUserWorkItem (delegate
        {
            DoWork();
         });
    }
    else
    {
         _timer = new Timer(delegate
        {
             DoWork();
        });
        _timer.Change(0,  5000);
    }
}

Visual Basic

Protected  Overrides Sub OnStart(ByVal args() As String)
    If  ServiceControl.IsServiceTriggerStart(ServiceName) Then
         ThreadPool.QueueUserWorkItem(AddressOf ProcessTriggerStart)
    Else
        _timer = New Timer(AddressOf ProcessTimerEvent)
         _timer.Change(0, 5000)
    End If
End Sub

5. 完成工作之後,應該使用從 ServiceBase 類中繼承的 Stop 方法停止服務。為此,將 以下代碼(用粗體顯示)添加到 OnStart 方法 (C#) 或 ProcessTriggerStart 方法 (Visual Basic) :

C#

protected override void OnStart(string [] args)
{
    if (ServiceControl.IsServiceTriggerStart (ServiceName))
    {
        ThreadPool.QueueUserWorkItem (delegate
        {
            DoWork();
             Stop();
        });
    }
     else
    {
     ...
    }
}
Visual  Basic
Private Sub ProcessTriggerStart()
    DoWork()
     Me.Stop()
End Sub

6. 下面開始測試服務。運行 RegisterService 項目。

注意: 項目應該由管理員運行。非管理員用戶可以通過提升的 Visual Studio 實例或者通過 shell 或命令提示符運行可執行文件運行項目。

7. 單擊 Register Demand-Start 按鈕將服務注冊為根據需要啟動的服務,並單擊 Start 按鈕啟動它 。它將每隔 5 秒自動輪詢一次 USB 設備。在檢測到帶有 ToCopy 目錄的 USB 設備時,它將 該目錄的內容復制到 C:\FromUSB 目錄。

圖 3

將服務注冊為根據需要啟動的服務

8. 啟動 Sysinternals Process Explorer 檢查服務的訪問令牌。在流程樹中右鍵單擊 UsbCopyService.exe 流程,選擇 Properties 並切換到 Security 選項卡。在服務權限列表下有很多權限,有些是啟用的,有 些是禁用的。

圖 4

檢查服務權限

注意: Sysinternals Process Explorer (procexp.exe) 的 下載鏈接可以從本實驗的先決條件小節找到。

9. 使用 Stop 按鈕停止服務。

10. 單擊 Remove Privileges 按鈕從服務中移除權限。

11. 單擊 Start 按 鈕啟動服務,並使用 Sysinternals Process Explorer 再次檢查訪問令牌。服務的權限列表 現在應該僅包含 SeChangeNotifyPrivilege (為了應用程序兼容目的,總是保留該權限)。

圖 5

服務權限(移除權限之後)

12. 使用 Stop 按鈕停止服務,使用 Delete 按 鈕刪除它。

13. 從計算機移除 USB 設備。

14. 單擊 Register Trigger- Start 按鈕將服務注冊為觸發器 - 啟動服務。不要手動啟動服務。

15. 使用最高權 限從命令提示符運行以下命令,檢查服務的觸發器 - 啟動配置。

命令

sc qtriggerinfo UsbCopyService

圖 6

檢查服務的觸發器 - 啟動配置

16. 插入帶有 ToCopy 目錄的 USB 設 備。該設備應該自動啟動,復制文件然後自動關閉。

17. 可以使用最高權限從命令提 示符運行以下命令,驗證服務關閉。

命令

sc queryex UsbCopyService

圖 7

驗證服務關閉

18. 完成後,單擊 Delete 按鈕刪除服務。

19. 可以使用最高權限從命令提示符運行與上一條相同的命令,確保服務已經刪除。

命令

sc queryex UsbCopyService

圖 8

驗證服務已經刪除

在本練習中,您修改了現有服務,使之變為觸發器 - 啟動服務,在系統檢測到 USB 設備時可以由觸發器啟動。您還減小了服務必需的權限數, 減少了系統的攻擊面。完整的練習解決方案位於 %TrainingKitInstallDir% \BackgroundServices\Ex1-TriggerStartService\End 目錄中,根據不同的語言有不同的版 本。

小結

在本實驗中,您提高了 Windows 服務的性能和安全性,使其更加適 合操作系統。您使用觸發器 - 啟動服務機制,僅在真正需要執行任務時才啟動服務,此外, 還移除了處理令牌中不必要的權限,最大程度地減少了服務的攻擊面。

本 Windows 7 課程代碼示例中包含完整的 USB 復制服務演示和 Weather Updater 服務。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved