|
I've been trying to get this to work effectively.... seems to be OK on a win2000/NT machine, but I've been having issues with it on win98.
Quite simply... when you're adding text, just do this too
nSize = (int) ::SendMessage( hEdit, WM_GETTEXTLENGTH, 0, 0 );
if ( nSize > MAX_TEXT_LEN ) {
::SendMessage( hEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) TEXT_TRUNCATE_LEN );
if ( g_bIsWinNT ) {
::SendMessageW( hEdit, EM_REPLACESEL, 0, (LPARAM) "" );
} else {
::SendMessage( hEdit, EM_REPLACESEL, 0, (LPARAM) _W2A( (LPCWSTR) "" ) );
}
nSize = (int) ::SendMessage( hEdit, WM_GETTEXTLENGTH, 0, 0 );
}
Then use EM_SETSEL message with nSize params to set selection point to end of text.
Not 100% but worth a start for discussion purposes
|
|
|
|
|
Hi,
I had problem using your wonderful CEditLog class with a CEditView in a otherwise also strange environment (SDI app, multiple Views and the latest addition CEditLog with CEditView). The problem is, that the CEditView::OnDestroy handler isn't called and therefor the EditView doesn't tell the MainFrame to deactivate the CEditView during shutdown resulting in a crash.
As far as I have understood CSubclassWnd there is no chance to send a message to the subclassed window, after you called UnsubclassWindow (). So the subclassed CEditView doesn't see the message, since you call UnsublcassWindow within the WM_DESTROY handler.
The first solution I came up with was to call the DefWindowProc myself within the CEditLog::OnDestroy before you unsubclass the CEditCtrl
void CEditLog::OnDestroy()
{
DefWindowProc ();
UnsubclassWindow();
}
The second solution would be to remove the WM_DESTROY handling in CEditLog all together, since UnsubclassWindow is called during the WM_NCDESTROY handler from within CSublcassWnd anyway, naturally after the mesage has been passed to the subclassed window
There is this OnFinalMessage handler that you can use to do custom cleanup.
I would prefer the second solution, but I'm not sure why you introduced this handling of WM_DESTROY into your CEditLog. Perhaps some artifical relict.
As a second note: even if the following case handling will do no harm, I doesn't look like good coding style
case WM_ERASEBKGND:
if( m_bNoPaint )
return TRUE;
case WM_PAINT:
if( m_bNoPaint )
return TRUE;
You better combine those two cases and introduce a break. This is the funny behavoir if you introduce a further case statement and wonder why it is called in some strange situations.
Thanks for the class
Dirk
|
|
|
|
|
Thanks Dirk!
Very good points!
I would prefer the second solution, but I'm not sure why you introduced this handling of WM_DESTROY into your CEditLog. Perhaps some artifical relict.
To be honest - I don't know exactly why I am doing this. I assume it is still there because the original V1.0 of CSubclassWnd did not handle WM_NCDESTROY.
I will remove it in the next version
As a second note: even if the following case handling will do no harm, I doesn't look like good coding style
case WM_ERASEBKGND:
if( m_bNoPaint )
return TRUE;
case WM_PAINT:
if( m_bNoPaint )
return TRUE;
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|
I am trying to display data from an array (lets just say a CString array) in a standard CEdit control with each item in the array on its own line. Is there a way to do this in a CEdit? I have tried to read the data into one big string with "\n"s between the items but this does not work. I am using SetWindowText() to display this data in the edit control. When I do that with "\n"s in the string, it just displays a "|" character where the line breaks are and keeps writing the rest of the text on the same line. There must be an easy way to do this. Please help!!!!!!
Newby,
Anthony
|
|
|
|
|
|
Perfect piece of code, especially because I needed the 'fast' thing since my debug-tool used this to handle a lot of data from several devices in a testbed.
Its based around Doc/View MDI architecture, where CEditLog is hooked into CEditView.
Now I've run into a small problem
When trying to print it cannot 'access' the textdata (and so crashes), and if saving the data, the file will be empty.
I do not have any clue where to start. Anyone a suggestion?
Thanks in advance
EiSl
|
|
|
|
|
Hm...
After a quick look into the sources of CEditView it seems that it sometimes uses a so-called "shadow buffer". This is needed on Win32s, but the code looks like it is also used on Win95 and derived systems. Maybe ths is the source for your problem.
I recommend to single step the save/print functions in your app and take a look where in the MFC sources they crash or fail.
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|
Sooo. that's what I'm calling fast-support!
I was already afraid for this. I will start with the easy route first, which will be stepping through the 'Save' code.
The funny part of this all is... this morning my collega started with 'Murphy's Laws' jokes, and now I'm subject to it
I will keep you in touch )!
EiSl
|
|
|
|
|
Really great class ! I give it a 5.
I've added small helper class, CStreamRedirector , that handles stream attaching / detaching.
The typical use would be: create a CEditLog instance and a CStreamRedirector instance attached to the CEditLog . When redirection has to be turned on, call CStreamRedirector::Redirect(). To turn it off, UnRedirect(). The stream pointer mess cleaning is handled by CStreamRedirector
Here it is, if anybody interested
#include "EditLog.h"
#include "editlog_stream.h"
class CStreamRedirector
{
public:
CStreamRedirector(CEditLog& log);
virtual ~CStreamRedirector();
void Redirect();
void UnRedirect();
protected:
CEditLog& m_log;
bool m_bRedirect;
std::editstreambuf m_EditCout;
std::weditstreambuf m_EditWCout;
std::editstreambuf m_EditCerr;
std::weditstreambuf m_EditWCerr;
std::basic_streambuf< char >* m_pCout;
std::basic_streambuf< wchar_t >* m_pWCout;
std::basic_streambuf< char >* m_pCerr;
std::basic_streambuf< wchar_t >* m_pWCerr;
};
And here's the implementation
CStreamRedirector::CStreamRedirector(CEditLog& log)
: m_log(log), m_bRedirect(false),
m_EditCout(log), m_EditWCout(log), m_EditCerr(log), m_EditWCerr(log)
{
}
CStreamRedirector::~CStreamRedirector()
{
UnRedirect();
}
void CStreamRedirector::Redirect()
{
if (m_bRedirect)
return;
m_bRedirect=true;
m_pCout= std::cout.rdbuf( &m_EditCout);
m_pWCout= std::wcout.rdbuf( &m_EditWCout);
m_pCerr= std::cerr.rdbuf( &m_EditCerr);
m_pWCerr= std::wcerr.rdbuf( &m_EditWCerr);
};
void CStreamRedirector::UnRedirect()
{
if (!m_bRedirect)
return;
m_bRedirect=false;
std::cout.rdbuf( m_pCout);
std::wcout.rdbuf( m_pWCout);
std::cerr.rdbuf( m_pCerr);
std::wcerr.rdbuf( m_pWCerr);
};
Jonathan de Halleux.
|
|
|
|
|
Thanks Jonathan
This looks like a useful utility class.
BTW: Your rating of the article seems to have bombed the rating system, it now always shows "0 users have rated this article". Maybe an overflow...
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|
Thanks for the quick response on my previous problem. You were right !
My other problem is that although any cout from the application do redirects to the edit control, other cout's, which are located inside dll functions that link with the application, don't.
I have tried to change the cout within the dll to use std::cout, and use
#include <iostream>
using namespace std
but nothing helped.
Can you help me?
|
|
|
|
|
Oh, oh, this way it is getting really difficult. It may be that the DLL links statically with the CRT and therefore use an own instance of cout. Or it is simply not using cout, but printf(), Win32 WriteFile() and so on.
I can think of only one way out here, but it is a rather hard and difficult one: All output to stdout is passed at one moment (deep inside the CRT) to WriteFile(). If you patch WriteFile() and redirect it to your own function, you could check for the file handle beeing the stdout handle (using GetStdHandle()) and in this case redirect it to the EditLog. However, patching API functions is not trivial, especially under Win9x/Me. There are articles about this here at Codeproject:
[1] HookImportetFunctionByName
[2] API hooking revealed
--
Daniel Lohmann
http://www.losoft.de
|
|
|
|
|
I tried to imlpement the code in my project and i get this 4 error messages which are all the same problem :
error C2065: 'cout' : undeclared identifier
error C2228: left of '.rdbuf' must have class/struct/union type
error C2065: 'wcout' : undeclared identifier
error C2228: left of '.rdbuf' must have class/struct/union type
Those error concern this lines :
m_pOldBuf = cout.rdbuf(&m_EditStrBuf);
m_pOldBufW = wcout.rdbuf(&m_EditStrBufW);
Can any one help me figure out why?
|
|
|
|
|
I suppose the reason is that you missed to put
std:: in front of the cout/wcout identifiers or forget to write a
using std directive in the CPP file.
Take a look at the demo project how it is handled there.
--
Daniel Lohmann
http://www.losoft.de
|
|
|
|
|
Daniel,
This code was exactly what I was looking for. It took me hours to integrate it into my current project, but it was worth it. So thanks from Munich!
|
|
|
|
|
I love the control, particularly the fact that you can connect it to std::cout. However, I'd also like to be able to clear the text from the edit control while it is connected to std::cout. Is this a natively supported option? I've tried ::SendMessage(hwnd, WM_SETTEXT, 0,0) from within a CEditLog method that I added, but this doesn't seem to do the trick. Do you know if this is possible? Thanks & keep up the great work. Cheers.
|
|
|
|
|
The usual way to clear an edit control is to select the whole content and then send it a WM_CLEAR. However, This seems not to work for read-only controls, so we have to remove the read-only flag first:
BOOL bReadOnly = ::GetWindowLong( hEdit, GWL_STYLE ) & ES_READONLY;
if( bReadOnly )
::SendMessage( hEdit, EM_SETREADONLY, FALSE, 0 );
::SendMessage( hEdit, EM_SETSEL, 0, -1 );
::SendMessage( hEdit, WM_CLEAR, 0, 0 );
if( bReadOnly )
::SendMessage( hEdit, EM_SETREADONLY, TRUE, 0 );
I personally prefer to put things like that not into CEditLog, because clearing it's content is a concern belonging to the underlaying edit control and CEditLog is designed to work with any edit control. However, one can also argue that clearing the log is a fundamental feature of CEditLog
--
Daniel Lohmann
http://www.losoft.de
|
|
|
|
|
How about:
<br />
::SetWindowText(hEdit, "");<br />
-Gernot Frisch
Dream Design
reverse Tonreg to reply...
|
|
|
|
|
Oh....
Thanks, Gernot
--
Daniel Lohmann
http://www.losoft.de
(Hey, this page is worth looking! You can find some free and handy NT tools there )
|
|
|
|
|
I have a dialog box with an edit control on it in my SDI application. My dialog class is derived from cdialog. I've done as the example says but I am getting the following errors:
EditLogDlg.cpp
F:\Src\EditLogDlg.cpp(21) : error C2512: 'basic_editstreambuf<char,struct std::char_traits<char="">,512>' : no appropriate default constructor available
F:\Src\EditLogDlg.cpp(21) : error C2512: 'basic_editstreambuf<unsigned short,struct="" std::char_traits<unsigned="" short="">,512>' : no appropriate default constructor available
what does that mean and how do i get rid of it? the example compiles fine so my assumption is I'm doing something wrong. can someone please help?
|
|
|
|
|
Do you include the necessary STL headers in your stdafx.h?
I do not include them "by hand" in EditLog.h/EditLog.cpp 'cause I think never changed C++ std-headers should be located in stdafx.h.
Just add the following to your projects stdafx.h
// Include core STL header
#include <string>
#include <streambuf>
#include <iostream>
Daniel
|
|
|
|
|
I had the same problem and it took me quite a while to figure it out, but in the end it's easy: if you declare something like..
class MyClass {
private:
CEditLog m_EditLogger;
std::editstreambuf m_EditStrBuf;
...
};
...the compiler needs a default constructor for editstreambuf (because you didn't give any parameters), unless you use the initialiser list of the constructor of MyClass:
MyClass::MyClass(params...) : m_EditStrBuf( m_EditLogger ) {
...
}
Doing it this way no default constructor is needed, because m_EditStrBuf is initialised with a parameter. Btw, that's how it's done in the example application
|
|
|
|
|
1. Missing ; on line 245 of EditLog.cpp
2. Dragging the scrollbar middle section while in fast mode can get it lost and it never logs to bottom of window again.
3. 64K size limitation of Windows 9.x can be removed by using a CRichEditCtrl instead of a CEdit control. Remember to call AfxInitRichEdit() in InitInstance()
This would be a good enhancement for adding color coding etc to the log. I tried a quick demo and it works but the scrolling gets hosed, however everything else works as is.
If I get time I may try enhancing it.
Great logging class though !!!!
Colin
|
|
|
|
|
Hi Colin,
Thanks for your comments.
The really nasty thing is, as you have also found out, the scrolling. I found no solution which works really reliable in all cases.
However, if you lost the bottom position, you can get it back by pressing CTRL+END.
The scrolling is also the thing that took me away from RichEdit so far. But I'm always open for suggestions and improvements
Danie
|
|
|
|
|
I too have written a similar CTranscriptCtrl based on CEdit for managing logging information. Yours is more sophisticated : }, mine was just a simple little convenience thing.
However, the 64K limit of CEdit is getting nasty, and I need a way to increase that, and RichEdit looks like it would provide that and open up some other interesting possibilities, but for the scrolling issue!$#!@
Do you or anyone know of how to figgure out whether a given line is on screen, or better, what the total display height (in pixels, twips, anything) is? That would be an adequate piece of info to do the scrolling, since one can determine the rect height, the margins, and the first line index to be visible.
Steve Wolf
|
|
|
|
|