文本编辑器: 基础中的基础
这是一个基于 Edit 控件的简单文本编辑器

引言
简单的文本编辑器是程序员在职业生涯之初编写的第一个应用程序。创建编辑器的最简单方法是使用 Microsoft Windows 编辑控件。该控件同时支持双字节的 Unicode 字符集和单字节的 ANSI 字符集。Microsoft Windows 记事本是所有 Windows 版本中都包含的常用纯文本编辑器。
我想提请您注意我的新程序 Newpad,它是一个类似于 Microsoft Windows 记事本的简单文本编辑器。该程序用于学习编辑器设计原理,并且代表了一个完整的软件产品。我希望它对那些编写过处理文本文件的应用程序的人会有所帮助。此项目包含几个有用的函数,可以访问文本文件并与编辑控件进行交互。
Newpad 是一个组织完善的完整产品,包含帮助文档、安装程序、源代码和文档。在本文中,我将简要介绍项目的本质和特点。由于篇幅有限,不可能详细概述其所有方面。有关详细信息,您可以查看 zip 包中的项目源代码。
项目中值得关注的内容
- 错误处理和调试
- 通用窗口创建和操作技术
- 创建和自定义对话框
- 自定义“保存文件”对话框
- 注册表操作
- 创建工具栏和状态栏及其操作
- 通过程序发送电子邮件
- 通用编辑控件操作技术(文本操作、缓冲区访问、字符串搜索、打印、处理控件中的插入符位置等)
Newpad 概述
用户界面
从用户角度来看,Newpad 具有一些与记事本不同的显著特点,使其更加用户友好。简而言之,您可以使用它和记事本一样,但 Newpad 和记事本之间存在一些区别。从用户角度来看,这些区别是:
- Newpad 具有工具栏,而记事本没有。
您可以通过点击“视图”菜单,然后点击您想要显示或隐藏的工具栏名称来显示或隐藏 Newpad 的工具栏和状态栏。
- Newpad 可以打开并正确显示包含 UNIX 和 Macintosh 样式的行尾符的文件,但记事本不能。
当 Newpad 打开文件时,它会自动区分行尾符的样式。Newpad 正确显示 Windows 和 UNIX 样式的行尾符文件。但是,如果您想保存文件,Newpad 将保留原有的格式。
- 记事本即使在缺少字节顺序标记的情况下也能检测 Unicode 文件。Newpad 始终检查 Unicode 文件中的字节顺序标记。
Using the Code
构建环境
您可以在 zip 压缩包中找到该程序的源代码。它包含一个 Microsoft Visual Studio 2008 解决方案文件。但是,您可以随意使用源代码并创建自己的项目文件。Newpad 也可以使用 Visual C++ 6.0 正确重新编译。Newpad 是一个经典的 C 应用程序,只使用 Win API 调用。目前,只有英语和俄语版本的资源脚本可用。
调试版本的特殊之处
在项目的调试版本中,您会发现一个用于库 NpDebug.dll 的附加项目。它为项目提供高级错误处理,并可能对您的应用程序有用。该库导出了几个函数,用于在程序调试版本中出现断言时向开发人员发送电子邮件消息。SimpleAssert
函数显示一个简单的对话框,告知用户有关断言的信息,而无法发送反馈消息。AdvancedAssert
函数显示一个对话框,告知用户有关发生的断言,并提供通过电子邮件向开发人员发送消息的选项。SendMail
是另一个有用的函数,它执行通过当前 Windows 电子邮件设置发送消息的操作。它由 AdvancedAssert
函数调用,但也可以独立使用。该函数使用 Windows MAPI 环境,并在调用 MAPI 函数后检查返回值。
关于超级链接控件
该程序使用自己的超级链接控件类似物。任何对话框上的任何按钮都可以转换为此控件。我的超级链接控件是通过替换按钮的窗口过程来实现的。DrawHyperlinkControl
函数绘制超级链接条目。当新的窗口过程处理 WM_PAINT
消息时会调用它。OpenHyperLink
函数执行您可以为超级链接设置的操作。
关于对话框的一个有趣特性
我介绍一个有趣的对话框功能,可用于 GPL 许可下的免费软件应用程序。您需要填写 PRODUCT_INFORMATION
结构字段,并将结构指针传递给 AboutFreewareDialog
函数。此函数将显示一个对话框,其中包括一个带有图标和格式化文本的横幅、简短的 GPL 片段以及指向完整 GPL 文本的超级链接。当然,您可以根据需要自定义所有这些。
项目参考
函数
如上所述,由于 Newpad 是一个完整的产品,因此不可能在短文中详细概述其所有方面。请查看程序的源代码以详细了解这些功能。
int ReadTextFile(HANDLE hFile, LPTSTR* ppszText,
LPDWORD pdwTextLength, LPUINT pnEndcoding, LPUINT pnEOL);
参数
[in] hFile |
文件句柄必须已使用 GENERIC_READ 权限创建。 |
[out] *ppszText |
指向一个缓冲区,该缓冲区接收从文件中读取的文本。此值不能为 NULL 。 |
[out] pdwTextLength |
指向一个 DWORD 值,该值将返回文本缓冲区的长度。 |
[out] pnEndcoding |
指向一个 UINT 值,该值将返回文本的编码。 |
[out] pnEOL |
指向一个 UINT 值,该值将返回文本的行尾标记。 |
返回值
如果函数成功,则返回值为 1
。如果函数失败,则返回值为 0
。
int WriteTextFile(HANDLE hFile, LPCWSTR pwcText,
DWORD dwTextLegth, UINT nEncoding, UINT nEOL);
参数
[in] hFile |
要读取的文件的句柄。文件句柄必须已使用 GENERIC_WRITE 访问权限创建。 |
[in] pwcText |
指向包含要写入文件的文本的 Unicode 缓冲区的指针。 |
[in] dwTextLegth |
文本长度(以字节为单位)。 |
[in] nEncoding |
文本编码。 |
[in] nEOL |
行尾标记。 |
返回值
如果函数成功,则返回值为 1
。如果函数失败,则返回值为 0
。
如果您想详细了解项目的其他函数,则必须查看源代码。
宏
您可以使用 CRT 调试库和 NpDebug 库来调试此应用程序。您需要包含相应的头文件 crtdbg.h 或 npdebug.h,并定义以下宏之一
#define ASSERT(s) _ASSERTE(s) or #define ASSERT(s) NP_ASSERT(s)
消息
程序中有两个地方未使用普通的“消息处理”。一是用于查找替换对话框的附加消息,二是出现在自定义“保存文件”对话框中的消息。
在第一种情况下,我们使用
uFindReplaceMsg = RegisterWindowMessage(FINDMSGSTRING);
来注册新消息。之后,IsFindReplaceMsg
函数会检查消息,如果它是来自查找替换无模式对话框的消息,则返回 TRUE
。我们在 main
消息循环中使用此函数(有关详细信息,请参阅 Newpad.c 中的 WinMain
)。
为了以不同的编码保存文本文件,我们使用了一个自定义的“文件保存”对话框。我们处理发送到“文件保存”自定义对话框的钩子对话框过程的 WM_NOTIFY
消息通知。但是,钩子过程中的新消息处理程序(有关详细信息,请参阅 NpFiles.c 中的 MyHookProc
)是空的。
Notifications
我们在 Newpad 主窗口过程中处理来自编辑控件的通知消息,以更新状态栏上的插入符位置消息。此外,当鼠标指针固定在编辑器窗口中时,我们替换编辑控件窗口过程来执行此任务。
结构体
FRDLG 结构保存有关在查找替换对话框中出现的 string
的信息。结构的第一个成员是 Windows FINDREPLACE
结构变量,第二个是搜索 string
,第三个是替换 string
。
EDITUI 结构保存描述工具栏、状态栏和自动换行用户界面的布尔字段。
EDITSTATE 结构管理查找替换对话框的用户界面。它的第一个成员是指向 FRDLG 结构的指针。其他成员描述了标准的 Windows 查找替换对话框用户控件。
TEXTDOC 结构描述一个文本文档。此结构中的成员是:字节顺序标记、文本文件编码、行尾标记和文件名。
常量
大多数常量都放在头文件中,以便您可以根据需要进行修改。
结论
我一直在努力以更易读的方式编写代码。程序的创建对我来说是一次很好的学习经历。我从“平淡无奇的打印”一步步完成了这个完整的应用程序。现在,我希望它能对那些刚开始学习编程的人有所帮助。对于读者来说,您对这个程序的所有反馈和愿望都很重要。如果您想提出建议、报告错误或提出其他功能需求,请写信至 alexshag@mail.ru。