156 lines
7.6 KiB
Plaintext
156 lines
7.6 KiB
Plaintext
==== ProbeWorker ====
|
|
34| for (HWND c = GetWindow(w, GW_CHILD); c; c = GetWindow(c, GW_HWNDNEXT))
|
|
35| ImmAssociateContext(c, nullptr);
|
|
36| }
|
|
37| }
|
|
38|
|
|
39| static void pump_for(int secs) {
|
|
40| #ifdef _WIN32
|
|
41| _set_invalid_parameter_handler(probe_invalid_param);
|
|
42| #endif
|
|
43| auto t0 = std::chrono::steady_clock::now();
|
|
44| while (std::chrono::duration_cast<std::chrono::seconds>(
|
|
45| std::chrono::steady_clock::now() - t0).count() < secs) {
|
|
46| MSG msg;
|
|
47| while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
48| printf("[pump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd);
|
|
49| fflush(stdout);
|
|
50| TranslateMessage(&msg);
|
|
51| DispatchMessageW(&msg);
|
|
52| }
|
|
53| Sleep(5);
|
|
54| }
|
|
55| }
|
|
56|
|
|
57| static void sleep_for(int secs) { Sleep((DWORD)(secs * 1000)); }
|
|
58|
|
|
59| // Per-channel persistent worker replicating the bridge (round-3 probe).
|
|
60| class ProbeWorker {
|
|
61| public:
|
|
62| void start() {
|
|
63| th = std::thread([this] {
|
|
64| #ifdef _WIN32
|
|
65| _set_invalid_parameter_handler(probe_invalid_param);
|
|
66| CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
|
67| ImmAssociateContextEx(nullptr, nullptr, IACE_DEFAULT);
|
|
68| #endif
|
|
69| std::unique_lock<std::mutex> lk(mu);
|
|
70| for (;;) {
|
|
71| if (stop && jobs.empty()) break;
|
|
72| if (!jobs.empty()) {
|
|
73| auto j = std::move(jobs.front());
|
|
74| jobs.pop_front();
|
|
75| lk.unlock();
|
|
76| try { j(); }
|
|
77| catch (const std::exception& e) { printf("[worker] JOB EXCEPTION: %s\n", e.what()); fflush(stdout); }
|
|
78| catch (...) { printf("[worker] JOB UNKNOWN EXCEPTION\n"); fflush(stdout); }
|
|
79| lk.lock();
|
|
80| continue;
|
|
81| }
|
|
82| cv.wait_for(lk, std::chrono::milliseconds(5));
|
|
83| lk.unlock();
|
|
84| try {
|
|
85| MSG msg;
|
|
86| while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
87| printf("[wpump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd);
|
|
88| fflush(stdout);
|
|
89| TranslateMessage(&msg);
|
|
90| DispatchMessageW(&msg);
|
|
91| }
|
|
92| }
|
|
93| catch (const std::exception& e) { printf("[worker] PUMP EXCEPTION: %s\n", e.what()); fflush(stdout); }
|
|
94| catch (...) { printf("[worker] PUMP UNKNOWN EXCEPTION\n"); fflush(stdout); }
|
|
95| lk.lock();
|
|
96| }
|
|
97| #ifdef _WIN32
|
|
98| CoUninitialize();
|
|
99| #endif
|
|
100| });
|
|
101| }
|
|
102| void post(std::function<void()> j) {
|
|
103| { std::lock_guard<std::mutex> lk(mu); jobs.push_back(std::move(j)); }
|
|
104| cv.notify_all();
|
|
105| }
|
|
106| std::thread th;
|
|
107| std::mutex mu;
|
|
108| std::condition_variable cv;
|
|
109| std::deque<std::function<void()>> jobs;
|
|
110| bool stop = false;
|
|
111| };
|
|
112|
|
|
113| int main(int argc, char* argv[]) {
|
|
114| if (argc < 2) {
|
|
115| printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
|
|
==== shared_worker ====
|
|
220| } else if (variant == "shared_worker" || variant == "ui_thread") {
|
|
221| // Proposed round-3 fix: ALL VST3 instances live in ONE apartment —
|
|
222| // one persistent worker thread hosts load + openGUI for every channel.
|
|
223| ProbeWorker w;
|
|
224| w.start();
|
|
225| std::atomic<bool> done{false};
|
|
226| int aRes = 0, bRes = 0;
|
|
227| w.post([&]() {
|
|
228| bool r = inst.loadPlugin(path, 44100.0);
|
|
229| printf("[shared_worker] load inst1=%d\n", r ? 1 : 0);
|
|
230| fflush(stdout);
|
|
231| if (r) {
|
|
232| HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeA", WS_OVERLAPPEDWINDOW,
|
|
233| 0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
|
234| disable_ime_contexts(h);
|
|
235| r = inst.openGUI(h);
|
|
236| disable_ime_contexts(h);
|
|
237| printf("[shared_worker] attach inst1=%d hwnd=%p\n", r ? 1 : 0, (void*)h);
|
|
238| fflush(stdout);
|
|
239| aRes = r ? 1 : 0;
|
|
240| }
|
|
241| r = inst2.loadPlugin(path, 44100.0);
|
|
242| printf("[shared_worker] load inst2=%d\n", r ? 1 : 0);
|
|
243| fflush(stdout);
|
|
244| if (r) {
|
|
245| HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeB", WS_OVERLAPPEDWINDOW,
|
|
246| 0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
|
|
247| disable_ime_contexts(h);
|
|
248| r = inst2.openGUI(h);
|
|
249| disable_ime_contexts(h);
|
|
250| printf("[shared_worker] attach inst2=%d hwnd=%p\n", r ? 1 : 0, (void*)h);
|
|
251| fflush(stdout);
|
|
252| bRes = r ? 1 : 0;
|
|
253| }
|
|
254| done = true;
|
|
255| });
|
|
256| auto t0 = std::chrono::steady_clock::now();
|
|
257| try {
|
|
258| while (!done.load() && std::chrono::duration_cast<std::chrono::seconds>(
|
|
259| std::chrono::steady_clock::now() - t0).count() < secs) {
|
|
260| MSG msg;
|
|
261| while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
262| TranslateMessage(&msg);
|
|
263| DispatchMessageW(&msg);
|
|
264| }
|
|
265| Sleep(2);
|
|
266| }
|
|
267| } catch (const std::exception& e) { printf("[main] PUMP1 EXCEPTION: %s\n", e.what()); fflush(stdout); }
|
|
268| catch (...) { printf("[main] PUMP1 UNKNOWN EXCEPTION\n"); fflush(stdout); }
|
|
269| // Stay alive 5s with BOTH editors attached and the worker pumping,
|
|
270| // while MAIN KEEPS PUMPING like the bridge audio loop: distinguishes a
|
|
271| // live crash (bad) from a stop-pumping probe artifact.
|
|
272| auto t1 = std::chrono::steady_clock::now();
|
|
273| try {
|
|
274| while (std::chrono::duration_cast<std::chrono::seconds>(
|
|
275| std::chrono::steady_clock::now() - t1).count() < 5) {
|
|
276| MSG msg;
|
|
277| while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
278| TranslateMessage(&msg);
|
|
279| DispatchMessageW(&msg);
|
|
280| }
|
|
281| Sleep(2);
|
|
282| }
|
|
283| } catch (const std::exception& e) { printf("[main] PUMP2 EXCEPTION: %s\n", e.what()); fflush(stdout); }
|
|
284| catch (...) { printf("[main] PUMP2 UNKNOWN EXCEPTION\n"); fflush(stdout); }
|
|
285| printf("RESULT: done=%d aRes=%d bRes=%d\n", done.load() ? 1 : 0, aRes, bRes);
|
|
286| return (done.load() && aRes && bRes) ? 0 : 3;
|
|
287| } else if (variant == "bridge_two") {
|
|
288| // Exact bridge architecture: per-channel persistent worker threads with
|
|
289| // own COM STA apartments and idle message pumps; inst1 loaded+attached
|
|
290| // on worker A, then inst2 loaded+attached on worker B; main pumps.
|