设置存储 - 轻松处理程序设置






4.67/5 (12投票s)
2004年4月13日
6分钟阅读

96416

2063
轻松处理程序设置,支持多种存储方案和多种框架。
引言
程序设置是许多人都实现过的功能。就我自己而言,我以多种方式实现了它,具体取决于框架(MFC、ATL 等)以及需要处理的数据类型。还有一个问题是设置存储在哪里?在注册表、文件中、XML 还是数据库中?
本文提供了一个解决方案,只需少量代码或零代码即可处理程序设置,同时考虑到您正在使用的框架以及您期望的存储方案。
背景
通常,我创建一个带有数据成员(设置)和一对 Load
/Save
函数的类。它看起来像这样:
class CMySettings { public: bool Load(...); bool Save(...); CString WindowName; int Height; int Width; COLORREF BackColor; };
当然,Load
/Save
的内容需要手动编写。例如,可以使用注册表,调用 RegCreateKey()
、RegSetValue()
等函数。当处理集合(数组)时,Load
/Save
函数会收到更多代码,每个集合都有一个循环。这会变得很麻烦。
在另一个程序中,甚至在同一个程序中使用不同的设置类时,我都会为新的数据成员重写 Load
/Save
函数。事实上,我复制/粘贴以前函数中的代码并稍微修改一下。我就是不想再经历这个过程了。这既耗时又容易出错,因为需要复制/粘贴。
寻找设置类
在重新发明轮子之前,我想“肯定有人已经做了一个很棒的类了”。于是我开始了寻找。我在网上找到了一些类,有些功能和我上面描述的差不多,有些甚至更糟。最后,我在 CodeProject 上找到了 CRegSettings
。
CRegSettings
是 Magomed G. Abdurakhmanov 制作的。这是一个非常出色的类。它使用 MFC 或 ATL 映射(MSG、COM、DDX)之类的映射机制。这里的映射是为了避免编写 Load
/Save
函数。这正是我想要的。
这是一个设置类的示例
class CMySettings : public CRegSettings { public: CString WindowName; int Height; int Width; COLORREF BackColor; BEGIN_REG_MAP(CMySettings) REG_ITEM(WindowName, "No Document") REG_ITEM_REQUIRE(Height) REG_ITEM_REQUIRE(Width) REG_ITEM(BackColor, 0xFF8800) END_REG_MAP() };
在程序的某个地方,您会调用 Load
/Save
函数。例如:m_Settings.Load()
;
您可能会问:“好吧,既然你找到了宝藏,那这篇文章还有什么意义?”因为注册表并非我总是希望我的设置存储的地方。
有什么新功能?
我的系统的主要思想是将设置类分成两个类。一个类负责数据成员和所有字段的加载/保存操作,第二个类负责针对特定存储机制的单个项的读写。
目标是让设置类与项的存储方式分离。这使您可以在运行时选择使用哪种存储机制。只需付出很小的努力,您也可以自己为系统添加新的存储方案。所有繁琐的工作都已经完成,因此您可以专注于真正的代码。
如何简单使用?
我将在这里解释定义一个管理某些设置的类的不同步骤。
步骤 1
让您的类从 CSettings
派生。请注意,CSettings
封装在 Mortimer
命名空间中。添加代表您设置的数据成员。
这是一个例子。
#include <MSettingsStorage.h> using namespace Mortimer; class CMySettings : public CSettings { public: CString WindowName; int Height; int Width; COLORREF BackColor; CDWordArray Measures; };
第二步
为每个数据成员添加一个映射条目。
每种处理的数据类型或数据类型组都提供三个映射宏,以下是简单类型的宏:
SETTING_ITEM(<数据成员名称>)
SETTING_ITEM_REQUIRE(<数据成员名称>)
SETTING_ITEM_DEFAULT(<数据成员名称>, <默认值的表达式 for default value>)
标准宏只是将处理过的数据成员名称作为参数。如果在 CSettings::Load()
时项未能读取其数据,则整个 Load()
过程将静默继续,并可能返回成功(如果没有任何必需项生成错误)。
REQUIRE
宏会改变这种行为,因为它的名称暗示该项是必需的,因此读取失败将导致 Load()
过程返回失败。请注意,根据 CSettingsStorage::ContinueOnError()
,其他项可能会被读取。
DEFAULT
宏的行为类似于标准宏,但当项读取失败时,项数据将被替换为给定的默认值。
在保存时(CSettings::Save()
),任何这些宏都可能失败,并返回错误状态。根据 CSettingsStorage::ContinueOnError()
函数的值,其他项可能会进一步保存。
这是每个宏处理的类型列表。
编辑者注释 - 为防止滚动,一些单词被连字符分隔
|
|
独立类型是 SETTING_ITEM
处理的所有类型。
带映射的示例
class CMySettings : public CSettings { public: CString WindowName; int Height; int Width; COLORREF BackColor; CDWordArray Measures; BEGIN_SETTING_MAP(CMySettings) SETTING_ITEM_DEFAULT(WindowName, "MyApp") SETTING_ITEM_REQUIRE(Height) SETTING_ITEM_REQUIRE(Width) SETTING_ITEM(BackColor) SETTING_ITEM_ALONE_ARRAY(Measures, DWORD) END_SETTING_MAP() };
请注意 SETTING_ITEM_ALONE_ARRAY
的附加参数,您必须传递元素的类型。
步骤 3
现在您已经定义了您的设置类,您必须加载和保存这些设置。这在您的代码中的某个位置通过调用设置类的 Load()
和 Save()
函数来完成。
为了能够调用它们,您必须传递一个 CSettingsStorage
对象。该对象与底层存储机制建立连接。
让我们用一个使用注册表的示例
#include <MSettingsStorageRegistry.h> using Mortimer::CSettingsStorageRegistry; #define APP_REGISTRY_KEY "Software\\Company\\MyApp" class CMyApp : public CSomeFrameworkApp { public: ... void OnInit() { CSettingsStorageRegistry Stg(HKEY_CURRENT_USER, APP_REGISTRY_KEY); m_MySettings.Load(Stg); } void OnChangeSettings() { CSettingsDialog Dlg; if (Dlg.DoModal() == IDOK) { CSettingsStorageRegistry Stg(HKEY_CURRENT_USER, APP_REGISTRY_KEY); m_MySettings.Save(Stg); } } ... protected: CMySettings m_MySettings; };
为清晰起见,已省略所有错误处理。嗯,就是这么简单。不需要别的了。
如何实现新的存储方案?
我创建了三个管理存储方案的类
CSettingsStorageRegistry
用于处理注册表。CSettingsStorageIniFile
用于处理 .ini 文件。CSettingsStorageMSXMLFile
用于处理 XML 文件(通过使用 msxml.dll)。
您可以通过从抽象类 CSettingsStorage
派生新类并为每种处理的类型实现 SaveLoadItem()
函数来添加自己的存储方案。有关详细信息,请参阅该类的文档。您也可以研究提供的类。
结论
您现在应该可以更轻松地处理您的设置,即使是您自己的类型或集合。要获得真实示例,请阅读演示项目,它向您展示了一些高级用法。提供了三个示例:一个用于 MFC 框架,一个用于 ATL/WTL,最后一个是用于纯 Win32 使用 STL 的简单示例。
请随意使用和修改此软件。也欢迎提出建议和评论。
敬请期待下一篇文章。我将介绍如何使用此设置系统与对话框结合使用来编辑它们,当然是以最少的代码。
历史
- 2004 年 5 月 28 日
- 添加了处理 XML 的类:
CSettingsStorageMSXML*
。
- 添加了处理 XML 的类:
- 2004 年 5 月 14 日
- 错误修复。
CSettingsStorage
已重构以添加标准行为。警告:INI 文件存在兼容性问题。集合的“
Count
”值现在存储在“Root.KeyName
”-“Count
”下,而不是“Root
”-“KeyName.Count
”。- 添加了
SETTING_ITEM_ALONE_
* 宏。感谢 CoolVini 先生提出这个想法。
- 2004 年 4 月 16 日
- 更新了源代码。
- 2004 年 4 月 5 日
- 文章已提交。
- 2003 年 12 月 31 日
- 首次发布。