RichEdit : New Features to All

Get help with using AutoHotkey and its commands and hotkeys
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

RichEdit : New Features to All

27 Jan 2018, 07:56

Hi everyone ;) ,

Update 06.02.2018
Well I have finally made all my wishes below working! :dance:
There are still some challenges in the OLE objects area but it works for most of it.
I will take some time to properly release it all with the updated RichEdit Class and Sample by just me.
Still I've put updates and shared some of the hints on this topic.

****************************************************************************************************
I love and am using a lot the RichEdit control. It is lightweight and quite capable.
I think it can be great in a lot of projects to display information with colors and such and as a light text editor as well.
I am myself implementing it in a note-taking software project.

One of the only quite complete class about it is
Class RichEdit - update on 2015-04-14 (v0.1.05.00) by just me

Over the time I have noticed there are still exciting features to add to this class and it would be really great that those are made available to all, like it is in other languages, such as autoit.

Unfortunately these subjects are quite advanced, and aside from hyperlinks where I made some progress, I haven't the complete skills to make this enhancements on my own.

Second point, just me, who as you know has so many different projects don't want to go too much involved in this class again.
(Nevertheless he agreed to bring some guidance if we're stucked)

Therefore the question is:
Could you help in any way bringing these features to the Class RichEdit ?
  • Friendly Hyperlinks [DONE see below]
  • Support for OLE embedded objects [DONE see below]
  • Support for images in Win7 [DONE see below]
Of course I would be more than happy to support, looking into the subjects for information, helping to code up to my skills and so on.
We can even plan Skype sessions or such.
I have already compiled some research on it BELOW
I just need some help because the subject is too complex for my current skills.

Any help would be very much appreciated, many thanks! :thumbup:
Last edited by DigiDon on 06 Feb 2018, 12:49, edited 9 times 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:
User avatar
jeeswg
Posts: 5280
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RichEdit : New Features to All

27 Jan 2018, 09:35

[I did some RichEdit text functions here:]
[you're welcome to use any RichEdit functions from here, and to change the function names/modify the functions to match your library]
GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514

[teadrinker did some nice object examples here:]
RichEdit controls: get/set text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=38385

Window Detective comes with a list of window messages, including RichEdit control messages:
[taken from:]
best utilities + best AutoHotkey scripts (+ useful tips) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=28149

[e.g. window_styles.ini, e.g. constants.ini]
[e.g. message_definitions.xml (previously window_messages.ini)]
[click a release, and download the 'src' zip file][see 'data' folder]
Window Detective - Browse Files at SourceForge.net
https://sourceforge.net/projects/window ... rce=navbar
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

27 Jan 2018, 10:05

Hey jeeswg,

Thanks for sharing these information ! ;)

I am posting here as well the
Documentation I found on the mentioned needed improvements:
Support for OLE object = embedded objects of Powerpoint, Excel spreadsheet and other files!
Considering what I saw here :
https://www.codeproject.com/Articles/95 ... s-and-Othe
that might be possible !
By the way, it seems autoit has implemented it no?
https://github.com/forcedotcom/dataload ... chEdit.au3

Support for images in Win 7:
I know this has been asked and answered that image support wworks on win>8 but for what I've read there might be the solution to load images as bitmap using Gdip so that win 7 would display images.
https://www.autoitscript.com/forum/topi ... e-problem/
However I have not been able to even see a icon of the image using the class.
If I use RE2.SetEventMask(["DROPFILES"]) and/or set RichEditGuiDropFiles label, that doesn't seem to produce something.

Friendly Hyperlink
I have been like others be able to implement AutoURL detection but then you have to put the link as such.
It would be great to have a text independant from the link.
The best would be to be able to right clic it and even be able to enter a new label for the link after it has been added.
But first things first.
Here are some documentation
https://blogs.msdn.microsoft.com/murray ... yperlinks/
https://stackoverflow.com/questions/425 ... -trichedit

I have given a try:
Spoiler
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

29 Jan 2018, 07:12

Well... my best shot right now seems to be trying to learn how to use DllCalls in AHK :roll: and how to convert the Autoit code ... :crazy:
https://github.com/forcedotcom/dataload ... chEdit.au3
I have searched quite intensively but it seems nobody in AHK gave a shot at implementing OLE methods :|
I have only found. But it seems really short compared to what is needed. I will see if I can use it though.
https://github.com/maul-esel/COM-Classe ... ditOLE.ahk

So...Wish me luck :lol:
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

29 Jan 2018, 09:35

Hum, interesting !

After some hours of research ... I think I am on my way to something ! :D

@jeeswg
Thanks to you I came to understand that @teadrinker managed to implement the IRichEditOle COM interface using the EM_GETOLEINTERFACE message
https://autohotkey.com/boards/viewtopic ... 74#p176774
and especially implemented the TOM interface ITextDocument
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
}
(BTW I have no idea how you know that ITextDocument was available from IRichEditOle, I can't see any mention on it in IRichEditOle page)

I discovered that what I need is the IRichEditOleCallback interface using the EM_SETOLECALLBACK message

And it seems... well pretty close to what teadrinker did!

So...I will look further more into this...later ! My brain is near to the point of boiling right now :lol:
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

01 Feb 2018, 09:40

Well... it took me several days of hard work to understand all these DllCall, structures, pointers and all but ...

I've finally nailed the most ambitious improvement ! :superhappy:
Support for OLE object DONE
embed Powerpoint, Excel spreadsheet and other files!

I will be releasing it soon, and empowered by this, will see if I can achieve the other two ;) .
Will keep posted on this topic as well in case someone has info on them.

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

Re: RichEdit : New Features to All

01 Feb 2018, 12:47

- Well done DigiDon.
- Btw here are two links that feature object implementations, in case they're useful as templates. Plus some useful links are mentioned.
Explorer column interaction (get/set: which appear, width, ascending/descending order) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=33129
FileSelectFile, add controls to the Open/Save As dialog - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29934
- Btw also, the AutoIt examples use $ in string names, but you're under no obligation to do so. Also, you can't use $ in string names in AHK v2. I didn't realise that I would find $ at the start of strings so hard to read. I did use them in QBasic at the end of variables I believe. Cheers.
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

01 Feb 2018, 14:39

Thanks jeeswg
-Seems interesting I'll check that out.
-The difficult thing I realized was that IRichEditOleCallback interface needed to be implemented: meaning you had to code each method yourself...vs implementing the interface and calling its already built-in methods.
-Hopefully I found the AutoIt class which had it implemented so I could take it as an example and study it, debug it to better understand.
-Nevertheless I am a AHK user, and it was the first time I was using AutoIt so I had quite a hard time to understand how it was working and how I could translate it into AHK (good luck with Dll complex calls).
Variables in AU3 MUST start with "$" but yes of course I know AHK is much more flexible ! :) Thanks for the hint on v2 though, I didn't start to use it yet.
Cheers too! ;)
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

03 Feb 2018, 12:54

I solved the problem mentioned about the friendly hyperlink that the EN_LINK message was not received before the doc was saved and reload.
That's thanks to a post from just me (again! ;))

There was an error in the MSDN doc about Friendly Hyperlink.
YOU MUST insert a whitespace AFTER the link
so
THIS WORKS

Code: Select all

RE2.SetText("{\rtf1{\field{\*\fldinst{ HYPERLINK "" http://www.msn.com ""}}{\fldrslt{ MSN} }}}", ["SELECTION"])
THIS DOES NOT WORK

Code: Select all

RE2.SetText("{\rtf1{\field{\*\fldinst{ HYPERLINK "" http://www.msn.com""}}{\fldrslt{ MSN} }}}", ["SELECTION"])
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

06 Feb 2018, 11:33

Here is an example to insert a custom friendly hyperlink suggesting either the selection or the link as the label (name) of the link:

Code: Select all

RN_InsertLink:
InputBox, LinkHyperlink, , Link Address,,,120
if ErrorLevel
	return
if !(DefaultLabel:=RE2.GetSelText())
	DefaultLabel:=LinkHyperlink
InputBox, LinkLabel, , Link Label,,,120,,,,, % DefaultLabel
if ErrorLevel
	return
RE2.SetText("{\rtf1{\field{\*\fldinst{ HYPERLINK "" " LinkHyperlink " ""}}{\fldrslt{ " LinkLabel "} }}}", ["SELECTION"])
;If you want it in color; e.g. blue
;RE2.SetText("{\rtf1{\colortbl `;\red0\green0\blue238`;}{\field{\*\fldinst{ HYPERLINK "" " LinkHyperlink " ""}}{\fldrslt{\ul\cf1 " LinkLabel "} }}}", ["SELECTION"])
return
Last edited by DigiDon on 06 Feb 2018, 12:48, 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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

06 Feb 2018, 11:48

OK I've tackled the remaining item in my list :)
(although there are still some challenges for the OLE implementation in some areas)

How to embed any images in rtf in windows 7 (Win10 works automatically)
Needs OLECallback to be activated (will be released soon)
You have to use a menu label
It uses Gdip to convert the image into Bitmap.
Then it pastes the bitmap into the control

Code: Select all

InsertImages:
	SelectedFiles=
	FileSelectFile, SelectedFiles, M3, %A_ScriptDir%,,Images (*.png; *.jpg; *.jpeg; *.bmp)
	if SelectedFiles =
		{
		; MsgBox, The user pressed cancel.
		return
		}
	pToken	:= Gdip_Startup()
	BackupClipboard:=ClipboardAll
	Loop, parse, SelectedFiles, `n
		{
		If (A_Index=1) {
			FilesDir:=A_Loopfield
			continue
			}
		Lcl_FilePath:=FilesDir "\" A_Loopfield
		pBitmap	:= Gdip_CreateBitmapFromFile(Lcl_FilePath)
		Gdip_SetBitmapToClipboard(pBitmap)
		Gdip_DisposeImage(pBitmap)
		DllCall("DeleteObject", "Uint", hBitmap)
		; Send ^v
		RE2.Paste()
		}
	Gdip_Shutdown(pToken)
	Clipboard:=BackupClipboard
	BackupClipboard:=""
Return
I will take some time to properly release it all.
But I just wanted to put updates and share some of the hints right away on this topic.
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

13 Feb 2018, 09:35

Hi,

I am putting the same post as in the RichEdit class topic because I think this is good that this topic has it too.

There are still room for improvement to understand and implement clearly the OLE features of Rich Edit into the class and the sample,.
Therefore there are some delays before a proper release.

Nevertheless, for those who would like to start right away I think it is good to release the major part of what was achieved recently.

It is now possible to embed any document into the Rich Edit control

Thanks to the OLE callback interface implementation done by just me and myself.

Many thanks to just me for all the help, work and knowledge you have brought/are still bringing.

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
}
Enjoy! ;)
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:
User avatar
kczx3
Posts: 666
Joined: 06 Oct 2015, 21:39

Re: RichEdit : New Features to All

13 Feb 2018, 11:44

Hi DigiDon, your implementation seems to mostly function for me. I am able to drag files onto the RichEdit control and have them appear with the familiar icon and file name. I am not sure how you were able to show the actual Excel table or PowerPoint slide though. I can copy an Excel table and paste it into the RichEdit but double-clicking does not open the file that the table came from. How are you showing the table in the Excel file in addition to linking to the originating file?
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

13 Feb 2018, 12:06

Hi kczx3,

Yes that's something we were working on as Drag and Drop doesn't seem to display content directly and it's hard to figure out why.
Other works on that in different languages often have the same behaviour.
Nevertheless if you D&D in Word, save as rtf and open it it with AHK RichEdit will work just fine.

Copy pasting content just copies the content, and does not embed the file.

BUT we have a solution to be able to Insert a in-place display object directly from the Editor.
You will need to use for now a special function written gracefully by just me :D :
And with that you can have all the different combinaison (Icon or Display, Embedd or Link !)

Code: Select all

; ================================================================================================================================
; Insert files as OLE objects
; How to Use OLE in Rich Edit Controls <- msdn.microsoft.com/en-us/library/windows/desktop/dd387916(v=vs.85).aspx
; InsertMode : Embedded / Link
; DisplayMode : Display / Icon
; ================================================================================================================================
RN_InsertObject(HRE, FilePath,InsertMode="Embedded",DisplayMode="Display") {
   ; EM_GETOLEINTERFACE := 0x43C
   HR := DllCall("SendMessage", "Ptr", HRE, "UInt", 0x043C, "Ptr", 0, "PtrP", IRichEditOle, "UInt")
   If (HR = 0) {
      ErrorLevel := Format("0x{:08X}", HR)
      Return False
   }
   ; OutputDebug, EM_GETOLEINTERFACE - %ErrorLevel% - %IRichEditOle%
   ; msdn.microsoft.com/en-us/library/windows/desktop/aa378977(v=vs.85).aspx
	HR := DllCall("Ole32.dll\CreateILockBytesOnHGlobal", "Ptr", 0, "Int", 1, "PtrP", ILockBytes, "UInt")
   If (HR) {
      ErrorLevel := Format("0x{:08X}", HR)
      ObjRelease(IRichEditOle)
      Return False
   }
   ; OutputDebug, CreateILockBytesOnHGlobal - %ErrorLevel% - %ILockBytes%
   ; msdn.microsoft.com/en-us/library/windows/desktop/aa380324(v=vs.85).aspx
   ; STGM_READWRITE = 0x02, STGM_SHARE_EXCLUSIVE = 0x10, STGM_CREATE = 0x1000
	HR := DllCall("Ole32.dll\StgCreateDocfileOnILockBytes", "Ptr", ILockBytes, "UInt", 0x1012, "UInt", 0, "PtrP", IStorage, "UInt")
   If (HR) {
      ErrorLevel := Format("0x{:08X}", HR)
      ObjRelease(ILockBytes)
      ObjRelease(IRichEditOle)
      Return False
   }
   ; OutputDebug, StgCreateDocfileOnILockBytes - %ErrorLevel% - %IStorage%
   GetClientSite := NumGet(NumGet(IRichEditOle + 0, "UPtr") + (A_PtrSize * 3), "UPtr")
   HR := DllCall(GetClientSite, "Ptr", IRichEditOle, "PtrP", IOleClientSite, "UInt")
   If (HR) {
      ErrorLevel := Format("0x{:08X}", HR)
      ObjRelease(IStorage)
      ObjRelease(ILockBytes)
      ObjRelease(IRichEditOle)
      Return False
   }
   ; OutputDebug, GetClientSite - %ErrorLevel% - %IOleClientSite%
   VarSetCapacity(FORMATETC, A_PtrSize = 8 ? 32 : 20, 0)
   if (DisplayMode="Icon")
		NumPut( 4, FORMATETC, A_PtrSize = 8 ? 16 : 8 , "UInt") ; DVASPECT_ICON
   else
		NumPut( 1, FORMATETC, A_PtrSize = 8 ? 16 : 8 , "UInt") ; DVASPECT_CONTENT
   NumPut(-1, FORMATETC, A_PtrSIze = 8 ? 20 : 12, "Int")  ; all data
   NumPut( 0, FORMATETC, A_PtrSize = 8 ? 24 : 16, "UInt") ; TYMED_NULL
   VarSetCapacity(CLSID_NULL, 16, 0)
   VarSetCapacity(IID_IOleObject, 16, 0)
   DllCall("Ole32.dll\CLSIDFromString", "WStr", "{00000112-0000-0000-C000-000000000046}", "Ptr", &IID_IOleObject)
   
	OLERENDER_DRAW := 1
   if (InsertMode="Link")
		HR := DllCall("Ole32.dll\OleCreateLinkToFile", "WStr", FilePath, "Ptr", &IID_IOleObject, "UInt", OLERENDER_DRAW, "Ptr", &FORMATETC, "Ptr", IOleClientSite, "Ptr", IStorage, "PtrP", IOleObject, "UInt")
   else
		HR := DllCall("Ole32.dll\OleCreateFromFile", "Ptr", &CLSID_NULL, "WStr", FilePath, "Ptr", &IID_IOleObject, "UInt", OLERENDER_DRAW, "Ptr", &FORMATETC, "Ptr", IOleClientSite, "Ptr", IStorage, "PtrP", IOleObject, "UInt")
   If (HR) {
      ErrorLevel := Format("0x{:08X}", HR)
      ObjRelease(IStorage)
      ObjRelease(ILockBytes)
      ObjRelease(IOleClientSite)
      ObjRelease(IRichEditOle)
      Return False
   }
   ; OutputDebug, OleCreateFromFile - %ErrorLevel% - %IOleObject%
   DllCall("Ole32.dll\OleSetContainedObject", "Ptr", IOleObject, "UInt", 1, "UInt")
   ; OutputDebug, OleSetContainedObject - %ErrorLevel%
   VarSetCapacity(CLSID_USER, 16, 0)
   GetUserClassID := NumGet(NumGet(IOleObject + 0, "UPtr") + (A_PtrSize * 15), "UPtr")
   HR := DllCall(GetUserClassID, "Ptr", IOleObject, "Ptr", &CLSID_USER, "UInt")
   If (HR) {
      ErrorLevel := Format("0x{:08X}", HR)
      ObjRelease(IStorage)
      ObjRelease(ILockBytes)
      ObjRelease(IOleClientSite)
      ObjRelease(IOleObject)
      ObjRelease(IRichEditOle)
      Return False
   }
   ; CLSID :=  Format("{:016X} {:016X}", NumGet(CLSID_USER, "Int64"), NumGet(CLSID_USER, 8, "Int64"))
   ; OutputDebug, GetUserClassID - %ErrorLevel% - %CLSID%
   SizeOfREOBJECT := A_PtrSize = 8 ? (A_PtrSize * 9) : (A_PtrSize * 10) + 16
   VarSetCapacity(REOBJECT, SizeOfREOBJECT, 0)
   Addr := &REOBJECT
   Addr := NumPut(SizeOfREOBJECT, Addr + 0, "UInt")
   Addr := NumPut(0xFFFFFFFF, Addr + 0, "UInt") ; REO_CP_SELECTION
   Addr := NumPut(NumGet(CLSID_USER, 0, "Int64"), Addr + 0, "Int64")
   Addr := NumPut(NumGet(CLSID_USER, 8, "Int64"), Addr + 0, "Int64")
   Addr := NumPut(IOleObject, Addr + 0, "UPtr")
   Addr := NumPut(IStorage, Addr + 0, "UPtr")
   Addr := NumPut(IOleClientSite, Addr + 0, "UPtr")
   Addr += 8 ; sizel = 0
   if (DisplayMode="Icon")
		Addr := NumPut(4, Addr + 0, "UInt") ; DVASPECT_ICON
   else
		Addr := NumPut(1, Addr + 0, "UInt") ; DVASPECT_CONTENT
   Addr := NumPut(11, Addr + 0, "UInt") ; REO_RESIZABLE | REO_BELOWBASELINE|REO_DYNAMICSIZE

   InsertObject := NumGet(NumGet(IRichEditOle + 0, "UPtr") + (A_PtrSize * 7), "UPtr")
   HR := DllCall(InsertObject, "Ptr", IRichEditOle, "Ptr", &REOBJECT, "UInt")
   ObjRelease(ILockBytes)
   ObjRelease(IStorage)
   ObjRelease(IOleClientSite)
   ObjRelease(IRichEditOle)
   ObjRelease(IOleObject)
   Return !(ErrorLevel := Format("0x{:08X}", HR))
}
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:
DigiDon
Posts: 176
Joined: 19 May 2014, 04:55
Contact:

Re: RichEdit : New Features to All

13 Feb 2018, 12:15

To make some precisions, I'm still struggling to understand all the OLE inside-working. It's hard to understand and doc is very scarce.

So I still have some topics right now I am currently unable to do/understand:
  • Directly display with drag and drop
  • React to an object on right clic and offer some actions
  • Drag object out of RichEdit : ex: to Explorer
  • Update all the display content when we open an rtf file
  • Change label of icon object
  • Why the label for MS file icon objects is just their type: ex: Microsoft Excel File
And other RTF stuff like:
  • Characters statistics include all the hidden chars (links/objects)
  • Table operation: how to simply add a column ?! (drives me c...)
So that's why I'm was not rushing to release ;)
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:

Return to “Ask For Help”

Who is online

Users browsing this forum: loek3000, Scr1pter and 56 guests