INNO RestartReplace does not Recognise {app} with UNC - inno-setup

I have an application that is on a network Drive. I have a DLL that needs to be unregistered, updated and reregistered. I understand that the DLL will be in use so have the RestartReplace option:
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
; EMPSecure Update
#define MyAppName "EMPSecure"
#define MyAppVersion "2.0.1"
#define MyAppPublisher "Empyrean Security Pty Ltd"
#define MyAppURL "http://www.empyreansecurity.com"
#define MyAppExeName "EMPSecure.exe"
#define MyAppCopyright "Copyright © Empyrean Security 2012-2022"
#define MySourcePath "\\mac\Dropbox\Embarcadero\EMPSecure Update"
[Setup]
PrivilegesRequired=admin
AllowNetworkDrive=yes
AllowUNCPath=yes
AppId={{#MyAppID}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
The actual line is:
Source: "{#MySourcePath}\SFMS_ShellExt.dll"; DestDir: "{app}"; Flags: regserver restartreplace replacesameversion 64bit; Check: Is64BitInstallMode;
If I have a UNC path for the app like \\mac\Dropbox\path....
I get this error:
I have tried using a mapped network drive, but that fails in other places, but regardless, I really need to be able to use a UNC path (most users will not install using a UNC path, but some, like me, will so need to cater for that option).
I did try this:
Deleting and unregistering DLL files during install using Inno Setup but the actual deletefile kept failing with Access Denied errors.
If the complete script would be useful then let me know, I just didn't want to confuse the issue with a deal of not relevant script lines.
Is there some 'trick' other than the AllowUNCPath in [Setup] that will get the restartreplace flag code to recognise the UNC path in {app}?
Thank you.
UPDATE
As advised by Martin, I have simply downloaded the file to {tmp} and converted my original batch file to pascal code. The operation unregisters the DLL in the {app} folder, copies the new DL:L from {tmp} to {app} and re-registers the DLL (because of what we do we need to kill and restart explorer, but that's not germane to the issue).
Because this is development stuff, the app folder is in my Dropbox on an iMac and the path is \mac\dropbox\etc etc. The original unregister precisely finds that path, but I get an error that says the folder not found. Here's the code which fails in the unregister statement because it cannot find the path:
[Code]
procedure RegisterShellExt();
var
ResultCode: integer;
begin
// Deregister and delete sfms_shellext.dll
if Exec(ExpandConstant('Regsvr32 /u "{app}\SFMS_ShellExt.dll"'), '', '', SW_SHOW,ewWaitUntilTerminated, ResultCode) then
begin
// Successfully unregistered, copy the new dll
Log('SFMS_ShellExt.dll successfully unregistered');
sleep(5000)
Exec('taskkill /f /fi "IMAGENAME eq explorer.exe"', '', '', SW_SHOW,ewWaitUntilTerminated, ResultCode);
// Overwrite if Exists
if FileCopy(ExpandConstant('"{tmp}\SFMS_ShellExt.dll"'), ExpandConstant('"{app}\SFMS_ShellExt.dll"'), False) then
begin
// Successfully copied, now register the dll
Log(ExpandConstant('SFMS_ShellExt.dll successfully copied from: {tmp} to {app}'));
if Exec(ExpandConstant('regsvr32 "{app}\SFMS_ShellExt.dll"'), '', '', SW_SHOW,ewWaitUntilTerminated, ResultCode) then
begin
// Successfully copied, now register the dll
Log('SFMS_ShellExt.dll successfully re-registered');
end
else
begin
// handle deregister failure
MsgBox('Failed to re-register SFMS_ShellExt.dll, Error: ' + SysErrorMessage(ResultCode), mbError, MB_OK);
end
end
else
begin
MsgBox(ExpandConstant('Failed to copy SFMS_ShellExt.dll from {tmp} to {app}'), mbError, MB_OK);
end;
end
else
begin
// handle deregister failure
MsgBox('Failed to deregister sfms_shellext.dll, registration not attempted, Error: ' + SysErrorMessage(ResultCode) + ' File: ' + ExpandConstant('"{app}\SFMS_ShellExt.dll"'), mbError, MB_OK);
end;
// Restart explorer and continue
Exec('explorer.exe"', '', '', SW_SHOW,ewNoWait, ResultCode);
end;
So question, I have tried this with a UNC path (thought that might be the erro), but tried it also with a straight c:.... path and even though the file exists, I get a file not found error. Any ideas what I might be doing wrong??

The restartreplace mode uses MoveFileEx MOVEFILE_DELAY_UNTIL_REBOOT API. And it indeed does not work on the network shares as the Microsoft documentation says:
If dwFlags specifies MOVEFILE_DELAY_UNTIL_REBOOT, the file cannot exist on a remote share, because delayed operations are performed before the network is available.
So you would have to come up with an alternative solution. Like creating a batch file scheduled to run on the next login that does the delete/replace.
Also what about CloseApplications? It should do the job without the restartreplace.

Related

Inno Setup - Register components as an administrator

Based on the excellent Excel add-in installer (Daniel's XL Toolbox), I have built a setup file that among other things needs to register some ActiveX's
[Files]
; The include file makes adds all .XLA and .XLAM files contained in the
; SOURCEDIR to the project.
Source: "c:\source\path\MSCOMCTL.OCX"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
Source: "c:\source\path\DAS_AX_Knob.dll"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
Source: "c:\source\path\GIF89.DLL"; \
DestDir: "\users\public\EzPasteFiles"; Flags: regserver
I need the addin to install, then before starting to register the files a check is done about administrator rights and if the user has none, a message is displayed asking for entering the admin password so that registration can take place. I am aware that it can be done at the beginning of the setup, but then the addin will not be activated, if it is a standard user account. The addin needs registered components, a standard user can't install it properly.
I am looking for something like this to fire before the registration starts:
MyProgChecked := not(IsAdminLoggedOn or IsPowerUserLoggedOn);
if MyProgChecked = True then
begin
MsgBox(
'Kindly notice:' #13#13
'It seems as you are not looged as an administrator' #13#13
'Please abort and reinstall EzPaste AS an administrator' #13#13
'(To install As an Adminstrator, just save the exe setup anywhere then Right Click on it to get to this feature or ask your IT administrator for proper directives)',
mbConfirmation, MB_OK);
{ Popup message asking for Pwd }
ExitProcess(0);
end;
I am naturally open for any other approach
I'll be glad also to understand how a domain user (Windows server) without admin rights should proceed to install the addin.
You can execute regsvr32.exe "as administrator", this way:
[Files]
Source: "MyDll.dll"; DestDir: "{app}"; AfterInstall: RegMyDll
[Code]
procedure RegMyDll;
var
Path: string;
RegSvr: string;
Params: string;
Registered: Boolean;
ErrorCode: Integer;
begin
Path := ExpandConstant(CurrentFilename);
RegSvr := 'regsvr32.exe';
Params := Format('/s "%s"', [Path]);
Log(Format('Registering %s using "%s" %s', [Path, RegSvr, Params]));
Registered :=
ShellExec('runas', RegSvr, Params, '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
if Registered and (ErrorCode = 0) then
begin
Log(Format('Registered %s', [Path]));
end
else
begin
MsgBox(Format('Registering %s failed with code %d', [Path, ErrorCode]), mbError, MB_OK);
end;
end;
Alternative implementation is to create subinstaller for the registration only that will require Administrator privileges.
For a similar example, see Inno Setup - Access unprivileged account folders from installer that requires privileges.
Or use an opposite approach. Require administrator privileges using
[Setup]
PrivilegesRequired=admin
(which is default)
But deploy files to the original user folder.
See my answer to Inno Setup always installs into admin's AppData directory.

Script for copying Files at the end of install : Inno Setup

I explain my project : my setup will be delivered with 2 licence files beside him who they must not be include inside the concerned setup. Like this :
Folder/
----Setup.exe
----CanBTL.dat
----CanBTP.dat
And i want, if that's sure they are here, to copy this files in a folder who will be build with Setup.exe. So i'm trying to make this code :
I edit my script :
EDIT :
[Code]
function CheckForFile(CurPageID: Integer): Boolean;
begin
if (CurPageID = wpFinished) and (FileExists('CanBTL.dat' + 'CanBTP.dat')) then
begin
FileCopy(ExpandConstant('CanBTL.dat' + 'CanBTP.dat'), ExpandConstant('{cf}\Folder\'), false);
end;
end;
The goal is to copy the two .dat file next to the setup in a folder created by the setup.exe
It compile, but seems to make nothing. My files are not copied.
I'm still a beginner to the section code in Inno Setup so if anyone can help me?
Thanks
Ok. No need of code section, that's working fine using external flags and the
{src} constant to say current directory :
Source: "{src}\CanBTL.dat"; DestDir: "{cf}\Folder"; Flags: external;
Source: "{src}\CanBTP.dat"; DestDir: "{cf}\Folder"; Flags: external;
Thanks TLama

Inno setup search for existing file

How can I search for an existing exe file and then use that directory for my installer ?
If the exe file is not found I would like the user to browse for the path. In case the exe file is installed somewhere else.
Senario 1 (most common cases):
Default dir is c:\test\My program
This should be shown as the path on the "Select Destination Location" page
When the user press Next, there should be a check. To make sure that the default dir exist (c:\test\My program)
If it exist, the user should just continue to the Ready to Install page.
Senario 2 (very seldom cases):
Default dir is c:\test\My program
This should be shown as the path on the "Select Destination Location" page
When the user press Next, there should be a check. To make sure that the default dir exist (c:\test\My program)
If it does not exist, the user should be prompt for the path to "My program". The user should afterwards continue to the Ready to Install page.
I then just trust that the user selects the correct path
How can I do this in InnoSetup ?
I do a similar thing with my installer already. The first thing I do is need to read the registry value from the program, and if that registry value is absent then I select that program's default directory. For example:
DefaultDirName={reg:HKLM\Software\Activision\Battlezone II,STInstallDir|reg:HKLM\Software\Activision\Battlezone II,Install121Dir|{pf32}\Battlezone II}
Now, the user runs the installer, and it has to check if the program is in the right folder. It does so by checking that the program's executable file already exists. I use this piece of code to do so.
{ Below code warns end user if he tries to install into a folder that does not contain bzone.exe. Useful if user tries to install into addon or any non-BZ2 folder. }
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Log('NextButtonClick(' + IntToStr(CurPageID) + ') called');
case CurPageID of
wpSelectDir:
if not FileExists(ExpandConstant('{app}\bzone.exe')) then begin
MsgBox('Setup has detected that that this is not the main program folder of a Battlezone II install, and the created shortcuts to launch {#MyAppName} will not work.' #13#13 'You should probably go back and browse for a valid Battlezone II folder (and not any subfolders like addon).', mbError, MB_OK);
end;
wpReady:
end;
Result := True;
end;
The above code simply checks that the target executable exists and warns the user if it does not, giving him the chance to go back and change directories but also to go ahead with the install anyways.
Also, as you appear to be installing a patch or addon to an existing program, I recommend you set
DirExistsWarning=no
AppendDefaultDirName=false
And optionally to prevent an unnecessary screen if you are not creating start menu entries
DisableProgramGroupPage=yes
I would make a file input page and let user choose the Picture.exe binary location manually, when it won't be found on expected location.
You can follow the commented version of this code:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
OutputDir=userdocs:Inno Setup Examples Output
[Files]
Source: "CurrentBinary.exe"; DestDir: "{app}"
Source: "PictureExtension.dll"; DestDir: "{code:GetDirPath}"
[Code]
var
FilePage: TInputFileWizardPage;
function GetDirPath(const Value: string): string;
begin
Result := '';
if FileExists(FilePage.Values[0]) then
Result := ExtractFilePath(FilePage.Values[0]);
end;
procedure InitializeWizard;
var
FilePath: string;
begin
FilePage := CreateInputFilePage(wpSelectDir, 'Select Picture.exe location',
'Where is Picture.exe installed ?', 'Select where Picture.exe is located, ' +
'then click Next.');
FilePage.Add('Location of Picture.exe:', 'Picture project executable|Picture.exe',
'.exe');
FilePage.Edits[0].ReadOnly := True;
FilePage.Edits[0].Color := clBtnFace;
FilePath := ExpandConstant('{pf}\Picture\Picture.exe');
if FileExists(FilePath) then
FilePage.Values[0] := FilePath;
end;

Running batch file or explorer.exe doesn't have full functionality/privilege

I've built an Inno Setup installer and am having some issues with my final step.
Initially I wanted to run a batch file upon completion (from the [Run] section). This batch file starts a yajsw service, and uses java.exe.
I ran into a problem where the batch file wouldn't recognize java.exe, even if I added it to the path locally or pathed directly to it in system32.
I thought this could have something to do with Java not being in sysWOW64, and being on a 64-bit machine.
Anyway, some requirements changed and I ended up simply opening an instance of explorer.exe in the appropriate folder (that has several batch files) and giving directions as to which one to use. I do this in CurStepChanged() when curStep is ssDone, by using Exec() (or ShellExec).
It seems that even double-clicking these batch files from this explorer window has the same problem of not recognizing java.exe, but if I manually open an explorer window it works just fine.
I'm not sure if it matters but the test machine I'm using it on is running Windows Server 2008. I've tried using PrivilegesRequired=admin, and I've tried calling ShellExec() as well as Exec(), and I've tried EnableFsRedirection(False).
Any ideas would be helpful, thanks!
EDIT: Since I'm not calling the batch file directly any more, and just trying to open the containing folder:
[Setup]
AppId={{264F5847-C34C-4DB1-9EBF-F4D730D7E846}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName=C:\TestInstall
DisableDirPage=yes
DefaultGroupName=foo
DisableProgramGroupPage=yes
OutputDir=C:\Project\installer
OutputBaseFilename=setup
Compression=lzma
SolidCompression=yes
UninstallFilesDir={code:InstallPath}
SetupIconFile=foo.ico
UninstallIconFile=foo.ico
SetupLogging=yes
...snip...
[Code]
...snip...
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: Integer;
cmd : String;
begin
if CurStep = ssDone then
begin
cmd := 'explorer.exe';
ShellExec('', cmd, InstallPath('') + '\server', '', SW_SHOW, ewNoWait, ResultCode);
end;
end;
and the batch file in the Installpath\server\ directory just is (basically...there are a couple batch files that all use java):
java.exe {various params}
Basically, even if I just have java.exe, whether I fully path to it or add to the path locally, it doesn't work in the Explorer window that Inno fires off, but it DOES work if you open another one manually.
Thanks!
You're running explorer with the wrong permissions. Use ShellExecAsOriginalUser instead, or (better) use a [Run] entry with Flags: postinstall shellexec and a suitable Description.
(Also, if you want to try going back to running java, then you could try using the useapppaths flag, or querying the registry to find the correct path to Java.)

PowerShell Script after install

Newbie question: I would like to run a powershell script (.ps1) at the end of the inno-setup install. Can anyone give me a tip on where to put this? I want the user prompted to be asked if he wants to run this script.
Oh yes, what this script does is run netsh.exe to open up a port, the script is clever and it grabs Env:username and Env:userdomain from the current context. Would the context be the admin who is running the setup? or would it be the original user that ran the setup.exe?
Another way is to run the script using the ShellExec from the code.
[Files]
Source: "yourPowershell.ps1"; DestDir: "{app}"; Flags: overwritereadonly replacesameversion promptifolder;
[Tasks]
Name: "runpowershell"; Description: "Do you want to run Powershell script?"
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
var
ErrorCode: Integer;
ReturnCode: Boolean;
begin
if CurStep = ssPostInstall then begin
if(IsTaskSelected('runpowershell')) then begin
ExtractTemporaryFile('yourPowershell.ps1');
ReturnCode := ShellExec('open', '"PowerShell"', ExpandConstant(' -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -File "{tmp}\YourPowershell.ps1"'), '', SW_SHOWNORMAL, ewWaitUntilTerminated, ErrorCode);
if (ReturnCode = False) then
MsgBox('Message about problem. Error code: ' + IntToStr(ErrorCode) + ' ' + SysErrorMessage(ErrorCode), mbInformation, MB_OK);
end;
end;
[Run]
.....; Description: Run Script; Flags: postinstall
(See the help for more details.) By default this will display a checkbox and run under the original user's context (although it depends a bit on how the installer is run).
You might want to reconsider this approach, though; if you are performing a machine-wide install then you should probably open the port machine-wide too. You can do this with pure Inno code calling WinAPIs -- no powershell required. (Which is a good thing, because it might not be installed.)
Alternatively if you want to keep it a per-user setting you should consider making your application prompt the user for a decision on first run. After all, why give an option to only one of the many possible users of your app?

Resources