chore: initial commit (extracted from Launchers monorepo)
Plugin: UniversalShellExt v1.0.0 Architectures: x64 License: MIT
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build script per UniversalShellExt64.dll
|
||||
Compila, copia, firma e registra la shell extension generica.
|
||||
Richiede Visual Studio 2022 e Windows SDK.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
import winreg
|
||||
|
||||
# =============================================================================
|
||||
# Configurazione
|
||||
# =============================================================================
|
||||
|
||||
# Percorsi
|
||||
SCRIPT_DIR = Path(__file__).parent.resolve()
|
||||
SOURCE_FILE = SCRIPT_DIR / "UniversalShellExt.cpp"
|
||||
DEF_FILE = SCRIPT_DIR / "UniversalShellExt.def"
|
||||
RC_FILE = SCRIPT_DIR / "UniversalShellExt.rc"
|
||||
RES_FILE = SCRIPT_DIR / "UniversalShellExt.res"
|
||||
|
||||
OUTPUT_DIR = SCRIPT_DIR / "bin" / "x64"
|
||||
OUTPUT_DLL = OUTPUT_DIR / "UniversalShellExt64.dll"
|
||||
|
||||
# Destinazione finale (cartella plugins)
|
||||
DEST_DLL = SCRIPT_DIR.parent.parent / "plugins" / "UniversalShellExt64.dll"
|
||||
|
||||
# Visual Studio
|
||||
VS_PATH = Path("C:/Program Files/Microsoft Visual Studio/2022/Community")
|
||||
VCVARS_BAT = VS_PATH / "VC/Auxiliary/Build/vcvars64.bat"
|
||||
|
||||
# Signtool
|
||||
SIGNTOOL = Path("C:/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe")
|
||||
CERT_THUMBPRINT = "DFF03ABA0E0156D8E7823A893F9588530A364410"
|
||||
TIMESTAMP_URL = "http://timestamp.digicert.com"
|
||||
|
||||
# Librerie
|
||||
LIBS = [
|
||||
"ole32.lib",
|
||||
"uuid.lib",
|
||||
"shell32.lib",
|
||||
"user32.lib",
|
||||
"gdi32.lib",
|
||||
"advapi32.lib",
|
||||
"shlwapi.lib"
|
||||
]
|
||||
|
||||
CLSID_STRING = "{5D849F86-F2A9-41FA-8A24-E01CD6D6696C}"
|
||||
|
||||
def run_command(cmd, shell=False, check=True):
|
||||
print(f" > {cmd if isinstance(cmd, str) else ' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, shell=shell, capture_output=True, text=True)
|
||||
if result.stdout: print(result.stdout)
|
||||
if result.stderr: print(result.stderr)
|
||||
if check and result.returncode != 0:
|
||||
print(f"ERRORE: Comando fallito con codice {result.returncode}")
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
def check_prerequisites():
|
||||
print("\n=== Verifica prerequisiti ===")
|
||||
errors = []
|
||||
if not SOURCE_FILE.exists(): errors.append(f"Manca sorgente: {SOURCE_FILE}")
|
||||
if not DEF_FILE.exists(): errors.append(f"Manca DEF: {DEF_FILE}")
|
||||
if not VCVARS_BAT.exists(): errors.append(f"Manca VS: {VCVARS_BAT}")
|
||||
if not SIGNTOOL.exists(): errors.append(f"Manca Signtool: {SIGNTOOL}")
|
||||
if errors:
|
||||
for e in errors: print(f" ERRORE: {e}")
|
||||
sys.exit(1)
|
||||
print(" Prerequisiti OK")
|
||||
|
||||
def update_version():
|
||||
print("\n=== Incremento Versione ===")
|
||||
import re
|
||||
|
||||
if not RC_FILE.exists():
|
||||
print(" ERRORE: .rc non trovato!")
|
||||
return
|
||||
|
||||
content = RC_FILE.read_text(encoding="utf-8")
|
||||
|
||||
# Cerca VER_FILEVERSION 1,0,0,X
|
||||
# Regex per catturare i primi 3 numeri e l'ultimo
|
||||
pattern = r'(#define VER_FILEVERSION\s+\d+,\d+,\d+,)(\d+)'
|
||||
match = re.search(pattern, content)
|
||||
|
||||
if match:
|
||||
prefix = match.group(1)
|
||||
build_num = int(match.group(2))
|
||||
new_build_num = build_num + 1
|
||||
|
||||
# Sostituisci VER_FILEVERSION
|
||||
new_content = re.sub(pattern, f'{prefix}{new_build_num}', content)
|
||||
|
||||
# Aggiorna anche VER_FILEVERSION_STR "1.0.0.X\0"
|
||||
str_pattern = r'(#define VER_FILEVERSION_STR\s+"[^"]*?)(\d+)(\\0")'
|
||||
# Nota: questa regex assume che la stringa finisca col numero da incrementare.
|
||||
# Se è "1.0.0.0\0", cattura "1.0.0." come gruppo 1, "0" come 2.
|
||||
|
||||
# Miglior approccio: ricostruire la stringa in base al nuovo numero
|
||||
# Ma dobbiamo parsare i primi 3 numeri per essere sicuri
|
||||
v_parts = prefix.strip().replace("#define VER_FILEVERSION", "").strip().split(',')
|
||||
if len(v_parts) >= 3:
|
||||
major, minor, patch = v_parts[0], v_parts[1], v_parts[2]
|
||||
new_ver_str = f'{major}.{minor}.{patch}.{new_build_num}'
|
||||
|
||||
# Sostituisci la stringa intera con gestione corretta di virgolette e backslash
|
||||
# La regex deve catturare tutto fino ai doppi apici finali
|
||||
new_content = re.sub(r'(#define VER_FILEVERSION_STR\s+)"(.*?)"', f'#define VER_FILEVERSION_STR "{new_ver_str}\\\\0"', new_content)
|
||||
|
||||
# Aggiorna anche VER_PRODUCTVERSION (spesso uguale)
|
||||
new_content = re.sub(r'(#define VER_PRODUCTVERSION\s+).*', f'#define VER_PRODUCTVERSION {major},{minor},{patch},{new_build_num}', new_content)
|
||||
|
||||
# Aggiorna VER_PRODUCTVERSION_STR (spesso solo 3 cifre, ma qui mettiamo 4 per coerenza o lasciamo stare?)
|
||||
# Nel file attuale è "1.0.0\0". Se vogliamo incrementare anche quello:
|
||||
# new_content = re.sub(r'#define VER_PRODUCTVERSION_STR\s+".*?"', f'#define VER_PRODUCTVERSION_STR "{major}.{minor}.{patch}\\0"', new_content)
|
||||
|
||||
RC_FILE.write_text(new_content, encoding="utf-8")
|
||||
print(f" Versione aggiornata a: {new_ver_str}")
|
||||
else:
|
||||
print(" Non riesco a parsare la versione corrente.")
|
||||
else:
|
||||
print(" Pattern versione non trovato.")
|
||||
|
||||
def clean_build():
|
||||
print("\n=== Clean build ===")
|
||||
to_remove = [
|
||||
SCRIPT_DIR / "bin",
|
||||
SCRIPT_DIR / "Release",
|
||||
SCRIPT_DIR / "Debug",
|
||||
SCRIPT_DIR / "x64",
|
||||
SCRIPT_DIR / "UniversalShellExt64.dll",
|
||||
SCRIPT_DIR / "UniversalShellExt64.exp",
|
||||
SCRIPT_DIR / "UniversalShellExt64.lib",
|
||||
SCRIPT_DIR / "UniversalShellExt64.pdb",
|
||||
SCRIPT_DIR / "UniversalShellExt.obj",
|
||||
RES_FILE,
|
||||
]
|
||||
for path in to_remove:
|
||||
if path.exists():
|
||||
if path.is_dir():
|
||||
try: shutil.rmtree(path)
|
||||
except: pass
|
||||
else:
|
||||
try: path.unlink()
|
||||
except: pass
|
||||
print(" Clean completato")
|
||||
|
||||
def kill_explorer():
|
||||
print("\n=== Termina Explorer ===")
|
||||
subprocess.run(["taskkill", "/F", "/IM", "explorer.exe"], capture_output=True, check=False)
|
||||
import time
|
||||
time.sleep(2)
|
||||
|
||||
def start_explorer():
|
||||
print("\n=== Riavvia Explorer ===")
|
||||
subprocess.Popen(["explorer.exe"])
|
||||
import time
|
||||
time.sleep(2)
|
||||
|
||||
def unregister_dll():
|
||||
print("\n=== Deregistra DLL ===")
|
||||
if DEST_DLL.exists():
|
||||
run_command(["regsvr32", "/u", "/s", str(DEST_DLL)], check=False)
|
||||
print(" DLL deregistrata")
|
||||
else:
|
||||
print(" DLL non trovata, skip")
|
||||
|
||||
def compile_dll():
|
||||
print("\n=== Compilazione DLL ===")
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Compile Resource
|
||||
rc_cmd = f'''call "{VCVARS_BAT}" && cd /D "{SCRIPT_DIR}" && rc.exe /fo"{RES_FILE.name}" "{RC_FILE.name}"'''
|
||||
subprocess.run(f'cmd /c "{rc_cmd}"', shell=True, check=True)
|
||||
|
||||
# Compile DLL
|
||||
libs_str = " ".join(LIBS)
|
||||
compile_cmd = f'''call "{VCVARS_BAT}" && cd /D "{SCRIPT_DIR}" && cl.exe /LD /EHsc /O2 /DUNICODE /D_UNICODE "{SOURCE_FILE.name}" "{RES_FILE.name}" /Fe:"{OUTPUT_DLL}" /link /DEF:"{DEF_FILE.name}" {libs_str}'''
|
||||
|
||||
result = subprocess.run(f'cmd /c "{compile_cmd}"', shell=True, capture_output=True, text=True)
|
||||
if result.stdout:
|
||||
for line in result.stdout.split('\n'):
|
||||
if line.strip() and not line.startswith('**') and not line.startswith('['):
|
||||
print(f" {line}")
|
||||
if result.returncode != 0 or not OUTPUT_DLL.exists():
|
||||
print("ERRORE: Compilazione fallita")
|
||||
sys.exit(1)
|
||||
|
||||
print(f" Compilata: {OUTPUT_DLL}")
|
||||
|
||||
def copy_dll():
|
||||
print("\n=== Copia DLL ===")
|
||||
shutil.copy2(OUTPUT_DLL, DEST_DLL)
|
||||
print(f" Copiata in: {DEST_DLL}")
|
||||
|
||||
def sign_dll():
|
||||
print("\n=== Firma DLL ===")
|
||||
cmd = [str(SIGNTOOL), "sign", "/sha1", CERT_THUMBPRINT, "/fd", "SHA256", "/t", TIMESTAMP_URL, str(DEST_DLL)]
|
||||
run_command(cmd)
|
||||
print(" Firma OK")
|
||||
|
||||
def register_dll():
|
||||
print("\n=== Registra DLL ===")
|
||||
result = run_command(["regsvr32", "/s", str(DEST_DLL)], check=False)
|
||||
if result.returncode == 0:
|
||||
print(" Registrazione OK")
|
||||
else:
|
||||
print(f" FALLITA: {result.returncode} (Serve Admin?)")
|
||||
|
||||
def verify_registration():
|
||||
print("\n=== Verifica Registrazione ===")
|
||||
try:
|
||||
key_path = fr"SOFTWARE\Classes\CLSID\{CLSID_STRING}\InprocServer32"
|
||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "")
|
||||
print(f" CLSID registrato: {val}")
|
||||
except FileNotFoundError:
|
||||
print(" ERRORE: CLSID non trovato")
|
||||
return False
|
||||
|
||||
try:
|
||||
key_path = r"SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\UniversalShellExt"
|
||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
|
||||
val, _ = winreg.QueryValueEx(key, "")
|
||||
print(f" Handler directory registrato: {val}")
|
||||
except FileNotFoundError:
|
||||
print(" ERRORE: Handler non trovato")
|
||||
return False
|
||||
return True
|
||||
|
||||
def main():
|
||||
print("UniversalShellExt Builder")
|
||||
args = sys.argv[1:]
|
||||
|
||||
if "--clean" in args:
|
||||
clean_build()
|
||||
return
|
||||
|
||||
if "--unregister" in args:
|
||||
unregister_dll()
|
||||
return
|
||||
|
||||
# STEP 0: Clean pre-build
|
||||
clean_build()
|
||||
|
||||
update_version()
|
||||
check_prerequisites()
|
||||
unregister_dll()
|
||||
kill_explorer()
|
||||
compile_dll()
|
||||
copy_dll()
|
||||
|
||||
if "--no-sign" not in args:
|
||||
sign_dll()
|
||||
|
||||
start_explorer()
|
||||
register_dll()
|
||||
verify_registration()
|
||||
clean_build()
|
||||
|
||||
print("\nDONE!")
|
||||
print(f"DLL: {DEST_DLL}")
|
||||
print("Ricorda di configurare HKLM\\Software\\UniversalShellExt")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user