我的想法是让用户选择在 {localappdata}
或 {commonappdata}
中安装应用程序,并且该部分工作正常。不过,我使用的是自定义 Inno Setup 皮肤支持,这需要在安装/卸载期间加载几个函数。
我有一个 DefaultAppDataFolder(): String
函数,它返回用户选择的文件夹(公共(public)文件夹或本地 appdata 文件夹),并且在安装过程中工作正常。但是,当我尝试卸载该应用程序时,它在执行时立即抛出以下错误:
Script error: Could not call proc
您可以看到,在 DefaultAppDataFolder()
函数中,我以一种有点阴暗的方式获取卸载目录,从 {UninstallExe}
中提取文件目录两次常量,也许有更好的方法来检索它?
这是我的脚本:
#define ApplicationName "MyApp"
#define ApplicationExe "app.exe"
#define ApplicationInstanceMutex "APPMUTEX"
#define InstallerFilename "install_app"
#define SkinName "Carbon.vsf"
[Setup]
AppName={#ApplicationName}
AppVerName={#ApplicationName}
DefaultDirName={code:DefaultAppDataFolder}
DefaultGroupName={#ApplicationName}
UninstallFilesDir={code:DefaultAppDataFolder}\uninstall
UninstallDisplayName={#ApplicationName}
Compression=lzma2
SolidCompression=yes
OutputDir=.\
DisableDirPage=yes
OutputBaseFilename={#InstallerFilename}
UninstallDisplayIcon={code:DefaultAppDataFolder}\{#ApplicationExe}
DisableProgramGroupPage=yes
AppMutex={#ApplicationInstanceMutex}
WizardImageFile=installer_images\installer-1.bmp
WizardSmallImageFile=installer_images\installer-2.bmp
[Files]
Source: "skins\VclStylesInno.dll"; DestDir: "{code:DefaultAppDataFolder}"; Flags: uninsneveruninstall ignoreversion
Source: "skins\{#SkinName}"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "root_files\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "client_files\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "ssl_libs\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#ApplicationName}"; Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; WorkingDir: "{code:DefaultAppDataFolder}"
Name: "{group}\Uninstall"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#ApplicationName}"; Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; Tasks: desktopicon
[Run]
Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; Description: "Launch {#ApplicationName}"; Flags: postinstall nowait runascurrentuser
[Tasks]
Name: commondir; Description: "&All users"; GroupDescription: "Install For:"; Flags: exclusive
Name: localdir; Description: "&Current user"; GroupDescription: "Install For:"; Flags: exclusive unchecked
Name: desktopicon; Description: "Create a &desktop icon"
[Code]
procedure LoadVCLStyleS(VClStyleFile: String); external 'LoadVCLStyleW@files:VclStylesInno.dll stdcall setuponly';
procedure UnLoadVCLStylesS; external 'UnLoadVCLStyles@files:VclStylesInno.dll stdcall setuponly';
procedure LoadVCLStyleU(VClStyleFile: String); external 'LoadVCLStyleW@{code:DefaultAppDataFolder}\VclStylesInno.dll stdcall uninstallonly';
procedure UnLoadVCLStylesU; external 'UnLoadVCLStyles@{code:DefaultAppDataFolder}\VclStylesInno.dll stdcall uninstallonly';
var
ApplicationUninstalled: Boolean;
WizardInitialized: Boolean;
function InitializeSetup(): Boolean;
var
C1: Integer;
begin
ExtractTemporaryFile('{#SkinName}');
LoadVCLStyleS(ExpandConstant('{tmp}\{#SkinName}'));
result := TRUE;
end;
procedure InitializeWizard();
begin
WizardInitialized := TRUE;
end;
procedure DeinitializeSetup();
begin
UnLoadVCLStylesS;
end;
function InitializeUninstall(): Boolean;
begin
LoadVCLStyleU(ExpandConstant('{code:DefaultAppDataFolder}\{#SkinName}'));
result := TRUE;
end;
procedure InitializeUninstallProgressForm();
begin
ApplicationUninstalled := TRUE;
end;
procedure DeinitializeUninstall();
begin
UnLoadVCLStylesU;
UnloadDLL(ExpandConstant('{code:DefaultAppDataFolder}\VclStylesInno.dll'));
if ApplicationUninstalled then
begin
DeleteFile(ExpandConstant('{code:DefaultAppDataFolder}\VclStylesInno.dll'));
RemoveDir(ExpandConstant('{code:DefaultAppDataFolder}'));
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
mres: integer;
begin
case CurUninstallStep of
usPostUninstall: begin
mres := MsgBox('Do you want to delete user data?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2)
if mres = IDYES then
begin
DelTree(ExpandConstant('{localappdata}\{#ApplicationName}'), TRUE, TRUE, TRUE);
DelTree(ExpandConstant('{userappdata}\{#ApplicationName}'), TRUE, TRUE, TRUE);
end;
end;
end;
end;
function DefaultAppDataFolder(Param: String): String;
begin
if IsUninstaller then
result := ExtractFileDir(ExtractFileDir(ExpandConstant('{uninstallexe}')))
else
if (WizardInitialized) and
(IsTaskSelected('localdir')) then
result := ExpandConstant('{localappdata}') + '\Programs\{#ApplicationName}'
else
result := ExpandConstant('{commonappdata}') + '\Programs\{#ApplicationName}';
end;
最佳答案
您遇到的错误与卸载程序无关;您可以将问题范围缩小到:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName=My Program
[Code]
procedure DoSomething;
external 'DoSomething@{code:GetLibPath}\MyLib.dll stdcall setuponly';
function GetLibPath(Value: string): string;
begin
Result := 'C:\ValidPathToLib';
end;
从上面的脚本看来,您不能对导入的 DLL 文件名使用脚本常量。即使延迟加载也无法解决这个限制。但对于你的情况,你可以只使用 {app}
路径,因为你的库实际上在那里(如果我明白你的意图):
...
procedure LoadVCLStyleU(VClStyleFile: string);
external 'LoadVCLStyleW@{app}\VclStylesInno.dll stdcall uninstallonly';
procedure UnLoadVCLStylesU;
external 'UnLoadVCLStyles@{app}\VclStylesInno.dll stdcall uninstallonly';
...
关于installation - 卸载时如何获取安装路径?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23638685/