.net 来自 Windows 批处理脚本的文件/文件夹选择器对话框
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15885132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
File / folder chooser dialog from a Windows batch script
提问by rojo
Typically, asking the user to supply a file name to a batch script is a messy affair, requiring no misspellings, quotes around paths with spaces, and so forth. Unfortunately, users aren't well-known for accuracy. In situations where input file location is not known until runtime, using a GUI for file selection input reduces the likelihood of user error.
通常,要求用户为批处理脚本提供文件名是一件很麻烦的事情,不需要拼写错误、在路径周围加上空格的引号等等。不幸的是,用户的准确性并不出名。在输入文件位置直到运行时才知道的情况下,使用 GUI 进行文件选择输入可以降低用户出错的可能性。
Is there a way to invoke a File... Openstyle gui file chooser or folder chooser from a Windows batch script?
有没有办法File... Open从 Windows 批处理脚本调用样式 gui 文件选择器或文件夹选择器?
If the script user has PowerShell or .NET installed, it is possible. See the answer below.
如果脚本用户安装了 PowerShell 或 .NET,则有可能。请参阅下面的答案。
I'm also interested to see what other solutions anyone else can offer.
我也很想知道其他人可以提供哪些其他解决方案。
回答by rojo
File Browser
文件浏览器
Update 2016.3.20:
2016.3.20 更新:
Since PowerShell is a native component of pretty much all modern Windows installations nowadays, I'm declaring the C# fallback as no longer necessary. If you still need it for Vista or XP compatibility, I moved it to a new answer. Starting with this edit, I'm rewriting the script as a Batch + PowerShell hybrid and incorporating the ability to perform multi-select. It's profoundly easier to read and to tweak as needed.
由于 PowerShell 是当今几乎所有现代 Windows 安装的本机组件,因此我宣布不再需要 C# 回退。如果您仍然需要它以兼容 Vista 或 XP,我将其移至新答案。从这次编辑开始,我将脚本重写为 Batch + PowerShell 混合,并结合执行多选的能力。它更易于阅读和根据需要进行调整。
<# : chooser.bat
:: launches a File... Open sort of file chooser and outputs choice(s) to the console
:: https://stackoverflow.com/a/15885133/1683264
@echo off
setlocal
for /f "delims=" %%I in ('powershell -noprofile "iex (${%~f0} | out-string)"') do (
echo You chose %%~I
)
goto :EOF
: end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $true
[void]$f.ShowDialog()
if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
This results in a file chooser dialog.
这会导致一个文件选择器对话框。


The result of a selection outputs You chose C:\Users\me\Desktop\tmp.txtto the console. If you want to force single file selection, just change the $f.Multiselectproperty to $false.
选择的结果输出You chose C:\Users\me\Desktop\tmp.txt到控制台。如果要强制选择单个文件,只需将该$f.Multiselect属性更改为$false.
(PowerShell command mercilessly leeched from the Just Tinkering Blog.) See the OpenFileDialog Classdocumentation for other properties you can set, such as Titleand InitialDirectory.
(PowerShell 命令无情地从Just Tinkering 博客中窃取。)请参阅OpenFileDialog 类文档以了解您可以设置的其他属性,例如Title和InitialDirectory。
Folder Browser
文件夹浏览器
Update 2015.08.10:
2015.08.10 更新:
Since there is already a COM method for invoking a folder chooser, it's pretty easy to build a PowerShell one-liner that can open the folder chooser and output the path.
由于已经有调用文件夹选择器的 COM 方法,因此构建一个可以打开文件夹选择器并输出路径的 PowerShell 单行程序非常容易。
:: fchooser.bat
:: launches a folder chooser and outputs choice to the console
:: https://stackoverflow.com/a/15885133/1683264
@echo off
setlocal
set "psCommand="(new-object -COM 'Shell.Application')^
.BrowseForFolder(0,'Please choose a folder.',0,0).self.path""
for /f "usebackq delims=" %%I in (`powershell %psCommand%`) do set "folder=%%I"
setlocal enabledelayedexpansion
echo You chose !folder!
endlocal
In the BrowseForFolder()method, the fourth argument specifies the root of the hierarchy. See ShellSpecialFolderConstantsfor a list of valid values.
在该BrowseForFolder()方法中,第四个参数指定层次结构的根。有关有效值的列表,请参阅ShellSpecialFolderConstants。
This results in a folder chooser dialog.
这会导致一个文件夹选择器对话框。


The result of a selection outputs You chose C:\Users\me\Desktopto the console.
选择的结果输出You chose C:\Users\me\Desktop到控制台。
See the FolderBrowserDialog classdocumentation for other properties you can set, such as RootFolder. My original .NET System.Windows.FormsPowerShell and C# solutions can be found in revision 4of this answer if needed, but this COM method is much easier to read and maintain.
请参阅FolderBrowserDialog 类文档,了解您可以设置的其他属性,例如RootFolder. 如果需要,System.Windows.Forms可以在此答案的修订版 4中找到我最初的 .NET PowerShell 和 C# 解决方案,但此 COM 方法更易于阅读和维护。
回答by Antoni Gual Via
This should work from XP upwards and does'nt require an hibrid file, it just runs mshta with a long command line:
这应该从 XP 开始工作并且不需要 hibrid 文件,它只是使用长命令行运行 mshta:
@echo off
set dialog="about:<input type=file id=FILE><script>FILE.click();new ActiveXObject
set dialog=%dialog%('Scripting.FileSystemObject').GetStandardStream(1).WriteLine(FILE.value);
set dialog=%dialog%close();resizeTo(0,0);</script>"
for /f "tokens=* delims=" %%p in ('mshta.exe %dialog%') do set "file=%%p"
echo selected file is : "%file%"
pause
回答by rojo
Windows Script Host
Windows 脚本宿主
File Selection
文件选择
Windows XP had a mysterious UserAccounts.CommonDialogWSH object which allowed VBScriptand JScript to launch the file selection prompt. Apparently, that was deemed a security riskand removed in Vista.
Windows XP 有一个神秘的UserAccounts.CommonDialogWSH 对象,它允许 VBScript和 JScript 启动文件选择提示。显然,这被视为安全风险并在 Vista 中删除。
Folder Selection
文件夹选择
However, the WSH Shell.Application object BrowseForFoldermethod will still allow the creation of a folder selection dialog. Here's a hybrid batch + JScript example. Save it with a .batextension.
但是,WSH Shell.Application 对象BrowseForFolder方法仍将允许创建文件夹选择对话框。这是一个混合批处理 + JScript 示例。用.bat扩展名保存它。
@if (@a==@b) @end /*
:: fchooser2.bat
:: batch portion
@echo off
setlocal
for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0"') do (
echo You chose %%I
)
goto :EOF
:: JScript portion */
var shl = new ActiveXObject("Shell.Application");
var folder = shl.BrowseForFolder(0, "Please choose a folder.", 0, 0x00);
WSH.Echo(folder ? folder.self.path : '');


In the BrowseForFolder()method, the fourth argument specifies the root of the hierarchy. See ShellSpecialFolderConstantsfor a list of valid values.
在该BrowseForFolder()方法中,第四个参数指定层次结构的根。有关有效值的列表,请参阅ShellSpecialFolderConstants。
回答by Aacini
A file / folder selection may be done with pure Batch, as shown below. Of course, the feel and look is not as pleasant as a GUI, but it works very well and in my opinion it is easier to use than the GUI version. The selection method is based on CHOICE command, so it would require to download it in the Windows versions that don't include it and slightly modify its parameters. Of course, the code may be easily modified in order to use SET /P instead of CHOICE, but this change would eliminate the very simple and fast selection method that only requires one keypress to navigate and select.
文件/文件夹选择可以使用纯批处理完成,如下所示。当然,感觉和外观不如 GUI 愉快,但它工作得很好,在我看来它比 GUI 版本更容易使用。选择方法基于CHOICE命令,因此需要在不包含它的Windows版本中下载它并稍微修改其参数。当然,为了使用 SET /P 而不是 CHOICE 可以很容易地修改代码,但是这种改变将消除只需要一个按键来导航和选择的非常简单和快速的选择方法。
@echo off
setlocal
rem Select a file or folder browsing a directory tree
rem Antonio Perez Ayala
rem Usage examples of SelectFileOrFolder subroutine:
call :SelectFileOrFolder file=
echo/
echo Selected file from *.* = "%file%"
pause
call :SelectFileOrFolder file=*.bat
echo/
echo Selected Batch file = "%file%"
pause
call :SelectFileOrFolder folder=/F
echo/
echo Selected folder = "%folder%"
pause
goto :EOF
:SelectFileOrFolder resultVar [ "list of wildcards" | /F ]
setlocal EnableDelayedExpansion
rem Process parameters
set "files=*.*"
if "%~2" neq "" (
if /I "%~2" equ "/F" (set "files=") else set "files=%~2"
)
rem Set the number of lines per page, max 34
set "pageSize=30"
set "char=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
rem Load current directory contents
set "name[1]=<DIR> .."
:ProcessThisDir
set "numNames=1"
for /D %%a in (*) do (
set /A numNames+=1
set "name[!numNames!]=<DIR> %%a"
)
for %%a in (%files%) do (
set /A numNames+=1
set "name[!numNames!]= %%a"
)
set /A numPages=(numNames-1)/pageSize+1
rem Show directory contents, one page at a time
set start=1
:ShowPage
set /A page=(start-1)/pageSize+1, end=start+pageSize-1
if %end% gtr %numNames% set end=%numNames%
cls
echo Page %page%/%numPages% of %CD%
echo/
if %start% equ 1 (set base=0) else set "base=1"
set /A lastOpt=pageSize+base, j=base
for /L %%i in (%start%,1,%end%) do (
for %%j in (!j!) do echo !char:~%%j,1! - !name[%%i]!
set /A j+=1
)
echo/
rem Assemble the get option message
if %start% equ 1 (set "mssg=: ") else (set "mssg= (0=Previous page")
if %end% lss %numNames% (
if "%mssg%" equ ": " (set "mssg= (") else set "mssg=%mssg%, "
set "mssg=!mssg!Z=Next page"
)
if "%mssg%" neq ": " set "mssg=%mssg%): "
:GetOption
choice /C "%char%" /N /M "Select desired item%mssg%"
if %errorlevel% equ 1 (
rem "0": Previous page or Parent directory
if %start% gtr 1 (
set /A start-=pageSize
goto ShowPage
) else (
cd ..
goto ProcessThisDir
)
)
if %errorlevel% equ 36 (
rem "Z": Next page, if any
if %end% lss %numNames% (
set /A start+=pageSize
goto ShowPage
) else (
goto GetOption
)
)
if %errorlevel% gtr %lastOpt% goto GetOption
set /A option=start+%errorlevel%-1-base
if %option% gtr %numNames% goto GetOption
if defined files (
if "!name[%option%]:~0,5!" neq "<DIR>" goto endSelect
) else (
choice /C OS /M "Open or Select '!name[%option%]:~7!' folder"
if errorlevel 2 goto endSelect
)
cd "!name[%option%]:~7!"
goto ProcessThisDir
:endSelect
rem Return selected file/folder
for %%a in ("!name[%option%]:~7!") do set "result=%%~Fa"
endlocal & set "%~1=%result%
exit /B
回答by Esperento57
Other solution with direct run PowerShell command in Batch
在批处理中直接运行 PowerShell 命令的其他解决方案
rem preparation command
set pwshcmd=powershell -noprofile -command "&{[System.Reflection.Assembly]::LoadWithPartialName('System.windows.forms') | Out-Null;$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog; $OpenFileDialog.ShowDialog()|out-null; $OpenFileDialog.FileName}"
rem exec commands powershell and get result in FileName variable
for /f "delims=" %%I in ('%pwshcmd%') do set "FileName=%%I"
echo %FileName%
回答by rojo
Batch + PowerShell + C# polyglot solution
Batch + PowerShell + C# 多语言解决方案
This is the same solution as the Batch + PowerShell hybrid, but with the C# fallback stuff re-added for XP and Vista compatibility. Multiple file selection has been added at xNightmare67x's request.
这与Batch + PowerShell 混合解决方案相同,但为 XP 和 Vista 兼容性重新添加了 C# 后备内容。应 xNightmare67x 的要求添加了多个文件选择。
<# : chooser_XP_Vista.bat
:: // launches a File... Open sort of file chooser and outputs choice(s) to the console
:: // https://stackoverflow.com/a/36156326/1683264
@echo off
setlocal enabledelayedexpansion
rem // Does powershell.exe exist within %PATH%?
for %%I in ("powershell.exe") do if "%%~$PATH:I" neq "" (
set chooser=powershell -noprofile "iex (${%~f0} | out-string)"
) else (
rem // If not, compose and link C# application to open file browser dialog
set "chooser=%temp%\chooser.exe"
>"%temp%\c.cs" (
echo using System;
echo using System.Windows.Forms;
echo class dummy {
echo public static void Main^(^) {
echo OpenFileDialog f = new OpenFileDialog^(^);
echo f.InitialDirectory = Environment.CurrentDirectory;
echo f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
echo f.ShowHelp = true;
echo f.Multiselect = true;
echo f.ShowDialog^(^);
echo foreach ^(String filename in f.FileNames^) {
echo Console.WriteLine^(filename^);
echo }
echo }
echo }
)
for /f "delims=" %%I in ('dir /b /s "%windir%\microsoft.net\*csc.exe"') do (
if not exist "!chooser!" "%%I" /nologo /out:"!chooser!" "%temp%\c.cs" 2>NUL
)
del "%temp%\c.cs"
if not exist "!chooser!" (
echo Error: Please install .NET 2.0 or newer, or install PowerShell.
goto :EOF
)
)
rem // Do something with the chosen file(s)
for /f "delims=" %%I in ('%chooser%') do (
echo You chose %%~I
)
rem // comment this out to keep chooser.exe in %temp% for faster subsequent runs
del "%temp%\chooser.exe" >NUL 2>NUL
goto :EOF
:: // end Batch portion / begin PowerShell hybrid chimera #>
Add-Type -AssemblyName System.Windows.Forms
$f = new-object Windows.Forms.OpenFileDialog
$f.InitialDirectory = pwd
$f.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
$f.ShowHelp = $true
$f.Multiselect = $true
[void]$f.ShowDialog()
if ($f.Multiselect) { $f.FileNames } else { $f.FileName }
For a folder chooser for XP or Vista, use either the WSH solutionor npocmaka's HTA solution.
对于 XP 或 Vista 的文件夹选择器,请使用WSH 解决方案或npocmaka 的 HTA 解决方案。
回答by npocmaka
Two more ways.
还有两种方式。
1.Using a hybrid .bat/hta (must be saved as a bat) script .It can use vbscript or javascript but the example is with javascrtipt.Does not create temp files.Selecting folder is not so easy and will require an external javascript libraries , but selecting file is easy
1.使用混合 .bat/hta(必须另存为bat)脚本。它可以使用 vbscript 或 javascript,但示例是使用 javascrtipt。不创建临时文件。选择文件夹不是那么容易,需要外部 javascript 库,但选择文件很容易
<!-- : starting html comment
:: FileSelector.bat
@echo off
for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do (
set "file=%%~fp"
)
echo/
if not "%file%" == "" (
echo selected file is : %file%
)
echo/
exit /b
-->
<Title>== FILE SELECTOR==</Title>
<body>
<script language='javascript'>
function pipeFile() {
var file=document.getElementById('file').value;
var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1);
close(fso.Write(file));
}
</script>
<input type='file' name='file' size='30'>
</input><hr><button onclick='pipeFile()'>Submit</button>
</body>
1.1 - without submit form proposed by rojo (see comments):
1.1 - 没有 rojo 提出的提交表格(见评论):
<!-- : starting html comment
:: FileSelector.bat
@echo off
for /f "tokens=* delims=" %%p in ('mshta.exe "%~f0"') do (
set "file=%%~fp"
)
echo/
if not "%file%" == "" (
echo selected file is : "%file%"
)
echo/
exit /b
-->
<Title>== FILE SELECTOR==</Title>
<body>
<script language='javascript'>
function pipeFile() {
var file=document.getElementById('file').value;
var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1);
close(fso.Write(file));
}
</script>
<input id='file' type='file' name='file' size='30' onchange='pipeFile()' >
</input>
<hr>
<button onclick='pipeFile()'>Submit</button>
<script>document.getElementById('file').click();</script>
</body>
2.As you already using powershell/net you can create selfcompiled jscript.net hybrid.It will not require temp cs file for compilation and will directly use the built-in jscrript.net compiler.There's no need of powershell too and the code is far more readable:
2.由于你已经在使用powershell/net,你可以创建自编译的jscript.net hybrid。它不需要临时cs文件进行编译,将直接使用内置的jscrript.net编译器。也不需要powershell,代码是更具可读性:
@if (@X)==(@Y) @end /* JScript comment
@echo off
:: FolderSelectorJS.bat
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
if not exist "%~n0.exe" (
"%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)
for /f "tokens=* delims=" %%p in ('"%~n0.exe"') do (
set "folder=%%p"
)
if not "%folder%" == "" (
echo selected folder is %folder%
)
endlocal & exit /b %errorlevel%
*/
import System;
import System.Windows.Forms;
var f=new FolderBrowserDialog();
f.SelectedPath=System.Environment.CurrentDirectory;
f.Description="Please choose a folder.";
f.ShowNewFolderButton=true;
if( f.ShowDialog() == DialogResult.OK ){
Console.Write(f.SelectedPath);
}
回答by Andry
I has been wrote my own portable solution: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/src/_gui/wxFileDialog/
我已经编写了自己的便携式解决方案:https: //sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/src/_gui/wxFileDialog/
You can download executable from here: https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/bin/wxFileDialog.exe
您可以从这里下载可执行文件:https: //sourceforge.net/p/contools/contools/HEAD/tree/trunk/Utilities/bin/wxFileDialog.exe
The utility has dependency on wxWidgets 3.1.x, so you can actually build it for other operating systems.
该实用程序依赖于 wxWidgets 3.1.x,因此您实际上可以为其他操作系统构建它。
回答by Garric
I will leave an 'echo' even to verify that multiple choice works in this code
我什至会留下一个“回声”来验证此代码中的多项选择是否有效
echo off
set cmd=Add-Type -AssemblyName System.Windows.Forms;$f=new-object Windows.Forms.OpenFileDialog;$f.InitialDirectory= [environment]::GetFolderPath('Desktop');$f.Filter='Text Files(*.txt)^|*.txt^|All Files(*.*)^|*.*';$f.Multiselect=$true;[void]$f.ShowDialog();if($f.Multiselect) {$f.FileNames}else{$f.FileName}
set pwshcmd=powershell -noprofile -command "&{%cmd%}"
for /f "tokens=* delims=" %%I in ('%pwshcmd%') do call :sum "%%I" ret
echo =========
echo --%ret%--
pause
:sum [mud] [ret]
echo "%~1"
set FileName=%FileName% "%~1"
set ret=%FileName%
exit /B

