Skip to content

Instantly share code, notes, and snippets.

@Beej126
Last active March 24, 2022 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Beej126/3064e77ddb5fad34ff04ee40ffec16bc to your computer and use it in GitHub Desktop.
Save Beej126/3064e77ddb5fad34ff04ee40ffec16bc to your computer and use it in GitHub Desktop.
Windows File Explorer auto extract zips like Mac Finder
function CheckFolderExistsAndCreate(folder) {
if (fso.FolderExists(folder)) {
WScript.Echo("destination folder already exists: " + folder);
WScript.Quit(1);
}
fso.CreateFolder(folder);
}
//probe the zip file for depth/complexity
//report back if it is a simple folder to immediately expand into concrete files
//or prompt for just viewing the zip as-is
function isBiggish(items, depth, why) {
var folderCount = 0;
var fileCount = 0;
//WScript.Echo("foldercount: " + folderCount + ", depth: " + depth + ", count: " + items.Count);
if (depth === 2) { why.why = "is at least 3 folders deep"; return true; }
for (var i = 0; i < items.Count; i++) {
var thisitem = items.Item(i);
//these don't count, would like to avoid them on the extract as well but that's even more "unecessary" enumeration
if (thisitem.Name == "__MACOSX") continue;
//WScript.Echo("depth: " + depth + ", name: " + thisitem.Name);
if (thisitem.IsFolder) {
folderCount++;
if (folderCount > 3) { why.why = "has more than 3 folders"; return true; }
if (isBiggish(thisitem.GetFolder.Items(), depth + 1, why)) { /*we already have a why from call stack*/ return true; }
}
if (!thisitem.IsFolder && ++fileCount > 5) { why.why = "has more than 5 files"; return true; }
if (fso.GetExtensionName(thisitem) == "zip") { why.why = "has zips inside"; return true; };
}
return false;
}
var objArgs = WScript.Arguments;
if (objArgs.length === 0) {
WScript.Echo("please pass zip file as first and only argument")
WScript.Quit(1);
}
var fso = new ActiveXObject("Scripting.FileSystemObject");
var sh = new ActiveXObject("Shell.Application");
var zipFileFullPath = objArgs.Item(0);
var zipItems = sh.NameSpace(zipFileFullPath).Items();
// if empty, bail out
if (zipItems.Count === 0) {
WScript.Echo("zip file empty. nothing to do.");
WScript.Quit(0);
}
var fileObj = fso.GetFile(zipFileFullPath);
var zipFileName = fso.GetBaseName(fileObj); //no path nor extension
var parentPath = fso.GetParentFolderName(fileObj); //yep this is full path not just individual FolderName, despite the name of the method!?
var extractToPath = parentPath + "\\" + zipFileName;
// cool convenience - pull files up out of single nested root folder
var firstItem = zipItems.Item(0);
if (zipItems.Count === 1) {
if (firstItem.IsFolder) {
zipItems = firstItem.GetFolder.Items();
//removed: zipFileName = firstItem.Name;
//keep full zip file name for single root folders
//this often preserves a more specfic zip file name with for example version numbers
}
else {
//single file inside zip, don't even create a subfolder, just put it right at the path where the zip itself is
extractToPath = parentPath;
}
}
var whyIsBig = { why: null };
// if many files, prompt for full extract versus just view
if (isBiggish(zipItems, 0, whyIsBig)) {
// https://ss64.com/vb/popup.html
var wsh = new ActiveXObject("WScript.Shell");
var result = wsh.Popup(
"This zip file " + whyIsBig.why + ".\n\n" +
"Yes = Extract All & ** DELETE .zip **\n" +
"No = Simply view the zip contents and leave it intact",
0 /* seconds to wait, 0 = no timeout */,
"Extract or View",
3 /* Yes / No / Cancel */ + 32 /* question mark icon */
);
switch (result) {
case 2 /*cancel*/: WScript.Quit(1); break;
case 7 /* no */:
//we have to specially fire the "open" verb via ShellExecute since we've overridden the default that would fire via sh.Open()
sh.ShellExecute(zipFileFullPath, "" /*args*/, "" /*working dir*/, "open" /*operation aka "verb"*/, 1 /*window style, 1=normal*/);
WScript.Quit(1);
break;
}
}
extractToPath !== parentPath && CheckFolderExistsAndCreate(extractToPath);
// finally! we get to do the extract
sh.NameSpace(extractToPath).Copyhere(zipItems);
// automatically pop open file explorer in our new folder, nice!! :)
sh.Open(extractToPath);
// delete the zip file, to recycle bin
// (FYI fso.DeleteFile() deletes permanently, doesn't preserve in recycle bin)
sh.NameSpace(parentPath).ParseName(zipFileName + ".zip").InvokeVerb("Delete");
::create the entry
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell\AutoUnZip" /f /ve /d "AutoUnZip"
::and the actual command to execute
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell\AutoUnZip\command" /f /ve /t REG_EXPAND_SZ /d "wscript /e:javascript \"%~dp0AutoUnZip.wsh.js\" \"^%%L\""
::set as the default command on .zip file double click
reg add "HKEY_CURRENT_USER\Software\Classes\CompressedFolder\shell" /f /ve /t REG_SZ /d AutoUnZip
@timeout /t 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment