1

Тема: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Всем добрый!
Хочу получить текст из диапазона Notepad++ с помощью Scintilla-команды SCI_GETTARGETTEXT https://www.scintilla.org/ScintillaDoc. … TARGETTEXT.
Не нашел работающего кода, самому не осилить, прошу помощи.

Есть попытка на AHK, но недопиленная: https://www.autohotkey.com/boards/viewt … TTEXTRANGE.

В плагине на С++ нормально работает такой код:

//--- Получаем текст из заданного диапазона TextRange
LPSTR text = (LPSTR)new char[TextLength + 1]; //задаем текстовую переменную, в которую поместим в дальнейшем текст диапазона
Sci_TextRange tr; //создаем объект TextRange — структуру текстового диапазона
	
//Задаем начало диапазона
tr.chrg.cpMin = (int)SelectionEnd; //задаем начало диапазона — конец выделения

//Задаем конец диапазона
tr.chrg.cpMax = (int)SelectionEnd + 1; //задаем конец диапазона — конец выделения + 1

//Задаем начальный текст диапазона, сначала пустой
tr.lpstrText = text;
	
::SendMessage(curScintilla, SCI_GETTEXTRANGE, 0, reinterpret_cast<LPARAM>(&tr)); //получаем текст диапазона и помещаем его в переменную tr

MessageBoxA(NULL, tr.lpstrText, "Текст диапазона", MB_OK); //выводит текст диапазона

2

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Попробуйте так:

ControlGet, hSci, hwnd,, Scintilla1, ahk_exe notepad++.exe
if !hSci
   throw "Не удалось получить хэндл контрола Scintilla"
MsgBox % Sci_GetTextRange(5, 10, hSci)

Sci_GetTextRange(start, end, hSci) {
   static SCI_GETTEXTRANGE := 2162, SCI_GETCODEPAGE := 2137
   WinExist("ahk_id " . hSci)
   WinGet, PID, PID
   VarSetCapacity(Sci_TextRange, size := A_PtrSize*3, 0)
   RB := new RemoteBuffer(PID, size + end - start + 1)
   NumPut(start, Sci_TextRange)
   NumPut(end, Sci_TextRange, A_PtrSize)
   NumPut(RB.ptr + size, Sci_TextRange, A_PtrSize*2)
   RB.Write(&Sci_TextRange, size)
   SendMessage, SCI_GETTEXTRANGE,, RB.ptr
   textSize := ErrorLevel + 1
   RB.Read(buff, textSize, size)
   SendMessage, SCI_GETCODEPAGE
   Return StrGet(&buff, "CP" . ErrorLevel)
}

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
   }
}

Хотя можно и так:

ControlGetText, text, Scintilla1, ahk_exe notepad++.exe
MsgBox % SubStr(text, 5, 5)
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3 (изменено: Vadus, 2023-04-25 00:57:03)

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Спасибо teadrinker!
Первый способ крашит Notepad++ без сообщения об ошибке в большинстве случаев. Но один раз вылезла такая:

Error:  Can't read data from remote buffer
A_LastError: 299

	Line#
	046: {
	047: DllCall("VirtualFreeEx", "Ptr", this.hProc, "Ptr", this.ptr, "UInt", 0, "UInt", MEM_RELEASE := 0x8000)  
	048: DllCall("CloseHandle", "Ptr", this.hProc)  
	049: }
	050: {
	051: VarSetCapacity(localBuff, size, 0)  
	052: if !DllCall("ReadProcessMemory", "Ptr", this.hProc, "Ptr", this.ptr + offset, "Ptr", &localBuff, "Ptr", size, "PtrP", bytesRead)  
--->	053: Throw,Exception("Can't read data from remote buffer
A_LastError: " . A_LastError, "RemoteBuffer.Read")
	054: Return,bytesRead
	055: }
	056: {
	057: if !res := DllCall("WriteProcessMemory", "Ptr", this.hProc, "Ptr", this.ptr + offset, "Ptr", pData, "Ptr", size, "PtrP", bytesWritten)  
	058: Throw,Exception("Can't write data to remote buffer
A_LastError: " . A_LastError, "RemoteBuffer.Write")
	059: Return,bytesWritten
	060:\ }

Использую AHK v. 1.1.33.10.

Второй вариант настолько гениально прост, что кажется подозрительным. Но работает .

4

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Vadus пишет:

Первый способ крашит Notepad++

У меня вроде не крашит, работает на Windows 7 и 10. На 11 не тестировал.
Хотя, возможно, у вас старый Notepad++, примерно год назад структура Sci_TextRange изменилась, а как раз старый вариант начал крашить.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

teadrinker пишет:

Хотя, возможно, у вас старый Notepad++, примерно год назад структура Sci_TextRange изменилась, а как раз старый вариант начал крашить.

Точно, у меня Notepad++ v. 8.2.1. В новых версиях что-то не заработало и я остался на старой. Буду бороться с прошлым .
Еще раз большое Вам спасибо и плюс.

6

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Обновил Notepad++ до v. 8.5.2, Ваш код работает. И спасибо за инфу, что структура Sci_TextRange изменилась, заодно обновил плагин.

7

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Да, помню, сначала тоже какие-то плагины отказались работать, потом обновились.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Перешел на Notepad++ 8.5.2. Все нужные мне команды Scintilla работают из AHK, кроме SCI_GETCURLINE — получить текст текущей строки https://www.scintilla.org/ScintillaDoc. … GETCURLINE.
В Notepad++ 8.2.1 пользовался Вашим кодом https://forum.script-coding.com/viewtop … 88#p112088, просто поменяв SCI_GETSELTEXT на SCI_GETCURLINE (см. ниже код). И все работало.

В Notepad++ 8.5.2 команда SCI_GETSELTEXT работает, а SCI_GETCURLINE возвращает пустую строку. Вроде бы SCI_GETCURLINE никак не связана с изменениями структур.

Что могло случиться?

ТекстТекущейСтроки := ТекстТекущейCтрокиNotepad(WinExist("ahk_class Notepad++")) ;получаем текст текущей строки в Notepad++. См. C:\!!Work\AutoHotkey.docx{[Текст_текущей_строки]}
MsgBox ТекстТекущейСтроки: '%ТекстТекущейСтроки%'

ТекстТекущейCтрокиNotepad(hNPP)  {
;Получить текст текущей строки Notepad++

   if !WinExist("ahk_id" hNPP)  {
      MsgBox, Окно Notepad++ не найдено
      Return
   }
   WinGet, PID, PID
   ControlGetFocus, focused
   if InStr(focused, "Scintilla")
      ControlGet, hScintilla, hwnd,, % focused
   else
      hScintilla := GetCurrentScintilla(PID)
   
   if !hScintilla  {
      MsgBox, Не удалось определить текущее поле редактирования
      Return
   }
   WinWait, ahk_id %hScintilla%
   ; SendMessage, SCI_GETSELTEXT := 2161 ;заменил
   SendMessage, SCI_GETCURLINE := 2027
      
   charCount := ErrorLevel
   if (charCount < 2)
      Return
   
   remoteBuff := new RemoteBuffer(PID, charCount)
   ; SendMessage, SCI_GETSELTEXT,, remoteBuff.ptr  ;заменил
   SendMessage, SCI_GETCURLINE,, remoteBuff.ptr
   
   VarSetCapacity(localBuff, charCount, 0)
   remoteBuff.Read(&localBuff, charCount)
   SendMessage, SCI_GETCODEPAGE := 2137
   Return StrGet(&localBuff, "CP" . ErrorLevel)
}

GetCurrentScintilla(PID)  {
   remoteBuff := new RemoteBuffer(PID, 4)
   SendMessage, NPPM_GETCURRENTSCINTILLA := 2028,, remoteBuff.ptr
   VarSetCapacity(localBuff, 4, 0)
   remoteBuff.Read(&localBuff, 4)
   
   if handles := GetScintillaHandles()
      Return handles[ NumGet(localBuff, "Int") + 1 ]
}


GetScintillaHandles()  {
   WinGet, list, ControlList
   WinGet, listHwnd, ControlListHwnd
   arrClassNN := StrSplit(list, "`n")
   arrHwnd := StrSplit(listHwnd, "`n")
   for k, v in arrClassNN  {
      if InStr(prevClass, "Scintilla") && InStr(v, "Scintilla")
         Return [ arrHwnd[k - 1], arrHwnd[k] ]
      prevClass := v
   }
}


class RemoteBuffer
{
   __New(PID, size)  {
      static PROCESS_VM_OPERATION := 0x8, PROCESS_VM_WRITE := 0x20, PROCESS_VM_READ := 0x10
           , MEM_COMMIT := 0x1000, PAGE_READWRITE := 0x4
         
      if !(this.hProc := DllCall("OpenProcess", UInt, PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, Int, 0, UInt, PID, Ptr))
         Return
      
      if !(this.ptr := DllCall("VirtualAllocEx", UInt, this.hProc, UInt, 0, UInt, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
         Return, "", DllCall("CloseHandle", Ptr, this.hProc)
   }
   
   __Delete()  {
      DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
      DllCall("CloseHandle", Ptr, this.hProc)
   }
   
   Read(pLocalBuff, size, offset = 0)  {
      if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, pLocalBuff, UInt, size, UInt, 0)
         Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка """ A_LastError """", Str, "", UInt, 0)
   }
}

9

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Так смотрите описание SCI_GETCURLINE, там первый параметр должен быть.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10 (изменено: Vadus, 2023-04-27 08:43:25)

Re: AHK: Получить текст диапазона в Notepad++ с помощью Scintilla

Конечно, спасибо, вместо

SendMessage, SCI_GETCURLINE,, remoteBuff.ptr

должно быть

SendMessage, SCI_GETCURLINE, charCount, remoteBuff.ptr

Но странно, что в старой версии Notepad++ с этой ошибкой работало...