65.9K
CodeProject 正在变化。 阅读更多。
Home

在文件中存储设置,替换 MFC 中的 CSettingsStore 类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.13/5 (9投票s)

2009 年 2 月 9 日

公共领域

4分钟阅读

viewsIcon

39383

downloadIcon

593

在文件中存储设置,替换 MFC 中的 CSettingsStore 类。

介绍 

MFC的默认CSettingsStore类将所有设置(包括UI自定义)都存储在注册表中。它还非常倾向于读写,导致注册表产生惊人的大量流量,并存储大量数据。

此贡献(由我编写并置于公共领域)允许您将此行为替换为一个类,该类将设置存储在用户应用程序数据文件夹中的单个文件中,并将设置保留在内存中以获得最佳读取速度。

Using the Code

要激活它,只需将这两个文件添加到您的项目中,并在
您的CMyApplication::InitInstance()函数中添加以下行

#include "LocalSettingsStore.h"

BOOL CMyApplication::InitInstance()
{
  //  store settings in a file in local appdata, not in the registry
  CSettingsStoreSP::SetRuntimeClass(RUNTIME_CLASS(CLocalSettingsStore));		

此外,您需要在文件LocalSettingsStore.cpp中更改APPLICATION_NAME宏定义
并删除说#undef APPLICATION_NAME的行,以将设置存储在特定于您的应用程序的文件中
中。

该代码已与MFC 9.0和Visual Studio 2008 SP1一起测试,但应该与使用相同基本CSettingsStore类的早期/后期版本一起使用。

它是如何工作的? 

MFC使用一个访问注册表的包装器,称为CSettingsStore。在您的应用程序中,您使用CSettingsStoreSP类来实际访问此包装器,然后您get/set您的首选项项(窗口位置、首选颜色等)。内部框架大量使用此功能来存储和读取工具栏、菜单自定义等的设置。事实上,一个什么都不做的MFC应用程序就会产生大约20KB的注册表数据!

此类中有大量的虚拟函数,允许您重写存储方式。不幸的是,没有关于如何实际执行此操作的文档,因此我深入研究了Microsoft Visual C++ 2008 SP1附带的源代码。

virtual BOOL CreateKey(LPCTSTR lpszPath);
virtual BOOL Open(LPCTSTR lpszPath);
virtual void Close();

virtual BOOL DeleteValue(LPCTSTR lpszValue);
virtual BOOL DeleteKey(LPCTSTR lpszPath, BOOL bAdmin = FALSE);

virtual BOOL Write(LPCTSTR lpszValueName, int nValue);
virtual BOOL Write(LPCTSTR lpszValueName, DWORD dwVal);
virtual BOOL Write(LPCTSTR lpszValueName, LPCTSTR lpszVal);
virtual BOOL Write(LPCTSTR lpszValueName, const CRect& rect);
virtual BOOL Write(LPCTSTR lpszValueName, LPBYTE pData, UINT nBytes);
virtual BOOL Write(LPCTSTR lpszValueName, CObject& obj);
virtual BOOL Write(LPCTSTR lpszValueName, CObject* pObj);

virtual BOOL Read(LPCTSTR lpszValueName, int& nValue);
virtual BOOL Read(LPCTSTR lpszValueName, DWORD& dwValue);
virtual BOOL Read(LPCTSTR lpszValueName, CString& strValue);
virtual BOOL Read(LPCTSTR lpszValueName, CRect& rect);
virtual BOOL Read(LPCTSTR lpszValueName, BYTE** ppData, UINT* pcbData);
virtual BOOL Read(LPCTSTR lpszValueName, CObject& obj);
virtual BOOL Read(LPCTSTR lpszValueName, CObject*& pObj);

您从哪里开始?好吧,事实证明,您需要重写的唯一函数是DWORDLPCTSTRBYTE/UINT的读/写函数,以及创建/删除管理函数: 

virtual BOOL CreateKey(LPCTSTR lpszPath);
virtual BOOL Open(LPCTSTR lpszPath);
virtual void Close();
virtual BOOL DeleteValue(LPCTSTR lpszValue);
virtual BOOL DeleteKey(LPCTSTR lpszPath, BOOL bAdmin = FALSE);

virtual BOOL Write(LPCTSTR lpszValueName, DWORD dwVal);
virtual BOOL Write(LPCTSTR lpszValueName, LPCTSTR lpszVal);
virtual BOOL Write(LPCTSTR lpszValueName, LPBYTE pData, UINT nBytes);

virtual BOOL Read(LPCTSTR lpszValueName, DWORD& dwValue);
virtual BOOL Read(LPCTSTR lpszValueName, CString& strValue);
virtual BOOL Read(LPCTSTR lpszValueName, BYTE** ppData, UINT* pcbData);

在内部,CSettingsStore类会将所有其他请求重新格式化为这三个请求之一。例如,CSettingsStore支持将CObject对象封送进出注册表,但在I/O级别,这会通过BYTE**UINT*

在实现中,所有内容都存储为std::vector<char>,我认为这是一个方便的“可调整大小的缓冲区”类。键存储为std::wstring,通过C语言环境小写化,以提供通常在注册表中发生的、不区分大小写的搜索。所有这些都进入一个std::map<std::wstring, std::vector<char>>,它实现了完整的键/值映射。(如果您了解Win32,您会知道注册表项有多个命名值;我将其实现为一个没有特殊处理的单一长路径。例如,带有值64名“Bits”的键FOO\bar\baz将存储在映射中,作为foo\bar\baz\bits,指向一个存储DWORD64的4字节向量。)

该实现仅加载一次设置,以避免不必要的磁盘I/O。加载后,它一直保留在内存中直到程序终止,这意味着性能非常好。为了确保在预期时将更改提交到文件,文件会在CLocalSettingsStore对象被销毁时写入。不幸的是,这种情况经常发生(MFC实现非常喜欢创建/销毁对象),但为了优化这种情况,它会保留一个脏标记,并且只有在某些内容已更改时才会实际写入磁盘。

文件格式本身相当简单,并带有优化以节省磁盘空间。由于许多键路径具有共同的前缀,因此一个键存储为文件中上一个键中要使用的字符数,加上一个要附加到该键以获得下一个键路径的字符串。这个简单的更改实际上使默认设置文件从大约30KB减少到大约20KB!

关注点

CSettingsStore的文档非常薄弱。事实证明,对象的封送需要使用基本实现所使用的特定格式,否则将无法正常工作。此外,事实证明您应该只重写三个用于写入数据的虚拟函数,而不是所有函数——有关详细信息,请参阅实现。 

历史

  • 版本1.0 -- 2009年2月4日
  • 版本1.1 -- 2009年2月7日
© . All rights reserved.