Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Continuous RichEditHighlight
Hi Gintaras,
Hope all is well!
I am trying to monitor continuously in text for the occurrence of certain string and then highlight it (as the user types). Simplified example below. Problem is that RichEditHighlight moves cursor back to starting position each time because of EM_SETSEL 0 0 message. Therefore can not have continuous typing because cursor position changes, etc.

Any thoughts on how to get around this?

Thanks for any thoughts,

Select All      Help
,int w=win("WordPad" "WordPadClass")
,int w1=win("Document - WordPad" "WordPadClass")
,int cid=id(59648 w) ;;editable text 'Rich Text Window'
,str text.getwintext(cid)
,out text
,;if findrx(text "\bred\b") > -1
,RichEditHighlight(cid "\bred\b" 4 ColorFromRGB(255 255 255) ColorFromRGB(255 0 0))
,RichEditHighlight(cid "\bgreen\b" 4 ColorFromRGB(255 255 255) ColorFromRGB(0 255 0))
In RichEditHighlight replace several last lines to
Function RichEditHighlight
Select All      Help
int selStart selEnd
SendMessage hwndre EM_GETSEL &selStart &selEnd

for i 0 a.len
,SendMessage hwndre EM_SETSEL a[0 i].cpMin a[0 i].cpMax
,SendMessage hwndre EM_SETCHARFORMAT SCF_SELECTION m.address

SendMessage hwndre EM_SETSEL selStart selEnd
ret a.len
Thanks Gintaras for this successful answer.
With this version of RichEditHighlight, I can almost make a continuously format-updating tool without changing the cursor position i.e. if i type the phrase "the ball is red" then as soon as findrx finds red, it will change the text to red, even as I go on to type additional text.
But the problem is that it gets glitchy because it keeps on trying to re-apply the formatting to previous instances which have already been turned red. Is it possible to determine whether some formatting, whether it be bold, underline, italicize or text/background color is already true before applying? I tried below using getsel with the CF_RTF limited to the offsets of interest. It works in theory, but it is so jumpy it makes interaction and typing practically impossible. Is there any way to make this more of a background process for the user?
I know from that I could use GetRTF with SendMessage(hre EM_STREAMOUT SF_RTF &es) but the application with the RichEdit control is external so I would need to do the dll injection via hook that you described in the other thread. If there is not a way to salvage the code below, could you suggest how I could do the dll injection. The external app is 32-bit. I work with some folks who could do the C programming if I could just give them some of the details.
Any advice?
Thanks so much, S

Trigger Aj     Help - how to add the trigger to the macro
Select All      Help
int w=win("Document - WordPad" "WordPadClass")
int hwndre =  id(59648 w) ;;editable text 'Rich Text Window'

rep 60
,if child(mouse) != hwndre; 0.1; continue
,str ReportText.getwintext(hwndre)
,ReportText.findreplace("[]" "[10]")
,;out ReportText
,int i; ARRAY(CHARRANGE) a ;ARRAY(str) s
,findrx(ReportText "(red)|(blue)|(green)" 0 4 a)
,findrx(ReportText "(red)|(blue)|(green)" 0 4 s)
,out F"Total: {a.len}"
,if !a.len; 1;continue
,for i 0 a.len
,,out F"[]{i}: '{s[0 i]}'"
,,int offset(a[0 i].cpMin) length(a[0 i].cpMax-a[0 i].cpMin)
,,str t.get(ReportText offset length)
,,;out "offset=%i length=%i %s" offset length t
,,int selStart selEnd        
,,SendMessage hwndre EM_GETSEL &selStart &selEnd
,,SendMessage hwndre EM_SETSEL a[0 i].cpMin a[0 i].cpMax
,,str ColorCaptured = s[0 i]
,,str SelRtf.getsel(0 CF_RTF hwndre)
,,out F"[9][9]{SelRtf}"
,,int r g b
,,str ColorRtf
,,sel s[0 i]
,,,case "red" ColorRtf = "\colortbl ;\red255\green0\blue0"; r = 255; g = 0; b = 0
,,,case "green" ColorRtf  = "\colortbl ;\red0\green255\blue0"; r = 0; g = 255; b = 0
,,,case "blue" ColorRtf  = "\colortbl ;\red0\green0\blue255"; r = 0; g = 0; b = 255
,,int textcolor = ColorFromRGB(r g b)
,,if find(SelRtf ColorRtf) > -1
,,,out F"[9]already '{ColorCaptured}'"
,,,__ProcessMemory m.Alloc(hwndre 1000)
,,,,CHARFORMAT2W cfw.cbSize=sizeof(cfw)
,,,,if(textcolor) cfw.dwMask|CFM_COLOR; cfw.crTextColor=textcolor
,,,,m.Write(&cfw sizeof(cfw))
,,,,CHARFORMAT2A cfa.cbSize=sizeof(cfa)
,,,,if(textcolor) cfa.dwMask|CFM_COLOR; cfa.crTextColor=textcolor
,,,,m.Write(&cfa sizeof(cfa))
,,,SendMessage hwndre EM_SETCHARFORMAT SCF_SELECTION m.address    
,,SendMessage hwndre EM_SETSEL &selStart &selEnd    
I found this in QM Scripting folder: __Tcc --> C example - global hook in dll  (pasted below).
I just don't know how to modify this to allow me to use the GetRTF function from
The child richedit text element is pretty straightforwawrd - i.e. with text extractable by .getwintext via child definition of 

Select All      Help
child("" "*.RICHEDIT50W.*" hWnd 0x0 "wfName=rtbReport") ;;editable text

Any advice?

Macro C example - global hook in dll
Select All      Help
__Tcc x.Compile("" "$desktop$\tcchook.dll" 2)

dll- "$desktop$\tcchook.dll" #Hook on

if Hook(1) ;;hook
,mes "Press a key and see what comes to DebugView. Don't close this message box now.[][]At first download and run DebugView."
,Hook(0) ;;unhook
else mes "failed to hook"


#include <windows.h>

#define EXPORT __declspec(dllexport)

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);

HMODULE g_hmod;

//printf for dll. You can see the text in DebugView:
void printf_dll(LPCSTR fmt, ...)
,char b[1000];
,if(!fmt || !*fmt) fmt="\n";
,_vsnprintf(b, 999, fmt, (va_list)(&fmt+1));
#define printf printf_dll

BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)

,return 1;

static HHOOK hhook;
,hhook=SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hmod, 0);
,if(!hhook) return 0;

return 1;

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
if(code!=HC_ACTION) goto g1;

printf("%s: key %i %s.", __func__, wParam, lParam&0x80000000 ? "released" : "pressed");

return CallNextHookEx(0, code, wParam, lParam);
I don't know other ways to get RTF and how to improve this code.
Dll injection - not QM-specific. Need to create a dll in Visual Studio, using C++ language. Inject it with SetWindowsHookEx(WH_CALLWNDPROC, HookFunctionInThisDll, ModuleHandleOfThisDll, ThreadIdOfTheWindow) and SendMessage(hwnd, MyMessage, 0, 0). Then to send data back use eg shared memory or ReadProcessMemory.
Thanks so much! I will see what I can do with your suggestions.
Quote:I found this in QM Scripting folder: __Tcc --> C example - global hook in dll  (pasted below).

With __Tcc possible but much much more difficult than with Visual Studio. No intellisense, no C++, no 64-bit, no exception handling. It is reasonable only for simple small code. Injecting dll, getting RTF and passing it back is not so easy. I cannot help.

Visual Studio Community 2017 is free. It's the best IDE. But it takes maybe 10 GB. If it's too much, you can find a smaller IDE that supports C++.

Quote:how to modify this to allow me to use the GetRTF function from

The function is very simple, easy to convert to C++. Or maybe better look for an EM_STREAMOUT example eg in stackoverflow.
That is all very helpful. I am downloading Visual Studio now.
I found these links which I hope will be helpful to me and others:
Code Project: Three Ways to Inject Your Code into Another Process
Code Project: InjLib - A library that implements remote code injection for all Windows versions
DLL Injection and function interception tutorial
Rohitab - Code Injections [beginner and advanced]
Apriorit Dev Blog - Windows API Hooking Tutorial (Example with DLL Injection)
Tutorial: Create a Sample DLL Project using CodeBlocks IDE in C/C++
Code Project - A More Complete DLL Injection Solution Using CreateRemoteThread
GitHub - DLL-Injector


Forum Jump:

Users browsing this thread: 1 Guest(s)