Is it possible to read package code through custom action as reading ProductCode and ProductName. I want to remove the MSI cache being created in %LOCALAPPDATA%/Downloaded Installations/GUID, where GUID is the package code during uninstallation.
2 Answers
Let's walk this through. First we have to answer where the package code is stored. Package Codes covers this in its third paragraph: "The package code is stored in the Revision Number Summary Property of the Summary Information Stream." Ok, so how do we read that? That's covered on Using the Summary Information Stream, where you start with a call to MsiGetSummaryInformation. But this code will be called from a custom action, so let's verify it's okay. Functions not for Use in Custom Actions covers this. Scanning the list we find no mention of the summary information functions (except MsiCreateTransformSummaryInfo which we don't need here).
So yes, this is possible.

- 15,737
- 2
- 28
- 44
-
Why would you have to go through all this trouble in a custom action? The installer sets the PackageCode property when the installation begins. – Christopher Painter Aug 20 '13 at 19:41
-
Yes Chris, PackageCode property is available during installation/uninstallation. My problem is solved. – gopu Aug 21 '13 at 06:02
-
BTW, does suite installers make this better? – Christopher Painter Aug 21 '13 at 11:37
-
That's a good point. My new made up defense is that this approach should scale to a secondary .msi file (but the OP didn't specifically request that). As far as suites making "this" better, I'm not 100% what "this" is, but I don't think they handle it differently. The fundamental question is in which scenarios you want to clean up vs. prevent a need to re-download. – Michael Urman Aug 21 '13 at 12:14
-
You don't need a made up defense. You are a well known and respected expert. We aren't always perfect. :) The "this" would be suite installers being aware of previous cached MSI's and cleaning up automatically as appropriate. – Christopher Painter Aug 22 '13 at 12:45
-
Thanks. I enjoyed writing up the walkthrough even though it's overkill for this need. Regarding "as appropriate," the devil is in the details. And currently no, suites do not clean up either. – Michael Urman Aug 23 '13 at 12:42
You might want to take a look at this code I wrote awhile back: (the entire thread is a good read)
Local cached MSI does not get deleted when uninstalling
<CustomAction Id="PurgeCache_CAD_Install" Execute="immediate" Property="PurgeCache" Value="/CacheRoot=[CommonAppDataFolder]Downloaded Installations\MyCompany\MyProduct /PackageCode=[PackageCode] /InstallMode=Install"/>
<CustomAction Id="PurgeCache_CAD_Uninstall" Execute="immediate" Property="PurgeCache" Value="/CacheRoot=[CommonAppDataFolder]Downloaded Installations\MyCompany\MyProduct /PackageCode=[PackageCode] /InstallMode=UnInstall"/>
<InstallExecuteSequence>
<Custom Action="PurgeCache_CAD_Install" After="ScheduleReboot">Not REMOVE="ALL"/>
<Custom Action="PurgeCache_CAD_Uninstall" After="ScheduleReboot">REMOVE="ALL"/>
</InstallExecuteSequence>
export prototype PurgeCache(HWND);
function PurgeCache(hMSI)
number nResult;
string szInstallMode;
string szCacheRoot;
string szDir;
string szPackageCode;
LIST listDirs;
begin
szInstallMode = MsiGetCustomActionDataAttribute( hMSI, "/InstallMode=" );
szCacheRoot = MsiGetCustomActionDataAttribute( hMSI, "/CacheRoot=" );
szPackageCode = MsiGetCustomActionDataAttribute( hMSI, "/PackageCode=" );
listDirs = ListCreate (STRINGLIST);
FindAllDirs( szCacheRoot, EXCLUDE_SUBDIR, listDirs );
nResult = ListGetFirstString (listDirs, szDir);
while (nResult != END_OF_LIST);
if ( szInstallMode = "Uninstall" || !( szDir % szPackageCode )) then
DeleteDir( szDir, ALLCONTENTS );
endif;
nResult = ListGetNextString (listDirs, szDir);
endwhile;
return ERROR_SUCCESS;
end;

- 54,556
- 6
- 63
- 100
-
Chris, I have gone through your thread. I have created a custom action which removes cache folder by reading PackageCode during uninstallation as my product has multi version support. So, the custom action removes only one folder. Is it a right way? – gopu Aug 21 '13 at 06:46
-
During an upgrade, the logic removes all folder except the current folder. During an uninstall it removes all folders. – Christopher Painter Aug 21 '13 at 11:31
-
PS- The MsiGetCustomActionDataAttribute function source can be found here: http://www.installsite.org/pages/en/msi/ca.htm – Christopher Painter Aug 21 '13 at 11:34
-
ISSetupFilesExtract is first action(which copies MSI cache to target machine) being executed in my project. how can i run a custom action before ISSetupFilesExtract action to remove MSI cache at start of installation. In your project you are running after ScheduleReboot. – gopu Aug 22 '13 at 09:40
-
ISSetupFilesExtract does not copy the MSI cache to the target machine. ISSetupFilesExtract extracts any files in the ISSetupFiles table to a temp directory and then sets the SUPPORTDIR property to the location of this directory. Setup.exe extracts the MSI to the cache and then runs the MSI from there. The point is, the MSI is already there before the MSI is actually invoked. – Christopher Painter Aug 22 '13 at 12:41
-
Additionally, custom actions that change the state of the machine (such as delete a file/directory) should always be scheduled between InstallInitialize and InstallFinalize in the deferred context. There is no hurry to clean up old directories. Save it until the very end of the installation. – Christopher Painter Aug 22 '13 at 12:42
-
As my product need admin privileges(for install/uninstall), the cache is getting created under LocalAppDataFolder of admin user if you install with standard user also. So if i read LocalAppDataFolder during uninstall, it will be appdata folder of standard user. so how can i remove that cache folder in admin appdata folder if i install/uninstall the product using standard user? – gopu Sep 03 '13 at 08:28
-
The custom action gets scheduled as deferred with no impersonation so it runs in the SYSTEM context. Perms shouldn't be a problem. – Christopher Painter Sep 03 '13 at 10:56