comactivexshortcutjscriptwsh

How can I use JScript to create a shortcut that uses "Run as Administrator"


I have a JScript script that runs using cscript.exe. It creates a shortcut on the desktop (and in the start menu) that runs cscript.exe with parameters to run another JScript script. It looks, in relevant part, like this:

function create_shortcut_at(folder, target_script_folder)
{
    var shell = new ActiveXObject("WScript.Shell");
    var shortcut = shell.CreateShortcut(folder + "\\Run The Script.lnk");
    shortcut.TargetPath = "cscript";
    shortcut.Arguments = "\""+target_script_folder+"\\script.js\" /aParam /orTwo";
    shortcut.IconLocation = target_script_folder+"\\icon.ico";
    shortcut.Save();
}

It gets called like create_shortcut_at(desktop_folder, script_folder).

And that works, as far as it goes. It creates the desktop icon, pointing properly to the script and runs it when double-clicked. The problem is that it really needs to run the script "as administrator".

And the script really does need to run "as administrator" -- it installs applications (for all users) and reboots the computer. (For those interested, the script is wpkg.js. Modifying it to self-elevate is undesirable.)

Since the target of the shortcut is actually "cscript.exe", I can't use a manifest for the escalation. I could probably theoretically install a cscript.exe.manifest in the windows directory, but even if that worked, it would be a terrible idea for reasons that are obvious.

I'd also prefer not to use a dummy script, since that is an extra file to deal with and there's another, seemingly reasonable, solution at hand: check the "Run as administrator" box on the shortcut.

Thirty-seconds of investigation reveals that the WScript.Shell ActiveX Object does not have the interfaces required for this. Additional investigation suggests that IShellLinkDataList does. However, IShellLinkDataList is a generic COM Interface. I see several examples around the Internet, most linking here. However, all the examples do it in compiled code (C++, C#, even JScript.NET). I significantly prefer to be able to do it directly in JScript, running from cscript.exe.

That said, I'm all for ideas I didn't contemplate or other solutions.


Solution

  • The official way to mark a shortcut file as requiring elevation is via IShellLinkDataList. It's difficult to use that interface from an automation environment.

    But, if you are happy with a hack, you can do it in script, just by flipping a bit in the .lnk file.

    When you tick the "run as administrator" box in the Advanced tab of the Shell Properties box, or when you use IShellLinkDataList to set the flags to include SLDF_RUNAS_USER, you're basically just setting one bit in the file.

    You can do that "manually" without going through the COM interface. It's byte 21, and you need to set the 0x20 bit on.

    (function(globalScope) {
        'use strict';
        var fso = new ActiveXObject("Scripting.FileSystemObject"),
            path = "c:\\path\\goes\\here\\Shortcut2.lnk",
            shortPath = path.split('\\').pop(),
            newPath = "new-" + shortPath;
    
        function readAllBytes(path) {
            var ts = fso.OpenTextFile(path, 1), a = [];
            while (!ts.AtEndOfStream)
                a.push(ts.Read(1).charCodeAt(0));
            ts.Close();
            return a;
        }
    
        function writeBytes(path, data) {
            var ts = fso.CreateTextFile(path, true),
                i=0, L = data.length;
            for (; i<L; i++) {
                ts.Write(String.fromCharCode(data[i]));
            }
            ts.Close();
        }
    
        function makeLnkRunAs(path, newPath) {
            var a = readAllBytes(path);
            a[0x15] |= 0x20; // flip the bit. 
            writeBytes(newPath, a);
        }
    
        makeLnkRunAs(path, newPath);
    
    }(this));
    

    ps:

    function createShortcut(targetFolder, sourceFolder){
        var shell = new ActiveXObject("WScript.Shell"),
            shortcut = shell.CreateShortcut(targetFolder + "\\Run The Script.lnk"),
            fso = new ActiveXObject("Scripting.FileSystemObject"),
            windir = fso.GetSpecialFolder(specialFolders.windowsFolder);
    
        shortcut.TargetPath = fso.BuildPath(windir,"system32\\cscript.exe");
        shortcut.Arguments = "\"" + sourceFolder + "\\script.js\" /aParam /orTwo";
        shortcut.IconLocation = sourceFolder + "\\icon.ico";
        shortcut.Save();
    }