cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Reureu
Level 10

Another bug: Is( FILE_LOCKED, szFilename ) always returns TRUE on Win Vista and Win 7

Hi,

Our setup uses the Is( FILE_LOCKED, szFilename ) InstallScript command in order to know whether an application is running when the user starts a maintenance or a deinstallation. The filename corresponds to an EXE file located in C:\Program Files\.

When we built the setup with IS 2008, this solution worked well on Windows XP, Vista and Windows 7 RC.

Now we are using IS 2010, this solution no longer works on Vista and Win 7. It always returns TRUE, pretending that the file is locked, UNLESS we start setup.exe as administrator (right click on setup.exe, and choose Run as administrator).

The problem is: the maintenance or deinstallation cannot be started as administrator when launched from the Control Panel/Programs/Programs and Features dialog.


  • Am I right in thinking that this is a bug that was introduced in IS 2010?
  • If that's the case, is there already a fix available?
  • If no fix is currently available, is there any chance Acresso can tell us when a fix could be released?


Regards
Labels (1)
0 Kudos
(4) Replies
joshstechnij
Level 10 Flexeran
Level 10 Flexeran

This is an intentional change made in regards to work order IOB-000051080. In cases where there are insufficient privileges to open a file for write access, FILE_LOCKED would previously always report the file as not locked, even if it was. Because there is no reliable way of determing whether a file is locked through the Win32 API, the InstallScript engine relies on a trick of trying to open a file for write access to determine if it is locked (if it can be opened for write, the file is not locked, if it cannot be opened for write, it is locked; this code works around files that are marked read-only if necessary). This works in most cases but is not foolproof. One case that was reported with the above work order is when checking against files with permissions set that do not allow the current process security access token to open the file for write access. In this case, the decision was made to report the file as locked because it effectively is locked from the standpoint of the script engine and the current process.
0 Kudos
Reureu
Level 10

Hi Josh,

Thank you for your answer.
This is a very annoying issue, and I am wondering why Acresso didn't implement that change in a different way.

You could have left the Is( FILE_LOCKED, szFilename ) how it was, and implemented another command like Is( FILE_WRITABLE_ACCESS_RIGHTS, szFilename ) which would have solved IOB-000051080. This would have avoided breaking working setups that were relying on this mechanism in the way it worked before.

I think you need to clearly differentiate 3 things:

  • A file can be locked, because it is in use by a process.
  • A file can be marked as read-only, for all users. It is a file system attribute.
  • A file can be opened for writing, taking the access rights of the current user into account.


Another point:
Let's assume the following scenario.

  • I am a local administrator of the target PC.
  • I am running my setup on Windows Vista or Windows 7 to deinstall my application.
  • I have launched the deinstallation from the control panel (ie I have NOT right clicked on setup.exe and NOT chosen "run as Administrator" ).
  • szFilename is the path of a file that belongs to my MSI package, which is installed and deinstalled by my setup. Let's assume this file is somewhere in C:\Program Files\.


Is( FILE_LOCKED, szFilename ) will return TRUE, which would mean that the file is locked, but Windows Installer will successfully remove this file, which proves that it is not locked!

So, no, the implementation of IOB-000051080 is clearly not the way to go!

Here are my questions

  • Why don't you use the LockFile or the LockFileEx Windows API functions? You could try to lock the file. If you can successfully lock it, it means it wasn't locked. By the way, these Windows API functions exist since Windows 2000.
  • As the current implementation of Is( FILE_LOCKED, szFilename ) can no longer be used, could you please suggest a reliable workaround to detect if a file is locked?


Regards
0 Kudos
joshstechnij
Level 10 Flexeran
Level 10 Flexeran

If needed, it is possible to determine that Is(FILE_LOCKED) failed due to insufficient privileges by checking Err.LastDllError. This will be set to ERROR_ACCESS_DENIED (5) if the process calling Is(FILE_LOCKED) does not have sufficient privileges to access the file.

The previous FILE_LOCKED behavior was reporting incomplete results and therefore was not actually working. A file that could not be accessed was always being reported as not locked. This being the case, script logic that would run if the file was locked would never be executed, even if the file was locked and therefore the conditional code should have been run.

Changing FILE_LOCKED to try using a different API such as LockFile would not have necessarily improved the behavior. The LockFile API attempts to lock a requested region of a file. However, as an installer, the file either needs to be open for write access or delete access. Opening the file for read and checking for a locked region would not necessarily report the desired results, it would only report whether or not someone has all or a portion of the file locked, but could miss things like sharing violations.

Unfortunately, there is no good answer for a reliable workaround to detect if a file is locked. The Win32 API does not provide a simple method of determining if a file is locked (a Google search on this will yield a number of different results, including some that call directly into undocumented NT API functions instead of trying to use Win32). Additional issues are introduced as race conditions that could occur between the install process and any other process on the machine while trying to detect if a file is locked.

A solution depends upon what you are trying to accomplish. If you are trying to determine if an EXE is running by checking to see if it is locked, you can either let MSI (FilesInUse or MsiRMFilesInUse) or InstallScript (OnFileLocked) do this for you, depending on your project type, or use something specifically which determines if the process is running (see the List and Shut Down Running Applications on InstallSite.org as an example).
0 Kudos
Reureu
Level 10

Hi Josh,

Thanks for your answer.
The problem of the MSI FilesInUse and MsiRMFilesInUse dialogs are that they only seem to appear at installation/upgrade.

As far as I see (and I might not see far enough), MSI does not check whether a file is in use at deinstallation.
If an EXE file is in use at deinstallation, it will be removed, but the folder that contains this file won't. This is the subject of another thread I started.
Hence we realized we needed to implement this check at deinstallation ourselves.
We used Is( FILE_LOCKED ) before, which seemed to work fine.
Now we can no longer use this InstallScript command.

As we only need to check whether an EXE file is in use, I managed to reach a similar effect be detecting whether the corresponding process is running.
My implementation is based on Windows Management Instrumentation (WMI).

The downsides of this approach are:

  • If a process of the same name is running, but was not started from the file I want to check, then my setup will display a message box asking the user to quit the application, although it does not correspond to the application the setup is trying to deinstall.
  • Such an implementation only works if we want to check if an EXE file is in use. Other file types do not launch a corresponding process. If I wanted to check if a DLL, a TXT or any resource file was in use, I would definitely need a working Is( FILE_LOCKED ) function.


Regards
0 Kudos