# waitscan for gui_probe bridge_exact11 (PASS case): dump threads while the # probe is attached, to compare against the hung bridge. import ctypes, ctypes.wintypes as w, struct, subprocess, sys, os, time sys.stdout.reconfigure(encoding='utf-8', errors='replace') LOG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "waitscan_probe.log") PROBE = r"C:\Users\locpham\SonicForgeStudio\native_bridge\build\Release\gui_probe.exe" PATH = r"C:\Program Files\Common Files\VST3\Nexus.vst3" def wait_log(pattern, timeout): end = time.time() + timeout while time.time() < end: try: with open(LOG, 'r', encoding='utf-8', errors='replace') as f: if pattern in f.read(): return True except FileNotFoundError: pass time.sleep(0.2) return False proc = subprocess.Popen([PROBE, PATH, "bridge_exact11", "60"], stdout=open(LOG, 'wb'), stderr=subprocess.STDOUT) pid = proc.pid print("pid", pid) if not wait_log("isPlatformTypeSupported=0", 120): print("TIMEOUT"); sys.exit(1) time.sleep(3) k32 = ctypes.windll.kernel32 PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 PROCESS_DUP_HANDLE = 0x0040 TH32CS_SNAPTHREAD = 0x4 THREAD_GET_CONTEXT = 0x0008 THREAD_QUERY_INFORMATION = 0x0040 THREAD_SUSPEND_RESUME = 0x0002 k32.OpenProcess.argtypes = [w.DWORD, w.BOOL, w.DWORD] k32.OpenProcess.restype = ctypes.c_void_p hProc = k32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE, False, pid) if not hProc: print("OpenProcess failed", ctypes.get_last_error()); sys.exit(1) psapi = ctypes.windll.psapi class MODULEINFO(ctypes.Structure): _fields_ = [("lpBaseOfDll", ctypes.c_void_p), ("SizeOfImage", w.DWORD), ("pad", w.DWORD), ("EntryPoint", ctypes.c_void_p)] psapi.EnumProcessModulesEx.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p), w.DWORD, ctypes.POINTER(w.DWORD), w.DWORD] psapi.GetModuleInformation.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(MODULEINFO), w.DWORD] psapi.GetModuleBaseNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, w.DWORD] nmods = w.DWORD(0) psapi.EnumProcessModulesEx(hProc, None, 0, ctypes.byref(nmods), 3) arr = (ctypes.c_void_p * (nmods.value // 8))() psapi.EnumProcessModulesEx(hProc, arr, nmods.value, ctypes.byref(nmods), 3) mods = [] for m in arr: if not m: continue mi = MODULEINFO() psapi.GetModuleInformation(hProc, m, ctypes.byref(mi), ctypes.sizeof(MODULEINFO)) name = ctypes.create_string_buffer(260) psapi.GetModuleBaseNameA(hProc, m, name, 260) mods.append((mi.lpBaseOfDll, mi.SizeOfImage, name.value.decode('latin1'))) def resolve(addr): for base, size, name in mods: if base <= addr < base + size: return f"{name}+0x{addr-base:x}" return f"0x{addr:x}" class THREADENTRY32(ctypes.Structure): _fields_ = [("dwSize", w.DWORD), ("cntUsage", w.DWORD), ("th32ThreadID", w.DWORD), ("th32OwnerProcessID", w.DWORD), ("tpBasePri", w.LONG), ("tpDeltaPri", w.LONG), ("dwFlags", w.DWORD)] te = THREADENTRY32() te.dwSize = ctypes.sizeof(THREADENTRY32) snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) ok = k32.Thread32First(snap, ctypes.byref(te)) threads = [] while ok: if te.th32OwnerProcessID == pid: threads.append(te.th32ThreadID) ok = k32.Thread32Next(snap, ctypes.byref(te)) k32.CloseHandle(snap) print(f"threads: {threads}") class CONTEXT(ctypes.Structure): _fields_ = [("raw", ctypes.c_ubyte * 0x4d0)] CONTEXT_AMD64 = 0x00100000 CONTEXT_FULL = CONTEXT_AMD64 | 0x3 def ctx_regs(ctx): b = bytes(ctx.raw) u = lambda o: struct.unpack_from('