SharedDlls refcount wrong after upgrade
After an upgrade, my SharedDlls reference count is wrong, so uninstall does not remove my file. Is this fixable?
I had to re-arrange our MSI and my file had to go in a different component. Original MSI1 had "myfile.exe" in component cA. The upgrade MSI2 does not have component cA, and "myfile.exe" is in component cB instead. The guids for components cA and cB are different; both components use SharedDllRefCount.
With MSI1 installed "myfile.exe" has refcount 1, but after the upgrade to MSI2 the refcount is 2, so "myfile.exe" is left on disk when MSI2 is uninstalled.
The upgrade has RemoveExistingProducts between InstallValidate and InstallInitialize.
I.e.
InstallValidate
RemoveExistingProducts
InstallInitialize
The log shows that myfile.exe is removed by RemoveExistingProducts.
The log also shows the reference count for myfile.exe is 3 (!) during ComponentRegister.
What's going on, and how do I fix it?
Here are some relevant log snippets:
Action start 12:02:20: InstallValidate.
MSI (s) (38:80): Component: cB; Installed: Absent; Request: Local; Action: Local
MSI (s) (38:80): Doing action: RemoveExistingProducts
Action 12:02:22: ProcessComponents. Updating component registration...
1: {96048FC8-1727-1028-BCCA-CD9BBD538EE5} 2: {7F027A27-59F9-11D3-85C0-0050040127F4} // this is the old component cA
Action 12:02:22: RemoveFiles. Removing files...
RemoveFiles: File: myfile.exe
Directory: C:\Program Files\Common Files\MYCO\Common files\
Action ended 12:02:22: RemoveExistingProducts. Return value 1.
MSI (s) (38:80): Executing op: ActionStart(Name=ProcessComponents,Description=Updating component registration...,)
MSI (s) (38:80): Executing op: ComponentRegister(ComponentId={0011E0EC-6D00-1014-BD12-A9DF4A925FCC},KeyPath=C:\Program Files\Common Files\MYCO\Common Files\myfile.exe,State=3,,Disk=1,SharedDllRefCount=3,BinaryType=0)
1: {1A6CFF6A-6D78-1014-8280-9F517627DEE3} 2: {0011E0EC-6D00-1014-BD12-A9DF4A925FCC} 3: C:\Program Files\Common Files\MYCO\Common Files\myfile.exe
Look, the action above has SharedDllRefCount=3 !?!
MSI (s) (38:80): Executing op: FileCopy(SourceName=myfile.exe,SourceCabKey=fmyfile.exe.61E46690_5629_4B8D_AB15_2910F5F466E3,DestName=myfile.exe,Attributes=1536,FileSize=149592,PerTick=32768,,VerifyMedia=1,,,,,CheckCRC=0,Version=4.0.1.18213,Language=103
3,InstallMode=58982400,,,,,,)
MSI (s) (38:80): File: C:\Program Files\Common Files\MYCO\Common Files\myfile.exe; To be installed; No patch; No existing file
MSI (s) (38:80): Source for file 'fmyfile.exe.61E46690_5629_4B8D_AB15_2910F5F466E3' is compressed
InstallFiles: File: myfile.exe
Directory: C:\Program Files\Common Files\MYCO\Common Files\
Size: 149592
MSI (s) (38:80): Note: 1: 2318 2: C:\Program Files\Common Files\MYCO\Common Files\myfile.exe
MSI (s) (38:80): Note: 1: 2360
==========================
The full, real logfile is in the MSI4977a.zip attachment
some other info:
the root problem is that custom actions depend on cB removal (?cB = 3 AND $cB = 2)
we cannot go back to the old component cA
there's actually more than one problem component and file
these components and files are in a merge module
Comments
Reference counting
There are actually two mechanisms for reference counting. Shared DLL reference count is the "old" method which was brought in for the 16 bit versions of windows and also for NT to reduce "DLL Hell". This method is based on registry counts linked to the DLL name.
Windows Installer brought in a second method, reference counting by Component GUID, which happens automatically, and which is the functionality that Merge Modules rely on.
The Shared DLL reference counting method is actually a legacy method for non MSI installers, and ideally you would rely entirely on the component GUID method in any modern environment.
What I suspect may be happening is that your Component GUID count for the component(s) in question is not being decremented to zero during your upgrade thus causing the component to remain installed.
Before you spend too much time on this, can I check that you are testing on a clean build EACH TIME as residual components from previous attempts could cause this problem as well. Check also that none of your components are marked "Permanent" as that would also prevent them being removed at uninstall.
Solutions - the simplest to implement would be to use the RemoveFile table to explicitly delete the files in question before the upgrade is installed. With your RemoveExistingProducts action sequenced where you have it, the old app should be removed first, then the RemoveFile action will run, and finally the InstallFiles action will run, as that will be their sequence order (amongst other actions) in the InstallExecute sequence.
More complex would be to examine the registry keys for the component registration (which is stored using the rearranged form of the component GUID) and attempt to change any erroneous reference count there
Out of interest, does an normal uninstall of the original MSI result in all files being removed? If not, then that would be the first item on my fix list, as identifying the reason for this will give you the best shot at the most elegant way of fixing this in your upgrade.
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Thanks EdT Our merge module
Thanks EdT
Our merge module uses the ShareDlls refcount for legacy reasons (we sell this product as a merge module, and as DLLs which some customers wrap in non-windows-installer packages.) I'll check what's happening with the component GUID references -- you could be right that it's not getting decremented, though I don't understand why.
Testing is always on a clean machine, VirtualPC VMs in fact (which make resetting to clean state a ten-second process by using suitable batch files: kill VirtPC, copy the base file from saved copy, restart VirtPC.) I start a clean machine, install MSI1 then upgrade to MSI2. And every time the refcount ends up as 2 instead of 1, so the uninstall of MSI2 doesn't remove the file. Aaargh.
Unfortunately adding the file to the RemoveFile table didn't make a difference. The sequence is theoretically as you describe: RemoveExistingProducts uninstalls MSI1 (during which the log shows the RemoveFiles action as removing the file), RemoveFiles in MSI2 theoretically deletes the file, though the log does not show that (perhaps because the file is gone already?) then InstallFiles copies the file from MSI2 into place (which the log does show).
> Out of interest, does an normal uninstall of the original MSI result in all files being removed?
Yes, the MSI1 package has been working properly for years. It's had a number of changes since first made in 2003 but this is the first time I've had to remove a component and replace it with another component.
Thanks again for the help. I'll investigate the component GUIDs next, and post results here though maybe not for a few days because of other work.
Things to check
In an ideal world. files that are identical should have the same component GUID - that is the raison d'etre for merge modules as I'm sure you know, but the same technique can also be implemented manually in your projects if you don't want to create and manage merge module libraries specifically. On the other hand, if the files are different in any way, then they must have a unique GUID. If you happen to have the same GUID on a component in your upgrade that is newer than the original, then that needs to be fixed.
One more thing I would check, is the registry table. If capture has been used at any time, it is possible that there is a registry key in the registry table that is directly modifying the reference count, instead of allowing windows to manage this. So check the registry table in both your merge module and in your main application for any registry keys that might be carrying reference count information, and if found, delete them.
Your log should record all activity, so if the deletion of the file is not recorded, then perhaps it is not being deleted for some reason. Putting a pause in your install after the RemoveExistingProducts action would allow you to check which files have been removed and which might still be there, before you allow the install to continue.
A pause is simple to implement using a Type 38 "Run VBScript from Embedded" custom action, containing a single line such as:
msgbox "Installation is paused", 4096, "PAUSED"
Just sequence the CA just after RemoveExistingProducts and the installation will display this message and wait for your OK before proceeding.
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Thanks again for the
Thanks again for the help!
Incidentally, for any future readers, the "pause" custom action needs value 38 rather than 4096 if it's between RemoveExistingroducts and InstallInitialize, and value 1062 (38+1024 = deferred) when it's after InstallInitialize.
The messy details boil down to ProcessComponents setting the refcount "wrong". It's consistent from Win2000 through Win7, so I guess it's a feature by now, not a bug :-(
After RemoveExistingProducts ("REP") the files are correctly gone, and the SharedDlls refcount is gone too. Unfortunately, if the incoming new component cB (containing myfile.exe) uses SharedDllRefCount, the standard action ProcessComponent sets the new refcount up by one (ie to 2 if there's only the single app using the file). Yuck.
I haven't poked into the windows-installer-native component reference management but believe that's correct, with only the SharedDlls count wrong, because when SharedDlls value is manually decremented to 1 the uninstall removes myfile.exe
> One more thing I would check, is the registry table. If capture has been used at any time, it is possible
> that there is a registry key in the registry table that is directly modifying the reference count
There's nothing relevant in the reg table; capture was never used.
I'm contemplating a custom action to "adjust" the refcount, but don't know how to differentiate between the refcount value=2 because of this ProcessComponent feature and value=2 because there are two products installed which use myfile.exe. I suspect there is no good, clean solution. I'm also going to try some other installer forums for suggestions, so will post any brilliant idea here.
Alan
Regarding Custom Action
Regarding Custom Action RefCountAdjuster ;)
If you are using a merge module for shared component you could check the state of the component holding the myfile.exe.
If not then start using shared component so you don't have to workaround your issue.
Another solution is to check if the "other" product installed (+ the component holding the file) and if set a property which you use as a condition for the RefCountAdjuster CA.
Now that you mention it, I do
Now that you mention it, I do recall experiencing a similar issue on what was probably NT4 back then, and could not quite figure out what was causing the ref count to increment by two when only one instance was installed.
The simplest solution may be to check any current setting before Installinitialize, then increment the property by 1, and overwrite the registry key using a custom action conditioned to run on install only, after InstallFinalize.
However, if you do find a more elegant solution then please post it here!
If your issue has been solved, please use the "Mark as Solution" link on the most relevant thread.
Would you like to reply?
Login or Register to post your comment.