从 wsh 脚本获取响应

Getting a response from a wsh script

在我的 HTA 应用程序中,有一些操作正在服务器上执行一些非常耗时的任务。例如,一个操作使用旧的 ActiveX 组件读取特定文件夹(一个文件夹中的 0 - ~200 个文件)中文件的某些文件属性(如主题和注释)。

最初这是通过设置一个时间间隔,并逐个文件读取文件属性来完成的。当使用快速本地连接连接到服务器时,应用程序变慢是可以接受的。但是现在远程工作明显增多,远程连接比内网连接慢很多,间隔时间已经不适合做任务了。

为了使应用程序在文件属性搜索期间更快,我将代码外包给了 wsh 作业。结果存储在一个文件中,该文件存在一个间隔(5 秒)。但是,即使以上述 5 秒的间隔轮询文件是否存在,一些用户仍然遇到应用程序明显变慢的问题。

现在我想知道,是否存在事件或其他内部机制,我可以使用它来检测 wsh 脚本何时完成其工作?如果可能的话,甚至可能有一种方法可以将结果直接从 wsh 作业发送到 HTA,而根本不使用中间临时文件?

下面是一些在 wsh 文件和 HTA 应用程序中执行的实际任务的简化代码。 HTA 有 HTML5 DTD,它在使用 IE11 的边缘模式下是 运行。 ui 是一个实用程序库,所引用的属性名称希望能够足够准确地描述用法。

WSF:

<package>
<job id="getFileProps">
<script language="JScript">
    (function () {
        var topRoot = WScript.Arguments(0),                           // The starting folder <String>
            fso = WScript.CreateObject('Scripting.FileSystemObject'), // Filesystem object <ActiveXObject>
            fileProps = readProps(topRoot),                           // Fileprops, the result <Array>
            file;                                                     // The result file to read in HTA <FileObject>

        function readProps (root) {
            var fileProperties = [];
            // A bunch of code reading the fileproperties on a disk
            return fileProperties;
        }

        file = fso.openTextFile(topRoot + '\$fileprops$.txt', 2, true);
        file.Write(fileProps.join(',');
        file.Close();
        WScript.Quit();
    )());
</script>
</job>
</package>

JSCRIPT:

ui.winShell.Exec('WScript //Job:getFileProps ' + '"' + ui.appRoot + '"');
function fpCheck () {
        var file, fileProps;
        try {
                file = ui.fileIO.OpenTextFile('$fileprops$.txt', 1, false);
        } catch (err) {
                file && file.Close();
                setTimeout(fpCheck, 5000);
                return;
        }
        // Write the fileprops to the view
}

如果有人对文件属性 reader 感兴趣,它的名称是 DSOFile.OleDocumentProperties。它起源于深层互联网档案的某个地方,但我不记得我是从哪里加载它的。

您的场景是否支持使用字典对象或数组列表来仅存储文件名和上次更新时间,以及仅检索新文件或已更改文件(和删除)的完整属性集(到第二个列表中)。我猜这取决于文件来来去去或更新的频率。如果大多数细节在轮询之间没有变化,这可能比生成整个文件属性数据集更快。

最终你需要的是进程间通信。

HTA 或 WSH 都没有为 IPC 提供内置的东西。

所以,我将分享一个我很少使用的 hacky 解决方案。

诀窍是像客户端-服务器场景一样在两端使用共享对象。

我们将使用 Internet Explorer 对象作为 IPC 服务器,我们将使用的方法是 GetProperty and PutProperty

由于 Internet Explorer windows 可以使用 Shell objects's Windows() 方法枚举,因此可以从另一个应用程序访问在 HTA 中创建的对象。

检查并创建以下两个文件,然后启动 test.hta 并单击 Start WSF 按钮。

test.hta:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge">
        <title>IPC</title>
        <script>
            sharedObject = {
                sendData: function(data){
                    // blocking here is not a good idea 
                    // so we process the data within another callback
                    // leaving the method as soon as possible
                    var receivedData = data;
                    setTimeout(function(){alert("Data received: " + receivedData)}, 50);
                }
            };
            
            shareName = "ipcShare";
            serverId = createIPCServer(shareName);
            
            function createIPCServer(shareName){
                var ie = new ActiveXObject("InternetExplorer.Application");
                ie.PutProperty(shareName, sharedObject);
                window.onbeforeunload = function(){ ie.Quit(); };                
                return ie.HWND; // window handle
            }
            
            function starWSFJob() {
                alert("please wait about 5 seconds...");
                var wshShell = new ActiveXObject("Wscript.Shell");
                wshShell.Run('wscript.exe test.wsf //Job:TestIPC /serverId:"' + serverId + '" /shareName:"' + shareName + '"');
            }
        </script>
    </head>
    <body>
        <button onclick="starWSFJob()">Start WSF</button>
    </body>
</html>

test.wsf:

<package>
    <job id="TestIPC">
        <script language="JScript">
            function getSharedObject(serverId, shareName){
                var shellApp = new ActiveXObject("Shell.Application");
                var windows = new Enumerator(shellApp.Windows());
                for (;!windows.atEnd();windows.moveNext()){
                    var window = windows.item();
                    if(window.HWND == serverId) {
                        return window.GetProperty(shareName);
                    }
                }
            }
            
            function App() {
                var serverId = Number(WScript.Arguments.Named("serverId"));
                var shareName = WScript.Arguments.Named("shareName");
                var sharedObject = getSharedObject(serverId, shareName);
                if(!sharedObject){
                    WScript.Echo("shared object not found.");
                    return 1;
                }
                
                // simulate long running job
                WScript.Sleep(5000);
                
                // send data
                sharedObject.sendData("Hello from WSF Job!");
                
                return 0;
            }

            WScript.Quit(App());
        </script>
    </job>
</package>