首先說說為什麼要寫這麼一個服務。由於電腦要在公司域中使用,所以不可避免的會繼承域中的組策略配置。域中95% 的電腦是XP系統,部分組策略對於Windows 7 系統來說有些多餘而且帶來很多麻煩。
問題一、清除虛擬記憶體策略
清除虛擬記憶體策略(Clear virtual memory pagefile)可以在一定程度上降低域中電腦病毒的傳播。但問題是如果啟動這個策略也會降低Windows 關機速度(3~5分鐘),對於關機速度奇快(13~16秒)的Windows 7 系統來說簡直是一個沉重的打擊。
修改這個組策略只需將[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management] 中的ClearPageFileAtShutdown 設為0x00000000 即可。
問題二、系統更新策略
在域中通過組策略可以使電腦通過WSUS 進行補丁更新,雖然是針對XP 系統設定的,但它卻完全破壞了Windows 7 系統本身的更新機制,詳情請參考《Windows Update Error: 80244019》。解決這個問題的辦法只需將註冊表中[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate] 完全刪除。
解決方案
由此可見解決上面兩個問題都可以通過修改註冊表來實現,當然有人會說也可以通過修改域組策略來達到不在某一個域使用者主機上應用組策略的效果,但這個方法還是不建議使用。起初寫了一個Reg 檔案運行一下就將註冊表更新了,但總這麼做也很麻煩。所以就想到建立一個服務讓它在後台運行並修改註冊表內容。
Windows Registry Editor Version 5.00[-HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate][HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]"ClearPageFileAtShutdown"=dword:00000000
建立一個Windows Service 項目,建立RegValueSet 類,並寫入以下代碼內容。在類中ChangeKeyValue() 方法用於完成上述註冊表修改操作。其中Registry.LocalMachine.DeleteSubKeyTree(updatePath); 將刪除系統更新群組策略資訊,RegSetValueEx(hKey, keyName, 0, RegistryValueKind.DWord, keyVal, 4); 會將ClearPageFileAtShutdown 索引值修改為0。
開始我嘗試通過RegNotifyChangeKeyValue 方法監測SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management 下的索引值是否被域組策略修改,如果被修改了便會執行RegSetValueEx 方法,但這種方式在服務中似乎行不通(啟動服務時它會一直處於監聽狀態)。經測試發現其實只需在開/關機時進行ChangeKeyValue() 操作即可,所以便將代碼改為以下方式。
using System;using System.Runtime.InteropServices;using Microsoft.Win32;namespace RegMonitor{ class RegValueSet { private static UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u); private static UIntPtr hKey; private const int keyRights = 0xF003F; //KEY_ALL_ACCESS (0xF003F)
private const UInt32 INFINITE = 0xFFFFFFFF;
private const UInt32 WAIT_FAILED = 0xFFFFFFFF;
public static void ChangeKeyValue() { string clrPath = @"SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management"; string updatePath = @"SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; string keyName = "ClearPageFileAtShutdown"; //Delete Windows Update Settings int vOpen = RegOpenKeyEx(HKEY_LOCAL_MACHINE, updatePath, 0, keyRights, out hKey); if (vOpen == 0) { Registry.LocalMachine.DeleteSubKeyTree(updatePath); } //Change Clear Page File Value RegOpenKeyEx(HKEY_LOCAL_MACHINE, clrPath, 0, keyRights, out hKey);
IntPtr hEvent = CreateEvent(IntPtr.Zero, true, false, null);
RegNotifyChangeKeyValue(hKey, true, 4, hEvent, true);
while (WaitForSingleObject(hEvent, INFINITE) != WAIT_FAILED)
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(clrPath); int val = (int)key.GetValue(keyName); if (val != 0) { IntPtr keyVal = Marshal.AllocHGlobal(4); Marshal.WriteInt32(keyVal, 0, 0); RegSetValueEx(hKey, keyName, 0, RegistryValueKind.DWord, keyVal, 4); key.Close(); } RegCloseKey(hKey);
}
} [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int RegOpenKeyEx( UIntPtr hKey, string subKey, int ulOptions, int samDesired, out UIntPtr hkResult); [DllImport("advapi32.dll", SetLastError = true)] public static extern int RegSetValueEx( UIntPtr hKey, [MarshalAs(UnmanagedType.LPStr)] string lpValueName, int Reserved, RegistryValueKind dwType, IntPtr lpData, int cbData); [DllImport("advapi32.dll", SetLastError = true)] public static extern int RegCloseKey( UIntPtr hKey);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateEvent(
IntPtr lpEventAttributes,
bool bManualReset,
bool bInitialState,
string lpName);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int RegNotifyChangeKeyValue(
UIntPtr hKey,
bool watchSubtree,
int dwNotifyFilter,
IntPtr hEvent,
bool fAsynchronous);
[DllImport("kernel32.dll", SetLastError = true)]
static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds);
}}
最後在Service 屬性中將CanShutdown 和CanStop 設為True,將Service 名稱設為RegistryMonitor。並在OnStart(服務啟動)、OnStop、OnShutdown(關機) 加入ChangeKeyValue() 方法就可以了,如下代碼所示。
using System.ServiceProcess;namespace RegMonitor{ public partial class Service1 : ServiceBase { public Service1() { InitializeComponent(); } protected override void OnStart(string[] args) { RegValueSet.ChangeKeyValue(); } protected override void OnShutdown() { RegValueSet.ChangeKeyValue(); } protected override void OnStop() { RegValueSet.ChangeKeyValue(); } }}
編譯項目後安裝RegistryMonitor 服務(Installutil),並在Services.msc 中啟動RegistryMonitor 服務。至此,就不用再擔心組策略的繼承問題了。
相關參考
1. RegOpenKeyEx Function
http://msdn.microsoft.com/en-us/library/ms724897(VS.85).aspx
2. RegSetValueEx Function
http://msdn.microsoft.com/en-us/library/ms724923(VS.85).aspx
3. RegistryKey Methods
http://msdn.microsoft.com/en-US/library/microsoft.win32.registrykey_methods(v=VS.80).aspx
來源程式下載