  115|     bool stop = false;
  116| };
  117| 
  118| int main(int argc, char* argv[]) {
  119|     if (argc < 2) {
  120|         printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
  121|                "variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker | ui_thread | two_workers_close\n");
  122|         return 2;
  123|     }
  124|     std::string path = argv[1];
  125|     std::string variant = argc > 2 ? argv[2] : "main_own";
  126|     int secs = argc > 3 ? atoi(argv[3]) : 20;
  127| 
  128|     CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  129| 
  130|     // Watchdog: never let a stuck plugin hang this probe forever.
  131|     std::thread([secs]() {
  132|         Sleep((DWORD)(secs + 10) * 1000);
  133|         printf("WATCHDOG-KILL\n");
  134|         fflush(stdout);
  135|         TerminateProcess(GetCurrentProcess(), 0);
  136|     }).detach();
  137| 
  138|     HWND ownHwnd = 0;
  139|     {
  140|         WNDCLASSEX wc = {};
  141|         wc.cbSize = sizeof(wc);
  142|         wc.lpfnWndProc = DefWindowProc;
  143|         wc.hInstance = GetModuleHandle(nullptr);
  144|         wc.lpszClassName = "GuiProbeClass";
  145|         RegisterClassEx(&wc);
  146|         ownHwnd = CreateWindowEx(0, "GuiProbeClass", "GuiProbe", WS_OVERLAPPEDWINDOW,
  147|                                  0, 0, 800, 600, nullptr, nullptr, wc.hInstance, nullptr);
  148|         printf("ownHwnd=%p\n", (void*)ownHwnd);
  149|     }
  150| 
  151|     void* targetHwnd = (void*)ownHwnd;
  152|     if (argc > 4) targetHwnd = (void*)(uintptr_t)strtoull(argv[4], nullptr, 0);
  153| 
  154|     printf("loading %s\n", path.c_str());
  155|     fflush(stdout);
  156|     Vst3Instrument inst;
  157|     Vst3Instrument inst2;
  158|     bool isDeferred = (variant == "bridge_like" || variant == "same_thread" ||
  159|                        variant == "two_instances" || variant == "two_instances_close" ||
  160|                        variant == "close_reopen" || variant == "bridge_two" ||
  161|                        variant == "shared_worker" || variant == "ui_thread");
  162|     bool ok = true;
  163|     if (!isDeferred) {
  164|         ok = inst.loadPlugin(path, 44100.0);
  165|         printf("load=%d\n", ok ? 1 : 0);
  166|         fflush(stdout);
  167|         if (!ok) return 1;
  168|     }
  169| 
  170|     std::atomic<bool> done{false};
  171|     std::atomic<bool> result{false};
  172| 
  173|     auto attach_job = [&]() {
  174|         CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  175|         printf("[%s] openGUI start hwnd=%p\n", variant.c_str(), targetHwnd);
  176|         fflush(stdout);
  177|         bool r = inst.openGUI(targetHwnd);
  178|         printf("[%s] openGUI returned=%d\n", variant.c_str(), r ? 1 : 0);
  179|         fflush(stdout);
  180|         result = r;
  181|         done = true;
  182|     };
  183| 
  184|     if (variant == "main_own") {
  185|         attach_job();  // editorhost pattern: attached on main thread, pump after
  186|         pump_for(secs);