RichEdit controls: get/set text

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

29 May 2021, 10:15

@neogna2
Try

Code: Select all

rng.Para.SetLineSpacing(3, 400)
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

29 May 2021, 12:32

teadrinker wrote:
29 May 2021, 10:15
Try

Code: Select all

rng.Para.SetLineSpacing(3, 400)
Works, thank you. I thought I had tried that already :oops:

I easily get lost in the Microsoft documentation for these types of objects. There are so many wordfilled pages that I have a hard time getting a good overview of the overall hiearchy of objects and methods/properties. Plus on top of that figuring out the correct method name to use. In the case of ITextPara::SetLineSpacing is it SetLineSpacing but for ITextRange::SetFont it is Font without the prefix "Set". Do you have some general tips on how you go about that? Is there some other tool or site that complements the Microsoft documentation? I use the terrific https://www.magnumdb.com/ for looking up constants. Something similar but for looking up the correct method name COM methods would help.
teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

29 May 2021, 15:30

neogna2 wrote: Do you have some general tips on how you go about that?
I didn't find any documentation about IText... interfaces for scripting either, so I just tried all possible variants. :)
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: RichEdit controls: get/set text

29 May 2021, 18:41

I found documentation here:
https://chromium.googlesource.com/chromium/deps/perl/+/refs/heads/main/c/i686-w64-mingw32/include/tom.h
Also You can examine ole objects with different software.
For example with MiTeC OLE/COM Object Explorer
http://www.mitec.cz/Downloads/OLExp.zip
But tom header does not help.
As I understand We should remove get/set and use assigning when We get/set 1 value.
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

01 Jun 2021, 02:31

teadrinker wrote:
29 May 2021, 15:30
I didn't find any documentation about IText... interfaces for scripting either, so I just tried all possible variants. :)
haha, who needs documentation! :)
malcev wrote:
29 May 2021, 18:41
As I understand We should remove get/set and use assigning when We get/set 1 value.
Good to know!
Thanks. It covers some of the interfaces but not all, for example not iTextRange2 which has methods for RichEdit tables.
malcev wrote:
29 May 2021, 18:41
Also You can examine ole objects with different software.
For example with MiTeC OLE/COM Object Explorer
http://www.mitec.cz/Downloads/OLExp.zip
But tom header does not help.
I tried both the MiTeC tool and the Microsoft tool OLE COM Object Viewer (filename oleview.exe , I found it in folder C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\ )
The Microsoft tool lets me navigate to interfaces and find ITextDocument in the list, but get error "IDataObject interface viewer only supports IID_IDataObject" if I doubleclick to open it for details.
The MiTeC tool lets me access those details. But it also appears to not show all interfaces. In particular I don't find ITextRow.

I am trying to use ITextRow to make changes to Rich Edit tables (rows and cells). One perhaps basic question I have is how to return multiple values from COM method calls like GetCellBorderWidths.
https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrow-getcellborderwidths
https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrow-setcellborderwidths
I tried calling it with both variables and variable references without success. Do we perhaps need to use ComObjArray?

MiTeC OLE/COM Object Explorer screenshot
Attachments
1.png
MiTeC tool screenshot
1.png (53.21 KiB) Viewed 1242 times
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: RichEdit controls: get/set text

01 Jun 2021, 08:07

Write Your full code.
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

01 Jun 2021, 09:40

malcev wrote:
01 Jun 2021, 08:07
Write Your full code.
Here is teadrinker's working COM Rich Edit code from above but I commented out the function call that opens WordPad, because irrelevant here, and added lines starting at ;######### start GetCellBorderWidths attempts #########. the InsertTable line works. After that the numbered items 1 2 3 4A 4B 4C 4D shows what I have tried without success and the comments show the error codes. I'm bad with COM code and have never attempted to use SafeArray before. I'm basically fumbling around :oops:

Code: Select all

#NoEnv

ComObjFromMyRichEdit()
;ComObjFromExternalRichEdit()
Esc:: ExitApp

ComObjFromMyRichEdit()  {
   hGui := CreateGui()
   hRichEdit := CreateRichEdit(hGui, 300, 100, 400, 400)  ; X Y W H
   oWordPad := GetComFromMyRE(hRichEdit)
   AddEventHandler( Func("EventHandler"), hGui, hRichEdit, oWordPad )
   Actions(oWordPad)
}

ComObjFromExternalRichEdit()  {
   hRichEdit := GetRichEdit(720, 100)  ; X Y
   oWordPad := GetComFromExternalRE(hRichEdit)
   Actions(oWordPad)
}

GetComFromMyRE(hRichEdit)  {
   static EM_GETOLEINTERFACE := 0x43C, VT_DISPATCH := 9, F_OWNVALUE := 1
        , IID_ITextDocument2 := "{01C25500-4268-11D1-883A-3C8B00C10000}"
        , IID_ITextDocument  := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
        
   VarSetCapacity(buff, A_PtrSize, 0)
   DllCall("SendMessage", Ptr, hRichEdit, UInt, EM_GETOLEINTERFACE, Ptr, 0, Ptr, &buff)
   pITextDocument := ComObjQuery(pIRichEditOle := NumGet(buff), IID_ITextDocument2)
   ObjRelease(pObj)
   Return ITextDocument := ComObject(VT_DISPATCH, pITextDocument, F_OWNVALUE)
}

GetComFromExternalRE(hRichEdit)  {
   Return AccObjectFromWindow(hRichEdit, OBJID_NATIVEOM := -16)
}

AccObjectFromWindow(hWnd, idObject = 0)  {
   static IID_IDispatch   := "{00020400-0000-0000-C000-000000000046}"
        , IID_IAccessible := "{618736E0-3C3D-11CF-810C-00AA00389B71}"
        , OBJID_NATIVEOM  := 0xFFFFFFF0, VT_DISPATCH := 9, F_OWNVALUE := 1
        , h := DllCall("LoadLibrary", Str, "oleacc", Ptr)
        
   VarSetCapacity(IID, 16), idObject &= 0xFFFFFFFF
   DllCall("ole32\CLSIDFromString", Str, idObject = OBJID_NATIVEOM ? IID_IDispatch : IID_IAccessible, Ptr, &IID)
   if DllCall("oleacc\AccessibleObjectFromWindow", Ptr, hWnd, UInt, idObject, Ptr, &IID, PtrP, pAcc) = 0
      Return ComObjEnwrap(VT_DISPATCH, pAcc, F_OWNVALUE)
}

Actions(ITextDocument)  {
   rng := ITextDocument.Range(0, 0)
   
   ;######### start GetCellBorderWidths attempts #########
   ;https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrange2-inserttable
   ;"the borders are solid black with 0.5 point widths"
   rng.InsertTable(3, 3 ,0) ;create 3x3 table
   
   ;https://docs.microsoft.com/en-us/windows/win32/api/tom/nn-tom-itextrow
   ;https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrow-getcellborderwidths
   ;HRESULT GetCellBorderWidths(
   ;  long *pduLeft,
   ;  long *pduTop,
   ;  long *pduRight,
   ;  long *pduBottom
   ;);
   ; long = AHK "Int"
  
   ;1
   ;pduLeft := pduTop := pduRight := pduBottom := 0
   ;rng.Row.GetCellBorderWidths(pduLeft, pduTop, pduRight, pduBottom)
   ;"Error:  0x80020005 - Type mismatch."
  
   ;2
   ;pduLeft := pduTop := pduRight := pduBottom := 0
   ;rng.Row.GetCellBorderWidths(&pduLeft, &pduTop, &pduRight, &pduBottom)
   ;"Critical Error:  Invalid memory read/write."
  
   ;3
   ;VarSetCapacity(pduLeft, 4, 0)
   ;VarSetCapacity(pduTop, 4, 0)
   ;VarSetCapacity(pduRight, 4, 0)
   ;VarSetCapacity(pduBottom, 4, 0)
   ;rng.Row.GetCellBorderWidths(&pduLeft, &pduTop, &pduRight, &pduBottom)
   ;"Critical Error:  Invalid memory read/write."

   ;4 attempts with SafeArray
   ;https://www.autohotkey.com/docs/commands/ComObjArray.htm
   ;"Creates a SafeArray for use with COM"
   ;"ArrayObj := ComObjArray(VarType, Count1 , Count2, ... Count8)"
   ;https://www.autohotkey.com/docs/commands/ComObjType.htm
   ;"VT_UI2       :=   0x12  ; 16-bit unsigned int"
   ;SafeArray := ComObjArray(VT_UI2 := 0x12, 4)
   
   ;4A
   ;rng.Row.GetCellBorderWidths(SafeArray)
   ;"Error:  0x80020005 - Type mismatch."

   ;4B
   ;rng.Row.GetCellBorderWidths(&SafeArray)
   ;"Error:  0x8002000E - Invalid number of parameters."

   ;4C
   ;rng.Row.GetCellBorderWidths(SafeArray.0, SafeArray.1, SafeArray.2, SafeArray.3)
   ;"Critical Error:  Invalid memory read/write."

   ;4D
   ;rng.Row.GetCellBorderWidths(&SafeArray.0, &SafeArray.1, &SafeArray.2, &SafeArray.3)
   ;"Error:  0x80020005 - Type mismatch."

    MsgBox % "end GetCellBorderWidths attempts"
    ExitApp
   ;######### end GetCellBorderWidths attempts #########
   
   rng.Font.Name := "Calibri"
   rng.Font.Size := 24
   rng.Text := "AutoHotkey forever!`n"
   rng.MoveEnd(1, -10)
   rng.Font.BackColor := 0xFF
   
   rng.MoveEnd(1, 9)
   rng.Start := 11
   rng.Font.BackColor := 0xFF8844
   rng.Font.ForeColor := 0xFFFFFF
   rng.Font.Name := "Bradley Hand ITC"
   
   rng.Start := ++rng.End
   SetDefaultFont(rng)
   rng.Text := "This is " . ComObjType(ITextDocument, "Name") . " implementation."
   sel := ITextDocument.Selection
   sel.Start := sel.End := rng.End
}

CreateGui()  {
   Gui, New, +hwndhGui
   Gui, Margin, 0, 0
   OnMessage(0x112, "WM_SYSCOMMAND")
   Return hGui
}

WM_SYSCOMMAND(wp)  {
   if (wp = 0xF060)  ; SC_CLOSE
      ExitApp
}

CreateRichEdit(hGui, xGui, yGui, width, height)  {
   static hLbr := DllCall("LoadLibrary", Str, "Msftedit.dll", Ptr)
       , _styles := { WS_VSCROLL: 0x200000, ES_SELECTIONBAR: 0x1000000
                    , ES_MULTILINE: 4, ES_AUTOVSCROLL: 0x40, ES_NOHIDESEL: 0x100
                    , ES_WANTRETURN: 0x1000, ES_SAVESEL: 0x8000 }, styles := 0
   for k, v in _styles
      styles |= v
   Gui, %hGui%: Add, Custom, ClassRICHEDIT50W w%width% h%height% hwndhRichEdit +%styles%
   GuiControl, Move, %hRichEdit%, w%width% h%height%
   Gui, %hGui%: Show, x%xGui% y%yGui%, My WordPad
   Return hRichEdit
}

AddEventHandler(funcObj, hGui, hRichEdit, ITextDocument)  {
   handler := funcObj.Bind(ITextDocument)
   GuiControl, %hGui%: +g, %hRichEdit%, % handler
}

EventHandler(ITextDocument)   {
   if (A_EventInfo = 0x400) {
      rng := ITextDocument.Range(0, 2)
      ( StrLen(rng.Text) = 1 && SetDefaultFont(ITextDocument) )
   }
}

SetDefaultFont(obj)  {
   obj.Font.Size := 14
   obj.Font.Name := "Calibri"
   obj.Font.BackColor := 0xFFFFFF
   obj.Font.ForeColor := 0xAA4400
}

GetRichEdit(x, y)  {
   if (x = "exit")  {
      Process, Close, %y%
      Return
   }
   Run, wordpad,,, PID
   WinWait, ahk_pid %PID%
   OnExit( Func(A_ThisFunc).Bind("exit", PID) )
   WinMove,,, x, y
   ControlGet, hRichEdit, hwnd,, RICHEDIT50W1
   Return hRichEdit
}
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: RichEdit controls: get/set text

01 Jun 2021, 11:11

How You can get width of cell borders if You dont have them?

Code: Select all

msgbox % rng.Row.CellCount
Function needs byref int.
You have to send it like this

Code: Select all

VarSetCapacity(var, 4, 0)
ComObject(0x4003, &var)
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

02 Jun 2021, 06:02

malcev wrote:
01 Jun 2021, 11:11
Function needs byref int.
You have to send it like this

Code: Select all

VarSetCapacity(var, 4, 0)
ComObject(0x4003, &var)
Thanks. I had missed the second usage pattern on the ComObjActive docs page. The code to get multiple values from the COM method call would go like this then

Code: Select all

VarSetCapacity(pduLeft, 4, 0)
VarSetCapacity(pduTop, 4, 0)
VarSetCapacity(pduRight, 4, 0)
VarSetCapacity(pduBottom, 4, 0)

;https://www.autohotkey.com/docs/commands/ComObjActive.htm#ByRef
;https://www.autohotkey.com/docs/commands/ComObjType.htm#vt
; VT_BYREF := 0x4000 ; Pointer to another type of value
; VT_I4 := 3 ; 32-bit signed int
; VT_BYREF | VT_I4 = 0x4003

rng.Row.GetCellBorderWidths(ComObj(0x4003, &pduLeft)
   , ComObj(0x4003, &pduTop)
   , ComObj(0x4003, &pduRight)
   , ComObj(0x4003, &pduBottom) )
... if, that is, we also update that code with an extra step to get the cells, because as you showed rng.Row.CellCount returns 0.
malcev wrote:
01 Jun 2021, 11:11
How You can get width of cell borders if You dont have them?

Code: Select all

msgbox % rng.Row.CellCount
I had assumed the GetCellBorderWidths could operate directly on a Row object (apply to all cells in that row) since it is a methods for ITextRow. But apparently not. I'm stumped on the extra step however. These give errors

Code: Select all

MsgBox % rng.Cells() ; "Error:  0x80004001 - Not implemented"
MsgBox % rng.row.Cells() ; "Error:  0x80020006 - Unknown name."
Predictably since the ITextRange2::GetCells page says "Not implemented" and there is no GetCells method for ITextRow. But I don't see what other method to use to get the cells. Given that GetCellBorderWidths and other methods that act on cells are implemented there must be some such method.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: RichEdit controls: get/set text

02 Jun 2021, 16:32

I do not understand what do You want.
Also I do not know this interface and not-interested to examine it.
But as I see here is written
Gets the row properties in the currently selected row.
https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrange2-getrow
Are You sure that needed row is selected.
May be You need to call SetIndex method to select what You need
https://docs.microsoft.com/en-us/windows/win32/api/tom/nf-tom-itextrange-setindex
But as I say before I am too lazy to test it.
Link to tom.h
https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/um/TOM.h
neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

03 Jun 2021, 05:37

malcev wrote:
02 Jun 2021, 16:32
I do not understand what do You want.
My general goal was to learn how to to create and control Rich Edit tables with data and different styling (border weight, border color, ...) through COM. To move toward that goal I began experimenting with a few methods, to get a first grip on how to use them.
I was hoping there was a simple way to select a table or a row in a table and iterate over its items and use get/set methods on the item (something like For Cell in rng.Row ), but it seems such operations has to go through an extra layer of repeated Range getting and setting and character counting. I haven't found any simple examples on how to do that at Microsoft or in the AHK forums or elsewhere. I've gotten stuck enough times on this that I'm for now giving up on the whole idea of using Rich Edit tables and will use some other type of GUI control instead. I learned some things about COM along the way, so there is that at least. Thanks for the help and suggestions.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: RichEdit controls: get/set text

03 Jun 2021, 06:52

It is not difficult, just need to read carefully manual.

Code: Select all

   rng.InsertTable(3, 3, 0) ;create 3x3 table
   rng.SetIndex(tomTable := 15, 1, 0)
   textRow := rng.Row
   loop % textRow.CellCount
   {
      textRow.CellIndex := A_Index-1
      textRow.CellWidth := 1000
      textRow.SetCellBorderColors(123456,123456,123456,123456)
   }
   textRow.Apply(-1, tomRowApplyDefault := 0)
   msgbox test

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Chunjee, Descolada, GroggyOtter, Mateusz53, moltenchees and 165 guests