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

How to remove registry entries conditionally

Hi,

I am working on a Basic MSI project and I need to remove some registry entries at installation/deinstallation only if some conditions are fulfilled.

I know I can use the Registry and the RemoveRegistry tables to remove these registry entries, but I need to remove them only if some MSI conditions are fulfilled, and I cannot specify some conditions in these table.

What would be a clever way to do that?
Any suggestion?

Regards
Labels (1)
0 Kudos
(11) Replies
Kevin_L
Level 5

Hi,

This registry entries are installed by your MSI package?

If not, maybe must write a Custom Action to do this.

Regards,

Kevin
0 Kudos
Reureu
Level 10

Hi,

I have finally gone for an extra table in my Basic MSI project.
It is called RemoveRegistryConditional and contains the following columns:

  • RemoveRegistry: The key for this table.
  • Root: The predefined root key for the registry value.
  • Key: The localizable key for the registry value.
  • Name: The localizable registry value name.
  • Component_: External key into the first column of the Component table referencing the component that controls the deletion of the registry value.
  • InstallMode: Same as the InstallMode column of the RemoveFile table. This allows to remove registry entries either when a component is installed or deinstalled.
  • Condition: A condition formatted as MSI condition.


Then I implemented an InstallScript custom action that iterates through the RemoveRegistryConditional table, checks the condition of each entry.
If the condition evaluates to MSICONDITION_TRUE, then it inserts the corresponding entry in either the Registry table and/or in the RemoveRegistry table, depending on the InstallMode value.

It works quite well, and it is rollbackable if the installation fails, as it relies on the existing RemoveRegistryValues to actually delete the registry entries.
0 Kudos
ikotlova
Level 4

Reureu,

The concept works well on XP as it allows to update the .msi database.

However I failed to do the same thing on Vista/Windows7 as the .msi database is read-only.

Do I miss something and there is a way to make the database writable?

Thanks,
IK
0 Kudos
Reureu
Level 10

You don't actually modify the MSI database permanently, as it is impossible.

You can use the following InstallScript code to modify your MSI database temporarily:
		
/**
* This function temporarily inserts an entry in the RemoveRegistry table.
@ param hMsi: handle to the installer
@ param szRegistry: Primary key, non-localized token
@ param iRoot: The predefined root key for the registry value, one of rrkEnum
@ param szKey: The key for the registry value
@ param szName: The registry value name
@ param szComponent: Foreign key into the Component table referencing component that controls the deletion of the registry value
@ returns: -ERROR_SUCCESS if everything went smoothly
-Other error codes if any error occured when processing the MSI tables.
*/
function INT addItemToRemoveRegistryTable( hMsi, szRegistry, iRoot, szKey, szName, szComponent )
HWND hDB;
HWND hView;
HWND hRecord;
NUMBER nTempResult;
begin
// Open the MSI database
hDB = MsiGetActiveDatabase(hMsi);
if( hDB = NULL ) then
return ERROR_INSTALL_FAILURE;
endif;

// Open a view into RemoveRegistry table
nTempResult = MsiDatabaseOpenView(hDB, "SELECT * FROM `RemoveRegistry`", hView);
if( nTempResult != ERROR_SUCCESS ) then
MsiCloseHandle( hDB );
return nTempResult;
endif;

// Execute the view
nTempResult = MsiViewExecute(hView, NULL);
if( nTempResult != ERROR_SUCCESS ) then
MsiCloseHandle( hDB );
return nTempResult;
endif;

// Create an empty MSI record
hRecord = MsiCreateRecord(5);
if( hRecord != NULL ) then
// Modify the MSI record
MsiRecordSetString(hRecord, 1, szRegistry);
MsiRecordSetInteger(hRecord, 2, iRoot);
MsiRecordSetString(hRecord, 3, szKey);
MsiRecordSetString(hRecord, 4, szName);
MsiRecordSetString(hRecord, 5, szComponent);

// Temporarily modify the running MSI database
nTempResult = MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hRecord);
if( nTempResult != ERROR_SUCCESS ) then
// Log some error here!
else
// Log the success
endif;
else
nTempResult = ERROR_INSTALL_FAILURE;
endif;

MsiViewClose(hView);

return nTempResult;
end;


And make sure this code is scheduled for immediate execution, somewhere between InstallInitialize and RemoveRegistryValues.

I hope that helps.
0 Kudos
Roman1
Level 9

Hello Reureu,

whatfor do you need "InstallMode" ?

Would you like to explain it shortly, please?
0 Kudos
Reureu
Level 10

Well, what I haven't detailed here is how it all fits together, but I mentioned it in a previous post in this thread:
Then I implemented an InstallScript custom action that iterates through the RemoveRegistryConditional table, checks the condition of each entry.
If the condition evaluates to MSICONDITION_TRUE, then it inserts the corresponding entry in either the Registry table and/or in the RemoveRegistry table, depending on the InstallMode value.


The idea was to have a reasonably flexible implementation which allows to remove registry entries when a specific component is either installed or removed. This is why I had to take a component install mode into account.

The function I posted in my previous post was the function to add an entry in the RemoveRegistry table.

There are obviously 3 main functions
[LIST=1]
  • One that iterates through the RemoveRegistryConditional table.
  • One that allows to add an entry in the RemoveRegistry table (the one detailed in my previous post).
  • One that allows to add an entry in the Registry table.
    and a few other helper functions.
  • 0 Kudos
    Roman1
    Level 9

    Hi Reureu,
    Thanks.

    Can you post a portion of code, where you are
    opening and reading the "RemoveRegistryConditional" Table, please ?
    0 Kudos
    Reureu
    Level 10

    Too easy.

    /**
    * This function iterates through the RemoveRegistryConditional table,
    * checks the condition of every entry.
    * If the condition evaluate to MSICONDITION_TRUE, the entry is inserted in
    * -the Registry table if InstallMode == msidbRemoveRegistryOnInstall
    * -the RemoveRegistry table if InstallMode == msidbRemoveRegistryOnRemove
    * -both above tables if InstallMode == msidbRemoveRegistryOnBoth
    * so that the registry entry/values get either deleted on installation of a specific component,
    * or on deinstallation of a specific component.
    @ param hMsi: handle to the installer
    @ returns: -ERROR_SUCCESS if everything went smoothly
    -Other error codes if any error occured when processing the MSI tables.
    */
    function INT scheduleRegistryRemoval( hMsi )
    HWND hDB;
    HWND hView, hRecord;
    NUMBER nBuffer;
    STRING szRemoveRegistry;
    NUMBER nRoot;
    STRING szKey;
    STRING szName;
    STRING szComponent;
    NUMBER nInstallMode;
    STRING szCondition;
    STRING szLogMsg;
    NUMBER nTempResult;
    INT iRet;

    begin
    nBuffer = 256;

    // Open the MSI database
    hDB = MsiGetActiveDatabase(hMsi);
    if( hDB = NULL ) then
    return ERROR_INSTALL_FAILURE;
    endif;

    // Open a view to the Feature table
    nTempResult = MsiDatabaseOpenView(hDB, "SELECT * FROM `" + REMOVEREGISTRYCONDITIONAL_TABLE_NAME + "`", hView);
    if( nTempResult != ERROR_SUCCESS ) then
    MsiCloseHandle( hDB );
    return ERROR_INSTALL_FAILURE;
    endif;

    nTempResult = MsiViewExecute(hView, NULL);
    if( nTempResult != ERROR_SUCCESS ) then
    MsiCloseHandle( hDB );
    return ERROR_INSTALL_FAILURE;
    endif;

    // Iterate through all rows of the RemoveRegistryConditional table
    while( MsiViewFetch(hView, hRecord) != ERROR_NO_MORE_ITEMS )

    MsiRecordGetString( hRecord, 1, szRemoveRegistry, nBuffer );
    nRoot = MsiRecordGetInteger( hRecord, 2 );

    MsiRecordGetString( hRecord, 3, szKey, nBuffer );
    MsiRecordGetString( hRecord, 4, szName, nBuffer );
    MsiRecordGetString( hRecord, 5, szComponent, nBuffer );
    nInstallMode = MsiRecordGetInteger( hRecord, 6 );
    MsiRecordGetString( hRecord, 7, szCondition, nBuffer );

    iRet = MsiEvaluateCondition(hMsi, szCondition);

    if ( iRet = MSICONDITION_TRUE) then
    if( ( nInstallMode & msidbRemoveRegistryOnInstall ) != 0 ) then
    nTempResult = addItemToRemoveRegistryTable( hMsi, szRemoveRegistry, nRoot, szKey, szName, szComponent );
    if( nTempResult != ERROR_SUCCESS ) then
    MsiViewClose( hView );
    return nTempResult;
    endif;
    endif;

    if( ( nInstallMode & msidbRemoveRegistryOnRemove ) != 0 ) then
    nTempResult = addItemToRegistryTable( hMsi, szRemoveRegistry, nRoot, szKey, szName, "", szComponent );
    if( nTempResult != ERROR_SUCCESS ) then
    MsiViewClose( hView );
    return nTempResult;
    endif;
    endif;
    endif;
    endwhile;

    MsiViewClose(hView);

    return ERROR_SUCCESS;
    end;


    It is the function that the InstallScript custom action calls.

    The function you might still need is addItemToRegistryTable, but you can easily extrapolate it from addItemToRemoveRegistryTable already detailed in this thread.
    0 Kudos
    Roman1
    Level 9

    Thanks!
    Thanks!
    0 Kudos
    ikotlova
    Level 4

    It is working now - Thanks MUCH, Reureu!
    0 Kudos
    Reureu
    Level 10

    No worries!
    Feel free to suggest any improvement to the source code.
    0 Kudos