AkelPad и другие приложения

Russian main discussion
  • Author
  • Message
Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

AkelPad и другие приложения

Post by Skif_off »

Возможно ли посылать полноценные команды AkelPad из другого приложения? Забирать текст как скрипты, а не передавая путь файла внешней утилите (предварительно сохранив этот самый файл (только так работает, например, Tidy AutoIt: вероятно, утилита крайне заточена под SciTE)) и делать с ним что угодно? Или хотя бы то, что можно записать как строку меню или кнопку тулбара. И если да, то как?

Научился скриптом AutoIt посылать внутренние команды (4101-5001, без опциональных параметров):

Code: Select all

$hAkelPad = WinGetHandle("[CLASS:AkelPad4]")
WinActivate($hAkelPad)

_SendMessage($hAkelPad, $WM_COMMAND, 4157, 0) ; Выделить все
If @error Then
    MsgBox(4096, "", "_SendMessage Error: " & @error)
    Exit
EndIf	
Описание функции _SendMessage, с @error пока не разбирался. Так понимаю, аналог SendMessage

WinActivate($hAkelPad), кажется, не обязательно.

Не знаю, что правильнее - класс окна AkelPad или окна редактирования - AkelPad4 или AkelEditW? Вероятно, зависит от целей?

$WM_COMMAND = 0x0111 - непонятно, это действительно константа или нужно менять? Для одной из команд ТС мне подсказали с оговоркой "в данном случае равно %число%", значит надо где-то посмотреть? :) Вероятно, грозит вдумчивое чтение AkelEdit.c?

Или только через COM-объекты и мне, как дилетанту, нечего соваться? :)

И если возможно, пару-тройку примеров: вызов плагина, скрипта с хотя бы одним параметром и что-нибудь из методов из Script-Rus.txt.

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

Post by Instructor »

Skif_off
Из другого приложения в одностороннем порядке можно посылать командую строку на обработку AkelPad'у с помощью WM_COPYDATA с CD_PARSECMDLINEW (см. AkelDLL.h).

Для целей двухстороннего диалога можно использовать Scripts плагин. Недавно была тема в англоязычной ветке.
Skif_off wrote:$WM_COMMAND = 0x0111 - непонятно, это действительно константа или нужно менять?
Это константа.
И если возможно, пару-тройку примеров: вызов плагина, скрипта с хотя бы одним параметром и что-нибудь из методов из Script-Rus.txt.
Это лучше спросить у пользователей AutoIt как послать WM_COPYDATA.

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Instructor
Кажется, не по Сеньке, так сказать, шапка :) Но энтузиазм (самонадеянность?) пока не угас: перегнал все Defines из AkelDLL.h в Global Const, нашел образец, но DllStructCreate пока за гранью понимания.

Вы можете привести пример кода на С/С++ для отправки AkelPad, например

Code: Select all

Call("Scripts::Main", 1, "ChmKeyword.js", `-Name="AutoIt.chm" -Maximize=false -CatchEsc=false`)
?

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

Post by FeyFre »

Вы можете привести пример кода на С/С++ для отправки AkelPad, например
Hotkeys плагин один большой пример :))

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

FeyFre
Еще ToolBar :) Я мало того, что не программист, бывает - туплю, особенно с абстрактными примерами. Конкретный актуальный пример - самое оно.

Собственно, у меня есть запасной вариант: найти подходящий скрипт с подходящим окном :) Хотя, конечно, сразу сдаваться нехорошо.

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Посмотрел AkelEdit.h, кажется, можно попробовать SendMessage.

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Продолжим потихоньку:
обсуждение Как работать с окнами многооконного приложения с одинаковыми Title/Class? на примере AkelPad.

После высказанной мысли одолело любопытство, родился аналог "Выбор окна..." Command(4327), не закрывающийся после выбора вкладки:

Code: Select all

#NoTrayIcon

#Include <GUIConstantsEx.au3>
#Include <GUIListBox.au3>
#Include <WinAPI.au3>
#Include <WindowsConstants.au3>
#include <Array.au3>
#include <GuiTab.au3>
#include <ListBoxConstants.au3>

;Смотрите справку AutoItSetOption()
Opt('MustDeclareVars', 1)
Opt('GUIDataSeparatorChar','|')

Global $hForm, $Msg, $Dummy, $List, $hList, $hWin, $hTab, $iCount, $aTabs, $F, $index

;Получаем дескриптор окна AkelPad
$hWin = WinGetHandle('[Class:AkelPad4]')
If Not $hWin Then Exit 1
;Получаем указатель для элемента SysTabControl32 (вкладки AkelPad)
$hTab = ControlGetHandle($hWin, '', '[CLASS:SysTabControl32; INSTANCE:1]')
If Not $hTab Then Exit 2
;Получаем количество вкладок
$iCount = _GUICtrlTab_GetItemCount($hTab)
;Создаем массив с названиями вкладок
Local $aTabs[1]
$aTabs[0] = $iCount
For $i = 0 To $iCount - 1
    _ArrayAdd($aTabs, _GUICtrlTab_GetItemText($hTab, $i))
Next

;Рисуем GUI
$hForm = GUiCreate('Выбор вкладки AkelPad', 280, 380)
$List = GUICtrlCreateList('', 5, 5, 270, 370, $GUI_SS_DEFAULT_LIST + BitNOT($LBS_SORT))
$hList = GUICtrlGetHandle(-1)
;и набиваем List списком вкладок
$F = ''
For $i = 1 To $iCount
    $F &= $aTabs[$i]
    $F &= '|'
Next
GUICtrlSetData(-1, $F, $aTabs[1])
;Создаем элемент Dummy
$Dummy = GUICtrlCreateDummy()
;Регистрация пользовательской функции и горячей клавиши
GUIRegisterMsg($WM_COMMAND, 'WM_COMMAND')
HotKeySet('{ENTER}', 'HKEnter')
GUISetState()

While 1
    $Msg = GUIGetMsg()
    Switch $Msg
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $Dummy
            $index = _GUICtrlListBox_GetCurSel($hList)
            WinActivate($hWin)
            _GUICtrlTab_SetCurFocus($hTab, $index)
    EndSwitch
WEnd

Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)

    Local $Index

    Switch $lParam
        Case $hList
            Switch _WinAPI_HiWord($wParam)
                Case $LBN_DBLCLK
                    $Index = _GUICtrlListBox_GetCurSel($hList)
                    If ($Index > -1) And (_GUICtrlListBox_GetCaretIndex($hList) = $Index) Then
                        GUICtrlSendToDummy($Dummy, $Index)
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

Func HKEnter()

    Local $Index

    If _WinAPI_GetFocus() = $hList Then
        $Index = _GUICtrlListBox_GetCurSel($hList)
        If $Index > -1 Then
            GUICtrlSendToDummy($Dummy, $Index)
            Return
        EndIf
    EndIf
    HotKeySet('{ENTER}')
    Send('{ENTER}')
    HotKeySet('{ENTER}', 'HKEnter')
EndFunc   ;==>HKEdit


По сути - функциональный набросок: список создаётся один раз при запуске, не запоминает положение, жёстко задан размер окна (добавить изменение размера и положение окна сравнительно легко, можно даже попробовать рассчитывать свободное место и открывать окно вне области, занятой окном AkelPad).

Нюансы: скомпилированный скрипт или интерпретатор должны быть той же разрядности, что и AkelPad; антивирусник/проактивка могут сделать стойку на строку

Code: Select all

    _ArrayAdd($aTabs, _GUICtrlTab_GetItemText($hTab, $i))
Точнее, вызов функции

Code: Select all

DllCall("User32.dll", "dword", "GetWindowThreadProcessId", "hwnd", $hWnd, "dword*", 0)
внутри _GUICtrlTab_GetItemText() (это не считая 2-3 вендоров на вирустотал, традиционно гавкающих на скомпилированные скрипты и/или UPX)

З.Ы. Это не WSH и плагин Scripts, не представляю, как получить полное имя файла, по сути: список создаётся для благообразного вида, фактически происходит оперирование индексами (номер вкладки от 0 до N и индекс в списке от 0 до N).

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Instructor
А как должна выглядеть строка запуска скрипта с WM_COPYDATA?

Code: Select all

Call("Scripts::Main", 1, "scriptname.js")
Call("Scripts::Main", 2, "scriptname.js")
/Call("Scripts::Main", 1, "scriptname.js") 
AkelPad.CallW("Scripts::Main", 1, "scriptname.js")
AkelPad.CallW("Scripts::Main", 2, "scriptname.js")
Или вообще /Exec? Нужна ли точка с запятой в конце? AkelPad.ScriptNoMutex()?
Наверное, AkelPad.xxx не то, раз методы плагина?

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

Post by Instructor »

Skif_off
Instructor wrote:Из другого приложения в одностороннем порядке можно посылать командую строку на обработку AkelPad'у с помощью WM_COPYDATA с CD_PARSECMDLINEW (см. AkelDLL.h).
Возможно так будет понятнее (взято из AkelDLL.h):

Code: Select all

 COPYDATASTRUCT cds;
 PARSECMDLINEPOSTW *pclp;
 wchar_t *wpCmdLine=L"/Call("Scripts::Main", 1, "scriptname.js") "

 if (pclp=(PARSECMDLINEPOSTW *)GlobalAlloc(GMEM_FIXED, sizeof(PARSECMDLINEPOSTW)))
 {
   pclp->bPostMessage=TRUE;
   pclp->nCmdLineLen=lstrcpynW(pclp->szCmdLine, wpCmdLine, COMMANDLINE_SIZE);
   pclp->nWorkDirLen=GetCurrentDirectoryWide(MAX_PATH, pclp->szWorkDir);

   cds.dwData=CD_PARSECMDLINEW;
   cds.cbData=sizeof(PARSECMDLINEPOSTW);
   cds.lpData=(PVOID)pclp;
   SendMessage(hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds);
   GlobalFree((HGLOBAL)pclp);
 }

Offline
Posts: 670
Joined: Thu Jun 03, 2010 8:47 am
Location: Сочи, Хоста
Contact:

Post by Andrey_A_A »

Code: Select all

Возможно ли посылать полноценные команды AkelPad из другого приложения? Забирать текст
Если конечно актуально ещё..

В Autoit есть функция ControlCommand

К примеру возвращает номер строки, в котором находится курсор в элементе

Code: Select all

$n=ControlCommand('[CLASS:AkelPad4]','','AkelEditW1','GetCurrentLine','')
MsgBox(4096,"Переменная ","$n =>" & $n & "<=")
или вставляет слово привет

Code: Select all

ControlCommand('[CLASS:AkelPad4]','','AkelEditW1','EditPaste','привет')
Можно посмотреть в сторону GuiEdit.au3 и получить текст и не только...

Code: Select all

#include <GuiEdit.au3>
#include <GUIConstantsEx.au3>

$hEdit=ControlGetHandle('[CLASS:AkelPad4]','','AkelEditW1')
MsgBox(4096,"Переменная ","$hEdit =>" & $hEdit & "<=")

$sText=_GUICtrlEdit_GetText($hEdit)
MsgBox(4096,"Переменная ","$sText =>" & $sText & "<=")
или без include напрямую

Code: Select all

Local $hEdit=ControlGetHandle('[CLASS:AkelPad4]','','AkelEditW1'),$iTextLen=_SendMessage($hEdit,14),$tText=DllStructCreate("wchar Text["&$iTextLen&"]")
_SendMessage($hEdit,13,$iTextLen,$tText,0,"wparam","struct*")
$sText=DllStructGetData($tText,"Text")
MsgBox(4096,"Переменная ","$sText =>" & $sText & "<=")

Func _SendMessage($hWnd,$iMsg,$wParam=0,$lParam=0,$iReturn=0,$wParamType="wparam",$lParamType="lparam",$sReturnType="lresult")
 	$aResult=DllCall("user32.dll",$sReturnType,"SendMessageW","hwnd",$hWnd,"uint",$iMsg,$wParamType,$wParam,$lParamType,$lParam)
 	Return @error ? SetError(@error,@extended,"") : (($iReturn>=0 And $iReturn<=4) ? $aResult[$iReturn] : $aResult)
EndFunc
Если не актуально, то так для информации...

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Andrey_A_A

Code: Select all

ControlCommand('[CLASS:AkelPad4]','','AkelEditW1','EditPaste','привет')
вставит текст в позицию курсора или заменив выделение. Для замены всего текста есть ControlSetText(), но с неприятностью: документ не изменит статус на "изменен" (запрос сохранения не вылезет при закрытии AkelPad). _GUICtrlEdit_SetText() и _GUICtrlEdit_InsertText() сработают аналогично? До их проверки не дошёл :)
Это если по взрослому, не задействуя буфер обмена.

Ну и добавлю, если кому-то понадобиться автоматизировать обработку:
WM_COPYDATA с CD_PARSECMDLINEW позволят если не всё, что угодно, то очень многое (см. параметры командной строки, включая методы, конечно).
Но есть нюанс: антивирусник может гавкнуть на попытку послать оконное сообщение.

Насчёт актуальности: сейчас, наверное, для информации :) Практически всё так или иначе решается плагинами и скриптами WSH. Ну, для декодирования Quoted-Printable скрипт всё-таки перепишу, наверное.

Offline
Posts: 670
Joined: Thu Jun 03, 2010 8:47 am
Location: Сочи, Хоста
Contact:

Post by Andrey_A_A »

но с неприятностью: документ не изменит статус на "изменен"
В Autoit часто приходится считать разные выражения с участием функций к примеру

Code: Select all

BitOR(4,64,768)
Захотелось сделать кнопку по быстрому вычислению и вставки обратно без буфера.
Так вот кнопка

Code: Select all

-"Вычислить выделенное выражение и вставить результат" Call("Scripts::Main", 1, "TCIMG.vbs", `"exect=ControlCommand('[ACTIVE]','''','AkelEditW1','EditPaste',#stext#)"`) Icon("%a\AkelPadImage.dll",1284)
При вставке обратно вычисленного изменяет документ

Code: Select all

вставит текст в позицию курсора или заменив выделение.
Как раз то что мне и надо.
А когда это сделал, то и вспомнил про эту тему... всё можно сделать, если есть в этом какая-то необходимость.

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

А не проще ли зарегистрировать AkelPad в системе, как ActiveX и иметь из любых приложений доступ напрямую ко всему API Scripts-плагина?

Offline
Posts: 670
Joined: Thu Jun 03, 2010 8:47 am
Location: Сочи, Хоста
Contact:

Post by Andrey_A_A »

Code: Select all

А не проще ли зарегистрировать AkelPad в системе
Для меня нет. Я сторонник портабельности. Да и без регистрации все решаемо, если очень надо.

Offline
Posts: 1161
Joined: Sun Oct 20, 2013 11:44 am

Post by Skif_off »

Andrey_A_A
ControlCommand() изменяет документ, да, но я писал о ControlSetText(), которая не изменяет, т.е., такой скрипт

Code: Select all

$hAkelPad = WinGetHandle('[Class:AkelPad4]') 
If Not $hAkelPad Then Exit

$Text = ControlGetText($hAkelPad, '', 'AkelEditW1')
$Text = StringRegExpReplace($Text, '(\r\n|\r|\n){2,}', ' ')
ControlSetText($hAkelPad, '', 'AkelEditW1', $Text)

; Закрываем вкладку
_SendMessage($hAkelPad, $WM_COMMAND, 4318, 0)
; или закрываем AkelPad
_SendMessage($hAkelPad, $WM_COMMAND, 4109, 0)
не изменит файл и AkelPad не выдаст запрос на сохранение изменений. Нужно дополнительно послать команду сохранения изменений (4105) и только потом закрывать.

Надо ещё проверить _GUICtrlEdit_SetText() и _GUICtrlEdit_InsertText() (только я не понял - они для собственных окон или любых?), но этот нюанс определённо нужно иметь в виду.
Post Reply