首先说说为什么要写这么一个服务。由于电脑要在公司域中使用,所以不可避免的会继承域中的组策略配置。域中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