1

Тема: HTA & JScript: HTA as 64 bit in focus - запуск HTA как 64 bit в фокусе

Еще одна наработка, которую я использовал в HTML приложении "Resource Extractor" это "HTA as 64 bit in focus".

Предназначение - перезапуск HTA в 64 bit без стороннего вмешательства и использования каких-либо системных путей.

Предлагаю вашему вниманию ее усовершенствованную версию с корректной простановкой фокуса при перезапуске.

Казалось бы, ничего сильно сложного с запуском HTA как 64 bit не должно быть, но все же есть некоторые трудности с универсальностью различных подходов, а также с получением фокуса для перезапущенного окна в Windows 7 и особенно в Windows XP.

Скриптовый пример имеет следующие специальные возможности:

+ открыть спойлер
  • не требует установки какого-либо стороннего программного обеспечения и поддерживает любую версию Windows, начиная с XP (возможно и 2000) с определенными ActiveX объектами, не запрещенными по умолчанию;

  • полностью написан на JScript в файле скриптового формата HTA и использует следующие ActiveX объекты: WScript.Shell, Scriptlet.TypeLib, WbemScripting.SWbemLocator, Shell.Application и Shell.Explorer.2, также известный как Microsoft Web Browser;

  • тестировался и адаптирован для Windows XP, Windows 7 и Windows 10;

  • не пишет каких-либо записей в реестр и не сохраняет каких-либо персональных данных на вашем жестком диске;

  • не выходит в сеть каким-либо способом;

  • не требует какой-либо вложенной структуры для 64-битной части скрипта (новый код может быть просто добавлен в конец);

  • получает все необходимые паузы и фокусы для того, чтобы приложение оставалось поверх других окон при старте;

  • получает собственный PID, который может быть использован вновь позже;

  • не требует никаких системных путей или деклараций, которые могут отсутствовать в некоторых версиях Windows (например, "SysNative" неизвестен для Windows XP 32 bit).

Скриптовый пример демонстрирует:

  • как скрыть HTA во время его динамического создания;

  • как запустить HTA в виде 64-битного процесса без использования системных путей или деклараций;

  • как получить скрытую остановку скрипта в HTA;

  • как решить подтвержденные проблемы с немедленным вызовом <moveTo> или <resizeTo> при старте в Windows XP

  • и как передать и использовать переменные среды для процесса, созданного с помощью WMI.


Различные способы запуска HTA как 64 bit.

Самый простой и часто предлагаемый способ запуска HTA как 64 bit - создание ярлыка с жестко прописанным путем к mshta.exe, но в этом случае приложение становится несколько ограниченным и может требовать стороннего вмешательства.

Другой способ - использование декларации "SysNative", но Windows XP 32 bit, например, о ней ничего не знает, и требуется дополнительная совместимость.

Однажды при исследовании WMI я обнаружил, что создаваемые им процессы - 64-битные в соответствующих операционных системах. Поменять, вероятно, можно и данное поведение (преимущества смены в этом случае спорны), но меня привлекло то, что это заложено по умолчанию, доступно в любой версии Windows, начиная с XP (возможно и 2000), и этого достаточно, чтобы отвязаться от "SysNative" и жестко прописанных путей.

Использование "AppActivate" для получения фокуса при перезапуске.

При скриптовом перезапуске HTML приложения с UI (которым оно обладает, в отличие от WScript и CScript-сценариев), в старых версиях Windows (особенно при уничтожении старой копии) возникают настолько нетривиальные проблемы с расстановкой фокуса новым и старым окнам, что простые вызовы "focus" или "blur" уже не помогают. На помощь приходит "AppActivate" от "WScript.Shell", но и с ним не все так просто.

Ипользовать метод "AppActivate" следует осторожно в том смысле, что он ищет приложения с похожими заголовками, если обращение было по заголовку. Если же заголовок недостаточно уникален, то и вовсе может быть активировано другое приложение.

Преимущество данного метода заключается в том, что он может активировать приложения еще и по их PID (Process ID), которые уникальны для текущей среды. Эта возможность нам далее и пригодится.

Глубокое исследование показало, что "AppActivate" работает корректно только в случаях, если окно с его "хозяином" - "WScript.Shell" - находится в фокусе или является родителем дочерних окон. При этом, даже если окно было активировано корректно, его фокус в старых версиях Windows часто будет все равно потерян при уничтожении родителя, который его активировал.

Еще одна интересная особенность - нельзя в окне с фокусом корректно активировать другое окно, если при этом использовать "WScript.Shell" чужого окна не в фокусе. Таким образом, для корректной активации принцип прежний - важно наличие фокуса не у самого окна, в котором вызывается метод "AppActivate", а у того окна, где объявлен его хозяин "WScript.Shell".

Передача переменных среды в WMI как сигнал о состоянии приложения.

На данный момент существует мало примеров передачи переменных среды процессу, созданному при использовании WMI. Но это может быть полезно, как в случае с наследованием переменных при использовании "WScript.Shell".

В данной работе при перезапуске скрипта отдано предпочтение анализу наличия определенной переменной среды как сигналу о состоянии приложения, тогда как перебор окон коллекции, анализ их параметров и сравнение признается более затратным по ресурсам и времени, чтобы проводить его чаще, чем один раз. Также, например, анализ параметров командной строки в качестве альтернативы требует инициализации определенного тега "hta:application", для которого до анализа еще не известно, стоит ли его прятать в Taskbar или еще нет.

Примечание: особенностью WMI является то, что переданные переменные являются единственными переменными, которые получит новый процесс. Таким образом, передача переменных процессу в WMI накладывает ограничения на работу процесса вплоть до того, что он даже не сможет нормально запуститься, если не получит на вход существенно важную для него переменную. На передачу переменных в WMI всегда нужно обращать внимание, так как возникающие ошибки при отсутствии необходимых переменных могут сложно определяться и быть самого разного характера. Если не уверены, что нужно для работы создаваемого в WMI процесса, то проще передать ему все доступные в среде переменные.

Принудительная остановка скрипта.

Чтобы избежать дополнительной вложенности и включения нового кода для 64-битной части скрипта в ветку if-else в данной работе я использовал собственную принудительную остановку скрипта.

Глубокое исследование показало, что браузер при работе пробегается вплоть до самой последней строки скрипта даже в случае, если было вызвано завершение приложения на более раннем этапе. Это приводит к возникновению нежелательных ошибок при линейном выполнении кода.
Опытным путем было установлено, что приостановить такую работу можно отвлекающими маневрами, одним из которых является блокировка выполнения скрипта модальным окном.

Специальная настройка такого модального окна позволяет полностью скрыть его из виду с сохранением основной функциональности.
Таким образом, скрипт можно приостановить, а затем принудительно завершить без пробегания всех его строк до последней.


Работа с исходным кодом.

  1. Для запуска скриптового примера скопируйте его исходный код и сохраните его в текстовом файле, сменив его расширение на "hta".

  2. Запуск скриптового примера осуществляется двойным нажатием, как и в случае с обычной программой.

Рекомендую использовать AkelPad для просмотра сохраненного исходного кода примера с правильными отступами.

Вы можете использовать скриптовый пример на свое усмотрение, но, пожалуйста, оставьте комментарий с прямой ссылкой на tastyscriptsforfree.wix.com/page/scripts в этом случае.


<script>
/*
HTA as 64 bit in focus v1.0.2 (h t t p s://tastyscriptsforfree.wix.com/page/scripts)
Copyright 2017-2020 Vladimir Samarets. All rights reserved.
tastyscriptsforfree@protonmail.com

Release date: October 27, 2020.


Use this script sample entirely at your own risk.
This script sample is copyrighted freeware and I am not responsible for any damage or data loss it could unintentionally cause.
You may modify it but please leave a comment with direct link to https://tastyscriptsforfree.wix.com/page/scripts in that case.


*******
The purpose of this script sample:
the script sample is intendent for launching HTA as 64 bit in focus and without using system paths.

*******
The script sample has the following special features:

it requires no third party software installation and supports any Windows version since XP with certain ActiveX objects not restricted by default;
it is entirely written in JScript in HTA scripting file format and uses the following ActiveX objects: WScript.Shell, Scriptlet.TypeLib, WbemScripting.SWbemLocator, Shell.Application and Shell.Explorer.2 also known as Microsoft Web Browser;
it was tested and adapted for Windows XP, Windows 7 and Windows 10;
it writes no registry values and stores no personal settings on your hard disk;
it doesn't access network in any way;
it requires no nested structure for 64 bit instance scripting (new code can be simply added to the end);
it obtains all necessary pauses and focuses in order for the application to stay on top of other windows during its start;
it obtains the application PID which can be reused later;
it requires no system paths or declarations which could be absent in some versions of Windows (for example, "SysNative" is unknown for Windows XP 32 bit).

*******
The script sample demonstrates:

how to hide HTA while having it dynamically created;
how to launch HTA as 64 bit without using system paths or declarations;
how to obtain hidden stop for script in HTA;
how to deal with confirmed immediate "moveTo" or "resizeTo" issues at application start in Windows XP
and how to pass and use environment variables for process created by WMI.

*******
Basic documentation and articles I used:

'VBScript Scripting Techniques: Environment Variables' by Rob van der Woude
    (h t t p s://w w w.robvanderwoude.com/vbstech_data_environment.php);
'Using Win32_ProcessStartup to change environment' post by Rico Rosenlund
    (h t t p s://microsoft.public.scripting.wsh.narkive.com/hW1GURnH/using-win32-processstartup-to-change-environment);

'WSH: exchanging data and objects between scripts - 2' by Xameleon (March, 2011) in Russian
    (h t t p://forum.script-coding.com/viewtopic.php?id=5573);

'VBScript Scripting Techniques: HTAs' by Rob van der Woude
    (h t t p s://w w w.robvanderwoude.com/vbstech_hta.php);

'InternetExplorer and WebBrowser objects' by Ludogovskiy Aleksander in Russian
    (h t t p s://script-coding.com/WSH/WebBrowser.html);

'Windows Script 5.6 Documentation' (script56.chm) by Microsoft Corporation
    (h t t p s://w w w.microsoft.com/en-us/download/confirmation.aspx?id=2764);
'MSDN' related documentation by Microsoft Corporation
    (h t t p s://docs.microsoft.com/en-us/);

Windows XP moveTo and resizeTo issues:
'"Access is denied" by executing .hta file with JScript on Windows XP x64' discussion on stackoverflow.com (January 21, 2009)
    (h t t p s://stackoverflow.com/questions/464679/access-is-denied-by-executing-hta-file-with-jscript-on-windows-xp-x64);
'Something strange with HTAs' discussion on social.technet.microsoft.com (September 19, 2011)
    (h t t p s://social.technet.microsoft.com/Forums/officeocs/en-US/92bf1e76-ebd5-4462-bd52-533e69305a5c/something-strange-with-htas?forum=ITCG).

*******
Basic software I used:

AkelPad 4.9.8 by Aleksander Shengalts and Alexey Kuznetsov (as development environment)
    (h t t p://akelpad.sourceforge.net/en/download.php);
OLE/COM Object Viewer v2.10.059 (oleview.exe) by Charlie Kindel, Michael Nelson, and Michael Antonio (for documentation purposes).
*/

offscreenBuffering = true;                                                                                          //postpone the application window appearance till its UI is ready
var O = function(o){return new ActiveXObject(o);},
WSS = O('WScript.Shell'),
env = WSS.Environment('Process'),
head = document.documentElement.firstChild,                                                        //head
PID;                                                                                                                            //PID of 64 bit HTA instance

if(!env('is64bit'))                                                                                                          //indicates whether the application is launched as 64 bit or not
{
    !function hide(e){try{moveTo(10000, 10000);}catch(e){try{hide();}catch(e){hide();}}}();                            //hiding the application window
    head.insertBefore(document.createElement('<hta:application showInTaskBar=0>'), head.firstChild);    //hiding the application in the Taskbar

    var WMI=                                                                                                                //a small library written by me for obtaining WMI instance, its common methods and properties
    {                                                                                                                              //below is a sample of creating a process with certain window shifts and environment variables
                                                                                                                                   //and obtaining its <ProcessId> by using WMI
        SWL:new ActiveXObject('WbemScripting.SWbemLocator'),
        PRMS:function(p)
        {
            var s = WMI.PS.SpawnInstance_();
            for(var i in p)
                s[i] = p[i];
            return s;
        },
        Create:function(c, s, d)
        {
            var CreateIn = WMI.CreateIn.SpawnInstance_();
            CreateIn.CommandLine = c;
            CreateIn.ProcessStartupInformation = s;
            CreateIn.CurrentDirectory = d;
            return WMI.PRC.ExecMethod_('Create', CreateIn).ProcessId;
        }
    };
    WMI.PRC = (WMI.WM = WMI.SWL.ConnectServer('.', 'root/cimv2')).Get('Win32_Process');
    WMI.PS = WMI.WM.Get('Win32_ProcessStartup');
    WMI.CreateIn = WMI.PRC.Methods_('Create').InParameters;

    var ID = O('Scriptlet.TypeLib').GUID.substr(0, 38),                                               //the unique ID obtaining
    EV = 'is64bit='+ID;                                                                                                 //passing the unique ID to 64 bit HTA instance as an Environment variable
    for(var items = new Enumerator(env); !items.atEnd(); items.moveNext())
        EV += '?' + items.item();                                                                                    //obtaining all Environment variables for current process

    PID = WMI.Create                                                                                                  //obtaining PID of 64 bit HTA instance
        (
            'mshta "' + decodeURIComponent(location.pathname) + '"',                        //current path
            WMI.PRMS
            (
                {
                    X:10000, Y:10000,                                                                                  //hiding the application window before it is shown in order to resize it smoothly
                    EnvironmentVariables:
                        EV.split('?')                                                                                         //obtaining an array of all Environment variables by using this approach is universal for different
                                                                                                                                   //versions of Windows
                        /*
                        [                                                                                                          //another way to pass certain Environment variables
                            'is64bit='+ID,                                                                                  //indicates that the application is launched as 64 bit
                            'SystemRoot='+env('SystemRoot'),                                              //for start
                            'SystemDrive='+env('SystemDrive'),                                            //for hyperlinks
                            'TEMP='+env('TEMP'),                                                                  //for "mailto" links
                            'CommonProgramW6432='+env('CommonProgramW6432')     //for ADODB.Stream
                        ]
                        */
                }
            )
        );

    head.firstChild.insertAdjacentHTML('afterEnd', '<object id=' + ID + ' PID=' + PID +
        ' classid=clsid:8856F961-340A-11D0-A96B-00C04FD705A2><param name=RegisterAsBrowser value=1>');                   //registering current HTA window
                                                                                                                                                                                                          //in collection of windows
    showModalDialog(0, 0, 'dialogWidth:0;unadorned:1;');                                                                                                                //stopping the script and obtaining window focus
}                                                                                                                                                                                                         //for "AppActivate"

head.insertBefore(document.createElement('<hta:application contextMenu=no selection=no scroll=no>'), head.firstChild);      //adding custom HTA header dynamically
resizeTo(380, 204);
document.title = 'HTA as 64 bit in focus';
for(var w, ws = O('Shell.Application').Windows(), i = ws.Count; i -- > 0;)
    if((w = ws.Item(i)) && w.id == env('is64bit'))
    {
        PID = w.PID;
        w.document.Script.WSS.AppActivate(PID);                                                                                                                            //using "WScript.Shell" in focus to activate
                                                                                                                                                                                                          //the application window of 64 bit HTA instance;
                                                                                                                                                                                                          //remember that "WScript.Shell" should be
                                                                                                                                                                                                          //in focus in order "AppActivate" to work properly
        setTimeout
        (
            function()
            {
                w.document.Script.close();                                                                                                                                              //closing previous 32 bit HTA instance
                moveTo(screen.availWidth / 2 - 190, screen.availHeight / 2 - 102);
            }
            ,
            0                                                                                                                                                                                            //a small pause for having necessary window focus
        );                                                                                                                                                                                                //for "AppActivate"
        break;
    }

//your code could be added below for 64 bit HTA instance

</script>

2

Re: HTA & JScript: HTA as 64 bit in focus - запуск HTA как 64 bit в фокусе

Дальнейшее исследование показало, что необходимая для сохранения фокуса пауза перед вызовом "w.document.Script.close();" коррелирует с ожиданием "document.body" во время динамического построения.
Таким образом, можно обойтись без нее, если провести предварительное создание "document.body" принудительно:


for(var ws = O('Shell.Application').Windows(), i = ws.Count; i -- > 0;)
    if((w = ws.Item(i)) && w.id == env('is64bit'))
    {
        PID = w.PID;
        w.document.Script.WSS.AppActivate(PID);     //using "WScript.Shell" in focus to activate
                                                       //the application window of 64 bit HTA instance;
                                                       //remember that "WScript.Shell" should be
                                                       //in focus in order "AppActivate" to work properly
        break;
    }

document.write('<body>');                           //obtaining body
if(w && w.id == env('is64bit'))
    w.document.Script.close();                      //closing previous 32 bit HTA instance while being in safe focus
moveTo(screen.availWidth / 2 - 190, screen.availHeight / 2 - 102);

На практике такой пример рассмотрен в "Extended Drop Target".

3

Re: HTA & JScript: HTA as 64 bit in focus - запуск HTA как 64 bit в фокусе

Дальнейшее исследование позволило устранить возможное мерцание плитки приложения на панели задач во время его запуска.
Обновленную версию скрипта вы можете найти на моем сайте.
Подробности о возникновении мерцания разобраны в теме HTA & JScript: HTA silent stop and close - тихая остановка и закрытие.