Create a hardlink with Inno Setup

2019-02-18 02:36发布

问题:

I have thousand of own installers that requires a critical dll file for uninstallation step, this dll file sizes about 2 mb then to avoid unnecessary disk space (2mb*100 installers) I would like to store the file once in {cf} then make a hardlink for the next installers that requires that file.

I could create a hardlink in Inno Setup without the need of external apps such as mklink.exe usage?

This is a brief example of what I have, all my installers follow the same "structure":

[Files]
; VCL Styles
Source: {tmp}\uninstall.vsf; DestDir: {app}; Flags: ignoreversion
Source: {tmp}\uninstall.dll; DestDir: {app}; Flags: ignoreversion uninsneveruninstall

; Temp files
Source: {tmp}\*; DestDir: {tmp}; Excludes: uninstall.dll, uninstall.vsf; Flags: recursesubdirs createallsubdirs ignoreversion

; Program
Source: {app}\*; DestDir: {app}; Flags: recursesubdirs createallsubdirs ignoreversion

As you could see, I'm moving the uninstall.dll to {app}, but what I would like to do is: If doesn't exists, copy the uninstall.dll file to {cf}\InnoSetup\uninstall.dll filepath and make a hardlink to {app}\uninstall.dll, if already exists the file then just make the hardlink, nothing more, I won't still store the uninstall.dll file in {app}\uninstall.dll, just I want a symbolic reference because the uninstall.dll file should never be uninstalled.

How I could do it?

回答1:

Inno Setup does not support creating hardlinks natively.


I wouldn't consider the mklink an external application. It's a built-in Windows tool. So if you do not need to support Windows XP, you can safely rely on it. Or you can fallback to installing the DLL regularly, if the mklink is not available.


Or use the CreateHardLink function from the Code section.

#define MyApp "MyApp"
#define UninstallDll "uninstall.dll"

[Files]
Source: "{#UninstallDll}"; DestDir: "{cf}\{#MyApp}"; \
  Flags: ignoreversion uninsneveruninstall

[Code]
function CreateHardLink(lpFileName, lpExistingFileName: string;
  lpSecurityAttributes: Integer): Boolean;
  external 'CreateHardLinkW@kernel32.dll stdcall';

procedure CurStepChanged(CurStep: TSetupStep);
var
  ExistingFile, NewFile: string;
begin
  if CurStep = ssPostInstall then
  begin
    ExistingFile := ExpandConstant('{cf}\{#MyApp}\{#UninstallDll}');
    NewFile := ExpandConstant('{app}\{#UninstallDll}');
    if CreateHardLink(NewFile, ExistingFile, 0) then
    begin
      Log('Hardlink created');
    end
      else
    if FileCopy(ExistingFile, NewFile, False) then
    begin
      { FAT file system? }
      Log('Hardlink could not be created, file copied instead');
    end
      else
    begin
      MsgBox('Cannot install {#UninstallDll}', mbError, MB_OK);
    end;
  end;
end;

(Tested on Unicode version of Inno Setup)

And do not forget to delete the file when uninstalling

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    if DeleteFile(ExpandConstant('{app}\{#UninstallDll}')) then
    begin
      Log('File deleted');
    end
      else
    begin
      Log('Cannot delete file');
    end;
  end;
end;

You can of course use also the [UninstallDelete] entry. I just like to uninstall the file using the same technology used to install it.


Your question title is "Create a hardlink with Inno Setup".

The CreateHardLink creates a hardlink. A hardlink is another reference to the same contents. Basically the hardlink is indistinguishable from the original file (even the original file is a hardlink actually). Both original file and the hardlink are just references to the same contents. If you delete the original file (or the new hardlink), you actually remove just one reference to the contents. The contents is still preserved. The contents is removed with the last reference only. The hardlink does not occupy an additional space on the disk (the contents is stored only once).

For details see Hard link article on Wikipedia.

While the mklink creates a symlink (aka symbolic link) by default. A symlink is like a shortcut, it's a reference to the the original file (not contents). It's a file on its own, that contains a path to the target file. The symlink has a size of its own (occupied by the reference to the target file). If you remove the original file, the symlink still exists (because there's no reference to the symlink from the original file), but becomes invalid (the contents is gone). Again, it's similar to a shortcut.

For details see Symbolic link article on Wikipedia.

You can create a hardlink with the mklink, if you add the /H switch:

/H Creates a hard link instead of a symbolic link.

If you want to create the symlink instead of the hardlink, it's a different question (though the answer is simple, use the CreateSymbolicLink function). Though again, note that the hardlink does not occupy additional space on the disk, what seems to be your concern. So I believe you should keep using the CreateHardLink function.