Тема: AHK: Поиск в Notepad++ с помощью Scintilla
Добрый всем!
Пытаюсь запрограммировать поиск из AHK в тексте Notepad++ с помощью Scintilla-команды SCI_SEARCHINTARGET.
За образец взял https://www.autohotkey.com/boards/viewt … =Scintilla. Там подсказали, как вставлять текст с помощью Scintilla-команды SCI_REPLACESEL, и это работает нормально.
Вот такой код для поиска получился:
SCI_SETSEARCHFLAGS := 2198
SCFIND_REGEXP := 0x00200000
SCI_SETTARGETSTART := 2190
SCI_SETTARGETEND := 2192
SCI_SEARCHINTARGET := 2197
SCI_GETTARGETSTART := 2191
SCI_GETTARGETEND := 2193
SCI_GETSEARCHFLAGS := 2199
ControlGet, hScintilla, hwnd,, Scintilla1, ahk_class Notepad++
if !hScintilla {
MsgBox, Scintilla not found
ExitApp
}
;--- Устанавливаем флаги поиска SCI_SETSEARCHFLAGS
SendMessage, SCI_SETSEARCHFLAGS, SCFIND_REGEXP,,, ahk_id %hScintilla% ;опция поиска, когда строку поиска следует интерпретировать как регулярное выражение
;--- Устанавливаем целевой диапазон поиска — от начала текста до 1000-й позиции
SendMessage, SCI_SETTARGETSTART, 0,,, ahk_id %hScintilla%
SendMessage, SCI_SETTARGETEND, 1000,,, ahk_id %hScintilla%
;--- Производим поиск с помощью регулярных выражений. В данном случае ищем простой текст "text"
firstMatchPos := SCI_SEARCHINTARGET(hScintilla, "text")
MsgBox firstMatchPos: '%firstMatchPos%' ;выводит 0
;--- Получаем позиции найденной фразы, помещенные в TARGET
SendMessage, SCI_GETTARGETSTART,,,, ahk_id %hScintilla%
foundStart := ErrorLevel
MsgBox foundStart: '%foundStart%' ;выводит 0
SendMessage, SCI_SETTARGETEND,,,, ahk_id %hScintilla%
foundEnd := ErrorLevel
MsgBox foundEnd: '%foundEnd%' ;выводит 0
SCI_SEARCHINTARGET(hScintilla, text)
; Функция поиска по регулярному выражению в Notepad++. Возвращает позицию начала найденного текста или -1, если не нашли
{
WinGet, PID, PID, ahk_id %hScintilla%
size := StrPut(text, "UTF-8")
; size := StrPut(text, "CP1251")
RB := new RemoteBuffer(PID, size) ;создаем новый RemoteBuffer
VarSetCapacity(buf, size, 0)
StrPut(text, &buf, "UTF-8")
; StrPut(text, &buf, "CP1251")
RB.Write(&buf, size)
firstMatchPos := -1
SendMessage, SCI_SEARCHINTARGET, 2, RB.ptr,, ahk_id %hScintilla% ; Производим поиск с помощью регулярных выражений
firstMatchPos := ErrorLevel
Return %firstMatchPos% ; должен возвращать позицию начала найденного текста или -1, если не нашли
}
class RemoteBuffer
{
__New(PID, size) {
static flags := (PROCESS_VM_OPERATION := 0x8) | (PROCESS_VM_WRITE := 0x20) | (PROCESS_VM_READ := 0x10)
, Params := ["UInt", MEM_COMMIT := 0x1000, "UInt", PAGE_READWRITE := 0x4, "Ptr"]
if !this.hProc := DllCall("OpenProcess", "UInt", flags, "Int", 0, "UInt", PID, "Ptr")
throw Exception("Can't open remote process PID = " . PID . "`nA_LastError: " . A_LastError, "RemoteBuffer.__New")
if !this.ptr := DllCall("VirtualAllocEx", "Ptr", this.hProc, "Ptr", 0, "Ptr", size, Params*) {
DllCall("CloseHandle", "Ptr", this.hProc)
throw Exception("Can't allocate memory in remote process PID = " . PID . "`nA_LastError: " . A_LastError, "RemoteBuffer.__New")
}
}
__Delete() {
DllCall("VirtualFreeEx", "Ptr", this.hProc, "Ptr", this.ptr, "UInt", 0, "UInt", MEM_RELEASE := 0x8000)
DllCall("CloseHandle", "Ptr", this.hProc)
}
Read(ByRef localBuff, size, offset = 0) {
VarSetCapacity(localBuff, size, 0)
if !DllCall("ReadProcessMemory", "Ptr", this.hProc, "Ptr", this.ptr + offset, "Ptr", &localBuff, "Ptr", size, "PtrP", bytesRead)
throw Exception("Can't read data from remote buffer`nA_LastError: " . A_LastError, "RemoteBuffer.Read")
Return bytesRead
}
Write(pData, size, offset = 0) {
if !res := DllCall("WriteProcessMemory", "Ptr", this.hProc, "Ptr", this.ptr + offset, "Ptr", pData, "Ptr", size, "PtrP", bytesWritten)
throw Exception("Can't write data to remote buffer`nA_LastError: " . A_LastError, "RemoteBuffer.Write")
Return bytesWritten
}
}
Но он всегда возвращает нули:
firstMatchPos=0, а должен возвращать позицию начала найденного текста или -1, если не нашли.
foundStart=0, а должен возвращать позицию начала найденного текста, то есть ту же, что firstMatchPos.
foundEnd=0, а должен возвращать позицию конца найденного текста.
В чем может быть проблема, подскажите плиз. Может быть есть другой путь автоматизировать поиск в Notepad++?
На всякий случай: сам поиск я пытался строить, используя код на C++ для плагина Notepad++, который работает:
// Получаем текущий экземпляр scintilla. Get the current scintilla
int which = -1;
::SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)&which);
if (which == -1)
return;
HWND curScintilla = (which == 0)?nppData._scintillaMainHandle:nppData._scintillaSecondHandle;
//- Задаем опции поиска
::SendMessage(curScintilla, SCI_SETSEARCHFLAGS, SCFIND_REGEXP, 0); //опция поиска, когда строку поиска следует интерпретировать как регулярное выражение
LRESULT currentPosition = ::SendMessage(curScintilla, SCI_GETCURRENTPOS, 0, 0); // получает номер текущей позиции курсора во всем тексте
//--- Устанавливаем целевой диапазон поиска — от текущей позиции к началу текста — ищем назад по тексту
::SendMessage(curScintilla, SCI_SETTARGETSTART, 0, 0); //задаём область поиска от начала текста
::SendMessage(curScintilla, SCI_SETTARGETEND, 1000, 0); //задаём область поиска до 1000-й позиции
string RegString = "Probe"; //текст (шаблон) для поиска
LRESULT firstMatchPos = -1; //создаем и задаем начальное значение переменной, в которой будет храниться найденное вхождение при поиске
firstMatchPos = ::SendMessage(curScintilla, SCI_SEARCHINTARGET, 2, (LPARAM)RegString.c_str()); // Ищем текст
//=== Если нашли, получаем найденный текст и установливаем на нем курсор
if (firstMatchPos == -1) //если не нашли — выходим
return;
//--- После поиска получаем диапазон (начало и конец) найденной фразы
LRESULT foundStart;
foundStart = ::SendMessage(curScintilla, SCI_GETTARGETSTART, 0, 0); //получаем позицию начала найденной фразы, помещенное в TARGET
LRESULT foundEnd;
foundEnd = ::SendMessage(curScintilla, SCI_GETTARGETEND, 0, 0); //получаем позицию конца найденной фразы, помещенное в TARGET
LPSTR НайденныйТекст = (LPSTR)new char[foundEnd - foundStart]; //задаем текстовую переменную, в которую затем поместим текст диапазона
Sci_TextRange tr; //создаем объект TextRange — текстовый диапазон
tr.chrg.cpMin = (int)foundStart; //задаем начало диапазона — начало найденной фразы
tr.chrg.cpMax = (int)foundEnd; //задаем конец диапазона — конец найденной фразы
//Задаем начальный текст диапазона, пока пустую переменную
tr.lpstrText = НайденныйТекст;
::SendMessage(curScintilla, SCI_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr)); //получаем текст диапазона TARGET (найденную фразу) и помещаем его в переменную tr
MessageBoxA(NULL, tr.lpstrText, "Текст диапазона tr.lpstrText", MB_OK); //Выводит сообщение — текст диапазона — найденную фразу
//Прокрутить экран до найденной фразы и установить на нее курсор
SendMessage(curScintilla, SCI_GOTOPOS, foundStart, NULL);
//--- Установить начало и конец выделения
::SendMessage(curScintilla, SCI_SETSELECTIONSTART, foundStart, NULL); //задаём начало выделения — номер позиции курсора во всем тексте
::SendMessage(curScintilla, SCI_SETSELECTIONEND, foundEnd, NULL); //задаём конец выделения — номер позиции курсора во всем тексте