Python 2.1 crashes under specific circumstances when called during processing of Win32 WM_PAINT message. Need help.

Parzival Herzog parz at shaw.SpamBucket.ca
Fri Nov 23 07:52:14 CET 2001


---------*---------*---------*---------*---------*---------*---------*------
---*
I am using a simple Python extension module written in C++ to
do some specialized graphics, on Win 20000, using Activestate
Python 2.1.

The module works very well generally, but under a specific
circumstance, a crash occurs, and I am unable to find a cause.
I am hoping someone could look at the code fragments below
and tell me what is wrong.

The specific circumstance is that a WM_PAINT message is
being processed for the window managed by the extension
module and the window is partially obscured. Then
a mouse right-click event is processed through the window
procedure, calling a python call back method, and a popup
menus is displayed. The menu appears, and the WM_PAINT
message arrives (presumably to bring the window to the
top). The window procedure attempts to call a python method
to do the re-drawing, and an access violation occurs somewhere
in python21.dll, before the python method call actually takes place.
(I.e. the crash is not caused by the subsecuent processing of
drawing code, it seems to be caused by the attempt to invoke
the interpreter using PyObject_CallMethod(...). There is no
difference in arguments to PyObject_CallMethod whether
successful or not: same Python object reference, same argument
list.

Other types of wondow messages can be processed successfully
while the managed window is obscured.

I am debugging by 1) using print statements (yes this GUI
application also has a console!), 2) starting the program,
and debugging by attaching to the running Python porcess.
Consequently, I can set breakpoints and examine variables
in my own extension module, but there when the access
violation occurs, the only information is the dissassembled
location, and that it is in python21.dll. There is no
call stack shown in the debugger.

So, (1) Does some experienced person know what could
be going wrong here?

(2) Can anyone give me simple directions for being able
to debug into Python21.dll? I am somewhat afraid of
getting mixed up with the sourec code, and I fear the
problems of having both the source and its executables
in an environment where the distributed binary is
installed. So perhaps someone can let me know how
to let a locally built Python coexist with a binary
distribution.


TIA,
    - Parzival

If replying by e-mail, please remove the spam bucket from
the reply-to address.

------------------------------------------------
Here is the code of the window procedure in my extension
module (MyWin.pyd).

_App is a global variable containing a reference to a Python class
instance, with member functions that are used as callbacks. (Yes,
the reference is kept alive with a PY_INCREF.)

static long PASCAL MyWinProc (HWND Window, UINT Message, UINT wParam, LONG
lParam)
{
   switch (Message) {

   case WM_CREATE: {
      return 0;
    } break;

   case WM_CLOSE: {
      if ( _App )
         if ( PyObject_CallMethod(_App, "OnClose", NULL) == NULL ) {
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnClose call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         }
      return 0;
   } break;


   case WM_DESTROY: {
      PostQuitMessage (0);
      return 0;
    } break;

   case WM_ENTERMENULOOP: {
      _MenuModal++;
      return 0;
    } break;

   case WM_EXITMENULOOP: {
      _MenuModal--;
      return 0;
    } break;

   case WM_COMMAND: {
      if ( !_MenuModal && _App )
         if ( PyObject_CallMethod(_App, "OnCommand",
               "i", wParam & 0xFFFF  /* new command identifier */ ) ==
NULL )
         {
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnCommand call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         } else
            return 0;
    } break;

   case WM_SIZE: {
      if ( (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED ) && _App )
         if ( PyObject_CallMethod(_App, "OnResize",
               "ii", lParam & 0xFFFF,  /* new width */
                     lParam >> 16      /* new height */) == NULL )
         {
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnResize call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         } else
            return 0;
   } break;

   case WM_MOVE: {
      if ( _App )
         if ( PyObject_CallMethod(_App, "OnMove",
               "ii", lParam & 0xFFFF,  /* new width */
                     lParam >> 16      /* new height */) == NULL )
         {
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnResize call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         } else
            return 0;
   } break;

   case WM_PAINT: {

      PAINTSTRUCT ps;
      if ( Window != _Window )
         break;

      BeginPaint (Window, &ps);

      RECT Rect;
      GetClientRect (Window, &Rect);

#define DOUBLEBUFFERING
#ifdef DOUBLEBUFFERING
      _Dc = CreateCompatibleDC(ps.hdc);
      HBITMAP MemBitmap = CreateCompatibleBitmap(ps.hdc, Rect.right -
Rect.left, Rect.bottom - Rect.top);
      HBITMAP OldBitmap = (HBITMAP) ::SelectObject(_Dc, MemBitmap);
#else
     _Dc = ps.hdc;
#endif

      printf("WM_PAINT 4\n"); fflush(stdout);
      int saved = SaveDC(_Dc);


      // Do the drawing here
      if (ps.fErase)
         FillRect (_Dc, &Rect, _BgndBrush);
      _Redrawing = true;
      if ( _App )
//--------------- An access violation occurs when PyObject_CallMethod is
called.
         if ( PyObject_CallMethod(_App, "OnRedraw", NULL) == NULL ) {
//--------------------------------------------------------------------------
---------------
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnRedraw call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         }

      _Redrawing  = false;

#ifdef DOUBLEBUFFERING
      // Copy buffer to screen
      BitBlt (ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right -
ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, _Dc, ps.rcPaint.left,
ps.rcPaint.top, SRCCOPY);
#endif

      // Reset graphics state
      RestoreDC(_Dc, saved);

#ifdef DOUBLEBUFFERING
      // Cleanup double-buffering
      SelectObject (_Dc, OldBitmap);
      DeleteObject (MemBitmap);
      DeleteDC (_Dc);
#endif
      printf("WM_PAINT 9\n"); fflush(stdout);

      EndPaint (Window, &ps);
      printf("WM_PAINT 10\n"); fflush(stdout);
      return 0;
    } break;

   case WM_RBUTTONDOWN: {
      SetFocus (Window);
      POINT Point;
      Point.x = (short) LOWORD(lParam);
      Point.y = (short) HIWORD(lParam);
      ClientToScreen (Window, &Point);
      if ( _App )
         if ( PyObject_CallMethod(_App, "OnRClick", "ii", Point.x, Point.y)
== NULL ) {
            if ( PyErr_Occurred() )
               PyErr_Print();
            // MessageBox (NULL, "OnRClick call failed", "MyWin callback
Error", MB_ICONSTOP | MB_APPLMODAL);
         }
      return 0;
    } break;


   }
   long rc = DefWindowProc(Window, Message, wParam, lParam);
   fflush(stdout);
   return rc;
}


------------------------------------------------
Here is the Python class definition of the object that receives all the
method
calls from the code above:

class wxApp:

    _top = None

    def __init__(self):
        MyWin.CreateWindow(self, "Sample")
        MyWin.SetBgndBrush(RGB(192, 220, 200), wxSOLID)
        global dw, dh
        dw,dh = MyWin.GetDisplaySize()
        MyWin.SetWindowPos (dw/4.0, dh/4, dw/2, dh/2)
        MyWin.Show()
        MyWin.MessageBox("Hi there","Message for you",0)

    def MainLoop(self):
        MyWin.MessageLoop()

    def OnClose(self):
        keep_going = 0
        MyWin.MessageBox("Closing Time","Time to close...",0)
        MyWin.Destroy()

    def OnRedraw(self):
        print "OnRedraw"; sys.stdout.flush()
        OnRedraw()
        print "End OnRedraw"; sys.stdout.flush()

    def OnResize(self, x, y):
        print "OnResize", x, y

    def OnMove(self, x, y):
        print "OnMove", x, y

    def OnRClick(self, x, y):
        print "OnRClick", x, y
        OnRClick(x, y)

    def OnEvent(self, dest, event):
        #print "OnEvent args=", str(dest), str(event)
        pass

    def OnCommand(self, id):
        print "OnCommand: id=", id


if __name__ == "__main__":
    app = wxApp()
    app.MainLoop()






More information about the Python-list mailing list