From: Hector Santos on
Joe, IMV, this is a MAJOR step back in the programming solid code in
order to support this UNICODE stuff. Wow!! Are you kidding me? Why
is UNICODE such an on-going issue with so many people even after all
these years. Its terrible. Wow!

Personally, in this topic, I think understanding more what the
CRichEditCtrl wants for UNICODE support is whats important here. I
don't see any consistent information about this.

--
HLS

Joseph M. Newcomer wrote:

> See below...
> On Tue, 27 Apr 2010 01:18:08 -0700, JY <sd(a)nospamgroup.com> wrote:
>
>> Hi,
>>
>> I have a Wizard based application, in which I have some property pages. In
>> one of the pages, I have a CRichEditCtrl, and I try to populate its contents
>>from a RTF file. The porblem is, it works correctly if the RTF file is small
>> (about 4-5 KB), but when I use a larger file, its contents either don't
>> display at all, or gets truncated.
>>
>> The code is shown below. What can I do to show the entire contents of the
>> RTF file in the control? Also, it should work for all languages - I have
>> UNICODE defined in the project. m_RECtrl is the rich edit control.
>>
>> static DWORD CALLBACK MyStreamInCallback(DWORD dwCookie, LPBYTE pbBuff, LONG
> ****
> Why did you declare the cookie as a DWORD and not a DWORD_PTR? This is incorrect,
> although in 32-bit Windows it would not have any impact
> ****
>> cb, LONG *pcb)
>> {
>> CFile* pFile = (CFile*)(DWORD_PTR)dwCookie;
>> *pcb = pFile->Read(pbBuff, cb);
> ****
> Note that the Read should be contained in a try/catch(CFileException * e) structure if you
> plan to detect errors correctly
> ****
>> return 0;
>> }
>>
>> BOOL CREPage::OnInitDialog()
>> {
>> CBasePage::OnInitDialog();
>>
>> if (m_strRTFFilePath.GetLength())
>> {
>> m_RECtrl.ShowScrollBar(SB_VERT, TRUE);
>> CFile eulaFile(m_strRTFFilePath, CFile::modeRead);
> ****
> Note that this constructor must be in a try/catch(CFileException * e) block, since if
> there is a problem (such as the file does not exist along the path) then it will throw an
> exception
> ****
>> EDITSTREAM es;
>>
>> es.dwCookie = (DWORD_PTR)&eulaFile;
>> es.pfnCallback = (EDITSTREAMCALLBACK)MyStreamInCallback;
>> m_RECtrl.StreamIn(SF_RTF, es);
>> }
>
>> return TRUE;
>> }
>>
>> int CREPage::OnCreate(LPCREATESTRUCT lpCreateStruct)
>> {
>> if (CBasePage::OnCreate(lpCreateStruct) == -1)
>> return -1;
>>
>> CRect rect;
>> GetClientRect(&rect);
>> rect.bottom -= 25;
>>
>> m_RECtrl.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE, rect,
>> this, IDC_RICHEDIT_EULA);
>> m_RECtrl.SetOptions(ECOOP_OR, ECO_AUTOVSCROLL | ECO_AUTOHSCROLL |
>> ECO_READONLY);
>> m_RECtrl.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED);
>>
>> return 0;
>> }
>>
>> TIA,
>> JY
> ****
> Here's the stream callback from my handler:
>
> class StreamInCookie {
> public:
> CFile * file;
> DWORD_PTR remaining;
> DWORD err;
> };
> ****
> This is retrieving data from a precomputed cache which is in rtf format, and if there is
> any timestamp change on an input, it resets all the content and will recompute it from
> scratch, ignoring the cache. The cache starts with a binary timestamp and a binary length
> value. It also marks the hyperlinks. If you want to see the hyperlink code, I've
> attached it
> *****
> BOOL CIndex::RetrieveFromCache()
> {
> CString filename;
> if(!GetCacheFile(filename))
> return FALSE;
>
> CFile f;
>
> if(!f.Open(filename, CFile::modeRead))
> return FALSE; // open failed
>
> try { /* try read */
> StreamInCookie streamIn; //
> streamIn.file = &f;
>
> //****************************************************************
> // [Timestamp]
> //****************************************************************
>
> TimeStamp ft;
> if(f.Read(&ft, sizeof(TimeStamp)) == 0)
> { /* failed */
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* failed */
>
> //****************************************************************
> // [eod] End of data position for RTF data
> //****************************************************************
>
> if(f.Read(&streamIn.remaining, sizeof(DWORD)) == 0)
> { /* failed */
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* failed */
>
> // Now convert from offset to length
> streamIn.remaining -= f.GetPosition();
>
> //****************************************************************
> // <...> RTF data
> //****************************************************************
> EDITSTREAM es;
> es.dwCookie = (DWORD_PTR)&streamIn;
> es.pfnCallback = StreamInCallback;
> c_Index.StreamIn(SF_RTF, es);
>
> if(streamIn.err != ERROR_SUCCESS)
> { /* failed */
> f.Close();
> ResetAllContent();
> return FALSE;
> } /* failed */
>
> //****************************************************************
> // Read the hyperlink length
> //****************************************************************
> DWORD len;
> if(f.Read(&len, sizeof(DWORD)) == 0)
> { /* failed to get links */
> f.Close();
> ResetAllContent();
> return FALSE;
> } /* failed to get links */
>
> hyperlinks.SetSize(len);
>
> //****************************************************************
> // Read the hyperlink data
> //****************************************************************
>
> for(DWORD i = 0; i < len; i++)
> { /* read each */
> hyperlinks[i] = new Reference;
> if(!hyperlinks[i]->Read(f))
> { /* failed */
> if(::GetLastError() == ERROR_HANDLE_EOF)
> break; // "failure" is EOF (see spec on Reference::Read)
> f.Close();
> ResetAllContent();
> return FALSE;
> } /* failed */
> MarkLink(c_Index, hyperlinks[i]->range);
> } /* read each */
>
> //****************************************************************
> // [eod] Read EOD for Fastlink RTF data
> //****************************************************************
>
> if(f.Read(&streamIn.remaining, sizeof(DWORD)) == 0)
> { /* failed */
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* failed */
>
> streamIn.remaining -= f.GetPosition(); // convert from offset to length
>
> //****************************************************************
> // <...> RTF data
> //****************************************************************
> c_FastIndex.StreamIn(SF_RTF, es);
>
> if(streamIn.err != ERROR_SUCCESS)
> { /* failed */
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* failed */
>
> //****************************************************************
> // Read the hyperlink length
> //****************************************************************
>
> if(f.Read(&len, sizeof(DWORD)) == 0)
> { /* failed to get links */
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* failed to get links */
>
> fastlinks.SetSize(len);
>
> //****************************************************************
> // Read the hyperlink data
> //****************************************************************
>
> for(i = 0; i < len; i++)
> { /* read each */
> if(!fastlinks[i].Read(f))
> { /* failed */
> if(::GetLastError() == ERROR_HANDLE_EOF)
> break;
> f.Close();
> ResetAllContent();
> return FALSE;
> } /* failed */
> MarkLink(c_FastIndex, fastlinks[i].range);
> } /* read each */
> //****************************************************************
> } /* try read */
> catch(CFileException * e)
> { /* read error */
> e->Delete();
> ResetAllContent();
> f.Close();
> return FALSE;
> } /* read error */
> f.Close();
> c_Index.SetSel(0,0);
> c_FastIndex.SetSel(0,0);
> return TRUE;
> } // CIndex::RetrieveFromCache
>
> void CIndex::MarkLink(CRichEditCtrlEx & ctl, CHARRANGE & range)
> {
> CHARFORMAT2 linkfmt;
>
> ctl.SetSel(range);
>
> linkfmt.cbSize = sizeof(CHARFORMAT2);
> linkfmt.dwMask = CFM_LINK;
> linkfmt.dwEffects = CFE_LINK;
> linkfmt.dwMask = CFM_LINK;
> ctl.SetSelectionCharFormat(linkfmt);
> } // CIndex::MarkLink
>
> /* static */ DWORD CALLBACK CIndex::StreamInCallback(DWORD_PTR cookie, LPBYTE buffer, LONG
> count, LONG * pcb)
> {
> StreamInCookie * streamIn = (StreamInCookie *)cookie;
> if(streamIn->remaining == 0)
> { /* all done */
> *pcb = 0;
> streamIn->err = ERROR_SUCCESS;
> return 1; // nonzero value terminates read
> } /* all done */
>
> DWORD bytesToRead = min(streamIn->remaining, (DWORD)count);
>
> UINT bytesRead;
> try {
> bytesRead = streamIn->file->Read(buffer, bytesToRead);
> }
> catch(CFileException * e)
> { /* catch */
> streamIn->err = e->m_lOsError;
> e->Delete();
> streamIn->remaining = 0;
> return 1;
> } /* catch */
>
> if(bytesRead == 0)
> { /* read error */
> streamIn->err = ::GetLastError();
> *pcb = 0;
> return 1; // return nonzero to stop operation
> } /* read error */
>
> streamIn->remaining -= bytesRead;
> *pcb = bytesRead;
> streamIn->err = ERROR_SUCCESS;
> return 0;
> } // CIndex::StreamInCallback
> Joseph M. Newcomer [MVP]
> email: newcomer(a)flounder.com
> Web: http://www.flounder.com
> MVP Tips: http://www.flounder.com/mvp_tips.htm



--
HLS
From: Goran on
On Apr 27, 4:20 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote:
> The problem with the particular code you have is that it doesn't
> support reading a RTF file saved as UNI-GARBAGE A.K.A UNICODE.
>
> It appears you need to convert each block read in into multi-byte
> array.  It works find when the RTF file is not stored in unicode.

Indeed, my file was not stored as unicode. But how do I save it so?
(Wordpad or swriter do not offer an easy option, e.g. something on the
menu, or in "save as" dialog).

Goran.
From: Hector Santos on
Goran, I couldn't see how this resolves reading in an RTF unicode?

I am not yet an UNI-GARBAGE expert, but I got it to work doing this
(excluding the finer details of passing an object):

In InitDialog()

#define BOM L'\xFEFF'
....

EDITSTREAM es = {0};
WCHAR ch;
int n = eulaFile2.Read(&ch, sizeof(WCHAR));
if(n == 2 && ch == BOM) {
es.pfnCallback = (EDITSTREAMCALLBACK)MyStreamInCallbackW;
m_RECtrlW.StreamIn(SF_RTF | SF_UNICODE, es);
} else {
eulaFile2.SeekToBegin();
es.pfnCallback = (EDITSTREAMCALLBACK)MyStreamInCallback;
m_RECtrlW.StreamIn(SF_RTF, es);
}

And the MyStreamInCallbackW() reads in cb amount and converts it a
multi-byte array.

Of course, this can all be done cleanly passing the cookie struct like
you have it to 1 callback and have it do the initial BOM test, but I
would like to know if converting it to a multi-byte array in the
callback was correct or it is redundant based on the StreamIn()
parameters (SF_RTF | SF_UNICODE), etc.

--

Goran wrote:

> Whoops! That went out sooner than I wanted... The above worked for
> me, regardless of the file size, using unicode in project settings,
> and using rtf in two rather distinctive languages.
>
> I don't know why you use OnCreate. It should work, your code, but...
> DDX_Control in DoDataExchange is easier.
>
> Other than that, note a canonical form for any non-trivial
> OnInitDialog is e.g:
>
> bool CXDialog::OnInitDialog()
> {
> CDialog::OnInitDialog();
> TRY
> {
> InitBabyInit();
> }
> CATCH(CException* pe)
> {
> ReportErrorSomehow(pe);
> EndDialog(PROBABLY_ID_CANCEL); // But it's up to you.
> }
> }
>
> It's a bad idea to let exception escape your callback (possible due to
> use of MFC in it), so you should e.g. do:
>
> struct RichEditStreamInContext
> {
> RichEditStreamInContext(const CString& csFileName) :
> m_File(csFileName, CFile::modeRead), m_pError(NULL) {}
>
> CFile m_File;
> CException* m_pError;
> };
>
> static DWORD CALLBACK MyStreamInCallback(DWORD_PTR dwCookie, LPBYTE
> pbBuff, LONG cb, LONG *pcb)
> {
> RichEditStreamInContext& Ctx =
> *reinterpret_cast<RichEditStreamInContext*>(dwCookie);
> try
> {
> *pcb = Ctx.m_File.Read(pbBuff, cb);
> return 0;
> }
> catch (CException* p)
> {
> ASSERT(FALSE);
> Ctx.m_pError = p;
> return -1;
> }
> }
>
>
> and in OnInitDialog:
>
> ...
> RichEditStreamInContext Ctx(RTF_PATH_HERE);
> EDITSTREAM es = { (DWORD_PTR)&Ctx, 0, &MyStreamInCallback };
> MCALLBACK)MyStreamInCallback;
> m_Edit.StreamIn(SF_RTF, es);
> if (es.dwError)
> HandleError(Ctx); // Including looking at Ctx.m_pError;
>
> HTH,
>
> Goran.



--
HLS
From: Hector Santos on
Goran wrote:

> On Apr 27, 4:20 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote:
>> The problem with the particular code you have is that it doesn't
>> support reading a RTF file saved as UNI-GARBAGE A.K.A UNICODE.
>>
>> It appears you need to convert each block read in into multi-byte
>> array. It works find when the RTF file is not stored in unicode.
>
> Indeed, my file was not stored as unicode. But how do I save it so?
> (Wordpad or swriter do not offer an easy option, e.g. something on the
> menu, or in "save as" dialog).
>
> Goran.


I tried Write.exe "save as" but it saves it as Unicode Code text
removing the RTF stuff.

I tried NotePad++ utility and it wasn't reading it right to begin with
so I wrote a quicky "ConvertToUnicode.cpp"

// File: G:\wc7beta\convert2unicode.cpp

#include <stdio.h>
#include <afx.h>

BYTE bom[2] = {0xFF,0xFE};

void main(char argc, char *argv[])
{
FILE *fvi = fopen("active-api.rtf","rt"); // ansi original
FILE *fvo = fopen("active-api-unc.rtf","wb");
fwrite(bom,sizeof(bom),1,fvo);
char sz[1024] = {0};
while (fgets(sz,sizeof(sz),fvi)) {
for (int i = 0; i < strlen(sz); i++) {
char szo[2] = {0};
szo[0] = sz[i];
fwrite(szo,sizeof(szo),1,fvo);
}
}
fclose(fvo);
fclose(fvi);
}

With this I was able to correctly read in active-api-unc.rtf but the
callback has to convert each block it reads into a multi-byte array.

I am not an expert yet with this unicrap but for the proof of concept:

static DWORD CALLBACK MyStreamInCallbackW(DWORD dwCookie, LPBYTE
pbBuff, LONG cb, LONG *pcb)
{
CFile* pFile = (CFile*)(DWORD_PTR)dwCookie;
BYTE *p = new BYTE[cb];
ZeroMemory(p,cb);
*pcb = pFile->Read(p, cb);
int n = 0;
for (int w = 0; w < *pcb;) {
pbBuff[n] = p[w];
w += 2;
n++;
}
delete p;
TRACE("MyStreamInCallback: cb: %d | pcb: %d | n: %d\n",
cb, *pcb, n);
*pcb = n;
return 0;
}

What I would like to figure out is what CRichEditCtrl expects for
UNICODE with the SF_XXXXXX StreamIn() option.

I also found a reference that about changing the control type in the
resource file to "RichEdit20W"

CONTROL "",IDC_RICHEDIT23,"RichEdit20W",ES_MULTILINE |
ES_AUTOHSCROLL | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,161,456,95

But that didn't work to read it in without having to convert it myself.

--
HLS
From: JY on
Hi Joe,

I've taken some snippets from what you'd posted and I still have the same
problem as earlier. Only a truncated portion of the file appears in the
CRichEditCtrl. Here's my latest code:

class StreamInCookie
{
public:
CFile *file;
DWORD_PTR remaining;
DWORD err;
};

static DWORD CALLBACK EULAStreamInCallback(DWORD_PTR dwCookie, LPBYTE
pbBuff, LONG cb, LONG *pcb)
{
StreamInCookie * streamIn = (StreamInCookie *)dwCookie;
if(streamIn->remaining == 0)
{
*pcb = 0;
streamIn->err = ERROR_SUCCESS;
return 1; // nonzero value terminates read
}

DWORD bytesToRead = min(streamIn->remaining, (DWORD)cb);

UINT bytesRead;
try
{
bytesRead = streamIn->file->Read(pbBuff, bytesToRead);
}
catch(CFileException * e)
{
streamIn->err = e->m_lOsError;
e->Delete();
streamIn->remaining = 0;
return 1;
}

if(bytesRead == 0)
{
streamIn->err = ::GetLastError();
*pcb = 0;
return 1; // return nonzero to stop operation
}

streamIn->remaining -= bytesRead;
*pcb = bytesRead;
streamIn->err = ERROR_SUCCESS;
return 0;
}

BOOL CREPage::OnInitDialog()
{
CBasePage::OnInitDialog();

if (m_strRTFFilePath.GetLength())
{
m_RECtrl.ShowScrollBar(SB_VERT, TRUE);

try
{
StreamInCookie streamIn;
CFile eulaFile(m_strRTFFilePath, CFile::modeRead|CFile::shareExclusive);
streamIn.file = &eulaFile;

EDITSTREAM es;
es.dwCookie = (DWORD_PTR)&streamIn;
es.pfnCallback = (EDITSTREAMCALLBACK)EULAStreamInCallback;
m_RECtrl.StreamIn(SF_RTF, es);
}
catch (CFileException *pEx)
{
//not shown for brevity
}
}

return TRUE;
}

Thanks,
JY
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6
Prev: Task
Next: compile error note