RichEdit controls: get/set text

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

RichEdit controls: get/set text

15 Oct 2017, 17:08

I have a function that works to set the text of an internal/external RichEdit control when you specify raw text, or RTF text (text that starts '{\rtf'), but setting the RTF text so far only works with ANSI mode and not Unicode.

Also I have not found a way to get the RTF text directly from an external RichEdit control, note: this is not a priority for me, is this possible? A workaround would be to save the text using WordPad, as an rtf file, or perhaps copy and paste the text into an internal RichEdit control and retrieve the RTF text via EM_STREAMOUT (not yet tested), or if it's possible, to copy the text to the clipboard and retrieve the RTF text somehow.

Code: Select all

;tested on WordPad (Windows XP and Windows 7 versions)
q:: ;RichEdit control - set text (RTF)
ControlGet, hCtl, Hwnd,, RICHEDIT50W1, A
FormatTime, vDate,, HH:mm dd/MM/yyyy
vRtf := "{\rtf{\b " vDate "}}"
JEE_RichEditSetText(hCtl, vRtf)
return

;==================================================

JEE_RichEditGetText(hCtl)
{
	ControlGetText, vText,, % "ahk_id " hCtl
	return vText
}

;==================================================

JEE_RichEditSetText(hCtl, vText, vFlags:=0x0, vCP:=0x0)
{
	vScriptPID := DllCall("kernel32\GetCurrentProcessId", UInt)
	WinGet, vPID, PID, % "ahk_id " hCtl
	if (vPID = vScriptPID)
		vIsLocal := 1, vPIs64 := (A_PtrSize=8)

	;ST_UNICODE := 0x8 ;ST_NEWCHARS := 0x4
	;ST_SELECTION := 0x2 ;ST_KEEPUNDO := 0x1
	;ST_DEFAULT := 0x0
	;CP_ACP := 0 ;Unicode (1200)
	VarSetCapacity(SETTEXTEX, 8)
	NumPut(vFlags, &SETTEXTEX, 0, "UInt") ;flags
	NumPut(vCP, &SETTEXTEX, 4, "UInt") ;codepage

	if (vCP = 0)
	{
		vSize := StrPut(vText, "CP0")
		VarSetCapacity(vOutput, vSize)
		StrPut(vText, &vOutput, "CP0")
	}
	else
	{
		vSize := StrLen(vText)*2+2
		vOutput := vText
	}

	if vIsLocal
		SendMessage, 0x461, % &SETTEXTEX, % &vOutput,, % "ahk_id " hCtl ;EM_SETTEXTEX := 0x461
	else
	{
		if !hProc := JEE_DCOpenProcess(0x438, 0, vPID)
			return
		if !pBuf := JEE_DCVirtualAllocEx(hProc, 0, 8+vSize, 0x3000, 0x4)
			return
		JEE_DCWriteProcessMemory(hProc, pBuf, &SETTEXTEX, 8, 0)
		JEE_DCWriteProcessMemory(hProc, pBuf+8, &vOutput, vSize, 0)
		SendMessage, 0x461, % pBuf, % pBuf+8,, % "ahk_id " hCtl ;EM_SETTEXTEX := 0x461
		JEE_DCVirtualFreeEx(hProc, pBuf, 0, 0x8000)
		JEE_DCCloseHandle(hProc)
	}
}

;==================================================

JEE_DCOpenProcess(vAccess, hInherit, vPID)
{
	return DllCall("kernel32\OpenProcess", UInt,vAccess, Int,hInherit, UInt,vPID, Ptr)
}
JEE_DCVirtualAllocEx(hProc, vAddress, vSize, vAllocType, vProtect)
{
	return DllCall("kernel32\VirtualAllocEx", Ptr,hProc, Ptr,vAddress, UPtr,vSize, UInt,vAllocType, UInt,vProtect, Ptr)
}
JEE_DCWriteProcessMemory(hProc, vBAddress, pBuf, vSize, vWritten)
{
	return DllCall("kernel32\WriteProcessMemory", Ptr,hProc, Ptr,vBAddress, Ptr,pBuf, UPtr,vSize, Ptr,vWritten)
}
JEE_DCReadProcessMemory(hProc, vBAddress, pBuf, vSize, vRead)
{
	return DllCall("kernel32\ReadProcessMemory", Ptr,hProc, Ptr,vBAddress, Ptr,pBuf, UPtr,vSize, Ptr,vRead)
}
JEE_DCVirtualFreeEx(hProc, vAddress, vSize, vFreeType)
{
	return DllCall("kernel32\VirtualFreeEx", Ptr,hProc, Ptr,vAddress, UPtr,vSize, UInt,vFreeType)
}
JEE_DCCloseHandle(hObject) ;e.g. hProc
{
	return DllCall("kernel32\CloseHandle", Ptr,hObject)
}

;==================================================
[Note: I worked on some related functions here: JEE_ClipboardSetHtml and JEE_ClipboardSetRtf]
GUI COMMANDS: COMPLETE RETHINK - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 34#p170834
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: RichEdit controls: get/set text

16 Oct 2017, 03:19

Not sure if this helps, but maybe it is somewhere here (Class_RichEdit.ahk)
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

17 Oct 2017, 04:48

RichEdit control has COM-interface similar to Word.Application. Do you consider using it?
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

17 Oct 2017, 10:31

Yes I would consider using that, for internal controls, and if it works, external controls also. Do you have any working code for that?
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

17 Oct 2017, 12:02

I've been working on getting/setting a stream for an internal RichEdit control, it works to get the stream (e.g. get RTF), but at the moment, it doesn't work to set the stream.

I'm working on various functions to get/set the text in internal/external controls, and this is the last function I need to complete (unless objects provide an additional way as mentioned above, that would mean perhaps 2 more functions).

Code: Select all

q:: ;richedit (internal control) - get stream
WinGet, hWnd, ID, A
ControlGet, hCtl, Hwnd,, RICHEDIT50W1, A
MsgBox, % vText := JEE_RichEditGetStream(hCtl, 0x11)
MsgBox, % vRtf := JEE_RichEditGetStream(hCtl, 0x2)
ControlSetText, RICHEDIT50W1, % "", % "ahk_id " hWnd
MsgBox, % vText := JEE_RichEditGetStream(hCtl, 0x11)
MsgBox, % vRtf := JEE_RichEditGetStream(hCtl, 0x2)
return

;not working
w:: ;richedit (internal control) - set stream
ControlGet, hCtl, Hwnd,, RICHEDIT50W1, A
vText := "abcdefghijklmnopqrstuvwxyz"
JEE_RichEditSetStream(hCtl, 0x11, vText)
Sleep, 2000
FormatTime, vDate,, HH:mm dd/MM/yyyy
vRtf := "{\rtf{\b " vDate "}}"
JEE_RichEditSetStream(hCtl, 0x2, vText)
return

e:: ;richedit - create GUI
DetectHiddenWindows, On
Gui, New, +HwndhGui, MyWinTitle
hModuleME := DllCall("kernel32\LoadLibrary", Str,"msftedit.dll", Ptr)
Gui, Add, Custom, ClassRICHEDIT50W r1
ControlSetText, RICHEDIT50W1, % "RICH 1", % "ahk_id " hGui
Gui, Show, w300 h300
return

;==================================================

;e.g.
;q::
;ControlGet, hCtl, Hwnd,, RICHEDIT50W1, A
;MsgBox, % JEE_RichEditGetStream(hCtl, 0x11)
;MsgBox, % JEE_RichEditGetStream(hCtl, 0x2)
;return

;only works on internal controls
JEE_RichEditGetStream(hCtl, vFormat)
{
	static  pFunc := RegisterCallback("JEE_RichEditGetStreamCallback")
	;SFF_SELECTION := 0x8000 ;SFF_PLAINRTF := 0x4000
	;SF_USECODEPAGE := 0x20 ;SF_UNICODE := 0x10
	;SF_RTFNOOBJS := 0x3 ;SF_RTF := 0x2 ;SF_TEXT := 0x1
	vSize := A_PtrSize=8?20:12
	VarSetCapacity(EDITSTREAM, vSize, 0)
	NumPut(vFormat, &EDITSTREAM, 0, "UPtr") ;dwCookie
	NumPut(pFunc, &EDITSTREAM, A_PtrSize=8?12:8, "Ptr") ;pfnCallback
	SendMessage, 0x44A, % vFormat, % &EDITSTREAM,, % "ahk_id " hCtl ;EM_STREAMOUT := 0x44A
	return JEE_RichEditGetStreamCallback("Get", 0, 0, 0)
}

;==================================================

JEE_RichEditGetStreamCallback(dwCookie, pbBuff, cb, pcb)
{
	static vTextOut := ""
	if (cb > 0)
	{
		if (dwCookie & 0x10)
			vTextOut .= StrGet(pbBuff, cb/2, "UTF-16")
		else
			vTextOut .= StrGet(pbBuff, cb, "CP0")
		return 0
	}
	if (dwCookie = "Get")
	{
		vTemp := vTextOut
		vTextOut := ""
		return vTemp
	}
	return 1
}

;==================================================

;not working
;only works on internal controls
JEE_RichEditSetStream(hCtl, vFormat, ByRef vText)
{
	static pFunc := RegisterCallback("JEE_RichEditSetStreamCallback")
	;SFF_SELECTION := 0x8000 ;SFF_PLAINRTF := 0x4000
	;SF_UNICODE := 0x10
	;SF_RTF := 0x2 ;SF_TEXT := 0x1
	vSize := A_PtrSize=8?20:12
	VarSetCapacity(EDITSTREAM, vSize, 0)
	NumPut(&vText, &EDITSTREAM, 0, "UPtr") ;dwCookie
	NumPut(pFunc, &EDITSTREAM, A_PtrSize=8?12:8, "Ptr") ;pfnCallback
	vSize2 := (StrLen(vText)+1)*((vFormat & 0x10)?2:1)
	if !(vFormat & 0x10)
		StrPut(vText, &vText, "CP0")
	JEE_RichEditSetStreamCallback("Init", vSize2, 0, 0)
	SendMessage, 0x449, % vFormat, % &EDITSTREAM,, % "ahk_id " hCtl ;EM_STREAMIN := 0x449
	vRet := ErrorLevel
	return vRet
}

;==================================================

;not working
JEE_RichEditSetStreamCallback(dwCookie, pbBuff, cb, pcb)
{
	static vRemain, vOffset
	if (dwCookie = "Init")
	{
		vRemain := pbBuff, vOffset := 0
		return
	}
	if (vRemain <= cb)
	{
		DllCall("kernel32\RtlMoveMemory", Ptr,pbBuff, Ptr,dwCookie+vOffset, UPtr,vRemain)
		NumPut(vRemain, pcb, 0, "Ptr")
		vOffset += vRemain
		vRemain := 0
		return 1
	}
	else
	{
		DllCall("kernel32\RtlMoveMemory", Ptr,pbBuff, Ptr,dwCookie+vOffset, UPtr,cb)
		NumPut(cb, pcb, 0, "Ptr")
		vOffset += cb
		vRemain -= cb
		return 0
	}
}

;==================================================
LINKS:
[this was useful as a template for getting a control's text (stream)]
cRichEdit - Standard RichEdit control for AutoHotkey scripts - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/1786 ... y-scripts/

[this has an example for setting a control's text (stream) based on a file (but not based on a variable)]
Class_RichEdit/Class_RichEdit.ahk at master · AHK-just-me/Class_RichEdit · GitHub
https://github.com/AHK-just-me/Class_Ri ... chEdit.ahk
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

17 Oct 2017, 14:16

jeeswg wrote:Yes I would consider using that, for internal controls, and if it works, external controls also. Do you have any working code for that?
Since I got interested in that just after your post and haven't found any examples for scripting, I don't have working code yet. But I'm going to have go at it, if I succeed, I'll post it here.
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

18 Oct 2017, 13:55

My exercises with RichEdit's COM-interfaces:

Code: Select all

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)
   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
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

18 Oct 2017, 14:21

If anyone can help me with my set stream function that would be most appreciated.

==================================================

Well done teadrinker, this is really good. It works on my Windows 7 version of WordPad, and on the Windows XP version that I also have.

The example script writes to an external RichEdit control, is it easy to read from an external RichEdit control? [EDIT: The script writes the same contents to both an internal and an external control, but there isn't a wholesale copy/paste, I'm experimenting ...]

It's only been one or two days since I first found out that RichEdit controls support objects, they really are quite powerful. You've also confirmed for me that they can do highlighting. This means now that RichEdit v. Scintilla is a tough choice. Thanks.

==================================================

My test script so far, which is basically the same as teadrinker's above.

Code: Select all

hGui := CreateGui()
hRE := CreateRichEdit(hGui, 300, 100, 400, 400)  ; X Y W H
oWordPad := GetComFromMyRE(hRE)
AddEventHandler( Func("EventHandler"), hGui, hRE, oWordPad )
Sleep, 2000

Actions(oWordPad)
Sleep, 2000

hRE2 := GetRichEdit(720, 100)
oWordPad2 := GetComFromExternalRE(hRE2)
Sleep, 2000

Actions(oWordPad2)
Sleep, 2000

rng := oWordPad.Range(0, 0)
rng.Text := "MORE"
rng := oWordPad2.Range(0, 0)
rng.Text := "MORE"
return
==================================================

[EDIT:] Btw are you using some documentation other than MSDN? Because it isn't even clear that you can do .Range(0, 10).Text, from what I've read.

The main things remaining that I've been struggling to do are: get/set all text/RTF.

Links:
ITextDocument interface (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
Text Object Model Interfaces (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
ITextRange interface (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: RichEdit controls: get/set text

18 Oct 2017, 18:18

Btw are you using some documentation other than MSDN? Because it isn't even clear that you can do .Range(0, 10).Text, from what I've read.
I didn't find any documentation, except the links you gave.
ITextDocument, Methods, Range —> ITextDocument::Range method —> ITextRange interface, Methods —> GetText, SetText
The problem is that method names for scripting may differ slightly, and one need to guess, how to change them to get them working for scripting.
The main things remaining that I've been struggling to do are: get/set all text/RTF.
Those interfaces give an access only to getting/setting plain text, anyway I didn't find any info about RTF.

Code: Select all

hRichEdit := GetRichEdit()
Return

$F10:: SetText( "The ITextDocument interface is the Text Object Model (TOM) top-level interface, which retrieves "
              . "the active selection and range objects for any story in the document—whether active or not."
              , hRichEdit )
              
$F11:: MsgBox, % GetText(hRichEdit)
              
SetText(text, hRichEdit)  {
   oWordPad := GetComFromExternalRE(hRichEdit)
   rng := oWordPad.Range(0, 0)
   rng.End := 0x7FFFFFFF
   rng.Text := text
}

GetText(hRichEdit)  {
   oWordPad := GetComFromExternalRE(hRichEdit)
   rng := oWordPad.Range(0, 0)
   rng.End := 0x7FFFFFFF
   Return rng.Text
}

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

GetRichEdit(mode := "")  {
   static PID
   if (mode = "exit")  {
      Process, Close, %PID%
      Return
   }
   Run, wordpad,,, PID
   WinWait, ahk_pid %PID%
   OnExit( Func(A_ThisFunc).Bind("exit", PID) )
   ControlGet, hRichEdit, hwnd,, RICHEDIT50W1
   Return hRichEdit
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

28 Oct 2017, 04:04

I managed to complete various functions for getting/setting the stream of a RichEdit control that belongs to the script.

Code: Select all

;==================================================

;===============
;e.g.
;q::
ControlGet, hCtl, Hwnd,, RICHEDIT50W1, A
MsgBox, % JEE_RichEditGetStream(hCtl, 0x11)
MsgBox, % JEE_RichEditGetStream(hCtl, 0x2)
return
;===============

;only works on internal controls
JEE_RichEditGetStream(hCtl, vFormat)
{
	static  pFunc := RegisterCallback("JEE_RichEditGetStreamCallback")
	vScriptPID := DllCall("kernel32\GetCurrentProcessId", UInt)
	WinGet, vPID, PID, % "ahk_id " hCtl
	if !(vPID = vScriptPID)
		return
	;SFF_SELECTION := 0x8000 ;SFF_PLAINRTF := 0x4000
	;SF_USECODEPAGE := 0x20 ;SF_UNICODE := 0x10
	;SF_RTFNOOBJS := 0x3 ;SF_RTF := 0x2 ;SF_TEXT := 0x1
	vSize := A_PtrSize=8?20:12
	VarSetCapacity(EDITSTREAM, vSize, 0)
	NumPut(vFormat, &EDITSTREAM, 0, "UPtr") ;dwCookie
	NumPut(pFunc, &EDITSTREAM, A_PtrSize=8?12:8, "Ptr") ;pfnCallback
	SendMessage, 0x44A, % vFormat, % &EDITSTREAM,, % "ahk_id " hCtl ;EM_STREAMOUT := 0x44A
	return JEE_RichEditGetStreamCallback("Get", 0, 0, 0)
}

;==================================================

JEE_RichEditGetStreamCallback(dwCookie, pbBuff, cb, pcb)
{
	static vTextOut := ""
	if (cb > 0)
	{
		if (dwCookie & 0x10)
			vTextOut .= StrGet(pbBuff, cb/2, "UTF-16")
		else
			vTextOut .= StrGet(pbBuff, cb, "CP0")
		return 0
	}
	if (dwCookie = "Get")
	{
		vTemp := vTextOut
		vTextOut := ""
		return vTemp
	}
	return 1
}

;==================================================

;e.g.
;JEE_RichEditSetStream(hCtl, 0x4002, &vData, vSize)

;only works on internal controls
JEE_RichEditSetStream(hCtl, vFormat, vAddr, vSize)
{
	static pFunc := RegisterCallback("JEE_RichEditSetStreamCallback")
	vScriptPID := DllCall("kernel32\GetCurrentProcessId", UInt)
	WinGet, vPID, PID, % "ahk_id " hCtl
	if !(vPID = vScriptPID)
		return
	;SFF_SELECTION := 0x8000 ;SFF_PLAINRTF := 0x4000
	;SF_USECODEPAGE := 0x20 ;SF_UNICODE := 0x10
	;SF_RTFNOOBJS := 0x3 ;SF_RTF := 0x2 ;SF_TEXT := 0x1
	;UTF-16 (1200)

	VarSetCapacity(EDITSTREAM, A_PtrSize=8?20:12, 0)
	NumPut(vAddr, &EDITSTREAM, 0, "UPtr") ;dwCookie
	NumPut(pFunc, &EDITSTREAM, A_PtrSize=8?12:8, "Ptr") ;pfnCallback
	JEE_RichEditSetStreamCallback("Init", vSize, 0, 0)
	SendMessage, 0x449, % vFormat, % &EDITSTREAM,, % "ahk_id " hCtl ;EM_STREAMIN := 0x449
	vRet := ErrorLevel ;chars read (any CRLFs are counted as single characters)
	return vRet
}

;==================================================

JEE_RichEditSetStreamCallback(dwCookie, pbBuff, cb, pcb)
{
	static vRemain, vOffset
	if (dwCookie = "Init")
	{
		vRemain := pbBuff, vOffset := 0
		return 0
	}
	if (vRemain <= cb)
	{
		DllCall("kernel32\RtlMoveMemory", Ptr,pbBuff, Ptr,dwCookie+vOffset, UPtr,vRemain)
		NumPut(vRemain, pcb+0, 0, "Ptr")
		vOffset += vRemain, vRemain := 0
		return 0
	}
	else
	{
		DllCall("kernel32\RtlMoveMemory", Ptr,pbBuff, Ptr,dwCookie+vOffset, UPtr,cb)
		NumPut(cb, pcb+0, 0, "Ptr")
		vOffset += cb, vRemain -= cb
		return 0
	}
}

;==================================================

;only works on internal controls
JEE_RichEditSetStreamFromFile(hCtl, vFormat, vPath)
{
	static pFunc := RegisterCallback("JEE_RichEditSetStreamFromFileCallback")
	vScriptPID := DllCall("kernel32\GetCurrentProcessId", UInt)
	WinGet, vPID, PID, % "ahk_id " hCtl
	if !(vPID = vScriptPID)
		return
	;SFF_SELECTION := 0x8000 ;SFF_PLAINRTF := 0x4000
	;SF_USECODEPAGE := 0x20 ;SF_UNICODE := 0x10
	;SF_RTFNOOBJS := 0x3 ;SF_RTF := 0x2 ;SF_TEXT := 0x1
	;UTF-16 (1200)

	if !(oFile := FileOpen(vPath, "r"))
		return 0
	VarSetCapacity(EDITSTREAM, A_PtrSize=8?20:12, 0)
	NumPut(oFile.__Handle, &EDITSTREAM, 0, "UPtr") ;dwCookie
	NumPut(pFunc, &EDITSTREAM, A_PtrSize=8?12:8, "Ptr") ;pfnCallback
	SendMessage, 0x449, % vFormat, % &EDITSTREAM,, % "ahk_id " hCtl ;EM_STREAMIN := 0x449
	vRet := ErrorLevel ;chars read (any CRLFs are counted as single characters)
	oFile.Close()
	return vRet
}

;==================================================

JEE_RichEditSetStreamFromFileCallback(dwCookie, pbBuff, cb, pcb)
{
	return !DllCall("kernel32\ReadFile", Ptr,dwCookie, Ptr,pbBuff, UInt,cb, Ptr,pcb, Ptr,0)
}

;==================================================
Example code to create a GUI with a RichEdit control:

Code: Select all

DetectHiddenWindows, On
Gui, New, +HwndhGui, MyWinTitle
hModuleME := DllCall("kernel32\LoadLibrary", Str,"msftedit.dll", Ptr)
;Gui, Add, Custom, ClassRICHEDIT50W r1
Gui, Add, Custom, ClassRICHEDIT50W h300 +0x4
ControlSetText, RICHEDIT50W1, % "RICH 1", % "ahk_id " hGui
Gui, Show, w300 h300
return

;==================================================

GuiClose:
ExitApp
return
Btw one interesting question relating to standard Edit controls:
Edit control: right-aligned control but left-aligned text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=38764
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
DigiDon
Posts: 178
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit controls: get/set text

02 Feb 2018, 03:58

Hi jeeswg,

I have some troubles trying to use your StreamIn function to set RTF text.

I am trying to insert for example a link. The rtf is correct. But If I try this it does not work

Code: Select all

LinkVar:="{\rtf1{\field{\*\fldinst{ HYPERLINK "" http://www.msn.com""}}{\fldrslt{ MSN} }}}"
JEE_RichEditSetStream(RE2.HWND, 0x2 | 0x8000, &LinkVar, (StrLen(LinkVar)+1)*2)
I'm not sure about the StringLength but I've tried different settings and it didn't work.

Your example of plain text did work in contrary:

Code: Select all

LinkVar:="abcdefghijklmnopqrstuvwxyz"
JEE_RichEditSetStream(RE2.HWND, 0x11, &LinkVar,(StrLen(LinkVar)+1)*2)
What am I not understanding ?
Thanks
EverFastAccess : Take Notes on anything the Fast way: Attach notes, Set reminders & Speed up research in 1 gesture - AHK topic
AHK Dynamic Obfuscator L - Protect your AHK code by Obfuscation - AHK topic
QuickModules for Outlook : Sort Outlook emails very quickly to multiple folders - AHK topic
Coding takes lots of time and efforts. If I have helped you or if you enjoy one of my free projects, please consider a small donation :thumbup:
Sorry I am working hard at the moment at a new job and can't commit on delays of answers & updates
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

02 Feb 2018, 05:19

- I had real trouble with the JEE_RESetStream, and finding information about it, I have one example for it here:
GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514
Also there is JEE_RESetStreamFromFile. In general I had problems with using UTF-16 in streams, and possibly just me did as well, although I believe it's possible to use Unicode in RichEdit via ANSI streams. Any progress you make with setting the stream, especially with 0x8000 if it can be made to work, would be worth carefully documenting.
GitHub - AHK-just-me/Class_RichEdit: AHK RichEdit Control
https://github.com/AHK-just-me/Class_RichEdit
- It can be useful to save RTF files in WordPad, and inspect the contents using Notepad.
- Btw here is an example to put RTF on the clipboard.
GUI COMMANDS: COMPLETE RETHINK - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 34#p170834
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
DigiDon
Posts: 178
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit controls: get/set text

03 Feb 2018, 12:38

Well just me's class function SetText is in fact working well.
But it is using the EM_SETTEXTEX = 0x0461 message and not the EM_STREAMIN = 0x0449 message.
Indeed it is passing the string as AINSI.
Here is the code from just me:

Code: Select all

SetText(ByRef Text := "", Mode := "") { ; Replaces the selection or the whole content of the control.
      ; Mode : Option flags. It can be any reasonable combination of the keys defined in 'ST'.
      ; For details see http://msdn.microsoft.com/en-us/library/bb774284(v=vs.85).aspx.
      ; EM_SETTEXTEX = 0x0461, CP_UNICODE = 1200
      ; ST_DEFAULT = 0, ST_KEEPUNDO = 1, ST_SELECTION = 2, ST_NEWCHARS = 4 ???
      Static ST := {DEFAULT: 0, KEEPUNDO: 1, SELECTION: 2, ST_NEWCHARS: 4}
      Flags := 0
      For Each, Value In Mode
         If ST.HasKey(Value)
            Flags |= ST[Value]
      CP := 1200
      BufAddr := &Text
      ; RTF formatted text has to be passed as ANSI!!!
      If (SubStr(Text, 1, 5) = "{\rtf") || (SubStr(Text, 1, 5) = "{urtf") {
         Len := StrPut(Text, "CP0")
         VarSetCapacity(Buf, Len, 0)
         StrPut(Text, &Buf, "CP0")
         BufAddr := &Buf
         CP := 0
      }
      VarSetCapacity(STX, 8, 0)     ; SETTEXTEX structure
      NumPut(Flags, STX, 0, "UInt") ; flags
      NumPut(CP  ,  STX, 4, "UInt") ; codepage
      SendMessage, 0x0461, &STX, BufAddr, , % "ahk_id " . This.HWND
      Return ErrorLevel
   }
So... I won't say this was easy ;) but I finally managed to translate the _GUICtrlRichEdit_StreamFromVar function from AU3 GuiRichEdit class.
And it is ... finally working !
So for the sake of it here is how it does it:

Code: Select all

Global $__g_pGRC_StreamFromVarCallback := RegisterCallback("__GCR_StreamFromVarCallback")
Global $__g_pGRC_sStreamVar := RegisterCallback("__GCR_StreamFromVarCallback")
; For Stream Callbacks :)
Global $SF_TEXT = 0x1
Global $SF_RTF = 0x2
Global $SF_RTFNOOBJS = 0x3
Global $SF_TEXTIZED = 0x4
Global $SF_UNICODE = 0x0010
Global $SF_USECODEPAGE = 0x20
Global $SFF_PLAINRTF = 0x4000
Global $SFF_SELECTION = 0x8000
Global $EM_STREAMIN = 0x0449
TestVar:="{\rtf1{\field{\*\fldinst{ HYPERLINK http://www.msn.com }}{\fldrslt{ MSN} }}}"
_GUICtrlRichEdit_StreamFromVar(RE2.HWND,TestVar)
return

; #FUNCTION# ====================================================================================================================
; Authors: DigiDon
; Translated from AutoIt GuiRichEdit.au3 (Chris Haslam)
; Modified ......:
; ===============================================================================================================================


_GUICtrlRichEdit_StreamFromVar($hWnd, $sVar) {
global
	Local $tEditStream 
	VarSetCapacity($tEditStream,A_PtrSize+4+A_PtrSize)
	NumPut($__g_pGRC_StreamFromVarCallback,$tEditStream,A_PtrSize+4,"Ptr")
	$__g_pGRC_sStreamVar := $sVar
	Local $s := SubStr($sVar, 1, 5)
	Local $wParam 
	$wParam := ($s == "{\rtf" Or $s == "{urtf") ? $SF_RTF : $SF_TEXT
	$wParam := $wParam | $SFF_SELECTION
	SendMessage, $EM_STREAMIN, $wParam, &$tEditStream, , % "ahk_id " . RE2.HWND
	; Local $iError = DllStructGetData($tEditStream, "dwError")
	Local $iError := NumGet($tEditStream, A_PtrSize, "Int")
	If $iError <> 1
		Return False ;Error 700
	Return True
}  ;==>_GUICtrlRichEdit_StreamFromVar

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __GCR_StreamFromVarCallback
; Description ...: Callback function for streaming in from a variable
; Syntax.........: __GCR_StreamFromVarCallback ( $iCookie, $pBuf, $iBufLen, $pQbytes )
; Parameters ....: $iCookie - not used
;                  $pBuf - pointer to a buffer in the control
;                  $iBuflen - length of this buffer
;                  $pQbytes - pointer to number of bytes set in buffer
; Return values .: More bytes to "return"  - 0
;                  All bytes have been "returned" - 1
; Authors: DigiDon
; Translated from AutoIt GuiRichEdit.au3 (Chris Haslam)
; Modified ......:
; Modified.......:
; Remarks .......:
; Related .......:
; Link ..........: @@MsdnLink@@ EditStreamCallback Function
; Example .......:
; ===============================================================================================================================
__GCR_StreamFromVarCallback($iCookie, $pBuf, $iBuflen, $pQbytes) {
	global
	Local $tQbytes
	NumPut(0,$pQbytes+0,0,"Int")
	Local $sCtl := SubStr($__g_pGRC_sStreamVar, - ($iBuflen - 1) + 1)
	If ($sCtl = "")
		Return 1
	StrPut($sCtl, $pBuf, "CP0")
	Len := StrPut(Text, "CP0")
	Local $iLen := StrLen($sCtl)
	NumPut($iLen,$pQbytes+0,0,"Int")
	$__g_pGRC_sStreamVar := SubStr($__g_pGRC_sStreamVar, $iLen + 1)
	Return 0
}
EverFastAccess : Take Notes on anything the Fast way: Attach notes, Set reminders & Speed up research in 1 gesture - AHK topic
AHK Dynamic Obfuscator L - Protect your AHK code by Obfuscation - AHK topic
QuickModules for Outlook : Sort Outlook emails very quickly to multiple folders - AHK topic
Coding takes lots of time and efforts. If I have helped you or if you enjoy one of my free projects, please consider a small donation :thumbup:
Sorry I am working hard at the moment at a new job and can't commit on delays of answers & updates
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

03 Feb 2018, 14:19

- Hmm, I have been curious about when exactly you can pass Unicode text, rather than ANSI text. (Although RTF text stored as ANSI has ways to store Unicode characters by their number I believe.)
- Does 'set text', handle plaintext only, or RTF text as well?
- Btw have you been testing occasionally in both x64 and x32? Sometimes, but not always, you need to use Ptr instead of Int, and specify different offsets. Thanks.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
DigiDon
Posts: 178
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit controls: get/set text

04 Feb 2018, 03:54

-Don't know about the first
-RTF works as well for SetText
-I have Unicode 64 bits. I believe if it works on it, given that I use A_PtrSize, it should work on 32bits.
I wish someone will be able to confirm it though.
Last edited by DigiDon on 04 Feb 2018, 03:56, edited 1 time in total.
EverFastAccess : Take Notes on anything the Fast way: Attach notes, Set reminders & Speed up research in 1 gesture - AHK topic
AHK Dynamic Obfuscator L - Protect your AHK code by Obfuscation - AHK topic
QuickModules for Outlook : Sort Outlook emails very quickly to multiple folders - AHK topic
Coding takes lots of time and efforts. If I have helped you or if you enjoy one of my free projects, please consider a small donation :thumbup:
Sorry I am working hard at the moment at a new job and can't commit on delays of answers & updates
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: RichEdit controls: get/set text

12 Mar 2018, 19:53

Hi, you guys are light years ahead of me on this, but it seems like it might interest you.
My quest here was to grab the RTF content from a file as it displays (not the RTF code) and be able to paste it via Ctrl-V.
After reading just me's tutorial on RTF yesterday, I put together this little scenario:

Using a function derived from just me's code, I grab the content of an RTF file, put it into a hidden GUI with a custom RichEdit like just me's and jeeswg's.
Then I grab the content by sending ^a^c to the control, send it a click to kill the highlighting, then kill the gui.

Function:

Code: Select all

SetWorkingDir, %A_ScriptDir%
global rtffile := ""
global TomDoc := ""
global TomFile := ""
global RE := ""

RTFtoClip(rtffile) {
   ;msgbox %rtffile%
DetectHiddenWindows, On
clipboard =
RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")
Gui, +hwndhGui
Gui +LastFound
Winset, Transparent
Gui, Margin, 10, 10
Gui, Font, s10, Arial
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
Gui, Show, , RichEdit

TomDoc := GetTomDoc(hRE)
TomFile := rtffile
TomDoc.Open(TomFile, 0x01, 0)

ControlFocus, ahk_class RICHEDIT50W1
Send, ^a^c
ControlClick, RICHEDIT50W1, RichEdit
}


GetTomDoc(HRE) {
   ; Get the document object of the specified RichEdit control
   Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
   DocObj := 0
   If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
      DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
      ObjRelease(IRichEditOle)
   }
   Return DocObj
}
Script with function:

Code: Select all

SetWorkingDir, %A_ScriptDir%
#Include RTFtoClip.ahk
#Persistent
SetWorkingDir, %A_ScriptDir%
;~ RTFtoClip("MyRTF.rtf")
FileSelectFile, rtfFile,,,,RichText Files (*.rtf)
If(rtffile<>"")
   RTFtoClip(rtfFile)
else {
   msgbox You did not pick a file!`nThe script will exit.
   ExitApp
   }
ExitApp
Then I fire up a jeeswg-ish (new adjective :twisted: It's yours from this thread nearly verbatim) GUI:

Code: Select all

DetectHiddenWindows, On
Gui, New, +HwndhGui, Jeeswg GUI
Gui, Margin, 10, 10
hModuleME := DllCall("kernel32\LoadLibrary", Str,"msftedit.dll", Ptr)
;Gui, Add, Custom, ClassRICHEDIT50W r1
Gui, Add, Custom, ClassRICHEDIT50W h400 w400 +0x4
ControlSetText, RICHEDIT50W1, % "RICH 1", % "ahk_id " hGui
Gui, Show, , Jeeswg GUI
return

;==================================================

GuiClose:
ExitApp
return
Then I paste the clipboard into the jeeswg-ish GUI (manually so far, not in the script, this is just proof-of-concept).
The real beauty of this for me is that if I paste into a non-RTF capable app, I still get the unformatted text!
If I paste into any RTF-capable app, it works so far, and I get the formatted text in LibreOffice, MS Word, and WordPad, which is to be expected since there's rich text on the clipboard.
I'm on Win7, so I haven't been able to take advantage of the new msftedit.dll with its image-pasting capability.
RTFtoClip working.zip
(12.76 KiB) Downloaded 228 times

Regards
burque505
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit controls: get/set text

12 Mar 2018, 20:47

- Interesting. So I suppose the idea is to get the underlying RTF content from a RichEdit control.
- To get the RTF content from a RichEdit control that belongs to your process, there is JEE_REGetStream.
GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514
- To get the RTF content from a RichEdit control from an external process, you can select the control's contents, copy to the clipboard, and use JEE_ClipboardGetRtf. I don't know if there is more direct way than this.
Is it possible to stringreplace rtf to clipboard? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45284
- To get the text from an internal or external RichEdit control, you use ControlGetText.
- You can get the RTF content from a file by using FileRead.
- For the clipboard, you can put multiple clipboard formats onto the clipboard, and other programs check for the presence of certain formats. You can use NirSoft InsideClipboard to see what's on the clipboard.
- There may be other methods I don't know about. Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: RichEdit controls: get/set text

13 Mar 2018, 10:06

Hi jeeswg, thanks for the links and the scripts. The main idea here for me was, when copying formatted text, to be able to get what Ctrl-C does under the hood onto the clipboard. The reason is that ( besides whatever else the Windows clipboard does) it allows pasting the content in either formatted or unformatted text with just Ctrl-V, depending on the application. My previous efforts allowed getting RTF from a file into the clipboard, but only the RTF would paste with Ctrl-V. In other words, whatever I copied wouldn't transfer with just Ctrl-V as unformatted text to, for example, Notepad.

A couple of hours ago I found this thread with DigiDon's RichEdit OleCallback.ahk function. Now I can pull RTF that contains images directly out of a file and paste it.

RTFtoClipCallback.ahk

Code: Select all

#Include RichEdit OleCallback.ahk
SetWorkingDir, %A_ScriptDir%
global rtffile := ""
global TomDoc := ""
global TomFile := ""
global RE := ""

RTFtoClip(rtffile) {
   ;msgbox %rtffile%
DetectHiddenWindows, On
clipboard =
RE_Dll := DllCall("LoadLibrary", "Str", "Msftedit.dll", "Ptr")
Gui, +hwndhGui
Gui +LastFound
Winset, Transparent
Gui, Margin, 10, 10
Gui, Font, s10, Arial
Gui, Add, Custom, ClassRICHEDIT50W w400 h400 vRE hwndHRE +VScroll +0x0804 ; ES_MULTILINE | ES_READONLY
RE_SetOleCallback(HRE) ; Thanks to DigiDon and just me. This uses a function from RichEdit OleCallback.ahk
Gui, Show, , RichEdit

TomDoc := GetTomDoc(hRE)
TomFile := rtffile
TomDoc.Open(TomFile, 0x01, 0)

ControlFocus, ahk_class RICHEDIT50W1
Send, ^a^c
ControlClick, RICHEDIT50W1, RichEdit
}


GetTomDoc(HRE) {
   ; Get the document object of the specified RichEdit control
   Static IID_ITextDocument := "{8CC497C0-A1DF-11CE-8098-00AA0047BE5D}"
   DocObj := 0
   If DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt") { ; EM_GETOLEINTERFACE
      DocObj := ComObject(9, ComObjQuery(IRichEditOle, IID_ITextDocument), 1) ; ITextDocument
      ObjRelease(IRichEditOle)
   }
   Return DocObj
}
RTFtoClipExampleCB (Includes the script above)

Code: Select all

SetWorkingDir, %A_ScriptDir%
#Include RTFtoClipCallback.ahk
#Persistent
SetWorkingDir, %A_ScriptDir%
;~ RTFtoClip("MyRTF.rtf")
FileSelectFile, rtfFile,,,,RichText Files (*.rtf)
If(rtffile<>"")
   RTFtoClip(rtfFile)
else {
   msgbox You did not pick a file!`nThe script will exit.
   ExitApp
   }
ExitApp
and DigiDon and just me's RichEdit OleCallback.ahk:

Code: Select all

;=========================================
; Name:     	  RichEdit OleCallback
; Namespace:      RichEdit
; Authors:        just me & DigiDon
; Description:    IRichEditOleCallback interface AHK implementation for the RichEdit control
;=========================================
;
;=========================================
;RE_SetOleCallback
;Need to call this function just after creation of the RichEdit control
;HRE - Handle of the RichEdit Control
;ex: RE_SetOleCallback(RE2.HWND)
;Specify your contextmenu in IREOleCB_GetContextMenu() if you have one because it won't be called otherwise
;and disable existing dropfiles special GUI label for the RichEdit Control
;Then you can start dragging and dropping any document into the RichEdit field.
;=========================================
RE_SetOleCallback(HRE) {
   ; EM_SETOLECALLBACK = 0x0446
   SendMessage, 0x0446 , 0, % IREOleCB_Create() , , ahk_id %HRE%
   If (ErrorLevel = "FAIL") || (ErrorLevel = 0) {
      MsgBox, 16, %A_ThisFunc%, ERROR: %ErrorLevel%!
      Return False
   }
   Return True
}
; ================================================================================================================================
; IRichEditOleCallback -> msdn.microsoft.com/en-us/library/windows/desktop/bb774308(v=vs.85).aspx
; ================================================================================================================================
IREOleCB_Create() {
   Static VTBL := [RegisterCallback("IREOleCB_QueryInterface")
                 , RegisterCallback("IREOleCB_AddRef")
                 , RegisterCallback("IREOleCB_Release")
                 , RegisterCallback("IREOleCB_GetNewStorage")
                 , RegisterCallback("IREOleCB_GetInPlaceContext")
                 , RegisterCallback("IREOleCB_ShowContainerUI")
                 , RegisterCallback("IREOleCB_QueryInsertObject")
                 , RegisterCallback("IREOleCB_DeleteObject")
                 , RegisterCallback("IREOleCB_QueryAcceptData")
                 , RegisterCallback("IREOleCB_ContextSensitiveHelp")
                 , RegisterCallback("IREOleCB_GetClipboardData")
                 , RegisterCallback("IREOleCB_GetDragDropEffect")
                 , RegisterCallback("IREOleCB_GetContextMenu")]
   Static HeapSize := A_PtrSize * 20 ; VTBL pointer + 13 method pointers + 4 unused pointers + reference count + HEAP handle
   Static HeapOffset := A_PtrSize * 19 ; offset to store the heap handle within the heap
   Heap := DllCall("HeapCreate", "UInt", 0x05, "Ptr", 0, "Ptr", 0, "UPtr")
   IREOleCB := DllCall("HeapAlloc", "Ptr", Heap, "UInt", 0x08, "Ptr", HeapSize, "UPtr")
   Addr := IREOleCB
   Addr := NumPut(Addr + A_PtrSize, Addr + 0, "UPtr")
   For Each, CB In VTBL
      Addr := NumPut(CB, Addr + 0, "UPtr")
   NumPut(Heap, IREOleCB + HeapOffset, "UPtr")
   Return IREOleCB
}
; --------------------------------------------------------------------------------------------------------------------------------
; IUnknown::QueryInterface
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_QueryInterface(IREOleCB, REFIID, ByRef IFPtr) {
   OutputDebug, %A_ThisFunc%
   Return 0 ; S_OK
}
; --------------------------------------------------------------------------------------------------------------------------------
; IUnknown::AddRef
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_AddRef(IREOleCB) {
   Static RefOffset := A_PtrSize * 18
   OutputDebug, %A_ThisFunc%
   NumPut(RefCount := NumGet(IREOleCB + RefOffset, "UInt") + 1, IREOleCB + RefOffset, "UInt")
   Return RefCount
}
; --------------------------------------------------------------------------------------------------------------------------------
; IUnknown::Release
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_Release(IREOleCB) {
   Static RefOffset := A_PtrSize * 18
        , HeapOffset := A_PtrSize * 19
   OutputDebug, %A_ThisFunc%
   NumPut(RefCount := NumGet(IREOleCB + RefOffset, "UInt") - 1, IREOleCB + RefOffset, "UInt")
   If (RefCount = 0) {
      Heap := NumGet(IREOleCB + HeapOffset, "UPtr")
      DllCall("HeapDestroy", "Ptr", Heap)
   }
   Return RefCount
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::GetNewStorage
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_GetNewStorage(IREOleCB, IStoragePtr) {
   OutputDebug, %A_ThisFunc%
   ; msdn.microsoft.com/en-us/library/windows/desktop/aa378977(v=vs.85).aspx
	If !(HR := DllCall("Ole32.dll\CreateILockBytesOnHGlobal", "Ptr", 0, "Int", 1, "PtrP", ILockBytes)) {
      ; msdn.microsoft.com/en-us/library/windows/desktop/aa380324(v=vs.85).aspx
      ; STGM_READWRITE = 0x02, STGM_SHARE_EXCLUSIVE = 0x10, STGM_CREATE = 0x1000
   	If (HR := DllCall("Ole32.dll\StgCreateDocfileOnILockBytes", "Ptr", ILockBytes, "UInt", 0x1012, "UInt", 0, "PtrP", IStorage))
         ObjRelease(ILockBytes)
      Else
         NumPut(IStorage, IStoragePtr + 0, "UPtr")
   }
   Return HR
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::GetInPlaceContext - not implemented
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_GetInPlaceContext(IREOleCB, Frame, Doc, FrameInfo) {
   OutputDebug, %A_ThisFunc%
   Return 0x80004001 ; E_NOTIMPL
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::ShowContainerUI - not implemented
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_ShowContainerUI(IREOleCB, Show) {
   OutputDebug, %A_ThisFunc%
   Return 0x80004001 ; E_NOTIMPL
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::QueryInsertObject - returns S_OK
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_QueryInsertObject(IREOleCB, CLSID, STG, CP) {
   OutputDebug, %A_ThisFunc%
   Return 0 ; S_OK
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::DeleteObject - returns S_OK
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_DeleteObject(IREOleCB, OleObj) {
   OutputDebug, %A_ThisFunc%
   Return 0 ; S_OK
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::QueryAcceptData - returns S_OK
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_QueryAcceptData(IREOleCB, DataObj, Format, Operation, Really, MetaPic) {
   OutputDebug, %A_ThisFunc%
   Return 0 ; S_OK
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::ContextSensitiveHelp - not implemented
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_ContextSensitiveHelp(IREOleCB, EnterMode) {
   OutputDebug, %A_ThisFunc%
   Return 0x80004001 ; E_NOTIMPL
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::GetClipboardData - not implemented
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_GetClipboardData(IREOleCB, CharRange, Operation, DataObj) {
   OutputDebug, %A_ThisFunc%
   Return 0x80004001 ; E_NOTIMPL
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::GetDragDropEffect - returns S_OK
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_GetDragDropEffect(IREOleCB, Drag, KeyState, Effect) {
   OutputDebug, %A_ThisFunc%
   Return 0 ; S_OK
}
; --------------------------------------------------------------------------------------------------------------------------------
; IRichEditOleCallback::GetContextMenu - not implemented
; --------------------------------------------------------------------------------------------------------------------------------
IREOleCB_GetContextMenu(IREOleCB, SelType, OleObj, CharRange, HMENU) {
	;PUT YOUR  CONTEXT MNU HERE
	;Menu, RN_ContextMenu, Show
   ; OutputDebug, %A_ThisFunc%
   Return 0x80004001 ; E_NOTIMPL
}
I run the script, which pulls the content of the RTF file into the transparent GUI, copies it to the clipboard, and exits.
The pasting is done manually with Ctrl-V.
The only thing the script does is get the RTF content (not code) of the RTF file onto the clipboard.

Regards,
burque505
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: RichEdit controls: get/set text

13 Mar 2018, 11:10

Hey jeeswg, just me sent me your RTF2Clip.ahk! I'll put in below for everyone else's benefit. It still pastes plain text where applicable, which was my goal.
Spoiler
It makes my Rube Goldberg attempts above obsolete. For Win7, I made the following change (line 14) so it would paste RTF that has images (requires RichEdit OleCallback.ahk be included, see above):
Spoiler
Here's a calling script:
Spoiler
DigiDon, jeeswg, just me, thanks again!
Regards,
burque505
neogna2
Posts: 586
Joined: 15 Sep 2016, 15:44

Re: RichEdit controls: get/set text

29 May 2021, 09:50

teadrinker wrote:
18 Oct 2017, 13:55
My exercises with RichEdit's COM-interfaces:
@teadrinker : I am trying to understand how to use ITextPara COM methods. Your script shows how to use ITextFont methods. Like in this snippet

Code: Select all

rng := ITextDocument.Range(0, 0)
rng.Font.Name := "Calibri"
I thought something similar would work for the Para methods

Code: Select all

rng := ITextDocument.Range(0, 0)
rng.Font.Name := "Calibri"
rng.Para.LineSpacing(3, 400)
But it throws Error: 0x80020011 - Does not support a collection. so I am doing something wrong in that line, but I don't know what. Do you?

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], marypoppins_1 and 140 guests