Click here to Skip to main content
Click here to Skip to main content

CEditLog - fast logging into an edit control with cout

By , 1 Nov 2003
 

Problem

I once wanted to use an edit control for fast, asynchronous text output. I need to log messages from a lot of threads, sometimes more than 1000 per second. And since I created already functions to dump my objects to std::cout I also wanted to use an edit control as std::ostream.

Features

  • Asynchronous, very fast output to any edit control
  • User can scroll back, select and copy text and so on during output
  • Can be used with or without MFC (Sample is a MFC project)
  • Uses window-subclassing, so you can use it even with CEdit-derived controls
  • Save to be used by multiple threads
  • Special basic_streambuf class to create or redirect a std::ostream (such as std::cout) to an edit control

Using CEditLog

Just create an Instance of CEditLog and attach an edit control to it using the SetEditControl() function. Then you can add text to the end of the edit control using one of the AddText() members:

class CEditLog : protected CSubclassWnd
{
public:
    typedef CSubclassWnd root_type;

    // Constructs the CEditLog. You can pass the edit controls handle

    // here or set it later using the SetEditCtrl() function.

    // To increase performance CEditLog repaints the edit-control

    // only if the caret resides in the last line. Otherwise the

    // control will be refreshed only every nMaxRefreshDelay msec.

    CEditLog( HWND hEdit = NULL, UINT nMaxRefreshDelay = 500 );
    
    // Adds some text to the end of the edit control. Works asynchronously

    // (using PostMessage()) and is save to be called by multiple threads.

    // If you pass true for bLFtoCRLF every LF (ASCII 10) (as it is used in

    // most cases for end-of-line) will be converted to a CR/LF (ASCII 10/13)

    // sequence as it is needed by the windows edit control. 

    virtual void AddText( LPCWSTR pwszAdd, bool bLFtoCRLF = false );
    
    // Converts pszAdd to UNICODE and calls the above

    void AddText( LPCSTR pszAdd, bool bLFtoCRLF = false );

    // Sets the edit-control to be used for logging.

    // Pass NULL to stop text logging.

    virtual void SetEditCtrl( HWND hEdit );

    HWND GetEditCtrl() const

    ...
};

The sample project

The included sample project shows the usage of CEditLog and how to use basic_editstrbuf to redirect std::cout to use the edit log.

CEditLog uses the CSubclassWnd class, written by William E. Kempf for implementation. I have included the soure of CSubclassWnd in the sample project, the complete article is also available here at CodeProject.

History

Version Comments
1.0 First Release
1.1 Minor Revision. Now uses V2.0 of SubclassWindow.
Now really works on Win9x (V1.0 used UNICODE for internal data handling and failed on Win9x systems. Now the code checks if it runs on Win9x and does the required conversions.)
1.2 Minor Revision. Now compiles fine even in VC.NET 2003.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Daniel Lohmann

Germany Germany
Daniel Lohmann (daniel@losoft.de) is Assistant Professor at the Distributed Systems and Operating Systems department at Friedrich-Alexander-University Erlangen-Nuremberg, Germany. His main research topic is the design of a highly customizable and scalable operating system product line for deeply embedded systems using static configuration and aspect-oriented techniques. Before joining Universität Erlangen he worked as a freelance trainer and consultant for NT system programming, advanced C++ programming and OOA/OOD. He is interested in upcoming programming techniques like aspect-oriented programming, generative programming and C++ meta coding and has written some nice and handy tools for Windows NT which you can download at his web site.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionhow I can add color to ceditlog?memberMember 328166623-Apr-13 5:18 
I want use ceditlog in my system but I have some log level, and I want add color to each level
if this possible
thank
herzl
GeneralPassed on compiling, but failed on running on Windows VistamemberGolden Lee3-Dec-09 19:25 
Dear Mr. Daniel Lohmann,
How are you? Thank you for your fast logging edit control.
 
CLogEdit control works (compile, link and execute by using VS 2008) like a charm on Windows XP operating system. On Windows Vista, CLogEdit control can pass compiling and linking, but con't run anymore. I trace from the application entry point step by step, and finally find an error in the statement ::SendMessage( hEdit, EM_LIMITTEXT, 0, 0 );
of the following routine:
 
void CEditLog::SetEditCtrl(
HWND hEdit
)
{
if( hEdit != GetHandle() && GetHandle() != NULL )
UnsubclassWindow();

if( SubclassWindow( hEdit ) ) {
// We want to support a lot of text...
// Note that Win9x supports no more than 64kb of text! - just too bad...
// Tip: Use NT/2k Smile | :)
::SendMessage( hEdit, EM_LIMITTEXT, 0, 0 );
}
}
 
When single-steped into the statement ::SendMessage( hEdit, EM_LIMITTEXT, 0, 0 ); an error message box was throwed, and said "Unhandled exception at 0x03613d1c in GVIPS.exe: 0x0000005; Access violation."
 
Could you help me to figure out this error. Thank you!
GeneralRe: Passed on compiling, but failed on running on Windows VistamemberDaniel Lohmann7-Dec-09 0:37 
Hi,
 
I am sorry, but my current work load does not leave me any time to look into this; moreover, I do not have a Vista box D'Oh! | :doh:
 
I assume, however, that the problem lies somewhere in the CSublassWindow-Code and the way it creates the Thunk-Code in a member variable. Probably Vista marks all data segments with the "no-exec" Flag supported by newer IA32/64 CPUs, which results in the Access Violation.
 
Maybe there is a newer Version of CSubclassWindow available somehwere (I found the original one here on CodeProject, but apparently it disappeared). Otherwise, someone has to rewrite the code so that the memory for the Tunk data structure is allocated from an executable memory region (VirtuallAlloc()). Another, simpler approach might be to use VirtualProtect() to set the "X"-flag on the memory region the Thunk member variables belong to.
 
--
 
Daniel Lohmann
 
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there Big Grin | :-D )

AnswerRe: Passed on compiling, but failed on running on Windows Vistamemberysliou19-May-11 3:53 
I have the same problem run on Windows 7. After setting the project property "Data Execution Prevention (DEP)" to off, the problem disapperred. Project property > Linker > Advanced > Data Execution Prevention (DEP) > Default or (/NXCOMPAT:NO). You can try it.
Y.S. Liou

GeneralRe: Passed on compiling, but failed on running on Windows Vistamember Randor 7-Jul-12 13:50 
Hi,
 
I took a look at the source code and found the problem. The CSubclassWnd class was written way back around 2000+/- by William E. Kempf. Back then processors did not have the NX bit and you could execute code located at arbitrary memory addresses with no problems. Today on more modern hardware/operating systems you will of course receive a DEP error.
 
In the file SubclassWnd.h find the the code block:
 
#if defined (_M_IX86)
	thunk.m_mov = 0x042444C7;  //C7 44 24 0C
	thunk.m_this = (DWORD)pThis;
	thunk.m_jmp = 0xe9;
	thunk.m_relproc = (int)proc - ((int)this+sizeof(Thunk));
#elif defined (_M_ALPHA)
 
Then change the memory protection on the thunk so it is executable:
 
#if defined (_M_IX86)
	thunk.m_mov = 0x042444C7;  //C7 44 24 0C
	thunk.m_this = (DWORD)pThis;
	thunk.m_jmp = 0xe9;
	thunk.m_relproc = (int)proc - ((int)this+sizeof(Thunk));
	DWORD dwOldProtection;
	VirtualProtect(&thunk,sizeof(Thunk),PAGE_EXECUTE_READWRITE,&dwOldProtection);
#elif defined (_M_ALPHA)
 
Best Wishes,
-David Delaune
GeneralRe: Passed on compiling, but failed on running on Windows VistamemberWalter Schmoll19-Sep-12 22:40 
The suggested solution might be a problem as, according to MSDN, one should avoid using VirtualProtect on memory that wasn't allocated with VirtualAlloc[Ex]. VirtualProtect changes the access rights on a complete memory page. If "thunk" crosses page boundaries, it even changes the rights of two pages, but that's just details. It's not done here, but if you remove read/write access from a page within the heap, you're in really big problems.
 
I came up with a similar solution, which is a little bit more complicated (and hopefully better Smile | :) ). For Details see my message "Fix to run with Data Execution Prevention" in the article "Pluggable Event Handler"
Pluggable Event Handler[^].
GeneralRe: Passed on compiling, but failed on running on Windows Vistamember Randor 17-Oct-12 14:09 
Hi Walter,
 
I have not looked at your modifications but I am sure that you have done an outstanding and thorough job. I was contacted by a former co-worker to have a look at this project and tell him why it was crashing. I think I spent a total of 5 minutes looking at the code. Thanks for the improved solution.
 
Best Wishes,
-David Delaune
QuestionHow can I change the text color in this Classmemberhuyphuc9-Dec-08 15:42 
I've merged this class with this one
Using colors in CEdit and CStatic

but it does not work. Please help me.
QuestionHow do i use it for CRichEditCtrl?membersmalti16-Aug-07 3:02 
Can I use CEditLog for CRichEditCtrl?
What do I change to work it?
GeneralMax. number of lines controlmemberVic Siu7-Oct-06 15:59 
Good work, thanks!
 
Is there any way to control number of lines in the control? I need to log an application for many days.

QuestionWhat about printfmemberAvri27-Sep-06 19:54 
Hi,
 
Great job !!!
 
I have a question,
 
My application used to be a console one and it uses printf a lot.
Is there a way to redirect printf as well ?
 
Thanks

AnswerRe: What about printfmembernikestone24-Nov-06 17:31 
Hello!
 
I met the same problem,and made no progress although i try to find many materials related to redirection problem.
 
Do you have any solution now? Thanks!!
GeneralNice but some error in VC2005memberansoncat13-Jun-06 19:02 
Hi
I've download your program and try to comiple it in VC05. I've got some error message like:
"editlog.cpp(22) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int". It denotes "const WM_ADDTEXT = WM_USER + 500;" in line 22 of editlog.cpp is illegal now. I've changed it to const int and it works fine now. Anyway, thanks you to create this nice class.
 
Anson Chen
GeneralRe: Nice but some error in VC2005memberw00tzenheimer21-Jul-06 19:39 
it was a mistake.
 
just read the error message:
 
const int WM_ADDTEXT = WM_USER+500;
 
is the correct statement.
 

GeneralNicememberucc8012-Feb-06 23:03 
Nice,
--------------------------------------------------------------------------
For high quality flow/diagram MFC/C++ Visio Like visualization Source Code,download XD++ at:
http://www.ucancode.net

GeneralSlow logmembervdaanen25-Nov-05 1:26 
Hi,
 
I'm using CEditLog to log some mathematical calculations.
However, the CEdit is not update after each cout (nor AddText) but only a the end of the computations.
 
Is there a way to force the update of the CEdit ?
 
Thanks
 
Vince
GeneralRe: Slow logmemberDaniel Lohmann26-Nov-05 3:28 
Hi Vince,
The edit control is updated "immediately" if the caret resides in the last line or at least every 500 msec. 500 msec is the default, the timeout is configurable at construction time.
 
Note that the update is actually performed by the thread who owns the edit control and not by the thread calling AddText(). Moreover, the UI thread (the thread owning the edit control) needs to process messages. So if your calculation thread has a higher priority than the UI thread and induces a high CPU load, this may cause the update delays as the UI thread never gets the chance to update the edit control. Maybe it would help to add a Sleep(0) after the output in the worker thread to yield the CPU to another thread.
If the calculation is actually performed by the same thread that owns the UI, this may also cause delays if your thread does not process messages during the calculations, which is required to update the UI. In this case it may help to invoke message processing explicitly.
 
Hope that helps Cool | :cool:
 
--
 
Daniel Lohmann
 
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there Big Grin | :-D )
QuestionApplication Crash on exitmemberProf.S.Mukherjee13-Nov-05 20:47 
Hi,
 
I am trying to use CEditLog in my application (MFC MDI). I am using Scintilla edit control in place of CEdit. Everything is working fine except when I close the app there is a memory exception happenning (Could not read the memory ....). Apperantly it seems that there is a problem with the following two lines

// Redirect cout to our Edit-Control
m_pOldBuf = cout.rdbuf( &m_EditStrBuf );
// and also the unicode version
m_pOldBufW = wcout.rdbuf( &m_EditStrBufW );
 
Am I doing something wrong. Plase help.
 

 

AnswerRe: Application Crash on exitmemberchurl-hee Jo9-May-06 18:29 
Hi,
 
I think that it was not recovered stream.
Simply, We need to restore the streams.
 
And, I added the following code in .h and .cpp.
 

//
class CEditLogger : public CDialog
{
private:
std::basic_streambuf* _cold_in;
std::basic_streambuf* _cold_out;
std::basic_streambuf* _cold_err;
std::basic_streambuf* _cold_log;
 
std::basic_streambuf* _wcold_in;
std::basic_streambuf* _wcold_out;
std::basic_streambuf* _wcold_err;
std::basic_streambuf* _wcold_log;
 

protected:
CEditLog m_EditLogger;
 
// ANSI and UNICODE stream buffers for the EditLogger
std::editstreambuf m_EditStrBuf;
std::weditstreambuf m_EditStrBufW;
 
// Used to save the previos values for cout and wcout
std::basic_streambuf* m_pOldBuf;
std::basic_streambuf* m_pOldBufW;
:
:
}
 

 
CEditLogger::CEditLogger(CWnd* pParent /*=NULL*/)
: CDialog(CEditLogger::IDD, pParent), m_EditStrBuf( m_EditLogger ), m_EditStrBufW( m_EditLogger )
{
_cold_in = cin.rdbuf();
_cold_out = cout.rdbuf();
_cold_err = cerr.rdbuf();
_cold_log = clog.rdbuf();
 
_wcold_in = wcin.rdbuf();
_wcold_out = wcout.rdbuf();
_wcold_err = wcerr.rdbuf();
_wcold_log = wclog.rdbuf();
 
//{{AFX_DATA_INIT(CEditLogger)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
 
void CEditLogger::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
cin.rdbuf( _cold_in );
cout.rdbuf( _cold_out );
cerr.rdbuf( _cold_err );
clog.rdbuf( _cold_log );
 
wcin.rdbuf( _wcold_in );
wcout.rdbuf( _wcold_out );
wcerr.rdbuf( _wcold_err );
wclog.rdbuf( _wcold_log );
 
CDialog::PostNcDestroy();
}
 

 
I
QuestionHow to change fontmemberMarko Winter13-Jul-05 1:38 
Hello Daniel, Hello other codeproject guys!
 
These lines of code are really powerful - really nice component. Thank you very much or "Herzliches Dankeschön"!
 
I use these code in a document-view project with splitter windows. All things work well but I am not able to change the font. I am always getting a NULL pointer back, that is the code:     
 
..CErrorEditView::OnCreate(...
{
   CEdit& pme = this -> GetEditCtrl();
   CFont* pf = pme.GetFont();
   if (pf)         <- this is always NULL
   {
      LOGFONT lf;
      ...
      pf -> getLogFont(&lf);
      lf.lfHeight = 15;
      ...
      ...
      SetFont(pf);
   }
...
}
 
Why I can't get a valid pointer?
 
This code works for other views (for instance with treeViews
 
Thank you very much / kind regards from Dresden Smile | :)
 
Marko Winter
SysMik GmbH Dresden
Germany
 

AnswerRe: How to change fontmemberDaniel Lohmann13-Jul-05 22:24 
Are you sure this is a CEditLog related problem?
Is it working with a plain CEditView derived class that does not use CEditLog?
 
Confused | :confused:
 
--
 
Daniel Lohmann
 
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there Big Grin | :-D )
GeneralRe: How to change fontmemberMarko Winter14-Jul-05 23:42 
Hello Daniel!
 
Thanks for the hint - no that isn't a CEditLog related problem! I see same behaviour if I use a plain CEditView derived class.
 
That surprises me - because such code works with other CView derived classes.
 
But now I get a solution: creating a CFont member of class, setting the properties and set these font in onCreate
 
Thank you very much - thanks for the code too!
 
I wish you a nice weekend! with kind regards
 
Marko Winter Smile | :)
-------------------------------------------
Software & Systems
-------------------------------------------
SysMik GmbH Dresden | www.sysmik.de
-------------------------------------------
Dezentral automatisieren
LON over IP mit INLINE von Phoenix Contact
-------------------------------------------
SysMik | ICS – InlineControlServer
die neue Controllergeneration
-------------------------------------------
SysMik GmbH Dresden
Bertolt-Brecht-Allee 24
01309 Dresden, Germany
Tel/Fax +49-(0)351-43358 -30 / -29
-----------------------------------------
Generalx64 (AMD64) portability problemmemberModMa28-Jun-05 6:36 
im post this problem too in http://www.codeproject.com/miscctrl/subclasswnd.asp?msg=1148534#xx1148534xx[^]
(original CSubclassWnd project example from William E. Kempf )
 
in SubclassWnd.h not choose the correct plataform to use in FlushInstructionCache() in x64 (only x86 & alpha), any can update the library with the actual x64 implementation?
or post a patch?
 
thanks
AnswerRe: x64 (AMD64) portability problemmemberpc_jisakker5-Apr-09 5:19 
You might find thunk structure for x64 on the source: "C:\Program Files\Microsoft Visual Studio ####\VC\atlmfc\include\atlstdthunk.h", but wont works well. D'Oh! | :doh:
 
I had managed to build x64 binary using other CSubclassWnd.Thumbs Up | :thumbsup:
GeneralVery GoodmemberIain Fraser / Zeus9-May-05 4:21 
This is some outstanding code I think, very very good idea.
 
Well Done.
 
Iain Fraser
 
sd

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130617.1 | Last Updated 2 Nov 2003
Article Copyright 2000 by Daniel Lohmann
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid