Scripts discussion (2)

Discuss and announce AkelPad plugins
Locked
  • Author
  • Message
DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post by DV »

Пока что "извратнулся" примерно вот так:

Code: Select all

AkelPad.Exec("TheConsoleApp.exe | redir.exe 2 AkelPad.exe");
Здесь redir.exe - это вспомогательная утилитка, превращающая вызов
'TheConsoleApp.exe | redir.exe 2 AkelPad.exe'
в
'AkelPad.exe "Output of TheConsoleApp.exe"'
Но это, во-первых, некрасиво (мелькает консоль + AkelPad вызывается из командной строки) и, во-вторых, использует "костыли".

Offline
Posts: 2247
Joined: Tue Aug 07, 2007 2:03 pm
Location: Vinnitsa, Ukraine

Post by FeyFre »

DV, общий алгоритм такой(на псевдо-псевдо коде, ибо под рукой нету работающего куска):
1. Создаем неименований пайп. CreatePipe (
&hRead, - конец чтения
&hWrite, - конец записи
в SECURITY_ATTRIBUTES устанавливаем наследование хендлов, либо иным фигом hWrite-у обеспечить валидность для детей )
2. Струкутра STARTUPINFO si;
si.dwFlags = STARTF_USESTDHANDLES|остальные флаги;
si.hStdOutput = hWrite - конец на запись пайпа.
3. CreateProcess( "Консольное.ехе", ....., &si, .... )
4. ReadFile( hRead, буффер куда читать из пайта искомое имя файла )

Offline
Site Admin
Posts: 6311
Joined: Thu Jul 06, 2006 7:20 am

Post by Instructor »

Infocatcher
Уже оговаривалось, что использование сторонних объектов и тем более ответственных за события по таймауту, может приводить к непресказуемым результатам.

DV
В 4.8.8 можно будет использовать следующий код:

Code: Select all

WScript.Echo(GetStdOut("ipconfig"));

function GetStdOut(pCmd)
{
  var hWndOutput=GetOutputWindow();
  var pStdOut="";
  var bClose=true;

  AkelPad.Call("Log::Output", 1, pCmd, "", "", "", -1, -1, 64);
  while (GetExecState())
    WScript.Sleep(100);

  if (hWndOutput)
    bClose=false;
  else
    hWndOutput=GetOutputWindow();

  if (hWndOutput)
  {
    AkelPad.SetEditWnd(hWndOutput);
    pStdOut=AkelPad.GetTextRange(0, -1);
    AkelPad.SetEditWnd(0);
    if (bClose) AkelPad.Call("Log::Output", 6);
  }
  return pStdOut;
}

function GetOutputWindow()
{
  var lpWnd;
  var hWnd=0;

  if (lpWnd=AkelPad.MemAlloc(_X64?8:4 /*sizeof(HWND)*/))
  {
    AkelPad.Call("Log::Output", 2, lpWnd);
    hWnd=AkelPad.MemRead(lpWnd, 2 /*DT_QWORD*/);
    AkelPad.MemFree(lpWnd);
  }
  return hWnd;
}

function GetExecState()
{
  var lpState;
  var nState=0;

  if (lpState=AkelPad.MemAlloc(4 /*sizeof(DWORD)*/))
  {
    AkelPad.Call("Log::Output", 3, lpState);
    nState=AkelPad.MemRead(lpState, 3 /*DT_DWORD*/);
    AkelPad.MemFree(lpState);
  }
  return nState;
}

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Instructor wrote:Infocatcher
Уже оговаривалось, что использование сторонних объектов и тем более ответственных за события по таймауту, может приводить к непресказуемым результатам.
Хм, и правда. :(
Еще одно наблюдение: вроде, достаточно сделать вот так

Code: Select all

		timer && window.clearTimeout(timer);
		window = null;
(но переделать я все же попробую)
И все же что насчет уникальности идентификаторов user32::SetTimer()?

И по ходу дела возник еще один вопрос: есть ли простой способ использовать SetTimer() в обычном скрипте (который сам по себе не остается висеть в памяти и сразу же завершается).
Не уверен насчет практического применения, но вот с тестированием без этого сложновато. :D

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Instructor
Эээ... беру tileTabs.js и тупо добавляю

Code: Select all

lpTimerCallback = oSys.RegisterCallback("timerProc");
перед кодом про

Code: Select all

var window = new ActiveXObject("htmlfile").parentWindow;
(глобальную функцию тоже добавляю)
Получаю ошибку, ведущую на добавленную строку:
Scripts плагин
---------------------------
Скрипт: ...\AkelFiles\Plugs\Scripts\tileTabs.js
Строка: 62
Символ: 3
Ошибка:
Код: 8002802C
Источник:
Upd
И еще одна странность: отключил код про new ActiveXObject("htmlfile"), а winMergeTabs.js все равно периодически падает.

Offline
Site Admin
Posts: 6311
Joined: Thu Jul 06, 2006 7:20 am

Post by Instructor »

Infocatcher wrote:И все же что насчет уникальности идентификаторов user32::SetTimer()?
В 4.8.8 будет доступно сообщение AKD_UNIQUEID.
Infocatcher wrote:И по ходу дела возник еще один вопрос: есть ли простой способ использовать SetTimer() в обычном скрипте (который сам по себе не остается висеть в памяти и сразу же завершается).
Условие противоречит самой задаче :) Если скрипт сразу завершается, то как он среагирует на событие по таймеру?
Получаю ошибку, ведущую на добавленную строку
Не воспроизводится. Покажите конечный код.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Instructor wrote:Условие противоречит самой задаче :) Если скрипт сразу завершается, то как он среагирует на событие по таймеру?
Не-не-не. Сам по себе, без использования таймеров, скрипт сразу же завершается. Теперь добавляем таймер, но он не работает, потому как скрипт завершается до того, как он сработает. Вот можно ли это как-нибудь простым способом обойти?
Instructor wrote:Не воспроизводится. Покажите конечный код.
https://gist.github.com/Infocatcher/9952951/revisions
1. Убираю использование new ActiveXObject("htmlfile"), но по-прежнему периодически падает.
2. Делаю заготовку для теймера, но сразу же дает ошибку на oSys.RegisterCallback().

В общем, я сперва сделал вот такое:
https://gist.github.com/Infocatcher/9953103
Но оно не запустилось, и я начал убирать лишнее.

DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post by DV »

(просто для информации)
Сегодня потребовалось из JScript использовать достаточно сложно конфигурируемый COM-объект, одним из свойств которого является то, что обычно называется ComArray. Здесь проблемы в том, что встроенный в JScript объект Array не совместим ни с ComArray, ни с VBArray (что невыгодно отличает JScript от VBScript). Здесь я привожу "внутреннее" решение для JScript без использования вспомогательного .vbs скрипта:

Code: Select all

function getComArray(arr)
{
  var dict = new ActiveXObject("Scripting.Dictionary");
  for (i in arr)
  {
    dict.add(i, arr[i]);
  }
  return dict.Items();
}

// Usage Example:
var myObj = new ActiveXObject("My.Object");
myObj.Properties = getComArray(new Array("prop1", "prop2"));
(Данное решение было подсмотрено здесь: http://krizzprograming.blogspot.co.uk/2 ... art-2.html)

Offline
Site Admin
Posts: 6311
Joined: Thu Jul 06, 2006 7:20 am

Post by Instructor »

Infocatcher wrote:Не-не-не. Сам по себе, без использования таймеров, скрипт сразу же завершается. Теперь добавляем таймер, но он не работает, потому как скрипт завершается до того, как он сработает. Вот можно ли это как-нибудь простым способом обойти?
Да, обойти можно. Чтобы не завершать скрипт, надо его не завершать. Т.е. использовать цикл сообщений (WindowGetMessage). Нужно понимать, что вызов "user32::SetTimer" никого не ждет, а сразу возвращается.

Code: Select all

var hMainWnd=AkelPad.GetMainWnd();
var oSys=AkelPad.SystemFunction();
var lpTimerCallback;
var nIDEvent=10; //AkelPad.SendMessage(hMainWnd, 1319 /*AKD_UNIQUEID*/, 0, 0);

if (lpTimerCallback=oSys.RegisterCallback("TimerProc"))
{
  oSys.Call("user32::SetTimer", hMainWnd, nIDEvent, 1000, lpTimerCallback);
  AkelPad.ScriptNoMutex();
  AkelPad.WindowGetMessage();
}

function TimerProc(hWnd, uMsg, nIDEvent, dwTime)
{
  oSys.Call("user32::SetWindowText" + _TCHAR, hMainWnd, "MyTitle");

  oSys.Call("user32::PostQuitMessage", 0);
  oSys.Call("user32::KillTimer", hMainWnd, nIDEvent);
  oSys.UnregisterCallback(lpTimerCallback);
}


1. Убираю использование new ActiveXObject("htmlfile"), но по-прежнему периодически падает.
Падения можно анализировать с итоговым вариантом, который преполагается, что работает.
2. Делаю заготовку для теймера, но сразу же дает ошибку на oSys.RegisterCallback().
TYPE_E_AMBIGUOUSNAME 0x8002802C - какие-то проблемы с наложением имен. GetTypeInfo не может получить информацию по функции. Видимо из-за использования функций в функциях. В данном случае указываем функцию явно:

Code: Select all

lpTimerCallback = oSys.RegisterCallback("", timerProc, 4);

function timerProc(hWnd, uMsg, nIDEvent, dwTime)
{
  ...
}
Last edited by Instructor on Thu Apr 03, 2014 7:18 pm, edited 1 time in total.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Хм,

Code: Select all

var nIDEvent=AkelPad.SendMessage(hMainWnd, 1319 /*AKD_UNIQUEID*/, 0, 0) || 10;
?
Сейчас ведь это сообщение не распознается и должно вернуть 0.
Instructor wrote:Падения можно анализировать с итоговым вариантом, который преполагается, что работает.
А он и работает, просто текст в строке состояния статичный и не мигает.
То есть тупо убрано мигание, так что если реализовать его другим способом – вряд ли падения исчезнут, как мне кажется.

Заодно вопрос. Вот такое ведь тоже должно работать:

Code: Select all

lpTimerCallback = oSys.RegisterCallback("", function(a, b, c, d) { ... }, 4);
?
И еще,

Code: Select all

function f(a, b) {
}
WScript.Echo(f.length); // 2
Нельзя ли это приспособить для автоматизированного получения количества аргументов (чтобы вручную не указывать)?

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Так и есть, падает.
winMergeTabs-test.js + timer.js

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

DV wrote:

Code: Select all

function getComArray(arr)
{
  var dict = new ActiveXObject("Scripting.Dictionary");
  for (i in arr)
  {
    dict.add(i, arr[i]);
  }
  return dict.Items();
}

// Usage Example:
var myObj = new ActiveXObject("My.Object");
myObj.Properties = getComArray(new Array("prop1", "prop2"));
Кстати, три момента:
1. for (i in arr) создаст глобальную переменную i, должно быть for (var i in arr):

Code: Select all

var arr = ["a", "b"];
(function() {
	for(i in arr);
})();
WScript.Echo(i); // 1
2. Перебирать массивы через for-in не рекомендуется, там может оказаться совсем не то, что ожидалось (в некоторых реализациях даже "length"):

Code: Select all

Array.prototype.forEach = function() {};
var arr = ["a", "b"];
for(var i in arr)
	WScript.Echo(i);
3. new Array("prop1", "prop2") эквивалентно простому ["prop1", "prop2"]. Тут вообще можно попасться на отличие между new Array(5) и new Array(5, 6) – в первом случае будет создан пустой массив с length == 5.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »


Offline
Site Admin
Posts: 6311
Joined: Thu Jul 06, 2006 7:20 am

Post by Instructor »

Infocatcher wrote:Сейчас ведь это сообщение не распознается и должно вернуть 0.
Да.
Заодно вопрос. Вот такое ведь тоже должно работать...
Возможно. У нас помоему только Infocatcher балуется такими конструкциями :)
Infocatcher wrote:Нельзя ли это приспособить для автоматизированного получения количества аргументов (чтобы вручную не указывать)?
Еще бы знать об этом свойстве при написании RegisterCallback :) Прийдется первый параметр совсем убрать.
Infocatcher wrote:Так и есть, падает.
Не воспроизводится. Из видимого я бы заменил
"AkelPad.WindowSubClass(hMainWnd, -> 1 /*WSC_MAINPROC*/", а после попробовал запустить под отладкой, чтобы найти момент ошибки.

Offline
Posts: 1862
Joined: Mon Aug 06, 2007 1:07 pm
Contact:

Post by Infocatcher »

Instructor wrote:а после попробовал запустить под отладкой, чтобы найти момент ошибки.
Нечем отлаживать:
Scripts плагин
---------------------------
Не удается инициализировать приложение для отладки.
---------------------------
ОК
:(

Пока вот так:
https://gist.github.com/Infocatcher/9952951/revisions
+ https://gist.github.com/Infocatcher/9953103/revisions
И по-прежнему иногда падает. :(

Upd: Да и tileTabs.js даже с отключенным миганием иногда падает...
Locked