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

5
我想在我的Delphi Win32应用程序中存储用户偏好设置(颜色、工具栏开/关、面板宽度-以像素为单位)和应用程序设置(最后10个文件、默认保存目录、默认打开目录)。如何最佳地实现这一目标?

注册表(当然,设置不会变成大的二进制对象 :)) - Premature Optimization
1
注册表很烦人。但是在程序文件夹中的Ini文件也一样。相关问题包含了很好的信息: https://dev59.com/rHVC5IYBdhLWcg3wdw65 - Warren P
@warren,注册表有什么不好的地方吗?它是Windows的一个伟大特性,其他系统缺乏这个功能。大型企业IT管理员喜欢注册表,却讨厌Linux缺乏类似的功能。 - David Heffernan
1
@warren - 与其指出哪些不好并提供使用已过时的Windows方法的文章链接,不如告诉我们你是怎么做的? - Michael Riley - AKA Gunny
5
我更喜欢将INI文件存放在用户的个人资料文件夹中。我发现使用注册表的应用程序越来越难以支持和理解,备份时保留状态更加困难,移动到新电脑并保持完整性更加困难,升级和测试也更加困难。基于纯文本配置(Ini或xml)的应用程序配置在这方面更为出色,而在Windows以外的任何平台上都不像注册表那样有它的存在,事实上这是注册表最大的问题所在。它是一个粗糙的Windows主义,需要消失。 - Warren P
5个回答

11
您有两个主要选项:
  1. 将设置存储在用户个人资料下的文件中。如果您的设置足够简单,那么INI文件完全可以胜任。
  2. 将设置存储在注册表中,在HKEY_CURRENT_USER下,这也是个人资料的一部分。
个人而言,我更喜欢使用注册表,因为它提供了免费的分层存储。如果您使用文件,则必须自己完成这个过程,这对于更复杂的数据可能会很麻烦。
另一方面,如果您想编写可移植应用程序,即可以存在于内存棒上的应用程序,则与可执行文件并列的用户设置文件是正确的选择。

2
个人而言,我更喜欢编写可移植应用程序,并且不将任何东西存储在注册表中。Delphi非常适合创建无需安装的应用程序:只需复制并运行,从任何位置都可以... 当然这是个人口味问题,但我发现用户/客户通常认为这是一个非常好的/独特的功能。关于UAC和配置文件,甚至一些应用程序现在倾向于将可执行文件放在用户数据中,例如Google Chrome。 ;) - Arnaud Bouchez
@arnaud,有很多不同的选项,不同的解决方案适用于不同的情况。我的应用程序通常在企业设置中运行,因此注册表是一个好的解决方案。 - David Heffernan
除了INI文件,您还可以使用JSON或XML格式,这些格式可以是分层的,因此可以更好地映射主/细节信息或首选项树 - 我发现以树形结构组织的首选项可能比列表更有意义。列表是一种特殊类型的树,而反之则不成立。Delphi 2010可以处理JSON和XML,并且您可以找到大量的第三方库。 - Arnaud Bouchez
关于注册表 - 这是一个品味问题,也取决于具体情况。我们都同意。 - Arnaud Bouchez
@Arnaud:同意。在我看来,可以通过简单的 XCOPY 安装的程序更好。-- 我喜欢 Mac 之处就在于此:.app 实际上是一个包含整个目录的文件,你可以立即使用它。所有资源都是目录中的文件。你可以像写入任何其他文件系统目录一样写入子目录。要安装应用程序,只需将其复制到硬盘上,例如用户的应用程序目录。 - Rudy Velthuis
分层,没错。在Windows中,它是不可移植的、隐藏的,并且是意外复杂性的巨大源泉。让用户在其中修改您应用程序的配置,他们可能会瘫痪他们的系统。注册表只是棒极了。如果我是恶意软件作者,试图让你的生活更加困难,那么这就是我会放置东西的地方。 - Warren P

4

正如 @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操作系统中也可以继续工作,并且还开始支持每个用户的设置,这是它们一开始就应该做的。我真的认为这是微软的主要优势,正如我以前所说的那样。


为什么你如此专注于非漫游应用数据?“设置”应该是可以“漫游”的。 - Premature Optimization
@Downvoter:可能是无知吧。我没有企业网络和计算方面的经验。 - Andreas Rejbrand
我不知道你具体存储了什么,但是本地应用数据是用户配置文件的机器特定部分,例如:在正常操作期间可以重新创建的数据,不值得与网络配置文件一起漫游。就像这样。 - Premature Optimization
@下行... - 什么是非漫游应用数据? - Michael Riley - AKA Gunny
1
谢谢你们两个,Downvoter和@David,让我学到了新东西。事实上,我感到有点“羞愧”,因为直到现在我还没有真正意识到“漫游”的重要性。我想我必须稍微重新编写一些我的应用程序... - Andreas Rejbrand
显示剩余3条评论

3

INI文件对我来说一直表现不错。有TIniFile,所以你可以很容易地读写文件,分配不存在的值的默认值,并且INI文件是人类可读的格式,用户可以查看并编辑它,如果他想的话。

其他解决方案,如将值存储在注册表中,也是可能的,但通常依赖于操作系统并且有点复杂。如果用户不应能够查看/编辑值,则我会使用此类设置,但您提到的应用程序设置并不是“机密”。


2
当您使用ini文件或注册表之外的任何内容时,重要的是使用正确的文件夹:一个文件夹,最好是应用程序数据文件夹的子文件夹,在用户配置文件中。这很重要,因为这样您将与漫游配置文件和UAC兼容。 - Marjan Venema
@Marjan UAC (用户账户控制) 总是一个问题,你说得完全正确。有些程序甚至倾向于将可执行文件安装在数据文件夹中,例如我注意到 Google Chrome 可执行文件在应用程序数据文件夹中,而不是在程序文件文件夹中... 我也为一些项目做过这个操作,从客户的角度来看,它是值得的。我知道这打破了 UAC 的设计,但在我看来,这个设计从一开始就有缺陷...真是个搞笑的话题!;) - Arnaud Bouchez
@arnaud 的 Chrome 方法在多用户设置中是无望的,因为每个用户都需要自己的安装。这不是一个好主意。 - David Heffernan

2
尽可能避免将任何内容存储在注册表中,我的应用程序可以通过仅从用户的CSIDL_APPDATA文件夹复制文件来轻松移动到另一台计算机。
我建议使用www.deepsoftware.ru的TrsStorage库。除了为各种数据类型(包括小型和大型数据类型--字符串、整数、浮点数、流、缓冲区等)提供易于持久化的方法外,他们还提供了一个组件,您可以将其放置在表单上,以访问表单上所有组件的已发布属性...您只需选中要持久保���的单个属性即可。不,它不能用于所有情况--例如您的“最近10个文件”需求。但是它应该适用于您的颜色、工具栏开关、面板宽度、默认保存目录、默认打开目录等。
TrsStorage还具有许多手动数据存储和遍历方法。我已经使用它们实现了一种报告设置继承树--使得“后代”报告可以存储重写“祖先”设置的设置,例如默认报告字体和字号。 (设置会模仿我的报告类型的对象层次结构。) 您可以使用其中一种手动方法调用手动保存“最近10个文件”列表;实际上,如果您将其保存在TStrings对象中,ReadText/WriteText过程应该适用于存储/检索“最近10个文件”列表。
TrsStorage从/写入您选择的数据文件类型:.INI、.XML或其自己的.BIN格式。并且有事件可用于拦截写入/读取文件,并将流转移至其他位置:我们有时会在数据库表的BLOB字段中存储每个记录的设置。
最后,我与这些人没有任何联系,但多年来我已经成功使用他们的TrsStorage组件。

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

TMemIniFile 可以使用 TStream 进行流式传输,而且比 TIniFile 更好。 - David Heffernan
据我所知,不能直接实现,但可以通过 SetStrings... 实现,因此您必须使用临时字符串列表从流中加载数据。换句话说,这是一种hack :) - ain
1
根据n-Tier模式,"主"数据库不是存储UI首选项、最近使用列表等的最佳位置。在用户计算机上的本地文件也可能是有意义的。关于TIniFile(或TMemIniFile),将BLOB内容暂时复制到文件中并不是问题。 - Arnaud Bouchez
2
你会把数据库连接信息存储在哪里?难道也是在数据库中吗? :-) - Warren P
我喜欢命令行参数和数据库的想法。我确实欣赏你在可移植性方面所付出的努力。 - Warren P
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接