VBS文件拖拽的个数限制(无法执行 – 参数列表过长)

标签: , , ,

有点VBS脚本经验的人应该都使用过WScript.Arguments来实现文件拖拽功能:

For Each argv In WScript.Arguments
    DoSomething(argv)
Next

这样可以把文件拖拽到VBS脚本上来进行相应的处理,但是不止一个人问过我,为什么拖拽的文件比较多时会出现“无法执行 – 参数列表过长”的错误?

其实这个错误跟VBS没有太大的关系,要怪就怪ShellExecuteEx函数吧。

文件拖拽(Drap And Drop)是由Windows Shell来实现的,打开注册表编辑器,定位到HKEY_CLASSES_ROOT\VBSFile,可以看到ShellEx项下有一个DropHandler,DropHandler就是用来处理文件拖拽的。

DropHandler的值为{60254CA5-953B-11CF-8C96-00AA00B8708C},我们来到HKEY_CLASSES_ROOT\CLSID\{60254CA5-953B-11CF-8C96-00AA00B8708C},其下面有一个InProcServer32项目,默认值为C:\WINDOWS\system32\wshext.dll,也就是说,VBS的文件拖拽是由wshext.dll来负责处理的。

用OllyDbg调试explorer.exe可以知道,wshext.dll会调用DragQueryFileA和DragFinish遍历每一个拖拽的文件的路径,调用PathQuoteSpacesA为含有空格的路径加上双引号,并把它们拼接起来,以空格分隔,作为SHELLEXECUTEINFOA结构的lpParameters字段,然后调用ShellExecuteExA

ShellExecuteExA

到这里为止,一切都是那么的顺利,遗憾的是,ShellExecuteEx函数的命令行参数是有长度限制的,根据《What is the command line length limit?》,这个长度是INTERNET_MAX_URL_LENGTH。INTERNET_MAX_URL_LENGTH在wininet.h中定义,值为2084。但是这2084并不是指lpParameters字段的长度限制是2084,而是把命令行展开后的长度限制是2084。

什么叫做把命令行展开后的长度?我们还是以VBS为例,HKEY_CLASSES_ROOT\VBSFile\Shell\Open\Command的默认值为%SystemRoot%\System32\WScript.exe "%1" %*,假设我们把D:\1.txt和D:\2.txt拖拽到D:\1.vbs上面,那么展开后的命令行就是:

"C:\Windows\System32\WScript.exe" "D:\1.vbs" D:\1.txt D:\2.txt

注意D:\2.txt后面还有一个空格。由此可以知道,拖拽到VBS上的文件越多,命令行也就越长,当长度超过ShellExecuteEx函数的限制时,ShellExecuteEx函数就会执行失败,而wshext.dll检测到失败就会弹出“无法执行 – 参数列表过长”的对话框。

上面只是理论分析,而我在实际测试的时候发现,展开后的命令行长度最多只能是2082个字符,算上最后的NUL也就2083个,还有一个字符跑到哪里去了?也许慢慢跟踪ShellExecuteEx函数可以找到原因,但我实在没有这样的闲情逸致。

其实具体值是多少并不重要,重要的是知道有那么个长度限制,也就是拖拽的文件个数是有限制的(具体多少个就要看你的文件路径的长短了)。如果你的VBS想要处理大量的文件,那么拖拽就不是一个很好的解决方案,这时可以考虑Shell.Application对象的BrowseForFolder方法。

最后再说一点,由于wshext.dll调用的是ShellExecuteExA函数,所以只能处理ANSI编码的字符,如果拖拽的文件路径中含有ANSI编码中没有的Unicode字符,那么VBS会无法得到正确的路径。例如,GBK编码中没有版权符号©,拖拽一个名为©.txt的文件到VBS上是不能得到©.txt的。

随机文章:

  1. VB6拾遗:函数指针与CallWindowProc函数
  2. 用C语言屏蔽键盘和鼠标
  3. 批处理技术内幕:随机数%RANDOM%
  4. 通过SSH访问iPad
  5. VBS创建系统服务

一条评论 发表在“VBS文件拖拽的个数限制(无法执行 – 参数列表过长)”上

  1. 枫谷剑仙说道:

    今天研究了下,发现可以用bat来传入unicode字符的参数。vbs保存成ANSI还是unicode都可以,bat里获取拖放的文件名,然后cscript name.vbs %文件名%或者不想要命令行办用wcsript也行。

    这是图:http://ww4.sinaimg.cn/large/6c84b2d6gw1eo9a085oekj20tw0ft77b.jpg

    另外bat也有写unicode字符的方法:http://bbs.bathome.net/viewthread.php?tid=12824

留下回复