2013-09-06 2 views
-1

Я пытаюсь создать окно, в котором я могу отображать графику, но внутри цикла сообщений. Я получаю сообщение об ошибке «Ошибка WindowsError: исключение: нарушение доступа с ошибкой 0xC0000000». Как я понимаю из других вопросов, заданных здесь, это связано с тем, что метод пытается писать не в том месте.WindowsError: exception: запись нарушения прав доступа 0xC0000000 с помощью winAPI

Код - это адаптация найденного here, но функция цикла сообщения осталась прежней; Я даже пробовал такие вещи, как использование PeekMessage и различные другие подходы.

from ctypes import * 
import win32con 

_WNDPROC = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int) 

_NULL = c_int(win32con.NULL) 
_user32 = windll.user32 
_gdi32 = windll.gdi32 

def _ErrorIfZero(handle): 
    if handle == 0: 
     raise WinError() 
    else: 
     return handle 

CreateWindowEx = _user32.CreateWindowExW 
CreateWindowEx.argtypes = [c_int, 
          c_wchar_p, 
          c_wchar_p, 
          c_int, 
          c_int, 
          c_int, 
          c_int, 
          c_int, 
          c_int, 
          c_int, 
          c_int, 
          c_int] 
CreateWindowEx.restype = _ErrorIfZero 

class _WNDCLASS(Structure): 
    _fields_ = [('style', c_uint), 
       ('lpfnWndProc', _WNDPROC), 
       ('cbClsExtra', c_int), 
       ('cbWndExtra', c_int), 
       ('hInstance', c_int), 
       ('hIcon', c_int), 
       ('hCursor', c_int), 
       ('hbrBackground', c_int), 
       ('lpszMenuName', c_wchar_p), 
       ('lpszClassName', c_wchar_p)] 

    def __init__(self, 
       wndProc, 
       style=win32con.CS_HREDRAW | win32con.CS_VREDRAW, 
       clsExtra=0, 
       wndExtra=0, 
       menuName=None, 
       className=u"PythonWin32", 
       instance=None, 
       icon=None, 
       cursor=None, 
       background=None, 
       ): 

     if not instance: 
      instance = windll.kernel32.GetModuleHandleW(c_int(win32con.NULL)) 
     if not icon: 
      icon = _user32.LoadIconW(c_int(win32con.NULL), 
            c_int(win32con.IDI_APPLICATION)) 
     if not cursor: 
      cursor = _user32.LoadCursorW(c_int(win32con.NULL), 
             c_int(win32con.IDC_ARROW)) 
     if not background: 
      background = windll.gdi32.GetStockObject(c_int(win32con.WHITE_BRUSH)) 

     self.lpfnWndProc=wndProc 
     self.style=style 
     self.cbClsExtra=clsExtra 
     self.cbWndExtra=wndExtra 
     self.hInstance=instance 
     self.hIcon=icon 
     self.hCursor=cursor 
     self.hbrBackground=background 
     self.lpszMenuName=menuName 
     self.lpszClassName=className 

class _RECT(Structure): 
    _fields_ = [('left', c_long), 
       ('top', c_long), 
       ('right', c_long), 
       ('bottom', c_long)] 
    def __init__(self, left=0, top=0, right=0, bottom=0): 
     self.left = left 
     self.top = top 
     self.right = right 
     self.bottom = bottom 

class _PAINTSTRUCT(Structure): 
    _fields_ = [('hdc', c_int), 
       ('fErase', c_int), 
       ('rcPaint', _RECT), 
       ('fRestore', c_int), 
       ('fIncUpdate', c_int), 
       ('rgbReserved', c_wchar * 32)] 

class _POINT(Structure): 
    _fields_ = [('x', c_long), 
       ('y', c_long)] 
    def __init__(self, x=0, y=0): 
     self.x = x 
     self.y = y 

class _MSG(Structure): 
    _fields_ = [('hwnd', c_int), 
       ('message', c_uint), 
       ('wParam', c_int), 
       ('lParam', c_int), 
       ('time', c_int), 
       ('pt', _POINT)] 

def RunMessageLoop(): 
    """Runs the loop to get messages from""" 
    msg = _MSG() 
    pMsg = pointer(msg) 

    while _user32.GetMessageW(pMsg, _NULL, 0, 0): 
     _user32.TranslateMessage(pMsg) 
     _user32.DispatchMessageW(pMsg) 

    return msg.wParam 

## Lifted shamelessly from WCK (effbot)'s wckTkinter.bind 
def EventHandler(message): 
    """Decorator for event handlers""" 
    def decorator(func): 
     func.win32message = message 
     return func 
    return decorator 

class Window(object): 
    """The application window""" 

    def __init__(self, title, updateHandler): 

     #Store update function 
     self.updateHandler = updateHandler 

     #Register event handlers 
     self._event_handlers = {} 
     for key in dir(self): 
      method = getattr(self, key) 
      if hasattr(method, "win32message") and callable(method): 
       self._event_handlers[method.win32message] = method 

     #Register window class 
     wndclass = _WNDCLASS(_WNDPROC(self.WndProc)) 
     wndclass.lpszClassName = u"HelloWindow" 

     if not _user32.RegisterClassW(byref(wndclass)): 
      raise WinError() 

     #Now create the _Window 

     self.Create(className=wndclass.lpszClassName, 
       instance=wndclass.hInstance, 
       windowName=title) 

     #Show Window 
     self.Show(win32con.SW_SHOWNORMAL) 
     self.Update() 

    def GetSize(self): 
     rect = _RECT() 
     _user32.GetClientRect(self.hwnd, byref(rect)) 
     return rect.width, rect.height 

    def Create(self, 
      exStyle=0 ,  # DWORD dwExStyle 
      className=u"WndClass", 
      windowName=u"Window", 
      style=win32con.WS_OVERLAPPEDWINDOW, 
      x=win32con.CW_USEDEFAULT, 
      y=win32con.CW_USEDEFAULT, 
      width=win32con.CW_USEDEFAULT, 
      height=win32con.CW_USEDEFAULT, 
      parent=_NULL, 
      menu=_NULL, 
      instance=_NULL, 
      lparam=_NULL, 
      ): 

     self.hwnd = CreateWindowEx(exStyle, 
           className, 
           windowName, 
           style, 
           x, 
           y, 
           width, 
           height, 
           parent, 
           menu, 
           instance, 
           lparam) 
     return self.hwnd 

    def Show(self, flag): 
     return _user32.ShowWindow(self.hwnd, flag) 

    def Update(self): 
     if not _user32.UpdateWindow(self.hwnd): 
      raise WinError() 

    def WndProc(self, hwnd, message, wParam, lParam): 

     event_handler = self._event_handlers.get(message, None) 
     if event_handler: 
      return event_handler(message, wParam, lParam) 
     return _user32.DefWindowProcW(c_int(hwnd), 
             c_int(message), 
             c_int(wParam), 
             c_int(lParam)) 


    """Yipee Events :'(""" 

    @EventHandler(win32con.WM_PAINT) 
    def OnPaint(self, message, wParam, lParam): 
     """Window is updating so update graphics inside""" 
     ps = _PAINTSTRUCT() 
     hdc = _user32.BeginPaint(c_int(self.hwnd), byref(ps)) 

     self.updateHandler(hdc) 

     _user32.EndPaint(c_int(self.hwnd), byref(ps)) 
     return 0 

    @EventHandler(win32con.WM_DESTROY) 
    def OnDestroy(self, message, wParam, lParam): 
     """Quit app when window is destroyed""" 
     _user32.PostQuitMessage(0) 
     return 0 

class DrawMethods: 
    @staticmethod 
    def DrawLine(handle, x1, y1, x2, y2): 
     """Draw a line between points""" 
     _gdi32.MoveToEx(c_int(handle), 
         x1, 
         y1, 
         NULL) 
     _gdi32.LineTo(c_int(hdc), 
         x2, 
         y2) 

    @staticmethod 
    def DrawRect(handle, x, y, width, height): 
     """Draw a rect at x and y of width and height""" 
     _gdi32.Rectangle(c_int(handle), 
         x, 
         y, 
         x+width, 
         y+height) 

    #@staticmethod 
    #def DrawCircle(x, y, radius): 

    @staticmethod 
    def DrawText(handle, x, y, width, height, text): 
     """Draw text at the specified coordinates"""         # UPDATE!!!! 
     rect = _RECT(x, y, width, height) 
     flags = win32con.DT_SINGLELINE|win32con.DT_CENTER|win32con.DT_VCENTER 
     _user32.DrawTextW(c_int(handle), 
          text, 
          c_int(-1), 
          byref(rect), 
          flags) 


class Test: 
    """Create window and start message loop""" 

    def __init__(self): 
     #So create a window (yes this is multi window compatible I think) titled "TestWindow!" that calls the Update method to draw everything 
     self.window = Window("Test Window!", self.Update) 

    def Update(self, handle): 
     """This is called when the window updates and you can then use the drawing functions available in Renderer.DrawMethods""" 
     #Renderer omitted as in the same file 
     DrawMethods.DrawText(handle, 0, 0, 500, 500, u"Helllloooo!!! Testy testy test test :D") 

t= Test() 
RunMessageLoop() 

Я надеюсь, что кто-то может помочь мне, код предназначен для работы на любом компьютере окна, так что вы должны быть в состоянии запустить его (сказав, что импорт win32con может быть частью пакета pywin32 ...)

Спасибо,

Джейми :)

+0

Используйте типы Windows, определенные в 'ctypes.wintypes'. Он также определяет «RECT», «POINT», «MSG» и правильно определяет «WPARAM» и «LPARAM» для 32-разрядных и 64-разрядных Windows. Использование 'c_int' предполагает 32-разрядную Windows. Кроме того, чтобы передать 'NULL' в качестве параметра, просто используйте' None'. – eryksun

+0

OK Я попробую, спасибо! Они были такими, как в исходном коде, поэтому предполагалось, что им тоже нужно быть такими, как вы думаете, это решит? – JamJar00

+0

Это просто лучший стиль и позволит вашему скрипту работать без изменений в 64-битном процессе. – eryksun

ответ

0

Ну, я получил его на работу ... Кажется, призыв к RunMessageLoop(), необходимое, чтобы быть в пределах класса Window (в нижней части подпрограммы инициализации).

+0

Вы не сохраняете ссылку на 'wndclass', которая ссылается на обратный вызов' _WNDPROC'. Поэтому он освобождается, когда возвращается '__init__'. – eryksun

+0

Ahhhh, что имеет смысл! Большое спасибо :) – JamJar00