Search code examples
pythoncomtypes

Release Installer Object after using comtypes.client.CreateObject()


I wrote a function in python using the comtypes.client module, the function should supposedly open the database from a .msi file and write a special (key, value) pair. My issue so far is once the function is called with no problems, I try to use os.rename() to rename the .msi file afterwards and get a permission error:

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process

what I understand is that my COM object is still in use and so I cannot access the file, The function and function calls look like (obviously this is very simplified but should look work as such):

import comtypes.client
import os, shutil

def setInstallerAttribute(installer_path, attribute_key, attribute_value):
    installerCOM = comtypes.client.CreateObject("WindowsInstaller.Installer")
    installerDatabase = installerCOM.OpenDatabase (installer_path, 1)
    view = installerDatabase.OpenView ("INSERT INTO Property (Property, Value) VALUES ('{0}', '{1}')".format(attribute_key, attribute_value))
    view.Execute
    installerDatabase.Commit
    view = None
    installerDatabase = None
    installerCOM = None

if __name__ == "__main__":
    input = '{}'.format(msi_fullapth)
    key = "Build"
    value = "test_value"
    if os.path.exists(input):
        setInstallerAttribute(input, key, value)
        os.rename(input, {some other path})

The function is written because previously I was using a VBScript to set this (key, value) pair:

Option Explicit

Dim installer, database, view, myproperty, stdout, key

Set installer = CreateObject("WindowsInstaller.Installer")
Set database = installer.OpenDatabase (WScript.Arguments.Item(0), 1)

' Update Property'
'Set view = database.OpenView ("UPDATE Property SET Value = '" & myproperty & "' WHERE Property = 'MYPROPERTY'")'

myproperty = WScript.Arguments.Item(2)
key = WScript.Arguments.Item(1)

' Add/Insert Property'
Set view = database.OpenView ("INSERT INTO Property (Property, Value) VALUES ('" & key & "', '" & myproperty & "')")

view.Execute
database.Commit

Set database = Nothing
Set installer = Nothing
Set view = Nothing

I would call this in my python code with os.system(cscript {VBScript} {path} {Key} {Value}), however I want minimal external dependencies as possible with my python code. I was looking around for some answers, I looked into the comtypes documentation to see if I can explicitly release or "uncouple" my COM object. I tried using installerCOM.Quit() and installerCOM.Exit() which seem not be options for WindowsInstaller.Installer Objects.

Finally, I read in several previous non-python (C# mainly) answers on StackOverflow stating that setting the COM object variables to null would solve this, this is also clear from the VBScript but this does not seem to work in python with None


Solution

  • Maybe:

    import gc
    def setInstallerAttribute(installer_path, attribute_key, attribute_value):
        installerCOM = comtypes.client.CreateObject("WindowsInstaller.Installer")
        installerDatabase = installerCOM.OpenDatabase (installer_path, 1)
        view = installerDatabase.OpenView ("INSERT INTO Property (Property, Value) VALUES ('{0}', '{1}')".format(attribute_key, attribute_value))
        view.Execute
        installerDatabase.Commit
        del view
        del installerDatabase
        del installerCOM
        gc.collect()