用 RunDLL32.exe 命令实现 xdg-open

xdg-open 是 Linux 下的一个命令,可以调用对应的程序来打开目标文件,等同于在文件管理器中双击打开该文件。Ubuntu 用户可以通过执行 sudo apt install xdg-utils -y 来安装 xdg-open 命令,这个命令的用法也很简单:

xdg-open { file | URL }

遗憾的是, Windows 系统并没用提供类似的命令,不过我们可以通过 Windows 自带的 rundll32.exe 命令来实现一个类似功能的脚本。

RunDLL32.exe 用来加载指定的 DLL 文件并调用指定的导出函数。用法如下:

rundll32 <dll name>,<entry point> <optional arguments>

其中:

能接受 rundll32.exe 调用的 API 函数签名应满足如下形式:

C++// __stdcall
void CALLBACK EntryPointW(
    HWND hWnd, 
    HINSTANCE hInst, 
    LPWSTR lpszCmdLine, 
    int nCmdShow
);

另外,函数签名不一致的 API 也有被调用成功的可能,但是这些 API 并不能正确地接收参数。以 ShellAboutA 为例,其函数原型为:

C++INT ShellAboutA(
  [in, optional] HWND   hWnd,
  [in]           LPCSTR szApp,
  [in, optional] LPCSTR szOtherStuff,
  [in, optional] HICON  hIcon
);

当执行 rundll32 shell32.dll,ShellAboutA Other Stuff 命令后,发现关于对话框的标题是 关于“MZ?”,版权信息下会显示 Other Stuff 字样。这是由于 rundll32.exe 将 hInst 当作 szApp 字符串指针传递给了 ShellAboutA 函数,而 hInst 的值就是可执行文件 rundll32.exe 在内存中的加载位置,我们看到的 MZ? 字符串正是 rundll32.exe 的 PE 文件头;而参数 lpszCmdLine 正好对应参数 szOtherStuff 显示在了对话框中。

用户在 Windows 资源管理器中双击一个文件后,最终会通过 Windows API ShellExecuteW 来打开文件。这个 API 由 shell32.dll 文件导出,不过由于参数的格式限制, rundll32.exe 并不能直接调用该 API 。所幸 shell32.dll 导出了一系列以 _RunDLL(A/W) 为结尾的 Undocumented API 专门用于被 rundll32.exe 调用:

OpenAs_RunDLL
OpenAs_RunDLLA
OpenAs_RunDLLW
PrintersGetCommand_RunDLL
PrintersGetCommand_RunDLLA
PrintersGetCommand_RunDLLW
SHHelpShortcuts_RunDLL
SHHelpShortcuts_RunDLLA
SHHelpShortcuts_RunDLLW
AppCompat_RunDLLW
Control_RunDLL
Control_RunDLLA
Control_RunDLLAsUserW
Control_RunDLLW
LaunchMSHelp_RunDLLW
Options_RunDLL
Options_RunDLLA
Options_RunDLLW
RunAsNewUser_RunDLLW
ShellExec_RunDLL
ShellExec_RunDLLA
ShellExec_RunDLLW

其中 ShellExec_RunDLL 正是对应 ShellExecuteW 的版本。用法如下:

rundll32.exe shell32.dll,ShellExec_RunDLL { file }

然而 ShellExec_RunDLL 并不像 xdg-open 那样支持打开 URL 。经过一番搜索,发现 url.dll 导出的 Undocumented API OpenURL 可以满足要求,且支持被 rundll32.exe 调用:

rundll32.exe url.dll,OpenURL { file | URL }

创建一个名为 xdg-open.ps1 的脚本文件,内容如下:

POWERSHELLparam($file)
rundll32 url.dll,OpenURL $file

将脚本文件保存在系统 PATH 路径下,就可以在命令行中使用 Windows 版的 xdg-open 命令了。


附录:常用的 RunDLL32 命令

描述命令
关于 Windowsrundll32 shell32.dll,ShellAbout
映射网络驱动器rundll32 shell32.dll,SHHelpShortcuts_RunDLL Connect
添加打印机rundll32 shell32.dll,SHHelpShortcuts_RunDLL AddPrinter
打印机文件夹rundll32 shell32.dll,SHHelpShortcuts_RunDLL PrintersFolder
字体文件夹rundll32 shell32.dll,SHHelpShortcuts_RunDLL FontsFolder
打开方式rundll32 shell32.dll,OpenAs_RunDLL { file }
文件资源管理器选项 - 常规rundll32 shell32.dll,Options_RunDLL 0
任务栏设置rundll32 shell32.dll,Options_RunDLL 1
文件资源管理器选项 - 搜索rundll32 shell32.dll,Options_RunDLL 2
开始设置rundll32 shell32.dll,Options_RunDLL 3
文件资源管理器选项 - 查看rundll32 shell32.dll,Options_RunDLL 7
打开控制面板rundll32 Shell32.dll,Control_RunDLL { CPL file },{@appletindex},{tabindex}
更多内容
交换鼠标主按钮rundll32 user32.dll,SwapMouseButton
锁定计算机rundll32 user32.dll,LockWorkStation
存储的用户名和密码rundll32 keymgr.dll,KRShowKeyMgr
Windows To Go 启动选项rundll32 pwlauncher.dll,ShowPortableWorkspaceLauncherConfigurationUX
添加标准 TCP/IP 打印机端口向导rundll32 tcpmonui.dll,LocalAddPortUI
休眠rundll32 powrprof.dll,SetSuspendState
环境变量rundll32 sysdm.cpl,EditEnvironmentVariables
添加网络位置向导rundll32 shwebsvc.dll,AddNetPlaceRunDll
设备管理器rundll32 devmgr.dll DeviceManager_Execute
打印机用户界面rundll32 printui.dll,PrintUIEntry [options] [@commandfile]
使用 rundll32 printui.dll,PrintUIEntry /? 查看所有参数
打开文件或 URLrundll32 url.dll,OpenURL { file | URL }
打开文件协议rundll32 url.dll,FileProtocolHandler { file scheme URL }
参数为 file:// 协议的文件 URL
打开 Internet 快捷方式rundll32 shdocvw.dll,OpenURL { internet shorcut file }
打开 *.url 文件
打开 Internet 快捷方式rundll32.exe ieframe.dll,OpenURL { internet shorcut file }
打开文件rundll32 zipfldr.dll,RouteTheCall { file }
下载并打开网络图片rundll32 shimgvw.dll,ImageView_Fullscreen { image url }