Win32 API dialogbox stalls/freezes on moving it - winapi

I have a modless Win32 API dialogbox as status box with a LISTBOX in it where I print some information of the communication between an IDE and the MSSCCI provider.
Everything works fine (the status messages are printed line by line and scrolled) unless I, as a IDE user, touch the status- dialogbox and try to move it around.
Then the status updating freezes (when clicking on the status dialogbox several times the "not responding" appear in the title bar) and with it the parent application (an IDE).
The program seems still to be running in the background (I also log to a file where the progress is "visible").
The dialogobox is not destroyed but hidden by the application when the task (getting several file from the repo) has finished. Later when a new task is started the dialogbox is shown again, the status message of the newly started task get printed and scrolled again as if nothing had happend.
I tried to catch the WM_MOVE and WM_MOVING in the dialogboxes callback function, set the statusbox the active window (see code). Nothing helped so far.
What do I have to do that the dialogbox does not freeze when the user moves it?
Any hints?
(By the way, when debugging, I can not move the dialogbox and the listbox in the dialogbox gets updated all the time and the parent get redrawn too)
Here some code (file processing loop):
pool = svnApiSvnIface.fp_svn_pool_create_ex(masterPool, NULL);
for(i=0; i<nFiles; i++)
{
WaspLoggerLogPrintf(0, "%s - Processing: '%s' \n",
__FUNCTION__,
StripFilename(lpFileNames[i]));
UpdateWindow(hwndParent);
SetActiveWindow (hwndStatusBox);
PrintMesgStatusBox("CheckFileStatus: for %s", StripFilename(lpFileNames[i]));
revison.kind = svn_opt_revision_unspecified;
:
Here the some code from the PrintMesgStatusBox() function:
if(UseStatusBox())
{
int lbIndex = 0;
int length = vsprintf(statusMessage, format, args);
int printedLength = 0;
while((printedLength + STATUSBOX_LINE_LENGTH) < length)
{
char tempChar = statusMessage[printedLength + STATUSBOX_LINE_LENGTH];
statusMessage[printedLength + STATUSBOX_LINE_LENGTH] = '\0';
SendDlgItemMessage(hwndStatusBox, IDC_LIST1, LB_ADDSTRING, 0, (LPARAM)&statusMessage[printedLength]);
statusMessage[printedLength + STATUSBOX_LINE_LENGTH] = tempChar;
printedLength += STATUSBOX_LINE_LENGTH;
}
SendDlgItemMessage(hwndStatusBox, IDC_LIST1, LB_ADDSTRING, 0, (LPARAM)&statusMessage[printedLength]);
//AUTOSCROLL
lbIndex = SendDlgItemMessage(hwndStatusBox, IDC_LIST1, LB_GETCOUNT, 0, 0) - 1;
//Scroll to the newest item (put lbIndex to the top of the lb)
SendDlgItemMessage(hwndStatusBox, IDC_LIST1, LB_SETTOPINDEX, lbIndex, 0);
//redraw
UpdateWindow(hwndStatusBox);
:

The issue was that the message queue seemed no longer to be processed due to my code looping and communicating to the SVN server for a long time.
Since I have not control over the IDE implementation I had to do it in my dll.
I the loop I implemented a function which consumes and dispatches the messages for the dialogbox (resp. all messages).
The results were different if all or only the dialogbox message were consumed:
Consuming dialogbox messages only: The dialogbox would updated after every SVN communication which results in a rather choppy UI response to the user action. But the IDE menus stay in accessible which is wanted.
Consuming all messages: The UI response to the user is relatively smooth. But the IDE menus are accessible and the IDE can be close or other actions start. This is unwanted.
Hence I stay with only consuming the dialogbox messages.
Some code (function processing messages):
void ProcessMessages(HWND hWnd, BOOLEAN peekHwndMsgsOnly)
{
MSG msg;
HWND peekHwnd = NULL;
if(peekHwndMsgsOnly)
{
peekHwnd = hWnd;
}
while(PeekMessage(&msg, peekHwnd, 0, 0, PM_REMOVE))
{
if(!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
The loop originally blocking messages processing:
for(i=0; i<nFiles; i++)
{
/*
WaspLoggerLogPrintf(0, "%s - Processing: '%s' \n",
__FUNCTION__,
StripFilename(lpFileNames[i]));
*/
PrintMesgStatusBox("CheckFileStatus: for %s", StripFilename(lpFileNames[i]));
revison.kind = svn_opt_revision_unspecified;
youngest = SVN_INVALID_REVNUM;
internalfileName = svnApiSvnIface.fp_svn_dirent_internal_style(lpFileNames[i], pool);
if (updateFromRepo) { PrintMesgStatusBox(" Retrieving repo status..."); }
UpdateWindow(hwndParent);
ProcessMessages(hwndStatusBox, TRUE);
rtn = svnApiSvnIface.fp_svn_client_status4( &youngest, // svn_revnum_t *result_rev,
internalfileName, // const char *path,
&revison, // const svn_opt_revision_t *revision,
This might not be a very elegant solution but is serving the purpose.

Related

MFC: How to use MsgWaitForMultipleObjects() from the main thread to wait for multiple threads to complete that use SendMessage()?

I have a main thread that fires off several other threads to complete various items of work based on what the user choose from the main UI. Normally I'd use WaitForMultipleObjects() with bWaitAll set to TRUE. However, in this case those other threads will log output to another window that uses a mutex to ensure the threads only output one at a time. Part of that process uses SendMessage() to send get the text size and send the text to the windows which will hang if using WaitForMultipleObjects() since it's running from the main UI thread. So I moved over to use MsgWaitForMultipleObjects with QS_SENDMESSAGE flag, only it's problem is the logic for bWaitAll which states it will only return if all objects are signaled AND an input event occurred (instead of returning when all objects are signaled OR an input event occurred). Had the logic been OR this should have worked:
DWORD waitres=WAIT_FAILED;
while (1)
{
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
// mfc message pump
if (!theApp.PumpMessage()) {
// program end request
// TO DO
}
}
// MFC idel processing
LONG lidlecount = 0;
while (theApp.OnIdle(lidlecount++));
// our wait
waitres = ::MsgWaitForMultipleObjects(threadcount, threadhandles, TRUE, INFINITE, QS_SENDMESSAGE);
// check if ended due to message
if (waitres!=WAIT_OBJECT_0+threadcount) {
// no, exit loop
break;
}
}
Rather than fire off a thread that then fires off the other threads I wondered what is the correct way to handle this from the main thread? I thought about using bWaitAll FALSE then using WaitForMultipleObjects() with bWaitAll set to TRUE and the dwMilliseconds set to 0 (or 1) and checking the result to see if completed. If not, it would need to loop back to the top of the loop and then to MsgWaitForMultipleObjects() which when using bWaitAll FALSE could return right away if one of the many threads completed (say 1 thread of 10 completed, I could check as mentioned above if all completed, but when going back with bWaitAll FALSE it will just return and not wait).
So what is the proper way to handle waiting for multiple threads (that use SendMessage()) to complete in the main thread of an MFC application?
Thanks.
So what is the proper way to handle waiting for multiple threads to
complete
need create some structure, with reference count and pass pointer to this structure to every thread. here also probably exist sense have some common task data. and HWND of some window in main(GUI) thread. when worked thread exit - it release reference on object. when last thread exit - delete object and post some message to window, from main thread.
so we not need store thread handles (can just close it) and wait om multiple handles. instead we got some window message when all thread finish task
example of code
struct Task
{
HWND _hwnd;
LONG _dwRefCount = 1;
// some common task data probably ..
Task(HWND hwnd) : _hwnd(hwnd) {}
~Task() {
PostMessageW(_hwnd, WM_USER, 0, 0);// WM_USER as demo only
}
void AddRef(){
InterlockedIncrementNoFence(&_dwRefCount);
}
void Release(){
if (!InterlockedDecrement(&_dwRefCount)) delete this;
}
};
ULONG CALLBACK WorkThread(void* pTask)
{
WCHAR sz[16];
swprintf_s(sz, _countof(sz), L"%x", GetCurrentThreadId());
MessageBoxW(0, L"working...", sz, MB_ICONINFORMATION|MB_OK);
reinterpret_cast<Task*>(pTask)->Release();
return 0;
}
void StartTask(HWND hwnd, ULONG n)
{
if (Task* pTask = new Task(hwnd))
{
do
{
pTask->AddRef();
if (HANDLE hThread = CreateThread(0, 0, WorkThread, pTask, 0, 0))
{
CloseHandle(hThread);
}
else
{
pTask->Release();
}
} while (--n);
pTask->Release();
}
}

Different behaviour between dialogbox and createdialog is not understood

I am creating a dialog in a DLL and while DialogBox creates it - but seems hard to be stopped programmatically, CreateDialog only shows the borders (and title) of the dialog and "does not respond" after creating
I am adding some functionality to an existing program - not under my control. Control of this added functionality - for some external device - requires the user to do some settings particular to settings for this device, not covered by controls in the main program. For this I add a DLL with a small dialog, one with three control elements. The dialog is started with the DialogBox function in a separate thread and funtions well, apart from termination: the use of the main program may want to switch from one device to another without having to explicity quit the dialog by touching some button or control on the dialog.
Therefore I figured it would be better to use CreateDialog since that gives a handle.
However, when doing that - of course adding ShowWindow (handle, WS_SHOW) to make the dialog visible, only the raw widget appears, no controles visible and suffering from "does not respond"
` // DialogBox (hInstance,`
` widgetHandle = CreateDialog (hInstance,`
` MAKEINTRESOURCE(IDD_DIALOG1), NULL, ialog1Proc);`
` err = GetLastError ();`
` fprintf (stderr, "Last Error = %d\n", err);`
` if (err == 0) {`
` ShowWindow (widgetHandle, SW_SHOW);`
` while (running)`
` Sleep (100);`
` }`
Since - when using CreateBox, the contours of the widget - and the title of the dialog - are visible, I assume that the resource is seen correctly,
but it is as such unusable.
Therefore two questions
a. Is my assumption that CreateBox can be used in a thread in a DLL wrong
b. I'm perfectly willing to use DialogBox here, but then I need some advice on how to terminate the dialog (box) programmatically, i.e. from elsewhere in the DLL.
Any help would be appreciated here
DialogBox creates its own message loop and does not return until the dialog is closed. It is essentially CreateDialog + a message loop.
CreateDialog returns after creating the window and it expects you to process messages for it.
HWND hDlg = CreateDialog(...);
ShowWindow(hDlg, SW_SHOW);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsWindow(hDlg)) break;
if (IsDialogMessage(hDlg, &msg)) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
If your only requirement is to be able to close the dialog from a different thread you can pull it off with DialogBox as well:
HWND g_hDlg = 0; // Assumes you only have one dialog instance open at the time
.. MyDlgProc(HWND hDlg, UINT Msg, ..)
{
switch(Msg)
{
case WM_INITDIALOG:
g_hDlg = hDlg;
return TRUE;
case WM_DESTROY:
g_hDlg = 0;
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
}
return FALSE;
}
EXTERN_C void WINAPI CloseDialog()
{
if (g_hDlg) SendMessage(g_hDlg, WM_CLOSE, 0, 0);
}

On Windows 10, the Cancel object never returns RPC_E_CALL_COMPLETE

In our application that we've had for 15+ years, we have a type of progress bar.
This progress bar is for long lasting C++ operations and there is also the case for when we make a COM call and it takes a long time for the COM call to return.
In general, we want the user to know that something is taking a long time to complete and give him a chance to cancel if he thinks it is taking too much time.
For COM operations, many years ago we implemented a custom IMessageFilter for COM calls that take too long. We would like the user to be able to cancel but also for the prompt to cancel go away on its own when the operation completes. It has been working fine for years. Most of our customers are conservative and are still running Windows 7. Recently a customer using Windows 10 has found an issue where a COM call on Windows 10 never seems to never finish.
Our progress bar comes up and the progress control cycles and recycles, but the operation never seems to finish. After investigating it, it seems that the ICancelMethodCalls::TestCancel() method always returns RPC_S_CALLPENDING, it never returns RPC_E_CALL_COMPLETE. On Windows 7, previous versions of Windows, and Windows 8.1, it works fine, but not on Windows 10.
I created a minimal test solution in Visual Studio 2013 that demonstrates the problem. One project is an ATL server, and the other project is an MFC client application. A link to a zip file of a sample solution is here: https://www.dropbox.com/s/1dkchplsi7d6tda/MyTimeOutServer.zip?dl=0
Basically the ATL server has a property that sets a delay, and a method that just waits the delay length. The purpose is to simulate a COM operation that is taking too long.
interface IMyTimoutServer : IDispatch{
[propget, id(1), helpstring("Timeout value in milliseconds")] HRESULT TimeOut([out, retval] LONG* pVal);
[propput, id(1), helpstring("Timeout value in milliseconds")] HRESULT TimeOut([in] LONG newVal);
[id(2)] HRESULT DoTimeOut([out, retval] VARIANT_BOOL* Result);
};
The next important thing is the IMessageFilter in the client application. At some point, someone decided it was good to derive from COleMessageFilter, the default MFC implementation. (Let's not argue whether that is a good idea.)
The important methods of the the class are the MessagePending() and MyNotResponding().
DWORD CMyMessageFilter::XMyMessageFilter::MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType)
{
METHOD_PROLOGUE_EX(CMyMessageFilter, MyMessageFilter);
ASSERT_VALID(pThis);
MSG msg;
if (dwTickCount > pThis->m_nTimeout && !pThis->m_bUnblocking && pThis->IsSignificantMessage(&msg))
{
if (pThis->m_bEnableNotResponding)
{
pThis->m_bUnblocking = TRUE; // avoid reentrant calls
// eat all mouse and keyboard messages in our queue
while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, AFX_WM_MOUSELAST, PM_REMOVE | PM_NOYIELD));
while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE | PM_NOYIELD));
// show not responding dialog
pThis->m_dwTickCount = dwTickCount;
bool bCancel = pThis->MyNotResponding(htaskCallee) == RPC_E_CALL_CANCELED;
pThis->m_bUnblocking = FALSE;
return bCancel ? PENDINGMSG_CANCELCALL : PENDINGMSG_WAITNOPROCESS; // if canceled, the COM call will return RPC_E_CALL_CANCELED
}
}
// don't process re-entrant messages
if (pThis->m_bUnblocking)
return PENDINGMSG_WAITDEFPROCESS;
// allow application to process pending message
if (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE | PM_NOYIELD))
pThis->OnMessagePending(&msg); // IK: could also return a value from extended OnMessagePending() to cancel the call
// by default we return pending MSG wait
return PENDINGMSG_WAITNOPROCESS;
}
After a timeout, we display a status type window that updates in the NotMyResponding() method.
int CMyMessageFilter::MyNotResponding(HTASK hTaskBusy)
{
TRY
{
CComPtr<ICancelMethodCalls> pCancel;
HRESULT hRes = ::CoGetCancelObject(0, IID_ICancelMethodCalls, (void**)&pCancel);
ATLASSERT(SUCCEEDED(hRes)); // COM should automatically create Cancel objects for pending calls [both on client and server]
if (pCancel == NULL)
{
return COleBusyDialog::retry;
}
m_pFrame->EnableWindow(FALSE);
CMyCancelDlg dlg;
dlg.Create(CMyCancelDlg::IDD);
dlg.ShowWindow(SW_SHOWNORMAL);
HWND hWndDlg = dlg.GetSafeHwnd();
do
{
MSG msg;
for (int i = 0; i < 100 && PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD); ++i)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (dlg.StepAndCheckCancel())
{
dlg.DestroyWindow();
m_pFrame->EnableWindow(TRUE);
return RPC_E_CALL_CANCELED; // signals to MessagePending() that the COM call should be cancelled
}
Sleep(250); // this call has been pending for many seconds now... sleep for some time to avoid CPU utilization by this loop and yield if needed
hRes = pCancel->TestCancel();
} while (hRes == RPC_S_CALLPENDING);
ATLASSERT(hRes == RPC_E_CALL_COMPLETE);
dlg.DestroyWindow();
m_pFrame->EnableWindow(TRUE);
}
END_TRY
return RPC_E_CALL_COMPLETE;
}
Basically, in MyNotResponding(), we create a Cancel object, create a window with a cancel button, pump messsages, and look for either a press on the cancel button or if TestCancel() returns something other than RPC_S_CALLPENDING.
Well, on Windows 10, it stays in the loop and RPC_S_CALLPENDING is always returned from TestCancel().
Has anyone seen anything like this on Windows 10? Are we doing something wrong that we are really only getting lucky on Windows 7?
The default implementation of the MFC puts up an OLEUIBusy dialog which pumps messages. It just never tests the cancel object.

Modal MFC dialog not shown due to idle checks in CWnd::RunModalLoop

Below I've put the source to CWnd::RunModal, which is the message loop run when you call CDialog::DoModal - it takes over as a nested message loop until the dialog is ended.
Note that with a couple of special case exception ShowWindow is only called when the message queue is idle.
This is causing a dialog not to appear for many seconds in some cases in our application when DoModal is called. If I debug into the code and put breakpoints, I see the phase 1 loop is not reached until this time. However if I create the same dialog modelessly (call Create then ShowWindow it appears instantly) - but this would be an awkward change to make just to fix a bug without understanding it well.
Is there a way to avoid this problem? Perhaps I can call ShowWindow explicitly at some point for instance or post a message to trigger the idle behaviour? I read "Old New Thing - Modality" which was very informative but didn't answer this question and I can only find it rarely mentioned on the web, without successful resolution.
wincore.cpp: CWnd::RunModalLoop
int CWnd::RunModalLoop(DWORD dwFlags)
{
ASSERT(::IsWindow(m_hWnd)); // window must be created
ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE);
HWND hWndParent = ::GetParent(m_hWnd);
m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
MSG *pMsg = AfxGetCurrentMessage();
// acquire and dispatch messages until the modal state is done
for (;;)
{
ASSERT(ContinueModal());
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
{
ASSERT(ContinueModal());
// show the dialog when the message queue goes idle
if (bShowIdle)
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
bShowIdle = FALSE;
}
// call OnIdle while in bIdle state
if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
{
// send WM_ENTERIDLE to the parent
::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
}
if ((dwFlags & MLF_NOKICKIDLE) ||
!SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
{
// stop idle processing next time
bIdle = FALSE;
}
}
// phase2: pump messages while available
do
{
ASSERT(ContinueModal());
// pump message, but quit on WM_QUIT
if (!AfxPumpMessage())
{
AfxPostQuitMessage(0);
return -1;
}
// show the window when certain special messages rec'd
if (bShowIdle &&
(pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
bShowIdle = FALSE;
}
if (!ContinueModal())
goto ExitModal;
// reset "no idle" state after pumping "normal" message
if (AfxIsIdleMessage(pMsg))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
}
ExitModal:
m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
return m_nModalResult;
}
So to answer my own question, the solution I found was to explicitly call the following two methods:
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
CWnd::RunModalLoop is supposed to call these, but only when it detects the message queue is empty/idle. If that doesn't happen then the dialog exists and blocks input to other windows, but isn't visible.
From tracking messages I found WM_ACTIVATE was the last message being sent before things got stuck, so I added an OnActivate() handler to my Dialog class.
I found a very similar problem in my application. It only happened when the application was under a heavy load for drawing (the user can open many views, the views show real time data, so they are constantly refreshing, and each view must be drawn independently, and the process of drawing takes a lot of time). So, if under that scenario, the user tries to open any modal dialog (let's say, the "About" dialog, or if the app needs to show any modal dialog like a MessageBox), it "freezes" and the dialog only shows after pressing the ALT key.
Analyzing RunModalLoop() I got to the same conclusion as you did, that is, the first loop (titled "phase1" in the code comments), is never called, since it needs the message queue to be empty, and it never is; the app then falls in the second loop, phase2, from which it never exits, because the call to PeekMessage() at the end of phase2 never returns zero (the message queue is very busy for so many views constantly updating).
Since this code is in wincore.cpp, my solution was to find the closest function that could be overloaded, and lucky me found ContinueModal() which is virtual. Since I already had a class derived from CDialog which I always use as a replacement, I only had to define in that class a BOOL variable m_bShown, and an overload of ContinueModal():
BOOL CMyDialog::ContinueModal()
{
// Begin extra code by Me
if (m_nFlags & WF_CONTINUEMODAL)
{
if (!m_bShown && !(GetStyle() & WS_VISIBLE))
{
ShowWindow(SW_SHOWNORMAL);
UpdateWindow();
m_bShown = TRUE;
}
}
// End extra code
return m_nFlags & WF_CONTINUEMODAL;
}
This causes ContinueModal() to actually show the window if it has never been shown before. I set m_bShown to FALSE both in its declaration and in OnInitDialog(), so it arrives to RunModalLoop() in FALSE, and set it to TRUE immediately after showing the window to disable the little additional code snippet.
This solved the problem for me and supposedly should solve it for anybody. The only doubts remaining are, 1) is RunModalLoop() called from somewhere else within MFC that would conflict with this modification? and 2) Why did Microsoft program RunModalLoop() this weird way leaving this glaring hole there that can cause an app to freeze without any apparent reason?
I recently had the same problem.
I solved by sending the undocumented message 0x118 before calling DoModal(), which is handled in the phase2.
...
PostMessage(0x118, 0, 0);
return CDialog::DoModal();

Best algorithm for syntax highlight (WINAPI)

I write a program on WINAPI. I must to implement syntax highlight. At this moment the I using following algorithm:
void PaintWords(const char *SearchWord,COLORREF rgb)
{
counter = TabCtrl_GetCurSel(TabControl_hWnd);
ft.chrg.cpMin = 0;
ft.chrg.cpMax = GetWindowTextLength(hWnd);
ft.lpstrText = (LPCSTR)SearchWord; //keyword
do
{
int poe_p = SendMessage(hWnd, EM_FINDTEXTEX, FR_DOWN | FR_WHOLEWORD | FR_MATCHCASE, (LPARAM)&ft);
if(poe_p != -1)
{
int selword = SendMessage(hWnd, EM_EXSETSEL,0,(LPARAM)&ft.chrgText);
ZeroMemory(&chd, sizeof(chd));
chd.cbSize = sizeof(CHARFORMAT);
chd.dwMask = CFM_SIZE | CFM_FACE | CFM_COLOR | CFM_CHARSET;
chd.crTextColor = rgb;
chd.bPitchAndFamily = FW_THIN;
lstrcpy(chd.szFaceName , "Courier New");
SendMessage(hWnd,EM_SETCHARFORMAT,SCF_WORD|SCF_SELECTION,(LPARAM)&chd);
ft.chrg.cpMin = ft.chrgText.cpMax;
}
else
{
break;
}
}while(ft.chrg.cpMin != ft.chrg.cpMax);
}
This code is too slow, because this is not best option, flicker is visible.
I interested in other variants.
I think you need to use double buffering to reduce the flicker. Other than that, you should not use SendMessage (or even PostMessage) to any window (within same thread). Why selection must happen in your syntax-highlighting paint code?
One of the article on double buffer is this.
I have come across this project's RichTextbox, which is used as an xml editor: http://xpathvisualizer.codeplex.com/SourceControl/changeset/view/42057#XPathVisualizer/CustomControls/RichTextBoxEx.cs
it's in C#, but the messages sent are visible.
When using this TextBox, before highlighting the text the BeginUpdateAndSuspendEvents function should be called.
public IntPtr BeginUpdateAndSuspendEvents()
{
// Stop redrawing:
User32.SendMessage(this.Handle, (int) User32.Msgs.WM_SETREDRAW, 0, IntPtr.Zero);
// Stop sending of events:
IntPtr eventMask = User32.SendMessage(this.Handle, User32.Msgs.EM_GETEVENTMASK, 0, IntPtr.Zero);
return eventMask;
}
This function prevents redrawing while you are working on the text, after you finish editing, you should call
public void EndUpdateAndResumeEvents(IntPtr eventMask)
{
// turn on events
User32.SendMessage(this.Handle, User32.Msgs.EM_SETEVENTMASK, 0, eventMask);
// turn on redrawing
User32.SendMessage(this.Handle, User32.Msgs.WM_SETREDRAW, 1, IntPtr.Zero);
NeedRecomputeOfLineNumbers();
this.Invalidate();
}
Double buffering does not solve this problem because it will not stop the paints from occurring, highlighting an editor without disabling the paint event can cause the program to halt for more than 5 minutes depending on the size of the file and the number of words to be highlighted.

Resources