时间:2021-07-01 10:21:17 帮助过:22人阅读
view sourceprint?1 var asocCommand = wshShell.RegRead("HKEY_CLASSES_ROOT\\htafile\\Shell\\Open\\Command\\").replace("%1", instPath + "\\" + appExec).replace("%*", '"%1"');
外壳关联的问题刚解决,紧接着就是命令行参数的解析问题:
最初我打算用WSH任务文件作为这个小工具的载体,但马上我发现默认情况下WSH文件缺乏UI支持——VBScript中的InputBox和HTML中的prompt在用JScript编写的WSH任务文件中是不存在的,如果坚持使用WSH就只能通过Windows控制台来获得用户输入。而如果通过控制台来获得用户输入的话,这个工具的标准使用流程就变成了“鼠标点击文件图标——快捷选单——Minimize——键盘输入一个字符”,在一系列鼠标操作(当然,用键盘操作也是可能的)之后突然改用键盘,这好像不太对劲。
因此,我选择了使用HTA这种可以提供丰富界面的文件类型来作为实现这个工具的途径。
而选择了HTA,也就意味着同样要失去WSH“不外传”的一些特性,例如缺少了用于解析命令行参数的“Arguments”对象。
而在我的构思中,应该是可以通过-level参数指定JSMin的代码缩减等级、通过-silent参数来关闭提示信息的。如果不能从命令行中解读出这些参数,这些功能就没有办法实现。
因此我编写了两个函数:
代码如下:输出“Unicode Big Endian”编码的文件,Charset属性应该设置为“unicodeFEFF”——我们很容易发现,“FEFF”正好符合BOM字符在文件中的字节顺序;我们同样可以发现为Charset属性设置“unicodeFXFX”时其实就相当于“用FXFX这样的字节顺序来进行编码”的意思,是享受特殊对待的,并不完全符合注册表中所写的样子。
//========//========
// parse command-line info
// 在HTA环境中从命令行中解读出被执行的HTA的实际路径和附加的参数
// 此部分代码由NanaLich原创,您可以不经书面许可在任何情况下直接使用。您不应擅自声称您或您的所属机构创作了这些代码,也不应该擅自以NanaLich的名义发布修改版本。
//========//========
function namedOrNot(args) {
var named = {}, not = [], c;
for(var i = 0; i < args.length; i++) {
c = args[i];
switch(c.charAt(0)) {
case "-":
case "/":
c = c.substring(1);
if(c.indexOf("=") > 0) {
c = c.split("=");
named[c.shift()] = c.join("=");
} else if(c.indexOf(":") > 0) {
c = c.split(":");
named[c.shift()] = c.join(":");
} else {
// 不能确定一个命名参数是不是也接受附加参数,这是个未解难题
//i++;
named[c] = args[i + 1];
}
break;
default:
not.push(c);
break;
}
}
args.named = named;
args.unnamed = not;
}
function parseArgs(str) {
var a = [], q = false, c = "", $ = "";
function mit() {
if(c)
a.push(c);
}
for(var i = 0; i < str.length; i++) {
$ = str.charAt(i);
if($ == '"') {
q = !q;
} else {
if($ == " " && !q) {
mit();
c = "";
} else {
c += $;
}
}
}
mit();
namedOrNot(a);
return a;
}
//========//========
这样,只要将HTA:Application对象的commandLine属性传入这个函数,就可以获得解析之后的命名和不命名参数了。
解决了命令行参数的问题之后,接下来就是文件的编码问题。
在我们实际的Web开发过程中,我们可能因为各种各样的原因使用“非Unicode区域编码”和“Unicode(UTF-16)编码”以外的其它编码格式,例如“Unicode(UTF-16) Big Endian”和“UTF-8”这两种编码方式。
如果我们通过既有的文本编辑工具来复制、粘贴代码,我们只要在保存文件的时候选择编码类型就可以了;但现在我们正在设计一种免去“打开——复制——粘贴——复制——粘贴——保存”这样繁琐的操作步骤的工具,我们就需要在这个工具中设计自动适应编码类型的功能。
很遗憾,在我的测试中FSO和ADODB.Stream都不具备自动识别文本编码的能力,必须另想其它办法——幸好,在HTA中VBScript也是默认支持的,VBScript虽然没有直接对字节组进行操作的功能,但在VBScript中把字节组当作字符串来进行操作仍然可以在一定程度上满足我们的要求。
因此,我编写了下面这个函数:
代码如下:输出的文件都是采用“Unicode (Little Endian)”编码的,这显然和文档所表达的意思不符。
Function vbDetectFileEncoding(fn)
Dim Stream, B3
Set Stream = CreateObject("ADODB.Stream")
Stream.Type = 1
Call Stream.Open()
Call Stream.LoadFromFile(fn)
B3 = CStr(Stream.Read(3))
Call Stream.Close()
Set Stream = Nothing
Dim L1
L1 = Left(B3, 1)
If (L1 = ChrW(&hFEFF)) Then
vbDetectFileEncoding = "unicode"
Exit Function
Elseif (L1 = ChrW(&hFFFE)) Then
vbDetectFileEncoding = "unicodeFEFF"
Exit Function
Elseif B3 = (ChrB(&hEF) & ChrB(&hBB) & ChrB(&hBF)) Then
vbDetectFileEncoding = "utf-8"
Exit Function
End If
vbDetectFileEncoding = defEncoding
End Function
这个函数根据一个文本文件中可能存在的BOM来推断文件所采用的编码方式。
这里需要注意的一点是:
ADODB.Stream的相关文档中写道Allowed values are typical strings passed over the interface as Internet character set names (for example, "iso-8859-1", "Windows-1252", and so on). For a list of the character set names that are known by a system, see the subkeys of HKEY_CLASSES_ROOT\MIME\Database\Charset in the Windows Registry.,而注册表中和“Unicode Big Endian”(Encoding 1201)相对应的项目是“unicodeFFFE”,从这些信息上推断在ADO Stream中使用“Unicode Big Endian”编码时应该指定Charset属性为“unicodeFFFE”;
这里有一个容易混淆的实施是:BOM字符的Unicode编号是U+FEFF,而“一般的”“Unicode编码”实为“Unicode Little Endian”——BOM字符会被写成“FF FE”这样两个字节,而在“Unicode Big Endian”中才会被写成“FE FF”。
那么到这里,问题就出现了——如果和“unicodeFFFE”正相反的是“unicodeFEFF”的话,我们可以理解这里面的“FEFF”是BOM字符的Unicode编号;但与此同时代表“Unicode Big Endian”的“unicodeFFFE”中的“FFFE”具有什么含义呢?显然这不可能是“一个Unicode编号为U+FFFE的字符”的意思。
而在实践中,我发现无论为Charset属性设置“unicode”还是“unicodeFFFE”,
当我再进一步作出尝试的时候,却发现如果希望
到现在为止,小工具已经可以读写“Unicode”、“Unicode Big Endian”、“UTF-8”和“非Unicode区域编码”这些编码方式的文件了,只是我仍然没想明白为什么文档和注册表中会写着有误或者完全无用的信息……
上面这些问题被一一解决以后,好像就再也没有什么值得研究的问题了。
但是在使用JSMin的过程中,却发现了另一个问题:
有些人(比方说我)主要使用Windows工作,Windows中文本文件的默认行分隔符号是CR LF对。
而JSMin会把所有的CR都替换成LF,这样的话CR LF对就变成了两个LF的“LF LF”对了。
按照JSMin本来的设计,控制字符LF通常都是要被缩减掉的,因此两个连续的LF也不是什么问题;但jsmin.js有一个新增的功能是保留具有一定特征(/*! ... */)的“重要注释”,而如果在这种“重要注释”中存在CR LF对的话,最终会变成无法去除的两个LF控制字符,这可不好。
为了解决这个问题,我对jsmin.js稍微作了一些修改……不过因为JSMin有点超出我的理解能力,我也只能把CR变成空格,不过好在这样做在Windows中也有一些好处(在大部分的Windows版本中,单独的LF控制字符在“记事本”程序中几乎无法观察到,用它分隔的两行文本看起来也像是粘在了一起),就这么凑合用吧……这部分修改我就不单独列出来了。
本文中所提到的一切代码,都可以在这个压缩包中找到。
压缩包内包含三个文件:install.js是注册文件关联的“安装”脚本、jsmin.hta是使用“Minimize”指令时实际运行的“应用程序”、jsmin.js则是我修改以后的jsmin.js。
将三个文件解压至同一个文件夹之后,双击install.js即可安装本工具。如果您重新安装了操作系统,您可能会发现工具仍然遗留在您的个人文件夹中;只要您双击个人文件夹中遗留下来的install.js,您就又可以使用本工具了。
注意:自Windows XP起,较新版本的Windows会为从网络上下载而来的文件设置一个标志,这个标志可能会让HTA文件不能正常执行,如果你在使用的时候碰到了这样的问题,请点击文件属性对话框中的“解除锁定”按钮以去除这个标志。
更新:修改了install.js。现在在64位Windows 7上也可以正确安装了;安装以后也不用手动“解除锁定”了。
秘密在这里:
代码如下:
var appsPath = wshShell.ExpandEnvironmentStrings(wshShell.RegRead(regUSF + "Personal")) + "\\Scriptlet";
try{
fso.OpenTextFile(instPath + "\\" + appExec + ":Zone.Identifier", 1).Close();
fso.OpenTextFile(instPath + "\\" + appExec + ":Zone.Identifier", 2).Close();
}catch(ex){ }
文件打包下载 文件附一个改名的jse.方便经常开发js的朋友,以免混淆。
因为好多朋友是用的win2003开发,.js文件使用普通文本打开的,不可能以后用js都让运行吧,直接改成install.jse即可运行了,呵呵。
感谢作者发布这么好的东西。作者的blog地址
http://www.cnblogs.com/NanaLich