From 3615f2bd88ee3fa12463dba6c6f05ba0041de403 Mon Sep 17 00:00:00 2001 From: Akiba So Date: Sun, 21 Jun 2026 16:19:51 +0800 Subject: [PATCH] fix(windows): don't crash on startup from OCR apartment The app showed no window because RegisterOcrChannel ran winrt::init_apartment() (defaults to MTA) on the Flutter platform thread, which main.cpp already put in an STA via CoInitializeEx. That throws RPC_E_CHANGED_MODE and kills the app before the window appears. Remove that call; run the WinRT OCR on a dedicated MTA worker thread per recognize call instead, joining before returning the result. --- windows/runner/ocr_channel.cpp | 40 ++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/windows/runner/ocr_channel.cpp b/windows/runner/ocr_channel.cpp index 2a44954..d522572 100644 --- a/windows/runner/ocr_channel.cpp +++ b/windows/runner/ocr_channel.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -70,8 +71,12 @@ std::unique_ptr> g_ocr_channel; } // namespace void RegisterOcrChannel(flutter::FlutterEngine* engine) { - winrt::init_apartment(); - + // NOTE: do NOT call winrt::init_apartment() here. This runs on the Flutter + // platform thread, which main.cpp has already initialized as an STA via + // CoInitializeEx(COINIT_APARTMENTTHREADED). Calling init_apartment() (which + // defaults to multi-threaded) would throw RPC_E_CHANGED_MODE and crash the + // app before the window appears. The WinRT OCR work runs on its own + // dedicated MTA worker thread per call instead (see below). g_ocr_channel = std::make_unique>( engine->messenger(), "badnote/ocr", @@ -91,14 +96,31 @@ void RegisterOcrChannel(flutter::FlutterEngine* engine) { return; } - try { - const auto png_bytes = ExtractBytes(*call.arguments()); - const auto text = RecognizePng(png_bytes); + const auto png_bytes = ExtractBytes(*call.arguments()); + + // Run the WinRT OCR on a dedicated multi-threaded-apartment worker so + // its blocking .get() calls behave correctly and the platform thread's + // STA apartment is left untouched. We join before returning so the + // MethodResult is completed on the platform thread. + std::string text; + std::string error; + std::thread worker([&]() { + try { + winrt::init_apartment(winrt::apartment_type::multi_threaded); + text = RecognizePng(png_bytes); + winrt::uninit_apartment(); + } catch (const winrt::hresult_error& e) { + error = winrt::to_string(e.message()); + } catch (...) { + error = "Unknown OCR error"; + } + }); + worker.join(); + + if (!error.empty()) { + result->Error("ocr_failed", error); + } else { result->Success(flutter::EncodableValue(text)); - } catch (const winrt::hresult_error& error) { - result->Error("ocr_failed", winrt::to_string(error.message())); - } catch (...) { - result->Error("ocr_failed", "Unknown OCR error"); } }); }