Skip to content

Fix single instance file forwarding on Windows - #994

Open
wudi901 wants to merge 2 commits into
Snapmaker:build_platform_engineeringfrom
wudi901:codex/fix-single-instance-window-title
Open

wudi901 wants to merge 2 commits into
Snapmaker:build_platform_engineeringfrom
wudi901:codex/fix-single-instance-window-title

Conversation

@wudi901

@wudi901 wudi901 commented Oct 12, 2026

Copy link
Copy Markdown

Description

Fixes #605.

On Windows, when "Allow only one Snapmaker Orca instance" is enabled and the app is already running, launching the executable again with a model file (double-clicking an STL/3MF, or exporting from Fusion) terminated the second process without forwarding the file path.

Root cause: EnumWindowsProc in src/slic3r/GUI/InstanceCheck.cpp searched for a main window whose title contains Snapmaker_Orca (underscore), but the Windows main frame title is set in Plater::priv::set_project_name as "<project> - Snapmaker Orca" (space). The primary window was therefore never found, WM_COPYDATA was never delivered, and the command line was dropped silently.

Fix: match the title against SLIC3R_APP_NAME instead of a hardcoded string, so the window search stays in sync with the branded title.

Screenshots/Recordings/Graphs

Not applicable (no UI change).

Tests

  • No automated coverage exists for the Windows single-instance IPC path; automated tests not run for this change.
  • Manual verification on Windows:
    1. Enable "Allow only one Snapmaker Orca instance" and launch the app.
    2. Double-click an STL/3MF file (or export from Fusion) while the app is running.
    3. The running instance is brought to the front and loads the model; the log shows win enum - found correct instance.

@zhangzhend0ng zhangzhend0ng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: impact scope & regression risk — non-blocking findings

The fix itself is verified correct: the title producer Plater.cpp:16407 sets " - Snapmaker Orca" (space) while the old matcher searched for the underscore variant; the second-instance lifecycle is unchanged (send_message's return value is ignored at InstanceCheck.cpp:361 — the wxSingleInstanceChecker mutex alone decides termination); and the WM_COPYDATA receiver (GUI_App.cpp:834) was already registered, so no new attack surface. Impact is limited to users with app/single_instance=true (default false).

Non-blocking findings in priority order (two more anchored inline on the changed line):

  1. Plater.cpp:16407 — title producer still hardcoded (one line, this PR or follow-up). The matcher now derives from SLIC3R_APP_NAME while the producer is a literal; they coincide today, and renaming either side resurrects this exact bug. Suggest SetTitle(m_project_name + " - " + SLIC3R_APP_NAME).

  2. MainFrame.cpp:240 — empty title during early startup (record only). The main frame is constructed with an empty title and only gets the app name via Plater::priv::set_project_name, so during that brief startup window a second instance still cannot forward. Pre-existing behavior, unchanged by this PR.

  3. Root-cure follow-up: sync upstream's approach. Upstream SoftFever/OrcaSlicer removed title matching entirely — their EnumWindowsProc matches class wxWindowNR + presence of Instance_Hash_Minor/Major window props + hash equality (props are set only on the real main frame). That decouples instance detection from branding strings and also eliminates the Settings-dialog over-match noted inline.

std::wstring wndTextString(wndText);
if (wndTextString.find(L"Snapmaker_Orca") != std::wstring::npos && classNameString == L"wxWindowNR") {
static const std::wstring app_name = boost::nowide::widen(SLIC3R_APP_NAME);
if (wndTextString.find(app_name) != std::wstring::npos && classNameString == L"wxWindowNR") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but worth one line in this PR: now that the window is actually found, the unconditional ShowWindow(hwnd, SW_SHOWMAXIMIZED) further down in this callback (~line 126) becomes live behavior: every second launch that forwards a file force-maximizes the running instance's window, even if the user had it restored or snapped. Upstream OrcaSlicer deliberately softened this:

// Do not alter the window state when opening a file in the existing instance.
// A minimized window still needs restoring before it can receive focus.
if (IsIconic(hwnd))
    ShowWindow(hwnd, SW_RESTORE);
SetForegroundWindow(hwnd);

Suggest adopting the same (SetForegroundWindow stays as-is).

std::wstring wndTextString(wndText);
if (wndTextString.find(L"Snapmaker_Orca") != std::wstring::npos && classNameString == L"wxWindowNR") {
static const std::wstring app_name = boost::nowide::widen(SLIC3R_APP_NAME);
if (wndTextString.find(app_name) != std::wstring::npos && classNameString == L"wxWindowNR") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, record: with the substring now matching, any top-level wxWindowNR window whose title contains the app name gets probed — including our own Settings dialog (SLIC3R_APP_NAME + " - " + _L("Settings"), MainFrame.cpp:4309). The instance-hash guard handles it correctly (GetProp returns NULL → hash 0 ≠ real instance hash → "win enum - found wrong instance" debug log and enumeration continues), so this is log noise only, no functional risk. It disappears if a follow-up adopts upstream's GetProp-based matching (no title string involved at all).

@wudi901

wudi901 commented Oct 13, 2026

Copy link
Copy Markdown
Author

Addressed both actionable findings in 2b83e3b:

  • EnumWindowsProc now restores the window only when minimized (IsIconic + SW_RESTORE) instead of unconditionally maximizing it, matching upstream behavior.
  • Plater::priv::set_project_name now builds the Windows title from SLIC3R_APP_NAME, so the producer and matcher share the same branding constant.

Thanks for the review! The Instance_Hash_*-based matching looks like the right follow-up as you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants