// convert.cpp : implementation file // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1995 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include "wordpad.h" #include "multconv.h" #include "mswd6_32.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif #ifdef CONVERTERS CConverter* CConverter::m_pThis = NULL; #endif #define BUFFSIZE 4096 CTrackFile::CTrackFile(CFrameWnd* pWnd) : CFile() { m_nLastPercent = -1; m_dwLength = 0; m_pFrameWnd = pWnd; VERIFY(m_strComplete.LoadString(IDS_COMPLETE)); VERIFY(m_strWait.LoadString(IDS_PLEASE_WAIT)); VERIFY(m_strSaving.LoadString(IDS_SAVING)); // OutputPercent(0); } CTrackFile::~CTrackFile() { OutputPercent(100); if (m_pFrameWnd != NULL) m_pFrameWnd->SetMessageText(AFX_IDS_IDLEMESSAGE); } UINT CTrackFile::Read(void FAR* lpBuf, UINT nCount) { UINT n = CFile::Read(lpBuf, nCount); if (m_dwLength != 0) OutputPercent((int)((GetPosition()*100)/m_dwLength)); return n; } void CTrackFile::Write(const void FAR* lpBuf, UINT nCount) { CFile::Write(lpBuf, nCount); OutputString(m_strSaving); // if (m_dwLength != 0) // OutputPercent((int)((GetPosition()*100)/m_dwLength)); } void CTrackFile::OutputString(LPCTSTR lpsz) { if (m_pFrameWnd != NULL) { m_pFrameWnd->SetMessageText(lpsz); CWnd* pBarWnd = m_pFrameWnd->GetMessageBar(); if (pBarWnd != NULL) pBarWnd->UpdateWindow(); } } void CTrackFile::OutputPercent(int nPercentComplete) { if (m_pFrameWnd != NULL && m_nLastPercent != nPercentComplete) { m_nLastPercent = nPercentComplete; TCHAR buf[64]; int n = nPercentComplete; if (SUCCEEDED(StringCchPrintf(buf, ARRAYSIZE(buf), (n==100) ? m_strWait : m_strComplete, n))) { OutputString(buf); } } } COEMFile::COEMFile(CFrameWnd* pWnd) : CTrackFile(pWnd) { } UINT COEMFile::Read(void FAR* lpBuf, UINT nCount) { UINT n = CTrackFile::Read(lpBuf, nCount); OemToCharBuffA((const char*)lpBuf, (char*)lpBuf, n); return n; } void COEMFile::Write(const void FAR* lpBuf, UINT nCount) { CharToOemBuffA((const char*)lpBuf, (char*)lpBuf, nCount); CTrackFile::Write(lpBuf, nCount); } #ifdef CONVERTERS HGLOBAL CConverter::StringToHGLOBAL(LPCSTR pstr) { HGLOBAL hMem = NULL; if (pstr != NULL) { size_t cch = (lstrlenA(pstr)*2) + 1; // Why are we allocating this much? hMem = GlobalAlloc(GHND, cch); if (NULL == hMem) AfxThrowMemoryException(); char* p = (char*) GlobalLock(hMem); if (p != NULL) EVAL(StringCchCopyA(p, cch, pstr) == S_OK); GlobalUnlock(hMem); } return hMem; } CConverter::CConverter(LPCTSTR pszLibName, CFrameWnd* pWnd) : CTrackFile(pWnd) { USES_CONVERSION; m_hBuff = NULL; m_pBuf = NULL; m_nBytesAvail = 0; m_nBytesWritten = 0; m_nPercent = 0; m_hEventFile = NULL; m_hEventConv = NULL; m_bDone = TRUE; m_bConvErr = FALSE; m_hFileName = NULL; m_bUseOEM = TRUE; #ifndef _X86_ //Prevent known alignment exception problems in write converter //from crashing the app on some RISC machines m_uPrevErrMode = SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT); #endif // Safe to call LoadLibrary - this should be a fully-qualified pathname. m_hLibCnv = LoadLibrary(pszLibName); if (NULL != m_hLibCnv) { LoadFunctions(); ASSERT(m_pInitConverter != NULL); if (m_pInitConverter != NULL) { // // For the current converters, you have to pass a *static* // string to InitConverter32 // VERIFY(m_pInitConverter(AfxGetMainWnd()->GetSafeHwnd(), "WORDPAD")); } if (m_pRegisterApp != NULL) { NegotiateForNonOEM(); } } } CConverter::~CConverter() { if (!m_bDone) // converter thread hasn't exited { m_bDone = TRUE; if (!m_bForeignToRtf) WaitForConverter(); m_nBytesAvail = 0; VERIFY(ResetEvent(m_hEventFile)); m_nBytesAvail = 0; SetEvent(m_hEventConv); WaitForConverter();// wait for DoConversion exit VERIFY(ResetEvent(m_hEventFile)); } if (m_hEventFile != NULL) VERIFY(CloseHandle(m_hEventFile)); if (m_hEventConv != NULL) VERIFY(CloseHandle(m_hEventConv)); if (m_hLibCnv != NULL) FreeLibrary(m_hLibCnv); if (m_hFileName != NULL) GlobalFree(m_hFileName); #ifndef _X86_ //Reset error mode to what it was before we changed it in //the constructor SetErrorMode(m_uPrevErrMode); #endif } void CConverter::WaitForConverter() { // while event not signalled -- process messages while (MsgWaitForMultipleObjects(1, &m_hEventFile, FALSE, INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0) { MSG msg; while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } void CConverter::WaitForBuffer() { // while event not signalled -- process messages while (MsgWaitForMultipleObjects(1, &m_hEventConv, FALSE, INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0) { MSG msg; while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } UINT AFX_CDECL CConverter::ConverterThread(LPVOID) // AFX_CDECL added by t-stefb { ASSERT(m_pThis != NULL); #if defined(_DEBUG) HRESULT hRes = OleInitialize(NULL); ASSERT(hRes == S_OK || hRes == S_FALSE); #else OleInitialize(NULL); #endif m_pThis->DoConversion(); OleUninitialize(); return 0; } BOOL CConverter::IsFormatCorrect(LPCWSTR pszFileName) { BOOL bRet = FALSE; if (m_hLibCnv == NULL || m_pIsFormatCorrect == NULL) { bRet = FALSE; } else { char buf[_MAX_PATH]; if (WideCharToMultiByte(CP_ACP, 0, pszFileName, -1, buf, ARRAYSIZE(buf), NULL, NULL)) { if (m_bUseOEM) CharToOemA(buf, buf); HGLOBAL hFileName = StringToHGLOBAL(buf); HGLOBAL hDesc = GlobalAlloc(GHND, 256); if (NULL == hDesc) AfxThrowMemoryException(); int nRet = m_pIsFormatCorrect(hFileName, hDesc); GlobalFree(hDesc); GlobalFree(hFileName); bRet = (nRet == 1) ? TRUE : FALSE; } else { bRet = FALSE; } } return bRet; } // static callback function int CALLBACK CConverter::WriteOutStatic(int cch, int nPercentComplete) { ASSERT(m_pThis != NULL); return m_pThis->WriteOut(cch, nPercentComplete); } int CALLBACK CConverter::WriteOut(int cch, int nPercentComplete) { ASSERT(m_hBuff != NULL); m_nPercent = nPercentComplete; if (m_hBuff == NULL) return -9; // // If m_bDone is TRUE that means the richedit control has stopped // streaming in text and is trying to destroy the CConverter object but // the converter still has more data to give // if (m_bDone) { ASSERT(!"Richedit control stopped streaming prematurely"); AfxMessageBox(IDS_CONVERTER_ABORTED); return -9; } if (cch != 0) { WaitForBuffer(); VERIFY(ResetEvent(m_hEventConv)); m_nBytesAvail = cch; SetEvent(m_hEventFile); WaitForBuffer(); } return 0; //everything OK } int CALLBACK CConverter::ReadInStatic(int /*flags*/, int nPercentComplete) { ASSERT(m_pThis != NULL); return m_pThis->ReadIn(nPercentComplete); } int CALLBACK CConverter::ReadIn(int /*nPercentComplete*/) { ASSERT(m_hBuff != NULL); if (m_hBuff == NULL) return -8; SetEvent(m_hEventFile); WaitForBuffer(); VERIFY(ResetEvent(m_hEventConv)); return m_nBytesAvail; } BOOL CConverter::DoConversion() { USES_CONVERSION; m_nLastPercent = -1; // m_dwLength = 0; // prevent Read/Write from displaying m_nPercent = 0; ASSERT(m_hBuff != NULL); ASSERT(m_pThis != NULL); HGLOBAL hDesc = StringToHGLOBAL(""); HGLOBAL hSubset = StringToHGLOBAL(""); int nRet = -1; if (m_bForeignToRtf && NULL != m_pForeignToRtf) { ASSERT(m_pForeignToRtf != NULL); ASSERT(m_hFileName != NULL); nRet = m_pForeignToRtf(m_hFileName, NULL, m_hBuff, hDesc, hSubset, (LPFNOUT)WriteOutStatic); // wait for next CConverter::Read to come through WaitForBuffer(); VERIFY(ResetEvent(m_hEventConv)); } else if (!m_bForeignToRtf && NULL != m_pRtfToForeign) { ASSERT(m_pRtfToForeign != NULL); ASSERT(m_hFileName != NULL); nRet = m_pRtfToForeign(m_hFileName, NULL, m_hBuff, hDesc, (LPFNIN)ReadInStatic); // don't need to wait for m_hEventConv } GlobalFree(hDesc); GlobalFree(hSubset); if (m_pBuf != NULL) GlobalUnlock(m_hBuff); GlobalFree(m_hBuff); if (nRet != 0) m_bConvErr = TRUE; m_bDone = TRUE; m_nPercent = 100; m_nLastPercent = -1; SetEvent(m_hEventFile); return (nRet == 0); } void CConverter::LoadFunctions() { m_pInitConverter = (PINITCONVERTER)GetProcAddress(m_hLibCnv, "InitConverter32"); m_pIsFormatCorrect = (PISFORMATCORRECT)GetProcAddress(m_hLibCnv, "IsFormatCorrect32"); m_pForeignToRtf = (PFOREIGNTORTF)GetProcAddress(m_hLibCnv, "ForeignToRtf32"); m_pRtfToForeign = (PRTFTOFOREIGN)GetProcAddress(m_hLibCnv, "RtfToForeign32"); m_pRegisterApp = (PREGISTERAPP) GetProcAddress(m_hLibCnv, "RegisterApp"); } #endif // #ifdef CONVERTERS /////////////////////////////////////////////////////////////////////////////// BOOL CConverter::Open(LPCWSTR pszFileName, UINT nOpenFlags, CFileException* pException) { USES_CONVERSION; BOOL bRet; // The converters only speak ansi char buf[_MAX_PATH]; if (WideCharToMultiByte(CP_ACP, 0, pszFileName, -1, buf, ARRAYSIZE(buf), NULL, NULL)) { if (m_bUseOEM) CharToOemA(buf, buf); // let's make sure we could do what is wanted directly even though we aren't m_bCloseOnDelete = FALSE; m_hFile = (UINT_PTR)hFileNull; BOOL bOpen = CFile::Open(pszFileName, nOpenFlags, pException); CFile::Close(); if (!bOpen) return FALSE; m_bForeignToRtf = !(nOpenFlags & (CFile::modeReadWrite | CFile::modeWrite)); // check for reading empty file if (m_bForeignToRtf) { CFileStatus _stat; if (CFile::GetStatus(pszFileName, _stat) && _stat.m_size == 0) return TRUE; } //create the events m_hEventFile = CreateEvent(NULL, TRUE, FALSE, NULL); m_hEventConv = CreateEvent(NULL, TRUE, FALSE, NULL); //create the converter thread and create the events ASSERT(m_hFileName == NULL); m_hFileName = StringToHGLOBAL(buf); m_pThis = this; m_bDone = FALSE; m_hBuff = GlobalAlloc(GHND, BUFFSIZE); ASSERT(m_hBuff != NULL); AfxBeginThread(ConverterThread, this, THREAD_PRIORITY_NORMAL, 0, 0, NULL); bRet = TRUE; } else { bRet = FALSE; } return bRet; } // m_hEventConv -- the main thread signals this event when ready for more data // m_hEventFile -- the converter signals this event when data is ready UINT CConverter::Read(void FAR* lpBuf, UINT nCount) { ASSERT(m_bForeignToRtf); if (m_bDone) return 0; // if converter is done int cch = nCount; BYTE* pBuf = (BYTE*)lpBuf; while (cch != 0) { if (m_nBytesAvail == 0) { if (m_pBuf != NULL) GlobalUnlock(m_hBuff); m_pBuf = NULL; SetEvent(m_hEventConv); WaitForConverter(); VERIFY(ResetEvent(m_hEventFile)); if (m_bConvErr) AfxThrowFileException(CFileException::generic); if (m_bDone) return nCount - cch; m_pBuf = (BYTE*)GlobalLock(m_hBuff); ASSERT(m_pBuf != NULL); } int nBytes = min(cch, m_nBytesAvail); memcpy(pBuf, m_pBuf, nBytes); pBuf += nBytes; m_pBuf += nBytes; m_nBytesAvail -= nBytes; cch -= nBytes; OutputPercent(m_nPercent); } return nCount - cch; } void CConverter::Write(const void FAR* lpBuf, UINT nCount) { ASSERT(!m_bForeignToRtf); m_nBytesWritten += nCount; while (nCount != 0) { WaitForConverter(); VERIFY(ResetEvent(m_hEventFile)); if (m_bConvErr) AfxThrowFileException(CFileException::generic); m_nBytesAvail = min(nCount, BUFFSIZE); nCount -= m_nBytesAvail; BYTE* pBuf = (BYTE*)GlobalLock(m_hBuff); ASSERT(pBuf != NULL); memcpy(pBuf, lpBuf, m_nBytesAvail); GlobalUnlock(m_hBuff); SetEvent(m_hEventConv); } OutputString(m_strSaving); } LONG CConverter::Seek(LONG lOff, UINT nFrom) { if (lOff != 0 && nFrom != current) AfxThrowNotSupportedException(); return 0; } DWORD CConverter::GetPosition() const { return 0; } void CConverter::Flush() { } void CConverter::Close() { if (!m_bDone) // converter thread hasn't exited { m_bDone = TRUE; if (!m_bForeignToRtf) WaitForConverter(); m_nBytesAvail = 0; VERIFY(ResetEvent(m_hEventFile)); m_nBytesAvail = 0; SetEvent(m_hEventConv); WaitForConverter();// wait for DoConversion exit VERIFY(ResetEvent(m_hEventFile)); } if (m_bConvErr) AfxThrowFileException(CFileException::generic); } void CConverter::Abort() { } DWORD CConverter::GetLength() const { ASSERT_VALID(this); return 1; } CFile* CConverter::Duplicate() const { AfxThrowNotSupportedException(); return NULL; } void CConverter::LockRange(DWORD, DWORD) { AfxThrowNotSupportedException(); } void CConverter::UnlockRange(DWORD, DWORD) { AfxThrowNotSupportedException(); } void CConverter::SetLength(DWORD) { AfxThrowNotSupportedException(); } //+-------------------------------------------------------------------------- // // Method: CConverter::NegotiateForNonOEM // // Synopsis: Try to tell the converter not to expect OEM filenames // // Parameters: None // // Returns: void // // Notes: The converter's RegisterApp function will return a handle // containing it's preferences (what it supports). The // data structure is a 16-bit size and then a sequence of // records. For each record the first byte is the size, the // second is the "opcode", and then some variable-length opcode // specific data. All sizes are inclusive. // //--------------------------------------------------------------------------- void CConverter::NegotiateForNonOEM() { ASSERT(NULL != m_pRegisterApp); HGLOBAL hPrefs; BYTE *pPrefs; __int16 cbPrefs; // // Tell the converter we don't want to use OEM // hPrefs = (*m_pRegisterApp)(fRegAppSupportNonOem, NULL); if (NULL == hPrefs) return; pPrefs = (BYTE *) GlobalLock(hPrefs); if (NULL == pPrefs) { ASSERT(!"GlobalLock failed"); GlobalFree(hPrefs); return; } // // Parse the returned structure looking for a RegAppOpcodeCharset opcode. // The argument for this opcode should be either ANSI_CHARSET or // OEM_CHARSET. If its ANSI_CHARSET then we can talk Ansi otherwise were // stuck with OEM. // cbPrefs = (__int16) ((* (__int16 *) pPrefs) - sizeof(cbPrefs)); pPrefs += sizeof(cbPrefs); while (cbPrefs > 0) { if (RegAppOpcodeCharset == pPrefs[1]) { ASSERT(ANSI_CHARSET == pPrefs[2] || OEM_CHARSET == pPrefs[2]); m_bUseOEM = (OEM_CHARSET == pPrefs[2]); break; } else { if (pPrefs[0] <= 0) { ASSERT(!"RegisterApp is returning bogus data"); break; } cbPrefs = (__int16) (cbPrefs - pPrefs[0]); pPrefs += pPrefs[0]; } } GlobalUnlock(pPrefs); GlobalFree(hPrefs); }