CEditView 雑記 |
Last Updated: Nov.24,2006 |
| 使用しているコンパイラは、Microsoft Visual C++ 6.0 です。これ以外のバージョンでは記述と食い違うことがあるかも知れません。 | |
CEditView クラスは、シンボル _UNICODE を定義してビルドすると Shift-JIS ファイルの読み書きが出来なくなります。これが不都合な場合には自分で実装する必要があります。
■ 実装例1
まず、CEditView の派生クラスで次の 3 つのメンバ関数を宣言します。
public:
#ifdef _UNICODE
void ReadFromArchiveA(CArchive& ar);
void WriteToArchiveA(CArchive& ar);
void SerializeA(CArchive& ar);
#endif
いろいろな実装法があると思いますが、以下の例では Win32 API の MultiByteToWideChar, WideCharToMultiByte 関数を使ってみました。この他、ATL マクロの A2W, W2A を使う方法もあります。
#ifdef _UNICODE
void CMyView::ReadFromArchiveA(CArchive& ar)
{
ASSERT_VALID(this);
CFile* pFile = ar.GetFile();
ASSERT(pFile->GetPosition() == 0);
DWORD nFileSize = pFile->GetLength();
char *lpszA = new char[nFileSize+1];
if (lpszA == NULL)
AfxThrowMemoryException();
if (ar.Read(lpszA, nFileSize) != nFileSize)
{
delete [] lpszA;
AfxThrowArchiveException(CArchiveException::endOfFile);
}
lpszA[nFileSize] = '\0';
int nLen = ::MultiByteToWideChar(CP_ACP, 0, lpszA, -1, NULL, 0);
if ((UINT)nLen > nMaxSize)
{
AfxMessageBox(AFX_IDP_FILE_TOO_LARGE);
delete [] lpszA;
AfxThrowUserException();
}
LPWSTR lpszW = new WCHAR[nLen+1];
::MultiByteToWideChar(CP_ACP, 0, lpszA, -1, lpszW, nLen);
SetWindowText(lpszW);
delete [] lpszW;
delete [] lpszA;
ASSERT_VALID(this);
}
void CMyView::WriteToArchiveA(CArchive& ar)
{
ASSERT_VALID(this);
LPCWSTR lpszW = LockBuffer();
ASSERT(lpszW != NULL);
int nLen = ::WideCharToMultiByte(CP_ACP, 0, lpszW, -1, NULL, 0, NULL, NULL);
char *lpszA = new char[nLen+1];
::WideCharToMultiByte(CP_ACP, 0, lpszW, -1, lpszA, nLen, NULL, NULL);
TRY
{
ar.Write(lpszA, strlen(lpszA));
}
CATCH_ALL(e)
{
UnlockBuffer();
delete [] lpszA;
THROW_LAST();
}
END_CATCH_ALL
UnlockBuffer();
delete [] lpszA;
ASSERT_VALID(this);
}
void CMyView::SerializeA(CArchive& ar)
{
ASSERT_VALID(this);
if (ar.IsStoring())
{
WriteToArchiveA(ar);
}
else
{
ReadFromArchiveA(ar);
}
ASSERT_VALID(this);
}
#endif
呼び出しは、CDocument の派生クラスの Serialize 関数を以下のように書き直します。
void CMyDoc::Serialize(CArchive& ar)
{
#ifdef _UNICODE
((CMyView*)m_viewList.GetHead())->SerializeA(ar);
#else
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
#endif
}
■ 実装例2
正攻法ではないのですが、CStdioFile クラスをロケールを切り替えて使うというちょっと面白い方法があります。CStdioFile クラスのソースをみると、ReadString, WriteString 関数はそれぞれ C ランタイムの fgetws, fputws 関数を使って実装されており、現在のロケールの LC_CTYPE カテゴリに依存して動作することが分かります。
BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
CStdioFile f;
CFileException fe;
CString sLine, sText;
if (!f.Open(lpszPathName,
CFile::modeRead | CFile::shareDenyWrite,
&fe))
{
ReportSaveLoadException(lpszPathName, &fe,
FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
return FALSE;
}
DeleteContents();
SetModifiedFlag();
CWaitCursor wait;
if (f.GetLength() != 0)
{
/* m_sLocale = _T(".932"); */
_tsetlocale(LC_CTYPE, m_sLocale);
while (f.ReadString(sLine))
sText += sLine + _T("\r\n");
f.Close();
_tsetlocale(LC_CTYPE, _T("C"));
}
SetModifiedFlag(FALSE);
((CEditView*)m_viewList.GetHead())->SetWindowText(sText);
return TRUE;
}
BOOL CMyDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
CStdioFile f;
CFileException fe;
CString sText;
if (!f.Open(lpszPathName,
CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive,
&fe))
{
ReportSaveLoadException(lpszPathName, &fe,
TRUE, AFX_IDP_INVALID_FILENAME);
return FALSE;
}
CWaitCursor wait;
/* m_sLocale = _T(".932"); */
_tsetlocale(LC_CTYPE, m_sLocale);
((CEditView*)m_viewList.GetHead())->GetWindowText(sText);
sText.Replace(_T("\r\n"), _T("\n"));
f.WriteString(sText);
f.Close();
_tsetlocale(LC_CTYPE, _T("C"));
SetModifiedFlag(FALSE);
return TRUE;
}
こちらは、CDocument の派生クラス CMyDoc に実装してみました。ファイルの末尾に勝手に改行を挿入してしまうなど、あまり厳密ではありません。なお、使用する場合には、<locale.h> をインクルードする必要があります。
| 【参考】 Microsoft Visual C++ 6.0 で Unicode アプリケーションを作成する場合の手順
|
| Back | Copyright (c) 2006 AOK. All Rights Reserved. |