https://www.programmersought.com/article/74726500719/
IconOverlay implemented by Python, providing dynamic icon switching and refresh support
Development Platform- windows10
- python3.8
- pywin32
background knowledgeIcon overlay concept:Controversy over IconOverlay in Windows
Widows implementation:How to overlay an icon over existing shell objects
Python implementation:Add my own icon overlays
SHChangeNotify:SHChangeNotify Problems with existing articlesinAdd my own icon overlaysThe author provides the code to implement the icon overlay function under python, but there are two problems - Does not explain how to realize the judgment and reality of different overlay icons under different conditions when there are multiple icons
- There is no illustration on how to dynamically implement icon overlay switching and refreshing. For example, the icon overlay status in the current file explorer is in synchronization, how to use application code to control the icon overlay to the synchronization completion, and actively refresh the icon display in the user file resource browser Without requiring the user to manually refresh
solutionMultiple icon overlays to realize condition judgmentCreate multiple IconOverlay classes and register them at the same time, one IconOverlay class corresponds to one icon
Note that multiple IconOverlay classes must be differentreg_clsid Properties and thenGetOverlayInfo Different icons are returned in the method, and then inIsMemberOf The method returns whether to display the icon according to different conditions
My implementation is inIsMemberOf In the method, a file status table of the sqlite database is queried, and according to the file path in the table and the corresponding file status, it is determined which icon is displayed for the file - import os
- import winreg
- import sqlite3
- import winerror
- import win32con
- // Pay attention to the import path here, the path of the old version of pywin32 is different
- from win32comext.shell import shell, shellcon
- class IconOverlayOk:
- """
- Icon overlay component
- The IShellIconOverlayIdentifier interface under the windows shell is used to implement the icon overlay function
- The following steps are required to implement functions in python
- 1. Use pywin32 to write python class to implement windows com components
- 2. Register the component to the windows system
- 3. Add the icon overlay component item in the registry
- 4. Restart explorer.exe
- When the windows file explorer browses the file
- 1. Explorer will call the IsMemberOf method of IShellIconOverlayIdentifier for each file currently to be displayed to determine whether the file needs to be processed
- 2. If IsMemberOf returns winerror.S_OK, the system will continue to call the GetOverlayInfo method to obtain the icon file to be superimposed
- 3. The operating system is responsible for superimposing the icon returned by GetOverlayInfo onto the original icon of the file
- _reg_remove_keys_: When uninstalling the component, the registry keys that need to be deleted at the same time. Here, add a space prefix to the last registry key name to ensure the priority of the icon overlay component and avoid other icon overlay programs such as oneDriver, Nut Cloud Unsuccessful competition causes the icon overlay to fail
- """
- _reg_clsid_ = '{EA258179-A91D-45F9-A237-FC92A5290423}'
- _reg_progid_ = 'SUN.PythonPackagesOverlayHandlerOK'
- _reg_desc_ = 'Icon Overlay Handler to indicate Python packages'
- _public_methods_ = ['GetOverlayInfo', 'GetPriority', 'IsMemberOf']
- _com_interfaces_ = [shell.IID_IShellIconOverlayIdentifier]
- _reg_remove_keys_ = [(r'Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\ PyPackageOverlay1',
- win32con.HKEY_LOCAL_MACHINE)]
- def GetOverlayInfo(self):
- """
- Get the location of the icon file to be superimposed
- Obtain the icon file path iconPath from the registry
- :return:
- """
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Dachengyun')
- iconPath, index = winreg.QueryValueEx(key, 'iconPath')
- key.Close()
- return os.path.join(iconPath, 'Ok.ico'), 0, shellcon.ISIOI_ICONFILE
- def GetPriority(self):
- return 1
- def IsMemberOf(self, fname, attributes):
- """
- Determine whether to overwrite the file icon
- Read the synchronization folder path syncPath, sqlite database file path dbPath from the registry
- If the current file is under syncPath, go to db to query the file synchronization status
- Determine whether icon overlay is needed according to the file synchronization status
- :param fname:
- :param attributes:
- :return:
- """
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Dachengyun')
- syncPath, index = winreg.QueryValueEx(key, 'syncPath')
- dbPath, index = winreg.QueryValueEx(key, 'dbPath')
- key.Close()
- if fname.startswith(syncPath):
- filePath = fname[len(os.path.dirname(syncPath)):]
- conn = sqlite3.connect(dbPath)
- c = conn.cursor()
- c.execute('SELECT state FROM file_sync_state WHERE path=:filePath', {'filePath': filePath})
- r = c.fetchone()
- c.close()
- conn.close()
- if r is not None and r[0] == 1:
- return winerror.S_OK
- return winerror.E_FAIL
- class IconOverlaySync:
- _reg_clsid_ = '{EA258179-A92D-45F9-A237-FC92A5290423}'
- _reg_progid_ = 'SUN.PythonPackagesOverlayHandlerSync'
- _reg_desc_ = 'Icon Overlay Handler to indicate Python packages'
- _public_methods_ = ['GetOverlayInfo', 'GetPriority', 'IsMemberOf']
- _com_interfaces_ = [shell.IID_IShellIconOverlayIdentifier]
- _reg_remove_keys_ = [(r'Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\ PyPackageOverlay2',
- win32con.HKEY_LOCAL_MACHINE)]
- def GetOverlayInfo(self):
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Dachengyun')
- iconPath, index = winreg.QueryValueEx(key, 'iconPath')
- key.Close()
- return os.path.join(iconPath, 'Sync.ico'), 0, shellcon.ISIOI_ICONFILE
- def GetPriority(self):
- return 2
- def IsMemberOf(self, fname, attributes):
- key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'Software\Dachengyun')
- syncPath, index = winreg.QueryValueEx(key, 'syncPath')
- dbPath, index = winreg.QueryValueEx(key, 'dbPath')
- key.Close()
- if fname.startswith(syncPath):
- filePath = fname[len(os.path.dirname(syncPath)):]
- conn = sqlite3.connect(dbPath)
- c = conn.cursor()
- c.execute('SELECT state FROM file_sync_state WHERE path=:filePath',
- {'filePath': filePath})
- r = c.fetchone()
- c.close()
- conn.close()
- if r is not None and r[0] == 2:
- return winerror.S_OK
- return winerror.E_FAIL
复制代码 Dynamic icon switching and refreshThe dynamic switching of icons only needs to modify the file status table data in the sqlite database in the external application code.
But this can only achieve the switching of icon overlays, not dynamic refresh; that is, when the user browses the file in the file explorer, if the current icon overlay file is being displayed, even if we re If the file status is changed in the database, the icon overlay will not be dynamically refreshed. You must manually refresh the file explorer or exit the folder and re-enter to see the changed overlay icon
The solution is to useSHChangeNotify To notify the operating system that the file has changed, so that the operating system does an active refresh - // Pay attention to the introduction here, the introduction path of the old version of pywin32 seems to be different, which is prone to errors
- from win32comext.shell import shell, shellcon
- // syncPath is the absolute path of the file to be refreshed
- shell.SHChangeNotify(shellcon.SHCNE_ATTRIBUTES,
- shellcon.SHCNF_PATH | shellcon.SHCNF_FLUSHNOWAIT,
- bytes(syncPath, 'gbk'), None)
复制代码 Component registration uninstallation and explorer.exe restartAfter the IconOverlay class is written, it needs to be registered to the windows system. The code is as follows - import os
- import win32api
- import win32con
- from win32com.server import register
- import iconOverlay
- // Multiple icons need to copy the following code, and register multiple classes after modifying the class name
- register.UseCommandLine(iconOverlay.IconOverlayOk)
- win32api.RegSetValue(win32api.RegCreateKey(iconOverlay.IconOverlayOk._reg_remove_keys_[0][1],iconOverlay.IconOverlayOk._reg_remove_keys_[0][0]),
- None, win32con.REG_SZ,
- iconOverlay.IconOverlayOk._reg_clsid_)
- register.UseCommandLine(iconOverlay.IconOverlaySync)
- win32api.RegSetValue(win32api.RegCreateKey(iconOverlay.IconOverlaySync._reg_remove_keys_[0][1],iconOverlay.IconOverlaySync._reg_remove_keys_[0][0]),
- None, win32con.REG_SZ,
- iconOverlay.IconOverlaySync._reg_clsid_)
- // Explorer needs to be restarted after component registration is complete.exe makes the component effective
- os.system('taskkill /f /im explorer.exe & start explorer.exe')
复制代码 Component uninstall
- import win32com.server.register
- win32com.server.register.UnregisterClasses(iconOverlay.IconOverlayOk)
- win32com.server.register.UnregisterClasses(iconOverlay.IconOverlaySync)
- os.system('taskkill /f /im explorer.exe & start explorer.exe')
复制代码
|