wxGrid segfault in ScrollWindow (Issue #26933)

50 views
Skip to first unread message

Dominik Gresch

unread,
Aug 27, 2026, 11:14:28 AM (7 days ago) Aug 27
to wx-...@googlegroups.com, Subscribed
greschd created an issue (wxWidgets/wxWidgets#26933)

Description

Bug description:

wxGrid crashes with a segmentation fault when scroll and overlay selection interact.

Expected vs observed behaviour:

The application crashes, but should not.

Stack trace:

# RetAddr               : Args to Child                                                           : Call Site
00 00007ffb`d11690b9     : 00007ffb`d24df460 00007ffb`d20189e8 0000022a`9c4b1080 00007ffb`d250cda0 : wxmsw333u_core_vc140_x64!wxGridSelection::GetBlockSelectionTopLeft+0x210
01 00007ffb`d1b0fcff     : 0000022a`9c4b9ff0 0000022a`9c4b9ff0 00000000`00000000 00000000`00000000 : wxmsw333u_core_vc140_x64!wxGrid::ScrollWindow+0x59
02 00007ffb`d1ec658c     : 0000022a`9c047920 00000000`00000001 00000000`00000000 00007ffb`00000000 : _core_cp313_win_amd64!PyInit__core+0x3ce9cf
03 00007ffb`d1f1c5be     : 00007ffb`d23e8da0 0000022a`9c4c2a70 00000000`00000000 8665b8f2`c318df3d : python313!PyThread_release_lock+0x8c
04 00007ffb`d1f1bd39     : 0000022a`99a70160 0000022a`9a251262 0000022a`99a70100 00000000`00000000 : python313!PyObject_Vectorcall+0x8be
05 00007ffb`d1ee5544     : 0000022a`00000001 00000000`00000007 000000a4`e7dedc00 00000000`000201ff : python313!PyObject_Vectorcall+0x39
06 00007ffb`d1ecffb0     : 0000022a`9c184530 0000022a`9c4bc7c0 0000022a`9c4bc7c0 00000000`00000002 : python313!PyEval_EvalFrameDefault+0x3444
07 00007ffb`d201be5a     : 00000000`00000000 0000022a`9a1f7989 00000000`00000000 00007ffb`d1f117a6 : python313!PyObject_CallNoArgs+0x414
08 00007ffb`d1f12793     : 0000022a`9c4e7e40 00000000`00000000 00000000`00000001 00007ffb`d250cda0 : python313!PySet_Pop+0x6e
09 00007ffb`d182d398     : 0000022a`9c4b9780 0000022a`9c4e7e40 00000000`00000001 0000022a`9c4f4050 : python313!PyObject_CallObject+0x117
0a 00007ffb`f346b508     : 0000022a`9c189e70 00000000`00000000 00000000`ffffff00 0000022a`9a2bf100 : _core_cp313_win_amd64!PyInit__core+0xec068
0b 00007ffb`f35a985f     : 0000022a`9a2bf100 00007ffc`c060d605 00000000`00000000 00007ffc`c239c0be : wxbase333u_vc140_x64!wxAppConsoleBase::CallEventHandler+0x58
0c 00007ffb`f35aa55e     : 00000000`ffffffff 00007ffb`d1788220 000000a4`e7dee160 0000022a`9a2bf3be : wxbase333u_vc140_x64!wxEvtHandler::ProcessEventIfMatchesId+0x4f
0d 00007ffb`f35aa94b     : 0000022a`9a41c480 00000000`00000000 00007ffb`d1b62e66 00000000`00000000 : wxbase333u_vc140_x64!wxEvtHandler::SearchDynamicEventTable+0x15e
0e 00007ffb`f35a97c8     : 00000000`00000000 00000000`00000000 0000022a`9a2bf100 00000000`00000000 : wxbase333u_vc140_x64!wxEvtHandler::TryHereOnly+0x2b
0f 00007ffb`d1788539     : 00000000`00351164 0000022a`9a2bf3b2 000000a4`ffff830c 00000000`00000000 : wxbase333u_vc140_x64!wxEvtHandler::ProcessEvent+0xf8
10 00007ffb`f35a8132     : 00000000`00000000 000000a4`e7dee200 00000000`00000000 00000000`00000000 : _core_cp313_win_amd64!PyInit__core+0x47209
11 00007ffb`f35aa3ed     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wxbase333u_vc140_x64!wxGetMSWDateTimeFormat+0x602
12 00007ffb`d0f8f37c     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wxbase333u_vc140_x64!wxEvtHandler::SafelyProcessEvent+0x1d
13 00007ffb`d0f30d64     : 00000000`00000000 0000022a`99d90860 00000000`00000001 000000a4`e7dee8a0 : wxmsw333u_core_vc140_x64!wxButton::SendClickEvent+0x7c
14 00007ffb`d0fa7988     : 00000000`00351164 00000000`00351164 00000000`00000000 00000000`00000000 : wxmsw333u_core_vc140_x64!wxWindow::HandleCommand+0x94
15 00007ffb`d0fa8467     : 00000000`00000000 00007ffc`c23ecd5f 00000000`00000000 00000000`00000215 : wxmsw333u_core_vc140_x64!wxFrame::HandleCommand+0x118
16 00007ffb`d0f3f862     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wxmsw333u_core_vc140_x64!wxFrame::MSWWindowProc+0x177
17 00007ffc`c060c366     : 00000000`00000000 000000a4`e7dee7b8 00000000`00000001 00000000`00000000 : wxmsw333u_core_vc140_x64!wxWndProc+0x82
18 00007ffc`c060b673     : 0000022a`9abaf080 00007ffb`d0f3f7e0 00000000`000b11d4 00000000`00000111 : user32!UserCallWinProcCheckWow+0x356
19 00007ffc`c060b424     : 00000000`000b11d4 00007ffc`c24c4300 00000000`0000830c 00000000`00351164 : user32!SendMessageWorker+0x223
1a 00007ffc`c060b20f     : 00000000`00000000 00000000`0000830c 00000000`00351164 00000000`00000111 : user32!SendMessageInternal+0x174
1b 00007ffc`8e4e0ba5     : 00000000`00000001 00000000`00000001 0000022a`99aa9390 00000000`00000001 : user32!SendMessageW+0xef
1c 00007ffc`8e4f2f8c     : 00000000`00000202 000000a4`e7dee939 0000022a`99aa9390 00000000`00000001 : comctl32!Button_ReleaseCapture+0x18d
1d 00007ffc`c060c366     : 00000000`00000000 0000022a`9a6f2c80 00000000`00000001 00000000`00000000 : comctl32!Button_WndProc+0xbdc
1e 00007ffc`c060be2c     : 0000022a`9ab6bde0 00007ffc`8e4f23b0 00000000`00351164 00000000`00351164 : user32!UserCallWinProcCheckWow+0x356
1f 00007ffb`d0f35f30     : 0000022a`9a2bf100 00000000`0009005c 00000000`00000202 0000022a`99b57640 : user32!CallWindowProcW+0x16c
20 00007ffb`d0f3a085     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wxmsw333u_core_vc140_x64!wxWindow::MSWDefWindowProc+0x40
21 00007ffb`d0f3f862     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wxmsw333u_core_vc140_x64!wxWindow::MSWWindowProc+0x45
22 00007ffc`c060c366     : 00000000`00000000 000000a4`e7deefe0 00000000`00000001 00000000`00000000 : wxmsw333u_core_vc140_x64!wxWndProc+0x82
23 00007ffc`c060a7bd     : 000000a4`e7def0d0 00007ffb`d0f3f7e0 00000000`00351164 00000000`00000000 : user32!UserCallWinProcCheckWow+0x356
24 00007ffb`d0f5f470     : 000000a4`e7def0d0 0000022a`9c183de0 000000a4`e7def0d0 00007ffb`d1788539 : user32!DispatchMessageWorker+0x1dd
25 00007ffb`d0f5ec13     : 00000000`00000000 00000000`00000000 0000022a`9c183de0 0000022a`9a5e3040 : wxmsw333u_core_vc140_x64!wxGUIEventLoop::ProcessMessage+0x2b0
26 00007ffb`f349ee23     : 0000022a`9c183de0 0000022a`9c183de0 0000a359`7e97d2ba 0000022a`9c183de0 : wxmsw333u_core_vc140_x64!wxGUIEventLoop::Dispatch+0x1c3
27 00007ffb`f349eada     : 000000a4`e7def1a8 00000000`00000000 00007ffb`d250cda0 00000000`00000008 : wxbase333u_vc140_x64!wxEventLoopManual::DoRunLoop+0x83
28 00007ffb`f349ed82     : 0000022a`9a5e3040 000000a4`e7def1d8 00000000`00000000 00007ffb`d19da513 : wxbase333u_vc140_x64!wxEncodingConverter::Init+0x8da
29 00007ffb`f349f0aa     : 00000000`00000000 000000a4`e7def1d0 0000022a`9a1f7740 0000022a`9c183de0 : wxbase333u_vc140_x64!wxEventLoopManual::DoRun+0x62
2a 00007ffb`f346d3ac     : 00000000`00000000 00000000`00000001 0000022a`9a1f78f0 00000000`00000000 : wxbase333u_vc140_x64!wxEventLoopBase::Run+0x6a
2b 00007ffb`d19d9e4e     : 00000000`00000001 00000000`00000000 0000022a`9c4c1fd0 0000022a`9a5e3040 : wxbase333u_vc140_x64!wxAppConsoleBase::MainLoop+0x5c
2c 00007ffb`d19d86f7     : 00000000`00000001 00007ffb`d1ed4256 00000000`00000001 000000a4`e7def310 : _core_cp313_win_amd64!PyInit__core+0x298b1e
2d 00007ffb`d1ec65be     : 0000022a`9c046de0 00000000`00000000 0000022a`9a1f7740 00000000`00000000 : _core_cp313_win_amd64!PyInit__core+0x2973c7
2e 00007ffb`d1f1c5be     : 00007ffc`a9c39be0 0000022a`9c4c1fd0 00000000`00000000 00007ffb`d1edc047 : python313!PyThread_release_lock+0xbe
2f 00007ffb`d1f1bd39     : 0000022a`99a700f0 0000022a`9a37d2fc 0000022a`99a70090 00000000`00000000 : python313!PyObject_Vectorcall+0x8be
30 00007ffb`d1ee5544     : 0000022a`9a37b3d0 00000000`00000002 000000a4`e7def540 0000022a`9a37b3d0 : python313!PyObject_Vectorcall+0x39
31 00007ffb`d1fb08b0     : 000000a4`e7def7b0 00007ffb`d250cda0 00007ffb`d250cda0 00000000`00000000 : python313!PyEval_EvalFrameDefault+0x3444
32 00007ffb`d1fb0766     : 0000022a`9a0f7e20 0000022a`9a0ffa00 0000022a`9a15cb20 00007ffb`d250cda0 : python313!PyEval_EvalCode+0x200
33 00007ffb`d1fb0b2d     : 00000000`00000000 0000022a`9a0ffa00 0000022a`99e8b6f0 00007ffb`d250cda0 : python313!PyEval_EvalCode+0xb6
34 00007ffb`d1fb0dac     : 0000022a`9a15cb20 00000000`00000000 0000022a`9a0ffa00 0000022a`9a10b1b0 : python313!PyEval_EvalCode+0x47d
35 00007ffb`d1f9ba2f     : 00000000`00000000 0000022a`99ac6560 0000022a`9a0ffa00 0000022a`9a421f98 : python313!PyEval_EvalCode+0x6fc
36 00007ffb`d1f9bce5     : 0000022a`9a0f2110 0000022a`9a10b1b0 0000022a`99ac6560 0000022a`9a0ffa00 : python313!PyErr_SetFromErrnoWithFilenameObjects+0x54b
37 00007ffb`d1f9a405     : 0000022a`9a10b1b0 00000000`00000000 00007ffb`00000000 000000a4`e7defa98 : python313!PyImport_GetMagicNumber+0x1c1
38 00007ffb`d1f9a359     : 0000022a`99ac6560 00007ffb`d218021c 0000022a`9a10b4b0 0000022a`9a10b4b0 : python313!PyUnicode_EqualToUTF8+0x19d
39 00007ffb`d1f99e8c     : 0000022a`9a10b1b0 000000a4`e7defb10 00007ffb`d24df4b8 0000000d`00000000 : python313!PyUnicode_EqualToUTF8+0xf1
3a 00007ffb`d1fa5e13     : 0000022a`99f04b80 0000022a`99f56eb0 00007ffb`d24dd628 81d176b7`01fb52e9 : python313!Py_UniversalNewlineFgetsWithSize+0xf44
3b 00007ffb`d1eede38     : 0000022a`99aa9ed0 0000022a`99f56eb0 00000000`00000000 0000022a`99ac05a0 : python313!PyPathConfig_ClearGlobal+0x44f
3c 00007ffb`d2073cd3     : 00000000`00000000 00000000`00000000 00007ff6`85f321e0 00007ff6`85f315d9 : python313!Py_RunMain+0x18
3d 00007ff6`85f31230     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : python313!Py_Main+0x2b
3e 00007ffc`c1c8ccb7     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : python+0x1230
3f 00007ffc`c240ad6c     : 00000000`00000000 00000000`00000000 000004f0`fffffb30 000004d0`fffffb30 : kernel32!BaseThreadInitThunk+0x17
40 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2c

Patch or snippet allowing to reproduce the problem:

[AI generated, verified to reproduce the crash]
The following snippet (using wxPython==4.3.1) triggers the crashing code path.

import wx
import wx.grid


class CrashFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="wxGrid overlay selection reproducer", size=(500, 300))

        self.grid = wx.grid.Grid(self)
        trigger = wx.Button(self, label="Trigger crash sequence")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer.Add(trigger, 0, wx.ALL | wx.ALIGN_RIGHT, 8)
        self.SetSizer(sizer)

        self.grid.CreateGrid(100, 10)
        self.grid.SetScrollLineX(1)
        self.grid.SetScrollLineY(1)

        for row in range(self.grid.GetNumberRows()):
            self.grid.SetCellValue(row, 0, f"Row {row}")

        trigger.Bind(wx.EVT_BUTTON, self.trigger_crash)

    def trigger_crash(self, event):
        print(f"wxPython: {wx.version()}")
        self.grid.BeginBatch()
        self.grid.SelectBlock(0, 0, 0, 0)
        print(f"wxGrid batch count before scroll: {self.grid.GetBatchCount()}")
        self.grid.ScrollWindow(0, -1)
        self.grid.EndBatch()


if __name__ == "__main__":
    app = wx.App()
    frame = CrashFrame()
    frame.Show()
    app.MainLoop()

To Reproduce:

Using the script pasted above:

  1. uv run --with=wxPython python <path_to_script>
  2. Click 'Trigger Crash Sequence'

Original sequence of interactions which caused this crash:

  1. Resize a table row
  2. Click on a cell in the resized row
  3. Scroll the table
  4. Resize the containing window

Platform and version information

  • wxWidgets version: 3.3.3
  • wxWidgets port: wxPython 4.3.1 msw (phoenix)
  • OS: Windows 11 10.0.26100 Build 26100


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933@github.com>

VZ

unread,
Aug 27, 2026, 12:05:59 PM (7 days ago) Aug 27
to wx-...@googlegroups.com, Subscribed
vadz left a comment (wxWidgets/wxWidgets#26933)

Sorry but stack trace without debug symbols is not useful, so someone would need to reproduce this in a build with debug to do anything about it.

Please make your reproduction instructions more precise, i.e.

  1. Which row to resize? By how much or at least in which direction?
  2. Which cell to click?
  3. Where to scroll?
  4. How to resize?

The more precise you are, more chances are there for somebody else to be able to debug and fix this.


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5441846051@github.com>

Dominik Gresch

unread,
Aug 27, 2026, 7:54:57 PM (7 days ago) Aug 27
to wx-...@googlegroups.com, Subscribed
greschd left a comment (wxWidgets/wxWidgets#26933)

Sorry, I should have looked at this more closely before reporting.

The "original sequence of interactions" reported above is misleading, since it only applies in the original context (application based on traitsui with custom cell editor. It's not reproducible interactively from the wxPython code above without resorting to the trigger_crash method that causes it explicitly.

Is this BeginBatch > SelectBlock > ScrollWindow > EndBatch something that should work, or is the application code what needs fixing?

As for the stack trace, I have compiled wxWidgets with debug symbols (Ubuntu 24.04, current master branch). With an adaptation of the minimal.cpp sample (see below), we get:

Thread 1 "minimal" received signal SIGSEGV, Segmentation fault.
0x00007ffff7b768fb in wxGridPrivate::SelectionShape::GetBoundingBox
    (this=0x0) at ../src/generic/gridsel.cpp:1169

#0  wxGridPrivate::SelectionShape::GetBoundingBox (this=0x0)
    at ../src/generic/gridsel.cpp:1169
#1  wxGrid::ScrollWindow (this=0x555555866760, dx=0, dy=-1, rect=0x0)
    at ../src/generic/grid.cpp:2770
    r = {x = 0, y = 0, width = 0, height = 0}
    oldSel = {x = -18960, y = 32767, width = 959958016, height = 917214354}
#2  MyFrame::OnTriggerCrash (this=0x55555589b430)
    at ../../../samples/minimal/minimal.cpp:188
#3  wxAppConsoleBase::HandleEvent (...)
    at ../src/common/appbase.cpp:681
#4  wxAppConsoleBase::CallEventHandler (...)
    at ../src/common/appbase.cpp:693
#5  wxEvtHandler::ProcessEventIfMatchesId (...)
    at ../src/common/event.cpp:1474
#6  wxEvtHandler::SearchDynamicEventTable (...)
    at ../src/common/event.cpp:1934
#7  wxEvtHandler::TryHereOnly (...)
    at ../src/common/event.cpp:1657
#8  wxEvtHandler::TryBeforeAndHere (...)
    at ../include/wx/event.h:4128
#9  wxEvtHandler::ProcessEventLocally (...)
    at ../src/common/event.cpp:1594
#10 wxEvtHandler::ProcessEvent (...)
    at ../src/common/event.cpp:1567
#11 operator() (...)
    at ../src/common/event.cpp:1681
#12 wxSafeCall<bool, ...>(...)
    at ../include/wx/private/safecall.h:40
#13 wxEvtHandler::SafelyProcessEvent (...)
    at ../src/common/event.cpp:1679
#14 wxWindowBase::HandleWindowEvent (...)
    at ../src/common/wincmn.cpp:1534
#15 wxgtk_button_clicked_callback (...)
    at ../src/gtk/button.cpp:38
#16 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#17 g_signal_emit_valist ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#18 g_signal_emit ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#19 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#20 g_closure_invoke ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#21 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#22 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#23 g_signal_emit_valist ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#24 g_signal_emit ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#25 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#26 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#27 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#28 g_signal_emit_valist ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#29 g_signal_emit ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#30 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#31 g_cclosure_marshal_VOID__BOXEDv ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#32 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#33 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#34 g_signal_emit_valist ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#35 g_signal_emit ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#36 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#37 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#38 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#39 gtk_event_controller_handle_event ()
    at /lib/x86_64-linux-gnu/libgtk-3.so.0
#40 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#41 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#42 g_closure_invoke ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#43 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#44 ??? () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#45 g_signal_emit_valist ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#46 g_signal_emit ()
    at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#47 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#48 ??? () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#49 gtk_main_do_event ()
    at /lib/x86_64-linux-gnu/libgtk-3.so.0
#50 ??? () at /lib/x86_64-linux-gnu/libgdk-3.so.0
#51 ??? () at /lib/x86_64-linux-gnu/libgdk-3.so.0
#52 ??? () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#53 ??? () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#54 g_main_loop_run ()
    at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#55 gtk_main ()
    at /lib/x86_64-linux-gnu/libgtk-3.so.0
#56 wxGUIEventLoop::DoRun (this=0x5555558e1210)
    at ../src/gtk/evtloop.cpp:68
#57 wxEventLoopBase::Run (this=0x5555558e1210)
    at ../src/common/evtloopcmn.cpp:88
#58 wxAppConsoleBase::MainLoop (this=0x5555555d6ea0)
    at ../src/common/appbase.cpp:393
#59 wxAppConsoleBase::OnRun (this=0x5555555d6ea0)
    at ../src/common/appbase.cpp:291
#60 wxAppBase::OnRun (this=0x5555555d6ea0)
    at ../src/common/appcmn.cpp:350
#61 operator() (...)
    at ../src/common/init.cpp:563
#62 wxSafeCall<int, ...>(...)
    at ../include/wx/private/safecall.h:40
#63 wxEntry (...)
    at ../src/common/init.cpp:550
#64 wxEntry (...)
    at ../src/common/init.cpp:577
#65 main (argc=1, argv=0x7fffffffd6f8)
    at ../../../samples/minimal/minimal.cpp:106

Modified minimal.cpp

/////////////////////////////////////////////////////////////////////////////
// Name:        minimal.cpp
// Purpose:     Minimal wxWidgets sample
// Author:      Julian Smart
// Modified by:
// Created:     04/01/98
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/grid.h"

#include <iostream>

// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------

// the application icon (under Windows it is in resources and even
// though we could still include the XPM here it would be unused)
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#endif

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp {
  public:
    // override base class virtuals
    // ----------------------------

    // this one is called on application startup and is a good place for the app
    // initialization (doing it here and not in the ctor allows to have an error
    // return: if OnInit() returns false, the application terminates)
    virtual bool OnInit() override;
};

// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame {
  public:
    // ctor(s)
    MyFrame(const wxString &title);

    // event handlers (these functions should _not_ be virtual)
    void OnQuit(wxCommandEvent &event);
    void OnAbout(wxCommandEvent &event);
    void OnTriggerCrash(wxCommandEvent &event);

  private:
    wxGrid *m_grid;

    // any class wishing to process wxWidgets events must use this macro
    wxDECLARE_EVENT_TABLE();
};

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// IDs for the controls and the menu commands
enum {
    // menu items
    Minimal_Quit = wxID_EXIT,

    // it is important for the id corresponding to the "About" command to have
    // this standard value as otherwise it won't be handled properly under Mac
    // (where it is special and put into the "Apple" menu)
    Minimal_About = wxID_ABOUT
};

// ----------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// ----------------------------------------------------------------------------

// the event tables connect the wxWidgets events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(Minimal_Quit, MyFrame::OnQuit)
    EVT_MENU(Minimal_About, MyFrame::OnAbout) wxEND_EVENT_TABLE()

    // Create a new application object: this macro will allow wxWidgets to
    // create the application object during program execution (it's better than
    // using a static object for many reasons) and also implements the accessor
    // function wxGetApp() which will return the reference of the right type
    // (i.e. MyApp and not wxApp)
    wxIMPLEMENT_APP(MyApp);

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------

// 'Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit() {
    // call the base class initialization method, currently it only parses a
    // few common command-line options but it could be do more in the future
    if (!wxApp::OnInit())
        return false;

    // create the main application window
    MyFrame *frame = new MyFrame("wxGrid overlay selection reproducer");

    // and show it (the frames, unlike simple controls, are not shown when
    // created initially)
    frame->Show(true);

    // success: wxApp::OnRun() will be called which will enter the main message
    // loop and the application will run. If we returned false here, the
    // application would exit immediately.
    return true;
}

// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------

// frame constructor
MyFrame::MyFrame(const wxString &title)
    : wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, wxSize(500, 300)) {
    // set the frame icon
    SetIcon(wxICON(sample));

    m_grid = new wxGrid(this, wxID_ANY);
    wxButton *trigger = new wxButton(this, wxID_ANY, "Trigger crash sequence");

    wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(m_grid, 1, wxEXPAND);
    sizer->Add(trigger, 0, wxALL | wxALIGN_RIGHT, 8);
    SetSizer(sizer);

    m_grid->CreateGrid(100, 10);
    m_grid->SetScrollLineX(1);
    m_grid->SetScrollLineY(1);

    for (int row = 0; row < m_grid->GetNumberRows(); ++row)
        m_grid->SetCellValue(row, 0, wxString::Format("Row %d", row));

    trigger->Bind(wxEVT_BUTTON, &MyFrame::OnTriggerCrash, this);
}

// event handlers

void MyFrame::OnQuit(wxCommandEvent &WXUNUSED(event)) {
    // true is to force the frame to close
    Close(true);
}

void MyFrame::OnAbout(wxCommandEvent &WXUNUSED(event)) {
    wxMessageBox(wxString::Format("Welcome to %s!\n"
                                  "\n"
                                  "This is the minimal wxWidgets sample\n"
                                  "running under %s.",
                                  wxVERSION_STRING, wxGetOsDescription()),
                 "About wxWidgets minimal sample", wxOK | wxICON_INFORMATION,
                 this);
}

void MyFrame::OnTriggerCrash(wxCommandEvent &WXUNUSED(event)) {
    std::cout << "wxWidgets: " << wxString(wxVERSION_STRING).ToStdString()
              << std::endl;
    m_grid->BeginBatch();
    m_grid->SelectBlock(0, 0, 0, 0);
    std::cout << "wxGrid batch count before scroll: " << m_grid->GetBatchCount()
              << std::endl;
    m_grid->ScrollWindow(0, -1, nullptr);
    m_grid->EndBatch();
}


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5446607258@github.com>

Dominik Gresch

unread,
Aug 27, 2026, 8:05:38 PM (7 days ago) Aug 27
to wx-...@googlegroups.com, Subscribed
greschd left a comment (wxWidgets/wxWidgets#26933)

Looking into the source, I believe GetSelectionShape returns an invalid reference here:

https://github.com/wxWidgets/wxWidgets/blob/3edb98ee534d494d5512227cde801353002661c3/src/generic/grid.cpp#L2770

There is a nullptr check that triggers ComputeSelectionShape, but that immediately returns in batch mode:

https://github.com/wxWidgets/wxWidgets/blob/3edb98ee534d494d5512227cde801353002661c3/src/generic/gridsel.cpp#L1124-L1133

https://github.com/wxWidgets/wxWidgets/blob/3edb98ee534d494d5512227cde801353002661c3/src/generic/gridsel.cpp#L1045-L1048

I'm unsure what's the correct way to fix this though (if it needs fixing): should ScrollWindow also return on positive GetBatchCount()?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5446679356@github.com>

VZ

unread,
Aug 27, 2026, 8:25:21 PM (7 days ago) Aug 27
to wx-...@googlegroups.com, Subscribed
vadz left a comment (wxWidgets/wxWidgets#26933)

Thanks for the reproducer, it helps a lot!

The immediate fix is simple enough, I think:

diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp
index 76fba277bf..8361f1f7ef 100644
--- a/src/generic/grid.cpp
+++ b/src/generic/grid.cpp
@@ -2764,7 +2764,7 @@ void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
 
 void wxGrid::ScrollWindow( int dx, int dy, const wxRect *rect )
 {
-    if ( UsesOverlaySelection() && IsSelection() )
+    if ( UsesOverlaySelection() && IsSelection() && !GetBatchCount() )
     {
         wxRect r; // dummy renderExtent
         wxRect oldSel = m_selection->GetSelectionShape(r).GetBoundingBox();

as we don't need to refresh anything while the grid is frozen.

But I'm pretty unhappy with wxGridSelection::GetSelectionShape() which can dereference a null pointer... I guess we also need

diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp
index fabfdc49d4..84e53b7703 100644
--- a/src/generic/gridsel.cpp
+++ b/src/generic/gridsel.cpp
@@ -1045,7 +1045,13 @@ void wxGridPrivate::MergeAdjacentRects(std::vector<wxRect>& rectangles)
 void wxGridSelection::ComputeSelectionShape(const wxRect& renderExtent)
 {
     if ( m_grid->GetBatchCount() )
+    {
+        // We still must have a non-null m_selectionShape after the call to
+        // this function, so allocate an empty one if we don't have it yet.
+        if ( !m_selectionShape )
+            m_selectionShape.reset(new wxSelectionShape);
         return;
+    }
 
     wxRect updateRect;

which is also sufficient to fix the crash.

If nobody proposes a better solution, I'm going to commit this soon (and backport to 3.2 too).


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5446816884@github.com>

AliKet

unread,
Aug 28, 2026, 7:27:03 AM (7 days ago) Aug 28
to wx-...@googlegroups.com, Subscribed
AliKet left a comment (wxWidgets/wxWidgets#26933)

Thanks for the reproducer, it helps a lot!

The immediate fix is simple enough, I think:

diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp
index 76fba277bf..8361f1f7ef 100644
--- a/src/generic/grid.cpp
+++ b/src/generic/grid.cpp
@@ -2764,7 +2764,7 @@ void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )

void wxGrid::ScrollWindow( int dx, int dy, const wxRect *rect )
{

  • if ( UsesOverlaySelection() && IsSelection() )
  • if ( UsesOverlaySelection() && IsSelection() && !GetBatchCount() )
    {
    wxRect r; // dummy renderExtent
    wxRect oldSel = m_selection->GetSelectionShape(r).GetBoundingBox();
    as we don't need to refresh anything while the grid is frozen.

I think using ShouldRefresh() instead of GetBatchCount() is more clear...

But I'm pretty unhappy with wxGridSelection::GetSelectionShape() which can dereference a null pointer... I guess we also need

diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp
index fabfdc49d4..84e53b7703 100644
--- a/src/generic/gridsel.cpp
+++ b/src/generic/gridsel.cpp

@@ -1045,7 +1045,13 @@ void wxGridPrivate::MergeAdjacentRects(std::vector& rectangles)


void wxGridSelection::ComputeSelectionShape(const wxRect& renderExtent)
{
if ( m_grid->GetBatchCount() )

  • {

  •    // We still must have a non-null m_selectionShape after the call to
    
  •    // this function, so allocate an empty one if we don't have it yet.
    
  •    if ( !m_selectionShape )
    
  •        m_selectionShape.reset(new wxSelectionShape);
       return;
    
  • }

    wxRect updateRect;
    which is also sufficient to fix the crash.

If nobody proposes a better solution, I'm going to commit this soon (and backport to 3.2 too).

I'm not sure about this, but if it prevents the crash from happening, let's do it!


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5451951693@github.com>

VZ

unread,
Aug 28, 2026, 8:14:49 AM (7 days ago) Aug 28
to wx-...@googlegroups.com, Subscribed
vadz left a comment (wxWidgets/wxWidgets#26933)

I think using ShouldRefresh() instead of GetBatchCount() is more clear...

Good point, thanks.

I'm not sure about this, but if it prevents the crash from happening, let's do it!

We need to either do this or change wxGridSelection::GetSelectionShape(), i.e. do this:

diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp
index fabfdc49d4..5ef2788d3d 100644
--- a/src/generic/gridsel.cpp
+++ b/src/generic/gridsel.cpp
@@ -1127,6 +1127,10 @@ wxGridSelection::GetSelectionShape(const wxRect& renderExtent)
     if ( !m_selectionShape )
     {
         ComputeSelectionShape(renderExtent);
+
+        // We still must have a non-null m_selectionShape to return something.
+        if ( !m_selectionShape )
+            m_selectionShape.reset(new wxSelectionShape);
     }
 
     return *m_selectionShape.get();

Do you prefer the latter?


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5452378120@github.com>

AliKet

unread,
Aug 28, 2026, 10:10:53 AM (6 days ago) Aug 28
to wx-...@googlegroups.com, Subscribed
AliKet left a comment (wxWidgets/wxWidgets#26933)

I think using ShouldRefresh() instead of GetBatchCount() is more clear...

Good point, thanks.

I'm not sure about this, but if it prevents the crash from happening, let's do it!

We need to either do this or change wxGridSelection::GetSelectionShape(), i.e. do this:

diff --git a/src/generic/gridsel.cpp b/src/generic/gridsel.cpp
index fabfdc49d4..5ef2788d3d 100644
--- a/src/generic/gridsel.cpp
+++ b/src/generic/gridsel.cpp
@@ -1127,6 +1127,10 @@ wxGridSelection::GetSelectionShape(const wxRect& renderExtent)
if ( !m_selectionShape )
{
ComputeSelectionShape(renderExtent);
+

  •    // We still must have a non-null m_selectionShape to return something.
    
  •    if ( !m_selectionShape )
    
  •        m_selectionShape.reset(new wxSelectionShape);
    

    }

    return *m_selectionShape.get();
    Do you prefer the latter?

    Yes, the latter is preferable because it eliminates the root cause of the crash—dereferencing a nullptr—for this case and for unforeseen ones.

    TIA!


    Reply to this email directly, view it on GitHub, or unsubscribe.
    Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

    You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5453544458@github.com>

    Dominik Gresch

    unread,
    Aug 29, 2026, 7:34:11 AM (6 days ago) Aug 29
    to wx-...@googlegroups.com, Subscribed
    greschd left a comment (wxWidgets/wxWidgets#26933)

    Thanks for the quick response and root cause analysis @vadz @AliKet!


    Reply to this email directly, view it on GitHub, or unsubscribe.
    Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

    You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/26933/5462135608@github.com>

    VZ

    unread,
    Aug 30, 2026, 7:23:07 PM (4 days ago) Aug 30
    to wx-...@googlegroups.com, Subscribed

    Closed #26933 as completed via ee86182.


    Reply to this email directly, view it on GitHub, or unsubscribe.
    Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

    You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issue/26933/issue_event/30249433149@github.com>

    Reply all
    Reply to author
    Forward
    0 new messages