在 Win32 Delphi 应用程序中存储用户首选项和设置的最佳实践是啥?

Posted

技术标签:

【中文标题】在 Win32 Delphi 应用程序中存储用户首选项和设置的最佳实践是啥?【英文标题】:What are the best practices for storing user preferences and settings in Win32 Delphi applications?在 Win32 Delphi 应用程序中存储用户首选项和设置的最佳实践是什么? 【发布时间】:2011-10-16 13:28:01 【问题描述】:

我想在我的 Delphi Win32 应用程序中存储用户首选项(颜色、工具栏开/关、面板宽度以像素为单位)和应用程序设置(最后 10 个文件、默认保存目录、默认打开目录)。这样做的最佳做法是什么?

【问题讨论】:

注册表(虽然设置不会变成大的二进制对象,ofc :) 注册表糟透了。但程序文件夹中的 Ini 文件也是如此。具有良好信息的相关问题:***.com/questions/285277/… @warren 注册表有什么不好?这是其他系统所缺乏的 Windows 的重要功能之一。大公司的 IT 管理员喜欢注册表,讨厌没有任何可以与 Linux 相媲美的东西。讨厌没有任何可以与 Linux 相媲美的东西。 @warren - 与其指出糟糕之处并提供指向使用折旧 Windows 方法的文章的链接,不如告诉我们你做了什么? 我更喜欢用户配置文件文件夹中的 INI 文件。我发现使用注册表的应用程序变得更难支持和理解,更难在保留状态的同时备份,更难移动到保持完好的新 PC,更难升级,更难测试。基于纯文本配置(Ini 或 xml)的应用程序配置在该领域是优越的,除了 windows 之外的任何平台上都没有注册表之类的东西实际上是注册表最大的缺点。这是一种草率的 Windows 主义,需要消亡。 【参考方案1】:

您有两个主要选择:

    将设置存储在用户配置文件下的文件中。如果您的设置足够简单,那么 INI 文件就可以完美运行。 将设置存储在注册表中的HKEY_CURRENT_USER 下,这也是配置文件的一部分。

我个人更喜欢使用注册表,因为它免费提供分层存储。如果您使用文件,那么您必须自己执行此操作,这可以绑定更复杂的数据。

另一方面,如果您想编写一个便携式应用程序,即可以存储在记忆棒上的应用程序,那么与可执行文件一起放置的用户设置文件是您的最佳选择。

【讨论】:

就我个人而言,我更喜欢始终编写便携式应用程序,并且不要在注册表中存储任何内容。 Delphi 非常适合创建无需设置的应用程序:复制并运行,从您想要的任何位置...这当然是个人喜好问题,但我经常看到用户/客户认为此功能非常好/与众不同的。关于 UAC 和配置文件,现在甚至一些应用程序也倾向于将可执行文件放在用户数据中,例如谷歌浏览器。 ;) @arnaud 有很多不同的选择,不同的解决方案最适合不同的情况。我的应用程序通常在公司环境中运行,因此注册表是一个很好的解决方案。 除了 INI 文件,您还可以使用 JSON 或 XML 格式,它们可以是分层的,因此可以更好地映射主/详细信息或首选项树 - 我发现首选项组织为树可能比列表更有意义。列表是一种特殊的树,反之则不然。 Delphi 2010 可以处理 JSON 和 XML,并且您周围有大量的第三方库。 关于注册表 - 这是个人喜好问题,也取决于具体情况。我们都同意。 @Arnaud:同意。可以使用简单的 XCOPY 安装的程序是首选,IMO。 -- 我喜欢 Mac:.app 实际上是一个文件中的整个目录,您可以立即使用它。所有资源都只是目录中的文件。您可以像写入任何其他文件系统目录一样写入子目录。要安装应用程序,您只需将其复制到硬盘上,例如到用户的应用程序目录。【参考方案2】:

正如@David 指出的那样,您可以使用注册表、文件,或者 - 自然地 - 它们的组合。

如果您要查找文件,则必须将它们存储在文件系统的当前用户部分中。事实上,一个用户不应影响系统的任何其他用户是一项基本原则,Windows 强制执行这一点(例如,在没有提升权限的情况下运行时,您不能将文件保存在 Program Files 目录中1 )。

例如,我的 AlgoSim 软件可以将其设置存储在

C:\Users\Andreas Rejbrand\AppData\Roaming\Rejbrand\AlgoSim\2.0

文件夹。这是一个典型的例子。你得到目录的第一部分,即

C:\Users\Andreas Rejbrand\AppData\Roaming

通过向操作系统询问每个用户的应用数据文件夹。您可以使用SHGetKnownFolderPath 函数来找到它。使用 FOLDERID_RoamingAppData 文件夹 ID。如果需要支持旧版本的 Windows,可以改用SHGetFolderPath,并使用CSIDL_APPDATA 常量。

路径的其余部分通常遵循该模式

Manufacturer Name\Product Name\Product Version

要存储什么文件?嗯,最简单的方法是使用老式的 INI 文件,但您也可以使用 XML、您自己格式的纯文本文件,甚至是您自己设计的二进制文件。

第二种方法是使用注册表而不是文件。您可能已经知道该怎么做。如果没有,您将很容易从示例中学到这一点。例如,我可以将每个用户的设置存储在

HKEY_CURRENT_USER\Software\Rejbrand\AlgoSim\2.0

1 在这种情况下,操作系统足够智能,可以“模拟”每个用户的“Program Files”文件夹。虽然程序认为它正在读取和写入 Program Files 文件夹,但实际上它正在读取和写入文件系统当前用户部分中的文件夹。这样,即使在较新版本的 Microsoft Windows 操作系统中,旧的和表现不佳的应用程序也能继续工作,此外,它们开始支持每个用户的设置,这是它们本来应该做的。正如我之前所说的,我真的认为这是对 Microsoft 的主要 +1。

【讨论】:

您为何如此关注非漫游应用程序数据? 设置将被漫游 @Downvoter:可能是无知。我没有企业网络和计算方面的经验。 我不知道您究竟在其中存储了什么,但local app data 用于用户配置文件的机器特定部分,例如:可以在正常操作期间重新创建的数据,不值得使用网络配置文件漫游.类似的东西。 @Down... - 究竟什么是非漫游应用程序数据? 感谢 Downvoter 和 @David,让我学到了一些新东西。事实上,直到现在我还没有真正意识到“漫游意识”,我感到有些“羞耻”。我想我必须稍微重写我的一些应用程序......【参考方案3】:

INI 文件总是对我有用。有TIniFile,因此您可以轻松地读取/写入文件而无需太多工作,您可以将默认值分配给不存在的值,并且 INI 文件是人类可读的格式,因此用户可以查看和编辑如果他愿意的话。

其他解决方案(例如在注册表中存储值)也是可能的,但通常取决于操作系统并且更复杂一些。如果用户不应该看到/编辑这些值,我会使用它,但你提到的应用程序设置并不是什么“秘密”。

【讨论】:

当您使用 ini 文件或注册表之外的任何内容时,使用正确的文件夹很重要:用户配置文件中的文件夹,最好是应用程序数据文件夹的子文件夹。这很重要,因为这样您将与漫游配置文件和 UAC 兼容。 @Marjan UAC 总是一个问题,你是完全正确的。有些程序甚至倾向于在数据文件夹中安装可执行文件,例如我注意到 Google chrome 可执行文件在应用程序数据文件夹中,而不是在程序文件文件夹中......我也为一些项目做了这个,从客户 POV 来看,这是值得的。我知道这破坏了 UAC 设计,但恕我直言,这个设计从一开始就被破坏了……真是个拖钓主题! ;) @arnaud chrome 方法在多用户设置中是没有希望的,因为每个用户都需要自己的安装。这不是一个好主意。【参考方案4】:

我尽可能避免在注册表中存储任何东西。只需从用户的 CSIDL_APPDATA 文件夹中复制一个文件,我的应用程序就可以轻松移动到另一台机器上。

...我会推荐一个有用的组件:来自 www.deepsoftware.ru 的 TrsStorage 库。除了为广泛的数据类型(包括小型和大型数据类型——字符串、整数、浮点数、流、缓冲区等)提供简单的持久化方法之外,它们还有一个可以拖放到表单上的组件,该组件可以访问表单上所有组件的已发布属性...您只需选中要保留的各个属性。不,它不适用于所有内容——比如您的“最后 10 个文件”要求。但它应该适用于您的颜色、工具栏开/关、面板宽度、默认保存目录、默认打开目录等。

TrsStorage 还有很多手动数据存储和遍历方法。我已经使用它们为报表设置实现了一种继承树——使“后代”报表可以轻松存储覆盖“祖先”设置的设置,例如默认报表字体和字体大小。 (这些设置模仿了我的报告类型的对象层次结构。)您可以使用这些手动方法调用之一手动保存“最后 10 个文件”列表;事实上,如果您将“最后 10 个文件”列表保存在 TStrings 对象中,则可以使用 ReadText/WriteText 过程来存储/检索它。

TrsStorage 读取/写入您选择的数据文件类型:.INI、.XML 或其自己的 .BIN 格式。事件可用于拦截文件的写入/读取,并将流转移到其他地方:我们有时会使用它来将每条记录的设置存储在数据库表的 BLOB 字段中。

最后,我和这些人没有任何关系,但多年来我已经成功使用了他们的 TrsStorage 组件。

【讨论】:

【参考方案5】:

如果它是一个数据库应用程序(使用数据库),那么也将用户偏好存储到数据库中。我更喜欢在用户表中有一个 BLOB,我在其中以 INI 文件格式存储用户的首选项。主要原因(每个用户使用一个 BLOB)是使用适当的规范化可能会减慢应用程序的启动速度。一个小警告是VCL's TIniFile 不支持从 TStream 加载内容,您必须使用一些第三方类或滚动您自己的类以避免将数据临时保存在磁盘上。

【讨论】:

TMemIniFile 可以使用 TStream 进行流式传输,也比 TIniFile 更好。 @David AFAIK 不是直接,而是通过 SetStrings... 所以你必须使用 tmp StringList 从流中加载数据。 IOW 这是一个 hack :) 根据 n-Tier 模式,恕我直言,“主”数据库不是存储 UI 首选项、上次使用列表等的最佳位置。用户计算机上的本地文件也可能有意义。关于 TINiFile(或 TMemIniFile),将 BLOB 内容临时复制到文件上不是问题。 您将在哪里存储数据库连接信息?也在数据库中!? :-) @Warren 我的程序确实具有内置的默认连接字符串,其中包含数据库服务器名称,例如“myAppDBserver”,其中“myApp”当然是应用程序“昵称”。每个客户只需在他们的 DNS 中定义“myAppDBServ”,它就可以工作,无需手动配置每台客户端机器。当然,如果需要,每个程序都可以使用命令行参数来覆盖默认连接字符串。

以上是关于在 Win32 Delphi 应用程序中存储用户首选项和设置的最佳实践是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Delphi win32 应用程序中的社交网络

Delphi:如何确定应用程序是不是在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本?

编译器指令 WIN32 和 CPUX86、WIN64 和 CPUX64 在 Delphi 中是不是可以互换?

Python脚本和一个win32程序之间的通信(用Delphi开发)

2008 64位标准服务器上的Delphi Win32服务“打印机选择无效”错误

数组属性、TList、TStringList 或 TCollection 等 (Delphi Win32)