import os import subprocess import time import re import _winreg # dir: string -> directory where installer exist # args: string -> which includes all parameters with space delimiter def spm_install(dir, args): os.chdir(dir) argslist = args.split() try: subprocess.check_call(argslist) try: tasklist = os.popen('Taskkill /IM "QfinderPro.exe" /F').read() except: pass print 'retcode' + str(0) + 'retcode' except subprocess.CalledProcessError as e: print 'retcode' + str(e.returncode) + 'retcode' # dir: string -> directory where installer exist # args: string -> which includes all parameters with space delimiter def spm_uninstall(dir, args): # PROBLEM: QNAP Qfinder Pro's uninstall.exe is an NSIS uninstaller that # copies itself to a temp folder and relaunches the copy, then the # ORIGINAL process exits with code 0 right away -- before the real # removal has finished. subprocess.check_call sees exit code 0 and # reports success even though the app is still sitting in the registry # and Control Panel. So this version does NOT trust the exit code -- # it launches the uninstaller, then polls the registry until the # DisplayName entry is actually gone, and force-removes it if it isn't. product_name = "QNAP Qfinder Pro" uninstallkey32 = 'SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall' uninstallkey64 = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' reg_list = [ (_winreg.HKEY_LOCAL_MACHINE, uninstallkey32, _winreg.KEY_WOW64_32KEY | _winreg.KEY_ALL_ACCESS), (_winreg.HKEY_LOCAL_MACHINE, uninstallkey64, _winreg.KEY_WOW64_64KEY | _winreg.KEY_ALL_ACCESS), ] def kill_qfinder(): try: os.popen('Taskkill /IM "QfinderPro.exe" /F').read() except: pass def find_entries(): found = [] for reg_key, sub_key, access in reg_list: try: reg = _winreg.OpenKey(reg_key, sub_key, 0, access) except: continue i = 0 while True: try: key_value = _winreg.EnumKey(reg, i) except: break path = os.path.join(sub_key, key_value) try: Hkey = _winreg.OpenKey(reg_key, path, 0, access) dis_name, _ = _winreg.QueryValueEx(Hkey, 'DisplayName') if product_name.lower() in dis_name.strip().lower(): try: uninstr, _ = _winreg.QueryValueEx(Hkey, 'UninstallString') except: uninstr = None try: instloc, _ = _winreg.QueryValueEx(Hkey, 'InstallLocation') except: instloc = None found.append([dis_name.strip(), path, reg_key, access, uninstr, instloc]) except: pass i += 1 return found def force_remove(entry): dis_name, path, reg_key, access, uninstr, instloc = entry kill_qfinder() if instloc and os.path.isdir(instloc): try: import shutil shutil.rmtree(instloc, ignore_errors=True) except: pass try: _winreg.DeleteKey(reg_key, path) except: pass kill_qfinder() entries = find_entries() if not entries: # already not present -- nothing to uninstall print 'retcode' + str(0) + 'retcode' return for dis_name, path, reg_key, access, uninstr, instloc in entries: if uninstr: m = re.match(r'^\s*"([^"]+)"(.*)$', uninstr) if m: exe_path = m.group(1) rest_args = m.group(2).strip() else: parts = uninstr.strip().split(' ', 1) exe_path = parts[0] rest_args = parts[1] if len(parts) > 1 else '' cmd = [exe_path] if rest_args: cmd += rest_args.split() if "/S" not in cmd: cmd.append("/S") try: # Popen, NOT check_call -- see note above, exit code 0 # does not mean the uninstall actually finished. subprocess.Popen(cmd) except Exception as e: print 'launch_error:' + str(e) kill_qfinder() # poll registry for actual removal instead of trusting exit codes removed = False attempt = 0 while attempt < 12: time.sleep(5) kill_qfinder() if not find_entries(): removed = True break attempt += 1 if not removed: remaining = find_entries() for entry in remaining: force_remove(entry) time.sleep(2) removed = not find_entries() if removed: print 'retcode' + str(0) + 'retcode' else: print 'retcode' + str(1) + 'retcode' # dir: string -> directory where installer exist # args: string -> which includes all parameters with space delimiter def spm_update(dir, args): #It uninstalls the older version which will match with the registry unins = ["QNAP Qfinder Pro"] blacklist = [] for i in unins: blacklist.append(i) if 'PROGRAMFILES(X86)' in os.environ.keys(): uninstallkey = 'SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall' reg_list = [(_winreg.HKEY_LOCAL_MACHINE, uninstallkey, _winreg.KEY_WOW64_32KEY | _winreg.KEY_ALL_ACCESS), (_winreg.HKEY_LOCAL_MACHINE, uninstallkey, _winreg.KEY_WOW64_64KEY | _winreg.KEY_ALL_ACCESS), (_winreg.HKEY_CURRENT_USER, uninstallkey, _winreg.KEY_WOW64_32KEY | _winreg.KEY_ALL_ACCESS), (_winreg.HKEY_CURRENT_USER, uninstallkey, _winreg.KEY_WOW64_64KEY | _winreg.KEY_ALL_ACCESS)] else: uninstallkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall' reg_list = [(_winreg.HKEY_LOCAL_MACHINE, uninstallkey, _winreg.KEY_READ, _winreg.KEY_ALL_ACCESS), (_winreg.HKEY_CURRENT_USER, uninstallkey, _winreg.KEY_READ, _winreg.KEY_ALL_ACCESS)] strun = [] list = [] for i in reg_list: reg_key = i[0] sub_key = i[1] Value = i[2] reg = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, i[1], 0, i[2]) i = 0 while True: try: key_value = _winreg.EnumKey(reg, i) path = os.path.join(sub_key, key_value) Hkey = _winreg.OpenKey(reg_key, path, 0, Value) try: key, dis_name = _winreg.QueryValueEx(Hkey, 'DisplayName') inlist = [key.strip(), path, sub_key] list.append(inlist) except: pass i += 1 except: break for c in list: for i in range(0, len(blacklist)): if blacklist[i] in c[0]: RawKey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, c[1], 0, Value) (value, type) = _winreg.QueryValueEx(RawKey, "UninstallString") strun.append(value) Uninstr = re.findall(".*", value) print Uninstr unin_str = ''.join(Uninstr[0]) command = unin_str + " /S" unarglist = command.splitlines(True) unarglist2 = ''.join(unarglist) try: tasklist = os.popen('Taskkill /IM "QfinderPro.exe" /F').read() subprocess.check_call(unarglist2) # FIX: don't just sleep 30s and assume it's gone -- # this uninstaller relaunches itself as a copy and # returns before the real removal finishes, so poll # the registry for the entry to actually disappear. removed = False attempt = 0 while attempt < 12: time.sleep(5) try: tasklist = os.popen('Taskkill /IM "QfinderPro.exe" /F').read() except: pass still_there = False try: _winreg.OpenKey(reg_key, c[1], 0, Value) still_there = True except: still_there = False if not still_there: removed = True break attempt += 1 if not removed: try: _winreg.DeleteKey(reg_key, c[1]) except: pass print blacklist[i] + 'older Version are removed' except subprocess.CalledProcessError as e: print blacklist[i] + 'older Version does not exist, uninstaller returncode:' + str(e.returncode) time.sleep(5) os.chdir(dir) argslist = args.split() try: subprocess.check_call(argslist) try: tasklist = os.popen('Taskkill /IM "QfinderPro.exe" /F').read() except: pass print 'retcode' + str(0) + 'retcode' except subprocess.CalledProcessError as e: print 'retcode' + str(e.returncode) + 'retcode'