Read Text from PureBasic Scintilla Control from outside

Share your advanced PureBasic knowledge/code with the community.
Axolotl
Enthusiast
Enthusiast
Posts: 435
Joined: Wed Dec 31, 2008 3:36 pm

Read Text from PureBasic Scintilla Control from outside

Post by Axolotl »

Hi everybody

maybe this can be of any help... for example the development process of IDE tools could be a little bit easier.
Anyway, I use this code for a small window previewer with a directly update of changes. (not really bullet proof)

Have fun with it.

Code: Select all

;' THE CODE IS PROVIDED "AS IS" With NO GUARANTEES OF ANY KIND!
;' USE THIS AT YOUR OWN RISK
;' Windows only
;' Tested on Win 10 with PB 5.60 (64)
;' --- 

EnableExplicit

;­--== Scintilla Procedures ==-----------------------------------------------
;­-- (first Character is ALT+0173 !---

;'
;' Define WindowID.i = Val(GetEnvironmentVariable("PB_TOOL_MAINWINDOW"))

Define s_hSciWnd ;­ semi global scintilla handle 


;­ new helper procedure ... 

Procedure.s GetClassName(hWnd)
  Protected cn${256} 
  GetClassName_(hWnd, @cn$, 256)                                       ;:Debug "GetClassname("+Str(hWnd)+") -> '"+cn$+"'"
  ProcedureReturn cn$ 
EndProcedure

Procedure EnumChildWndProc(hWnd, lParam)
  Shared s_hSciWnd
  Protected result = #False, cn$
 
  If hWnd
    cn$ = GetClassName(hWnd)                        ;:Debug cn$+", hWnd="+Str(hWnd)
    If cn$ = "Scintilla"
      s_hSciWnd = hwnd
    Else
      result = #True ;' continue
    EndIf 
  EndIf
  ProcedureReturn result
EndProcedure

Procedure.i Scintilla_GetHandle()  ;' returns hWnd of the 'Scintilla Control'
  Shared s_hSciWnd
  Protected hWnd, cap${256}

  If s_hSciWnd = 0
    hWnd = FindWindowEx_(GetDesktopWindow_(), 0, "WindowClass_2", 0)     :Debug "WindowClass_2, hWnd="+Str(hWnd)
    If hWnd <> 0 ;And WindowIsPureBasic(hWnd)
      SendMessage_(hWnd, #WM_GETTEXT, 255, @cap$) 
      If Left(cap$, 9) = "PureBasic" 
        s_hSciWnd = 0
        EnumChildWindows_(hWnd, @EnumChildWndProc(), 0)
      EndIf
    EndIf  
    If s_hSciWnd <> 0
      Debug "  Scintilla found."
    EndIf
  EndIf
  ProcedureReturn s_hSciWnd
EndProcedure


Procedure.i Scintilla_CurrLine()  ;' get the current line, where the cursor is!
  Protected hSciWnd, pos, Line

  hSciWnd = Scintilla_GetHandle()
  If hSciWnd
    pos = SendMessage_(hSciWnd, #SCI_GETCURRENTPOS, 0, 0)         ;:Debug "CurrentPos="+Str(pos)
    Line = SendMessage_(hSciWnd, #SCI_LINEFROMPOSITION, pos, 0)   ;:Debug "Line="+Str(Line)
  EndIf
  ProcedureReturn Line
EndProcedure

Procedure.s Scintilla_GetTextline(Line.i=#PB_Any)
  Protected Result$, ProcessID, PID, hSciWnd
  Protected *MemoryID, *Buffer, Format, Length, BytesRead, pos, temp

  hSciWnd = Scintilla_GetHandle()
  If hSciWnd And GetWindowThreadProcessId_(hSciWnd, @PID)
    ProcessID = OpenProcess_(#PROCESS_ALL_ACCESS, #False, PID)
  EndIf

  If ProcessID
    Select SendMessage_(hSciWnd, #SCI_GETCODEPAGE, #Null, #Null)
      Case 0     : Format = #PB_Ascii : Debug "ASCII"
      Case 65001 : Format = #PB_UTF8  : Debug "UTF8"
    EndSelect
 
    If Line = #PB_Any
      pos = SendMessage_(hSciWnd, #SCI_GETCURRENTPOS, 0, 0)         ;:Debug "CurrentPos="+Str(pos)
      Line = SendMessage_(hSciWnd, #SCI_LINEFROMPOSITION, pos, 0)   ;:Debug "Line="+Str(Line)
    EndIf

    Length = SendMessage_(hSciWnd, #SCI_LINELENGTH, Line, 0)
    Length * StringByteLength("A", Format)

    *Buffer = AllocateMemory(Length+SizeOf(Character))
    If *Buffer
      *MemoryID = VirtualAllocEx_(ProcessID, #Null, Length, #MEM_RESERVE|#MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
      If *MemoryID
        temp = SendMessage_(hSciWnd, #SCI_GETLINE, Line, *MemoryID)               ;: Debug "read chars: "+Str(temp)
        ReadProcessMemory_(ProcessID, *MemoryID, *Buffer, Length, @BytesRead)     ;: Debug "BytesRead="+Str(BytesRead)
        Result$ = PeekS(*Buffer, BytesRead, Format)                           ;­ correction: add the 'Format'
        VirtualFreeEx_(ProcessID, *MemoryID, Length, #MEM_RELEASE)
      EndIf
      FreeMemory(*Buffer)   
    EndIf
    CloseHandle_(ProcessID)
  EndIf
  ProcedureReturn Result$ 
EndProcedure

Procedure.s Scintilla_GetText()
  Protected Result$, ProcessID, PID, hSciWnd
  Protected *MemoryID, *Buffer, Format, Length, BytesRead

  hSciWnd = Scintilla_GetHandle()
  If hSciWnd And GetWindowThreadProcessId_(hSciWnd, @PID)
    ProcessID = OpenProcess_(#PROCESS_ALL_ACCESS, #False, PID)
  EndIf

  If ProcessID
    Select SendMessage_(hSciWnd, #SCI_GETCODEPAGE, #Null, #Null)
      Case 0     : Format = #PB_Ascii : Debug "ASCII"
      Case 65001 : Format = #PB_UTF8  : Debug "UTF8"
    EndSelect
 
    Length = SendMessage_(hSciWnd, #SCI_GETTEXTLENGTH, 0, 0) ;:Debug "TextLength = "+Str(Length)
    Length * StringByteLength("A", Format)                   ;:Debug "TextLength = "+Str(Length)

    *Buffer = AllocateMemory(Length+SizeOf(Character))
    If *Buffer
      *MemoryID = VirtualAllocEx_(ProcessID, #Null, Length, #MEM_RESERVE|#MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
      If *MemoryID
        SendMessage_(hSciWnd, #SCI_GETTEXT, Length, *MemoryID)
        ReadProcessMemory_(ProcessID, *MemoryID, *Buffer, Length, @BytesRead)     ;: Debug "BytesRead="+Str(BytesRead)
        Result$ = PeekS(*Buffer, BytesRead, Format)                           ;­ correction: add the 'Format'
        VirtualFreeEx_(ProcessID, *MemoryID, Length, #MEM_RELEASE)
      EndIf
      FreeMemory(*Buffer)
    EndIf
    CloseHandle_(ProcessID)
  EndIf
  ProcedureReturn Result$ 
EndProcedure


;­--== Test Procedures ==----------------------------------------------------

Enumeration Gadgets
  #Edt
  #BtnReadLine
  #BtnReadAll
EndEnumeration

If OpenWindow(0, 20, 20, 400, 200, "Pure Preview ", #PB_Window_SystemMenu|#PB_Window_Tool|#PB_Window_SizeGadget)
  ButtonGadget(#BtnReadAll, 2, 2, 76, 18, "Read All")
  ButtonGadget(#BtnReadLine, 80, 2, 76, 18, "Read Line")
  EditorGadget(#Edt, 0, 24, 400, 200-26)

  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_SizeWindow
        ResizeGadget(#Edt, #PB_Ignore, #PB_Ignore, WindowWidth(0), WindowHeight(0)-26)
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #BtnReadAll
            ClearGadgetItems(#Edt)
            SetGadgetText(#Edt, Scintilla_GetText())
          Case #BtnReadLine
            ClearGadgetItems(#Edt)
            SetGadgetText(#Edt, Scintilla_GetTextline())
        EndSelect
      Case #PB_Event_CloseWindow
        Break
    EndSelect
  ForEver
EndIf
;-^ Bottom of File 
Take care.
Andreas
Last edited by Axolotl on Sun May 28, 2017 5:52 pm, edited 1 time in total.
Mostly running PureBasic <latest stable version and current alpha/beta> (x64) on Windows 11 Home
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Read Text from PureBasic Scintilla Control from outside

Post by Kwai chang caine »

Cool !!! Can be very useful :D
Works fine, thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Axolotl
Enthusiast
Enthusiast
Posts: 435
Joined: Wed Dec 31, 2008 3:36 pm

Re: Read Text from PureBasic Scintilla Control from outside

Post by Axolotl »

Hi KCC,
thank you, for your response.
I like the browsing for solutions and sometimes I have borrowed some ideas from the forum.
So I thought it is time to give some ideas back to the community.
Have a good time.
Andreas
Mostly running PureBasic <latest stable version and current alpha/beta> (x64) on Windows 11 Home
Axolotl
Enthusiast
Enthusiast
Posts: 435
Joined: Wed Dec 31, 2008 3:36 pm

Re: Read Text from PureBasic Scintilla Control from outside

Post by Axolotl »

Hi everyone,

I have changed some lines of code.
a) use EnumWindow
b) works on win10 with PB 5.60 (64)

Thanks to KCC for using and fault detection.
Take care.
Andreas
Mostly running PureBasic <latest stable version and current alpha/beta> (x64) on Windows 11 Home
fryquez
Enthusiast
Enthusiast
Posts: 362
Joined: Mon Dec 21, 2015 8:12 pm

Re: Read Text from PureBasic Scintilla Control from outside

Post by fryquez »

Code: Select all

Procedure.s GetClassName(hWnd)
  Protected cn${256} 
  GetClassName_(hWnd, @cn$, 256)                                       ;:Debug "GetClassname("+Str(hWnd)+") -> '"+cn$+"'"
  ProcedureReturn cn$ 
EndProcedure
Hi, unlike the Space() function defining a string using {n} does not allocate additional memory for the terminating null character.
So the GetClassName_(hWnd, @cn$, 256) should be GetClassName_(hWnd, @cn$, 255)
Post Reply