如何共享应用程序和关联的数据文件

How to share an application and associated datafile

我使用 delphi 社区版编写了一个程序 (firemonkey)。我想分享这个程序,但是我的朋友们要下载的 .exe 文件必须时不时地访问一个文本文件来检索字符串。在编写程序时,我使用事件处理程序在启动时加载文本文件:

tform3.formCreate(发件人:Tobject); ... assignfile(myfile,('C:** ********.txt)); ...

期间工作得很好 设计阶段。

作为一个爱好者,我现在发现自己被困住了。如果我使用 INNO 安装编译器创建一个安装程序,我打算这样做,我不能对数据有相同的硬连线引用 ('C:*****)文件的位置。我需要的是更改上面的代码,以便 .exe 文件可以找到支持的数据文件,而不管该 .exe 文件(和数据文件)在其他人的 PC 上的最终位置。

我该怎么做?即我需要什么代码(代替上面的代码)来确保我分发的安装程序将安装一个可以找到它引用的数据文件的 .exe 文件?

任何帮助,非常感谢。还在学习中。

Read-only 访问

如果数据文件应该始终以read-only 模式打开,最简单的解决方案是将其放在*.exe 文件旁边。然后,在运行时,您动态找到 *.exe 文件的路径并修改它以找到数据文件的路径。例如,

uses
  IOUtils;

procedure TForm1.FormCreate(Sender: TObject);
var
  FileName: string;
begin
  FileName := TPath.Combine(ExtractFilePath(ParamStr(0)), 'data.txt');
  ShowMessage(TFile.ReadAllText(FileName, TEncoding.UTF8));
end;

ParamStr(0)包含*.exe文件的路径,如

'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\Project1.exe'

那么ExtractFilePath(ParamStr(0))就是

'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\'

最后,TPath.Combine(ExtractFilePath(ParamStr(0)), 'data.txt')

'C:\Users\Andreas Rejbrand\Documents\Embarcadero\Studio\Projects\test\Win32\Debug\data.txt'

确保安装程序将数据文件放在 *.exe 文件旁边。

读写权限

如果我们谈论的是每个用户都需要更改(通过软件)的设置文件或其他文件,则不能将其放在 *.exe 文件旁边,因为 *.exe 文件通常位于Program Files 文件夹,它是只读的。另外,Program Files文件夹只有一个,但PC上可能有很多用户,每个用户都应该有自己的一份。

解决方法是将文件保存在用户自己的文件夹中,具体为AppData文件夹:

FileName := TPath.GetHomePath + '\Mariner\My Word Processor App\Settings\settings.ini';

(使用不同的路径构建方法)。

在我的系统上,这变成了

'C:\Users\Andreas Rejbrand\AppData\Roaming\Mariner\My Word Processor App\Settings\settings.ini'

您的安装程序 (Inno Setup) built-in 支持在此位置放置文件。

如果只读访问,也可以考虑将其作为资源添加到可执行文件中。这样一来,您就可以简单地分发此可执行文件而无需安装程序。

Delphi Dabbler 有一个示例,但我发现它有点令人困惑。无论如何我都会 link to it (PDF)。

您可以让用户select 保存文件的位置。如果文件是针对每个单独用户的,则建议使用 AppData 文件夹;如果文件必须在不同用户之间共享,则建议使用 CommonAppData。

当使用selected 数据文件目标时,您可以将其保存到 INI 文件中。 INI 文件可以在不询问用户的情况下存储到注册表或保存在 AppData 文件夹或 ProgramData 文件夹中的 INI 文件。

这里是一段源代码片段,用于获取那些特殊文件夹:

const
    SectionWindow            = 'Window';
    SectionData              = 'Data';
    CompanyFolder            = 'YourCompanyName';


constructor TForm1.Create(AOwner: TComponent);
    var
        CommonPath   : array [0..MAX_PATH] of Char;
        LocalPath    : array [0..MAX_PATH] of Char;
        LangFileName : String;
    begin
        SHGetFolderPath(0, CSIDL_COMMON_APPDATA, 0, SHGFP_TYPE_CURRENT, @CommonPath[0]);
        SHGetFolderPath(0, CSIDL_LOCAL_APPDATA,  0, SHGFP_TYPE_CURRENT, @LocalPath[0]);
        FIniSection     := SectionWindow;
        FIniSectionData := SectionData;
        FAppName        := ChangeFileExt(ExtractFileName(Application.ExeName), '');
        FCommonAppData  := IncludeTrailingPathDelimiter(CommonPath) +
                           CompanyFolder + '\' + FAppName + '\';
        FLocalAppData   := IncludeTrailingPathDelimiter(LocalPath) +
                           CompanyFolder + '\' + FAppName + '\';
        FIniFileName    := FLocalAppData + FAppName + '.ini';
        ForceDirectories(FCommonAppData);
        ForceDirectories(FLocalAppData);
        inherited Create(AOwner);
    end;