今天回答论坛上的一个问题,搜索了一下网上,并没有找到一个完整的例子,下面根据网上的一些资料,经过转换、完善成一个完整的例子。下面的例子可以实现添加、删除、检测网站是否在兼容性网站列表中的功能。
以下代码在IE8、Windows2003环境下测试通过。测试时要重新打开浏览器进行验证。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/*
*
* 添加、删除兼容性网站列表的代码。
*
* 孟宪会@2014-04-20
*
*/
//兼容性列表在注册表中的位置,注意:不同位的操作系统可能不同,请注意测试。
private const string CLEARABLE_LIST_DATA = @"Software\Microsoft\Internet Explorer\BrowserEmulation\ClearableListData";
private const string USERFILTER = "UserFilter";
private byte[] header = new byte[] { 0x41, 0x1F, 0x00, 0x00, 0x53, 0x08, 0xAD, 0xBA };
private byte[] delim_a = new byte[] { 0x01, 0x00, 0x00, 0x00 };
private byte[] delim_b = new byte[] { 0x0C, 0x00, 0x00, 0x00 };
private byte[] checksum = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
private byte[] filler = BitConverter.GetBytes(DateTime.Now.ToBinary());//new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
private byte[] regbinary = new byte[] { };
/// <summary>
/// 得到已经存在的所有兼容网站列表,如果没有,则返回空数组。
/// </summary>
/// <returns></returns>
private string[] GetDomains()
{
string[] domains = { };
using (RegistryKey regkey = Registry.CurrentUser.OpenSubKey(CLEARABLE_LIST_DATA))
{
//可能不存在此key.
Object filterData = regkey.GetValue(USERFILTER);
if (filterData != null)
{
byte[] filter = filterData as byte[];
domains = GetDomains(filter);
}
}
return domains;
}
/// <summary>
/// 从byte数组中分析所有网站名称
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
private string[] GetDomains(byte[] filter)
{
List<string> domains = new List<string>();
int length;
int offset_filter = 24;
int totalSize = filter.Length;
while (offset_filter < totalSize)
{
length = BitConverter.ToUInt16(filter, offset_filter + 16);
domains.Add(System.Text.Encoding.Unicode.GetString(filter, 16 + 2 + offset_filter, length * 2));
offset_filter += 16 + 2 + length * 2;
}
return domains.ToArray();
}
/// <summary>
/// 从兼容性列表中删除一个网站。
/// </summary>
/// <param name="domain">要删除网站</param>
private void RemoveUserFilter(string domain)
{
String[] domains = GetDomains();
if (!domains.Contains(domain)) {
return;
}
using (RegistryKey regkey = Registry.CurrentUser.OpenSubKey(CLEARABLE_LIST_DATA,true))
{
object oldData = regkey.GetValue(USERFILTER);
if (oldData != null)
{
byte[] filter = oldData as byte[];
byte[] newReg = GetRemovedValue(domain, filter);
if (GetDomains(newReg).Length == 0)
regkey.DeleteValue(USERFILTER);
else
regkey.SetValue(USERFILTER, newReg, RegistryValueKind.Binary);
}
}
}
/// <summary>
/// 得到一个网站的存储的数据
/// </summary>
/// <param name="domain"></param>
/// <param name="filter"></param>
/// <returns></returns>
private byte[] GetRemovedValue(string domain, byte[] filter)
{
byte[] newReg;
int length;
int offset_filter = 24;
int offset_newReg = 0;
int totalSize = filter.Length;
newReg = new byte[totalSize];
Array.Copy(filter, 0, newReg, 0, offset_filter);
offset_newReg += offset_filter;
while (offset_filter < totalSize)
{
length = BitConverter.ToUInt16(filter, offset_filter + 16);
if (domain != System.Text.Encoding.Unicode.GetString(filter, offset_filter + 16 + 2, length * 2))
{
Array.Copy(filter, offset_filter, newReg, offset_newReg, 16 + 2 + length * 2);
offset_newReg += 16 + 2 + length * 2;
}
offset_filter += 16 + 2 + length * 2;
}
Array.Resize(ref newReg, offset_newReg);
byte[] newSize = BitConverter.GetBytes((UInt16)(offset_newReg - 12));
newReg[12] = newSize[0];
newReg[13] = newSize[1];
return newReg;
}
/// <summary>
/// 向兼容性列表中添加一个网站
/// </summary>
/// <param name="domain"></param>
private void AddNewSiteToCompatibilityViewList(String domain)
{
String[] domains = GetDomains();
if (domains.Length > 0)
{
if (domains.Contains(domain))
{
return;
}
else
{
domains = domains.Concat(new String[] { domain }).ToArray();
}
}
else
{
domains = domains.Concat(new String[] { domain }).ToArray();
}
int count = domains.Length;
byte[] entries = new byte[0];
foreach (String d in domains)
{
entries = this.Combine(entries, this.GetDomainEntry(d));
}
regbinary = header;
regbinary = this.Combine(regbinary, BitConverter.GetBytes(count));
regbinary = this.Combine(regbinary, checksum);
regbinary = this.Combine(regbinary, delim_a);
regbinary = this.Combine(regbinary, BitConverter.GetBytes(count));
regbinary = this.Combine(regbinary, entries);
Registry.CurrentUser.OpenSubKey(CLEARABLE_LIST_DATA,true).SetValue(USERFILTER, regbinary, RegistryValueKind.Binary);
}
/// <summary>
/// 得到一个网站在兼容性列表中的数据,跟GetRemovedValue类似
/// </summary>
/// <param name="domain"></param>
/// <returns></returns>
private byte[] GetDomainEntry(String domain)
{
byte[] tmpbinary = new byte[0];
byte[] length = BitConverter.GetBytes((UInt16)domain.Length);
byte[] data = System.Text.Encoding.Unicode.GetBytes(domain);
tmpbinary = Combine(tmpbinary, delim_b);
tmpbinary = Combine(tmpbinary, filler);
tmpbinary = Combine(tmpbinary, delim_a);
tmpbinary = Combine(tmpbinary, length);
tmpbinary = Combine(tmpbinary, data);
return tmpbinary;
}
//把两个byte[]数组合并在一起
private byte[] Combine(byte[] a, byte[] b)
{
byte[] c = new byte[a.Length + b.Length];
System.Buffer.BlockCopy(a, 0, c, 0, a.Length);
System.Buffer.BlockCopy(b, 0, c, a.Length, b.Length);
return c;
}
private void Form1_Load(object sender, EventArgs e)
{
//添加10个
for (int i = 0; i < 10; i++)
{
AddNewSiteToCompatibilityViewList("meng" + i + ".com");
}
//删除2个
for (int i = 3; i < 5; i++)
{
this.RemoveUserFilter("meng" + i + ".com");
}
}
}
}
网上的3篇资料与本文关系较大。
http://www3.xuebuyuan.com/1567759.html
http://jeffgraves.me/2014/02/19/modifying-ie-compatibility-view-settings-with-powershell/
http://stackoverflow.com/questions/16722454/editing-binary-registry-key
为了确保原文的丢失能找到内容,先把有用的PowerShell脚本粘贴过来。
$key = "HKCU:\Software\Microsoft\Internet Explorer\BrowserEmulation\ClearableListData"
$item = "UserFilter"
. .\Get-IPrange.ps1
$cidr = "^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/(3[0-2]|[1-2]?[0-9])$"
[byte[]] $regbinary = @()
#This seems constant
[byte[]] $header = 0x41,0x1F,0x00,0x00,0x53,0x08,0xAD,0xBA
#This appears to be some internal value delimeter
[byte[]] $delim_a = 0x01,0x00,0x00,0x00
#This appears to separate entries
[byte[]] $delim_b = 0x0C,0x00,0x00,0x00
#This is some sort of checksum, but this value seems to work
[byte[]] $checksum = 0xFF,0xFF,0xFF,0xFF
#This could be some sort of timestamp for each entry ending with 0x01, but setting to this value seems to work
[byte[]] $filler = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
#Examples: mydomain.com, 192.168.1.0/24
$domains = @("google.com","192.168.1.0/24")
function Get-DomainEntry($domain) {
[byte[]] $tmpbinary = @()
[byte[]] $length = [BitConverter]::GetBytes([int16]$domain.Length)
[byte[]] $data = [System.Text.Encoding]::Unicode.GetBytes($domain)
$tmpbinary += $delim_b
$tmpbinary += $filler
$tmpbinary += $delim_a
$tmpbinary += $length
$tmpbinary += $data
return $tmpbinary
}
if($domains.Length -gt 0) {
[int32] $count = $domains.Length
[byte[]] $entries = @()
foreach($domain in $domains) {
if($domain -match $cidr) {
$network = $domain.Split("/")[0]
$subnet = $domain.Split("/")[1]
$ips = Get-IPrange -ip $network -cidr $subnet
$ips | %{$entries += Get-DomainEntry $_}
$count = $count - 1 + $ips.Length
}
else {
$entries += Get-DomainEntry $domain
}
}
$regbinary = $header
$regbinary += [byte[]] [BitConverter]::GetBytes($count)
$regbinary += $checksum
$regbinary += $delim_a
$regbinary += [byte[]] [BitConverter]::GetBytes($count)
$regbinary += $entries
}
Set-ItemProperty -Path $key -Name $item -Value $regbinary
IE11下的PowerShell
$key = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft"
if(-Not (Test-Path "$key\Internet Explorer")) {
New-Item -Path $key -Name "Internet Explorer" | Out-Null
}
if(-Not (Test-Path "$key\Internet Explorer\BrowserEmulation")) {
New-Item -Path "$key\Internet Explorer" -Name "BrowserEmulation" | Out-Null
}
if(-Not (Test-Path "$key\Internet Explorer\BrowserEmulation\PolicyList")) {
New-Item -Path "$key\Internet Explorer\BrowserEmulation" -Name "PolicyList" | Out-Null
}
. .\Get-IPrange.ps1
$cidr = "^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/(3[0-2]|[1-2]?[0-9])$"
#Examples: mydomain.com, 192.168.1.0/24
$domains = @("google.com","192.168.1.0/24")
$regkey = "$key\Internet Explorer\BrowserEmulation\PolicyList"
foreach($domain in $domains) {
if($domain -match $cidr) {
$network = $domain.Split("/")[0]
$subnet = $domain.Split("/")[1]
$ips = Get-IPrange -ip $network -cidr $subnet
$ips | %{$val = New-ItemProperty -Path $regkey -Name $_ -Value $_ -PropertyType String | Out-Null}
$count = $count - 1 + $ips.Length
}
else {
New-ItemProperty -Path $regkey -Name $domain -Value $domain -PropertyType String | Out-Null
}
}