小感

刚刚吃完饭回来, 在想, 如果自己选择的是前端, 那现在大可以到处接项目, 逃一堆的课, 不仅可以实现经济独立, 大学毕业说不定还能有一辆车的钱甚至一个小房子的首付. 但, 转念, 又是自己愈发明确的理想.

经常和自己说话, 说自己的过去. 三四岁的时候, 想当科学家; 再大点, 相当物理学家; 后来, 一直到现在, 觉得要当理论物理学家. 计算机于我而言或许只是个插曲, 但这个插曲抢了不少风头.

我清楚, 要成为一个物理学家并不容易, 需要付出的努力或许和成为一名优秀的前端工程师全然不在一个数量级. 我也知道自己有太多不足需要克服. 最大的一点, 我需要学会读书. 因为读书才能把我从无知的幻想中解救出来, 让我的想象力落在它应该落在的轨道上. 但我也必须承认, 爱读书本身就是一种天赋, 纵使我天赋颇多, 却正好没有这么一个.

一定有捷径. 我不觉得毅力是能培养的, 甚至毅力本身在不同的地方也有完全不同的表现. 我没有读书的毅力, 所以只有寻找捷径.

我在拿两年, 四年甚至是一辈子的时间给梦想押注, 不论成败. 但说回来, 到目前, 我还是个决定论者. 一切于我而言, 早就注定了. 我的思考, 也不过是大自然的神功.

大学寝室等通用网络共享方式 (视情况需交换机/路由)

刚进大学的时候, 还可用使用Connectify, 不过随着这家伙的普及, 也很快被Dr.COM封杀了. 后来一直使用的是通过无线路由中转, 然后在其中一台电脑上架设代理来实现共享网络. 虽然前后遇到过一些升级, 但经过调整总算是还能正常使用, 下面就介绍下具体的方式和所需要的软件.

首先需要选定一台主机, 用来登录相关账号, 比如我们的是Dr.COM, 需要一款代理软件, 推荐Privoxy. 这里需要说明的是, 可能有些同学联网的客户端会进行代理软件检测, 或许Privoxy不能幸免, 但只要稍作修改即可. 绝大多数检测代理是通过关键字, 比如这里的Privoxy就是Dr.COM的一个关键字. 所以只要改掉这个关键字就好. 如果你懂得一点编程知识, 可以自己下载源码, 在源码中全局替换之后进行编译. 不过Privoxy编译Windows版本还比较麻烦, 所以不推荐这种方式. 这时需要一个可以编辑字节文件的软件, 比如010 Editor. 使用010 Editor, 查找并替换Privoxy主程序中所有的 “Privoxy” 字符串为另一个长度相等的字符串. 比如我改的是 “PcgPrvx”. 当然如果使用其他专门用于编辑这些资源的编辑器, 可以不用一定长度相等, 但这里为了保证没其他问题, 取相同长度.

改好Privoxy程序之后, 需要修改其配置文件, 一般为config.txt, 内容可以全部删除掉, 只写一行, 9257这里是代理端口:

listen-address :9257

当然如果你有耐心看都有哪些设置自然最好了. 更多信息官网上也有提供.

之后就是进行防火墙相关的设置了, 这个就不细说啦. 完成后, 这台电脑就成了一个代理服务器啦. 不过还需要别的电脑能连接到它, 才能真正用起来… 我们学校的情况是, 同一台交换机下的设备无法互相访问, 所以在完成上面的设置之后, 其他交换机上的同学已经可以通过我的IP和代理端口访问网络, 但同一台交换机下的同学, 比如寝室里的, 则不行. 这个时候就需要一台路由器或者自己的交换机啦. 话说买个无线的路由/交换机还是很方便的, 比插网线舒服多了… 将需要共享的设备连接到同一个路由或者交换机下, 然后则可以通过相应的IP和端口访问网络了. 至于两台设备是否可以正常互相访问, 可以互ping IP看看.

还有一个问题, 比如也是我们学校, 不允许使用自己的路由器, Dr.COM会检测, 如果发现则断开网络连接, 这个时候则必须使用交换机. 多数路由器都是可以当做交换机使用的, 具体设置请自行搜索. 🙂

最后再推荐ProxyCap作为共享网络的客户端, 可以把全部程序添加到代理中, 免得麻烦. 否则像QQ等, 还需要更改代理设置, 有些软件甚至根本不支持代理设置… 另外使用ProxyCap之后, 自己的科学上网软件也工作正常, 其实这个才是用它的最大动力啦…

正则表达式匹配JavaScript字符串字面量

第一次遇到这个问题, 是大概两年前写代码高亮, 从当时的解决方案到现在一共有三代, 嘎嘎. 觉得还是算越来越好的.

第一代:

//那个时候自己正则还不算很精通, 也没有(?:...)这种习惯, 是以寻找结束引号为入口写出的这个正则. 思路混乱, 也存在错误.
//比如像字面量 "abc\\\"", 则会匹配为 "abc\\\", 而正确的结果应该是 "abc\\\"".
var re = /('('|.*?([^\\]'|\\\\'))|"("|.*?([^\\]"|\\\\")))/g;

第二代:

//这个匹配其实和第一代思路基本相同, 也是寻找结束引号, 通过给\\添加*解决了第一代的bug.
var re = /(['"])(?:.*?[^\\](?:\\\\)*)?\1/g;

第三代:

//老实说第三代是昨天晚上出题的时候突然想出来的(后来又修改过), 支持多行字符串字面量, 思路也有了较大的转变, 从匹配结束引号变味了匹配中间内容.
var re = /(['"])(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*?\1/g

这里有一个正则里非常常用的技巧, 姑且称之为 “抢占”, 在诸如/a|./这样的正则里, 越靠前越先匹配, 通过 “抢占” 一些字符, 可以避开很多麻烦. 说起来有些抽象, 在上面这个例子里, /\\[\s\S]/就可以很自然地抢到 \” 这样的转义字符, 所以不必担心转义字符中的 ” 给匹配造成影响. 这是局部的 “抢占”, 还有更大范围的, 比如注释里的字符串或者字符串里的注释, 只要在同一个正则中, 把相关匹配都写入, 则一定是先遇到的优先. 如 /(注释正则)|(字符串正则)/g, 可以先通过这样的方式, 把内容匹配出来, 再进行进一步判断处理.

虽然可能对于多数人来说, 用处不大, 但万一呢? Best wishes~

Visual Studio 2012 for Web JavaScript自动完成体验

从Visual Studio 2005用到2010, 现在又用上了2012. 实际上之前就有体验过, 但并没有测试什么细节, 今天偶然想到, 对自己感兴趣的几个细节进行了测试, 感受是: It’s just AWESOME!

之前喜欢VS的JavaScript自动提示的原因, 是因为它会在某个环境执行你的代码, 并且用真实的结果给以智能提示, 如:

 

但VS 2010的时候, 类似的功能并不能体现到函数的实参上:

我一直很期待这个功能, 因为如果有它, 在更新自己VEJIS库的intellisense文件之后, 可以实现相当全面的自动提示. 结果, 它真的支持了!

但不仅仅是如此! 另外一个相当棒的功能就是支持异步, 比如异步加载脚本, 还有setTimeout. 先说可能大家不如我关心的setTimeout. 之后在介绍VEJIS的时候会提到前者.

除此之外, 智能提示还对数组的提示进行了优化, 会根据数组中之前出现的数据类型猜测之后出现的类型.

除了这些关于代码执行的提示, VS 2012对于代码注释的提示也有改进, 不过不是说让你写注释更方便, 而是让你写的注释被看得更方便. 并且支持多行, //和/**/都可以. 虽然之前也有<summary>等一些列的注释可以实现这样的功能, 但还是有点繁琐.

那接下来, 我就针对VEJIS谈谈对于VS 2012智能提示特性的进一步应用(这个时候在文件开头添加脚本引用了). 首先是动态加载.  在VEJIS里, require_函数用于动态加载一个脚本文件, 并且该文件会以<script>标签的形式添加到文档中. 例中动态加载的是我面向数据的一个基础库.

接下来是关于函数实参的, 像上面看到的那样, VS 2012可以根据函数被首次调用时传入的参数, 给实参加上提示, 但我们平时书写的时候, 多数时候是先写完函数再去调用. 当然, 在VEJIS下这种境况稍有不同, 因为VEJIS的函数支持重载, 也是需要指定类型的. 这样一来, 稍作修改, 就可以实现如下的功能.

同时, 因为之前说到的对于数组的提示支持, 让我还可以对VEJIS中的params类型进行处理.

又因为对于setTimeout的支持, VEJIS中类的提示也得到了加强, 虽然这个可能解释起来不是那么直观, 毕竟牵涉到其实现了.

强悍吧~ 话说如果是VEJIS老些的版本, 还支持with_, return_这些东西, 分别用于确定this和返回值的类型, 那配合上VS 2012, 就可以实现媲美强类型语言的自动提示了, 所以… 其实我已经动心开发下一个版本, 添加上这些支持. 囧. VS 2012除了这些运行时间的提示优化之外, 还增加了对很多常见对象的支持, 比如XMLHttpRequest.

顺便不得不提的是, Visual Studio 2012 for Web也有Express版, 也就意味着这么强大的工具是免费的! 各位前端同学们也加入VS阵营吧!

Adobe AIR实现支持代理的HTTP请求

也是因为那个采集项目的需要. 很失落地发现Adobe AIR的XMLHttpRequest也好, URLLoader也好, 都不支持设置代理, 一般似乎是用的系统设置. 于是自己实现了一个, 比较简单, 不支持HTTPS, 但是cookie什么的都是做到位了的. 有responseText和responseBody, 分别用于读取文本和字节文件, 如图片. 暂不支持压缩, 不过chunked是支持的.

代码已经传到github上了, 这里上一段实例, 当然, 只用到少数功能. 顺便这个是基于VEJIS, 如果不想或者不会用VEJIS可以自己稍作修改.

这里是VEJIS https://github.com/vilic/vejis
这里是这个HTTP请求 https://github.com/vilic/air-proxy-enabled-http-request-for-js

use_("http-request", function (hr) {
    var req = new hr.Request();

    //you can turn off cookies
    //req.cookieEnabled = false;
    //or turn off auto redirect
    //req.autoRedirect = false;

    req.proxy.host = "localhost";
    req.proxy.port = 1107;

    req.open("get", "http://www.vilic.info/blog/");

    //you can set request headers
    //req.setRequestHeader("Referer", "http://www.vilic.info/");

    req.send(function (req) {
        if (req.error) {
            alert(req.error);
            return;
        }

        alert(req.status);
        alert(req.responseText);
    });

    //if you use post, you'll also need to send the data
    //string and ByteArray are supported
    //req.send(data, callback);
});

Adobe AIR JavaScript跨域获取iframe内容

老实说在想到这个解决方案之前, 超级吓, 因为不解决这个问题, 就意味着项目无法实现预期的功能.

Adobe AIR中跨域的问题貌似主要是因为安全沙箱(security sandbox)不同导致的, 官方提供了两个相关的解决方案, 一个是使用iframe的sandboxRoot和documentRoot属性, 如:

<iframe
    src="http://www.example.com/local/ui.html"
    sandboxRoot="http://www.example.com/local/"
    documentRoot="app:/sandbox/">
</iframe>

我花了很长很长的时间才理解这两个属性到底指什么, 一方面是因为官方文档说得不够清楚, 我一开始一直以为app:/sandbox/是表示应用沙箱的固定用法, 一方面也是因为相关资料相对较少.

这个方法使得跨域页面中的内容(不包括iframe的src, 但包括iframe加载的内容里的各种东西, 甚至是XMLHttpRequest), 如果其URL在sandboxRoot指定的URL之下, 则并不会请求该URL在互联网上的位置, 而是请求documentRoot所指定的文件目录中的内容. 比如按上面的例子, 如果iframe加载的页面中有一个脚本的URL是http://www.example.com/local/sample.js, 则实际会加载的则是app:/sandbox/sample.js, 这里的documentRoot可以是任意目录.

但是这个方案似乎只能说是加载下文件, 并不能让脚本获取iframe的内容. 另外则有两个bridge(桥), 可以实现脚本的交互. 一个是parentSandboxBridge, 另一个是childSandboxBridge. 这两个bridge都是iframe.contentWindow的属性, 指向一个对象, 这样双方就可以通过这个对象下的属性或者方法进行信息交换了. 但需要补充的是, 不要企图传document这类的对象, 只能传传自己的(非严格界定, 但是大体意思大家应该懂). 官网上也提醒道, 如果要在iframe页面加载过程中让其中的脚本访问parentSandboxBridge, 则要在iframe中的脚本执行前就给iframe绑上parentSandboxBridge. 这时可以使用 “dominitialize” 事件.

这样一来, 跨域交互看起来终于是可行的了. 但是… 我做的是一个采集程序, 对方如果不调用我的bridge, 即使我搭好了又有什么意义呢?

后来才明白前面提到的sandboxRoot和documentRoot的意义. 对于现在多数的网页, 里面一般都会有脚本, 但那天看Google Adwords Keyword Tool的时候差点就伤心了, 从上面往下, 眼看着全是内嵌的脚本… 终于… 发现一个cues.js文件, 可能是GWT相关的信息初始化文件, 其URL是: https://adwords.google.com/cues/cues.js 于是, 将sandboxRoot设置为https://adwords.google.com/cues/, documentRoot设置为app-storage:/, 再把经过更改的cues.js文件放到app-storage:/, 这样一来, 当网页加载完成, 我安排在cues.js里的 “木马” 也就可以执行他的任务了~

如果要替换的JS文件是静态的, 那完全也可以更改后放到app:/目录(只读, 可以用trick写入, 但有兼容问题), 我放到app-storage:/是因为cues.js是动态生成的, 悲催吧…

虽然不能说是一个完美的解决方案, 但至少解决了自己的问题. 不过, 要是真遇上一个页面里没有脚本或者全是内嵌脚本的时候… 就伤了. 有人会问为什么要用那么低级的方式, 不直接发HTTP请求来解决? 老实说我也觉得蛮低级. 但Google Adwords Keyword Tool是用GWT开发的, 里面用来交换数据的格式并非常见的application/x-www-form-urlencoded, 而是text/gwt-rpc, 因为时间紧, 不能保证这是能轻松破解的(同样, 资料极少), 于是退而求其次了.

Adobe AIR写程序目录(app:/)的兼容性问题

最近接了个Adobe AIR的采集项目, 遇到不少问题, 当然也解决了不少问题. 这篇是几篇经验的第一篇.

网上应该可以很容易的搜到一个解决方案, 将路径转换为nativePath:

var file = new air.File("app:/some-file");
file = new air.File(file.nativePath.toString());

经过这样一个转换, 在Windows下似乎就没有问题了, 但是程序到了Mac上还是会报错. 这就比较伤心了…

我想写这个目录主要是因为想存储一些程序设置文件, 还有一些必要的东西. 继续搜索, 发现有人提出使用用户目录来保存. 当然, 当我最后知道答案的时候非常想不通为什么还有那么多人跟我一样笨…

除了app:/这个目录之外, 还有一个目录是app-storage:/, 一开始我以为这两个目录是等价的, 没有意识到差别. 后来才清楚… 所以用后者就可以了… 但是如果你是想更改程序文件, 就没办法了, 除非不考虑兼容, 或者直接把程序文件写在app-storage里. 囧.

浅谈Web程序设计中的面向数据的编程(DOP)

所以说, 趋势就是趋势, 并不在于谁提出来, 该有的东西自然便会有. 我不知道我们这一代人是否算是见证了DOP的诞生, 但至少在这些年, DOP被用得越来越多了.

说到这个, 首先想提两点. 一个是之前众所周知的变成模式, 面向对象编程(OOP). 对与我来说, 面向对象在我的代码中扮演了相当重要的角色. 它让程序内部的交流变得更加清晰, 提升了程序的可读性, 降低了出现bug的概率. 在有些应用上, 到目前还是不能替代的. 另一个是今后Web编程的发展, 我目测会有几个大方向: 1. 基于Canvas或CSS3的Web游戏. 2. 功能复杂的Web应用程序(如在线办公). 3. 以内容呈现与交互为主的Web页面(如SNS). 只所以想要提这两点, 是为之后将要说到的OOP的局限性, 和DOP的应用范畴做铺垫.

那首先说说, 什么是DOP. 老实说, 我并非是从这个名字开始接触这个概念的, 而是从很多Web页面的改变上开始思考这个问题. 不过很自然地, 使用了同一个词语来描述这样同一个概念. 其实其中有一点YY的成分, 但我想也应该八九不离十.

如果说OOP让人们只用关心如何去调用一个功能, 而不用关心功能的实现的话, 那么DOP则只用人们去关心一个模块/对象所管理的数据, 而不用关心这些数据的改变会对其他模块/对象造成什么影响. 当然, 对自己的影响还是要关心的. 其实细想, 在一些高级编程语言中, 对象的属性就是一种简单地DOP模型. 但显然, 它是局限在某一个对象上的. 然而很多时候, 同一个数据在多个对象上是公用的, 这或许就是DOP和传统OOP一个很大的区别, 也是传统OOP的局限.

但说到这里, 大家可能也会有种想法, 认为DOP也是OOP. 我认为这种想法是正确的, DOP的最佳实践应该对象化/模块化, 只是与直接调用其他对象的方法不同, 通过数据来间接达到目的. 这一点来讲, 倒和面向事件的编程(EOP)有些相似, 但事件是瞬时的, 而数据则是可持续的. 或许DOP约等于OOP+EOP+Data?

近年来, 在很多网站中出现了Hash, 很多MVC框架也支持相应的技术, 极大地方便了复杂的无刷新页面的实现, 这应该算是典型的DOP. 不过如果仅仅是这样, 还只能说是Hash上的数据在和整个页面一个对象交互, 这就有点伤感了. 还好看到一些框架, 并非这样的如果, 比如淘宝的MagixJS似乎就不错. 不过很显然, DOP也有其局限性, 毕竟中间穿插了一个环节, 在效率上或许比不过传统的变成手段. 所以像Web游戏的有些部分则是不便于使用的, 但我想做做UI还是完全能够胜任.

按我的理解, 相对于OOP, DOP能进一步减小复杂程序的思维难度, 提高开发效率, 以及降低bug的概率, 应该是未来Web编程某些方面的趋势, 于是我也有自己的DOP框架计划, 只是准备在DOP的基础上, 增加强大的模板功能, 也许未来, 很多AJAX操作, 都只需要一个简单的模板了.

Windows 7 开机以管理员身份启动应用程序

这个也是最近做的代理整合软件过程中遇到的问题, 因为要修改Internet Settings, 可能还有其他一些操作, 需要有管理员权限, 强制程序使用管理员权限可以添加一个manifest文件, 再将其中的requestedExecutionLevel, level改为”requireAdministrator”即可. 其实当时做到这里, 我以为就大功告成了, 但重启之后发现程序没有启动, 搜了搜, 才知道是自动启动的家伙获取不了管理员权限.

最后找到的解决方法有二, 一个是先启动一个不需要管理员权限的程序, 再延时打开需要管理员权限的程序, 不过效果不理想, 也不知道延时多久才能搞定. 并且, 每次开机都会请求权限, 用户用着也不爽. (话说, 关了UAC多好… 这儿整个都不用了.)

后来发现了Task Scheduler(任务计划)这个东西, 顿时觉得捡到了宝, 于是继续查找各种资料, 发现不仅能启动, 还能以管理员权限启动. 不过貌似只能用在Windows Vista/7 上(XP上貌似也有任务计划, 但可能接口不同或者没这么强大吧, 这个我没有细查, 但代码在XP上的确会进入catch), 所以XP上还要优雅降级, 添加普通的开机启动项, 反正XP又没有UAC, 是吧.

我使用的是C#, 同时需要添加COM引用, TaskScheduler 1.1 Type Library, 在system32目录下的taskschd.dll. 代码如下:

var dir = @”SOFTWARE\Microsoft\Windows\CurrentVersion\Run”;
try {
    var scheduler = new TaskSchedulerClass();
    scheduler.Connect(); //连接, 还有一些登录参数可选.
    var task = scheduler.NewTask(0); //官方文档上, 这个参数后面加了注释reserved.
    task.RegistrationInfo.Author = “Pacgen”;
    task.RegistrationInfo.Description = “Start Pacgen with Windows”;
    task.Settings.Enabled = true; //or false, 开关.

    //在启动的时候执行, 一开始只写了Logon, 不过发现开机的时候登录并没有触发.
    task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_BOOT);
    //注销后登录什么的.
    task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
    var action = task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC) as IExecAction;
    //上面的Triggers.Create也会像Actions.Create一样分别返回类型为IBootTrigger, ILogonTrigger的对象(自己as或者强制转换一下).
    //可以做更多设置.

    //这里就是设置为用户能达到的最高权限.
    task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
    action.Path = Application.ExecutablePath; //需要启动的程序路径.
    action.Arguments = “background”; //参数.

    var folder = scheduler.GetFolder(@”\”); //这里是Task的根文件夹, 还可以用folder.CreateFolder来创建自己的目录.

    //注册任务. 这里的TASK_LOGON_INTERACTIVE_TOKEN就是说使用用户当前的登录信息(如果已经登录).
    folder.RegisterTaskDefinition(“PacgenStartup”, task, (int)_TASK_CREATION.TASK_CREATE_OR_UPDATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN);

    //注册成功, 删除注册表内的启动项, 这里的SetRegister是我自己写的, 替换掉即可.
    //SetRegistry(dir, “Pacgen”, null);
}
catch {
    //注册失败, 添加开机启动项.
    //SetRegistry(dir, “Pacgen”, settings.AutoStart ? “\”” + Application.ExecutablePath + “\” background” : null);
}

现在程序就能悄无声息地以管理员权限启动了, 连请求用户都免了. 开心.

最后附上官方说明: http://msdn.microsoft.com/en-us/magazine/cc163350.aspx

通过注册表修改系统PAC代理设置

最近在写一个代理工具的整合, 不过不便发到博客上来, 就说说里面单纯的技术问题, 今天先写这一篇.

修改代理的办法貌似不少, 常用的貌似就注册表和Win API – InternetSetOption, 一开始我当然是想从相对正规的渠道, API来搞定, 花了相当大的力气, 才搜到相关内容, 并且修改成我需要的样子. 不过代码被我删了… 所以… 就不能分享了, 这里直接说说通过注册表改PAC设置的问题.

首先, 为什么我最后要用注册表而不是Win API? 原因是我没找到Win API修改Dial-up和VPN的PAC设置的方法, 所以也是无奈之举. 毕竟很多用户, 比如我自己, 就是通过Dial-up或者VPN联网的. 那, 下面开始主体内容. (我使用的是C#)

1. 注册表位置

CurrentUser, Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections

2. 值

值的名称就是连接的名称, 值是字节数据, 所以需要简单地分析下结构. (后来有个学长提醒说, C++什么的里面处理这种东西特别方便, 一个结构体搞定, 想想貌似是那个道理)

1) 最前面4字节应该是版本号, Win 7下是0x00000046, XP下貌似是3D, 记不清了…
2) 紧接着的4字节是修改次数, 每次修改都应该+1.
3) 接下来的4字节是flags, 其实貌似只有第一个字节用到了, 后面三个字节始终都是0, 这里开启PAC, 把这个值设为0x00000005就可以了, 其他开关什么的, 需要的话自己试试吧.
4) 接下来的4字节是保存了一个或多个(http, socks, ftp等)代理服务器及端口的字符串的长度,  当然, 再接下来的这个长度的字节就是这个字符串的数据了.
5) 接下来的4字节是保存了代理的例外服务器的字符串的长度,  再接下来的这个长度的字节也自然就是这个字符串的数据了.
6) 然后我们需要的东西终于来了, PAC文件的字符串长度, 4字节, 和PAC文件的字符串.
7) 因为我只需要在计数器(第5~8字节)上 +1, 并修改PAC文件即可, 所以后面的数据直接拷贝.

C#代码片段如下, 相关变量请自助:

var data = key.GetValue(name) as byte[];
var newData = new List<byte>();

var pos = 0;

//skip head, 4 bytes
newData.AddRange(data.Skip(pos).Take(4));
pos += 4;

//modify count
newData.AddRange(BitConverter.GetBytes(BitConverter.ToInt32(data, pos) + 1));
pos += 4;

//config flags
newData.AddRange(BitConverter.GetBytes(configFlags));
pos += 4;

//skip proxy setting
var skipCount = BitConverter.ToInt32(data, pos) + 4;
newData.AddRange(data.Skip(pos).Take(skipCount));
pos += skipCount;

//skip passby setting
skipCount = BitConverter.ToInt32(data, pos) + 4;
newData.AddRange(data.Skip(pos).Take(skipCount));
pos += skipCount;

//now, pac file
skipCount = BitConverter.ToInt32(data, pos) + 4;

var pathByteList = new List<byte>();
foreach (var chr in path) {
    var chrBytes = BitConverter.GetBytes(chr);
    chrBytes = chrBytes.Take(chrBytes.Length – 1).ToArray();
    pathByteList.AddRange(chrBytes);
}
newData.AddRange(BitConverter.GetBytes(pathByteList.Count));
newData.AddRange(pathByteList);
pos += skipCount;

//the rest
newData.AddRange(data.Skip(pos).ToArray());

var chrs = new char[newData.Count];
for (var i = 0; i < newData.Count; i++)
  chrs[i] = (char)newData[i];

//set the reg value
key.SetValue(name, newData.ToArray());