# Probe Ample GUI render with current bridge exe. import ctypes, ctypes.wintypes as w, struct, subprocess, sys, os, time, re sys.stdout.reconfigure(encoding='utf-8', errors='replace') SHM_NAME = "SonicForge_DAW_IPC_GUIPROBE2" SIZE = 32768 BRIDGE = sys.argv[1] if len(sys.argv) > 1 else r"C:\Users\locpham\SonicForgeStudio\native_bridge\build\Release\daw_vst_bridge.exe" LOG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "probe_gui2.log") PLUGIN = sys.argv[2] if len(sys.argv) > 2 else r"C:\Program Files\Common Files\VST3\Ample Sound\ACDD.vst3" PID_TAG = sys.argv[3] if len(sys.argv) > 3 else "ample" SHOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), f"probe_gui2_{PID_TAG}.png") user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 kernel32.CreateFileMappingA.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_char_p] kernel32.CreateFileMappingA.restype = ctypes.c_void_p kernel32.MapViewOfFile.argtypes = [ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_size_t] kernel32.MapViewOfFile.restype = ctypes.c_void_p INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value PAGE_READWRITE = 0x04 FILE_MAP_ALL_ACCESS = 0xF001F hMap = kernel32.CreateFileMappingA(INVALID_HANDLE_VALUE, None, PAGE_READWRITE, 0, SIZE, SHM_NAME.encode()) ptr = kernel32.MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0) def w32(off, fmt, *args): ctypes.memmove(ptr + off, struct.pack(fmt, *args), struct.calcsize(fmt)) def write_control(type_, arg0, arg1, ch, path): for i in range(8): off = 2836 + i * 1040 t = ctypes.c_uint32.from_address(ptr + off).value if t == 0: w32(off, 'IIII', type_, arg0, arg1, ch) if path: b = path.encode('utf-8')[:1023] + b'\x00' ctypes.memmove(ptr + off + 16, b, len(b)) return i raise RuntimeError("control queue full") def push_controls(events): for i in range(8): w32(2836 + i * 1040, 'I', 0) for (type_, arg0, arg1, ch, path) in events: write_control(type_, arg0, arg1, ch, path) w32(11156, 'I', len(events)) proc = subprocess.Popen([BRIDGE, "--shm", SHM_NAME], env={**os.environ, "SF_SHM_NAME": SHM_NAME, "SF_PARENT_PID": str(os.getpid()), "SF_SAMPLE_RATE": "48000", "SF_BLOCK_SIZE": "256"}, stdout=open(LOG, 'wb'), stderr=subprocess.STDOUT) time.sleep(1.5) def wait_log(pattern, timeout, since=0): t0 = time.time() while time.time() - t0 < timeout: try: with open(LOG, 'r', encoding='utf-8', errors='replace') as f: lines = f.readlines() for i in range(since, len(lines)): if pattern in lines[i]: return lines[i].strip(), i + 1 except FileNotFoundError: pass time.sleep(0.2) return None, 0 push_controls([(2, 0, 0, 5, PLUGIN)]) r, pos = wait_log("instrument loaded ch=5", 90) print("[load]", r) push_controls([(4, 0, 0, 5, PID_TAG)]) r, pos = wait_log("GUI attached", 40) print("[open]", r) hwnd = None if r: m = re.search(r'hwnd=([0-9A-Fa-f]+)', r) if m: hwnd = int(m.group(1), 16) print("hwnd =", hex(hwnd) if hwnd else None) time.sleep(25) # let the editor paint def dump_tree(hwnd, depth=0): out = [] buf = ctypes.create_unicode_buffer(256) user32.GetClassNameW(hwnd, buf, 256) cls = buf.value user32.GetWindowTextW(hwnd, buf, 256) title = buf.value r = w.RECT() user32.GetWindowRect(hwnd, ctypes.byref(r)) vis = user32.IsWindowVisible(hwnd) out.append(f"{' '*depth}h={hwnd:08X} cls='{cls}' title='{title}' rect=({r.left},{r.top},{r.right},{r.bottom}) vis={vis}") children = [] @ctypes.WINFUNCTYPE(w.BOOL, w.HWND, w.LPARAM) def cb(child, lp): children.append(child) return True user32.EnumChildWindows(hwnd, cb, 0) for c in children: out.extend(dump_tree(c, depth + 1)) return out print("--- window tree ---") if hwnd: for ln in dump_tree(hwnd): print(ln) if hwnd: r = w.RECT() user32.GetWindowRect(hwnd, ctypes.byref(r)) wdt, hgt = r.right - r.left, r.bottom - r.top ps = f""" Add-Type -AssemblyName System.Drawing $bmp = New-Object System.Drawing.Bitmap({wdt}, {hgt}) $g = [System.Drawing.Graphics]::FromImage($bmp) $g.CopyFromScreen({r.left}, {r.top}, 0, 0, $bmp.Size) $bmp.Save('{SHOT}') $g.Dispose(); $bmp.Dispose() """ subprocess.run(["powershell", "-NoProfile", "-Command", ps], check=False) print("screenshot saved", SHOT) kernel32.UnmapViewOfFile.argtypes = [ctypes.c_void_p] kernel32.CloseHandle.argtypes = [ctypes.c_void_p] kernel32.UnmapViewOfFile(ptr) kernel32.CloseHandle(hMap) try: proc.terminate() except Exception: pass time.sleep(0.5) print("done")