2010-06-23 19 views
8

Escribo una aplicación win32. Implementé el bucle de mensajes yo mismo así:Win32: Mi aplicación se congela mientras el usuario cambia el tamaño de la ventana

 bool programcontinue = true; 
    while(programcontinue) 
    { 
       while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) 
       { 
         TranslateMessage(&Msg); 
         DispatchMessage(&Msg); 
       } 

       IdleProcess(); 
    } 

Hay una ventana de tamaño variable en mi aplicación. Normalmente, IdleProcess() se llama varias veces por segundo. Cuando el usuario agarra una esquina o un borde de la ventana de tamaño variable, IdleProcess() no recibe más llamadas hasta que el usuario suelta el botón del mouse.

¿Qué sucede aquí?

He intentado intercambiar el interior con un if, pero eso no cambia el comportamiento. Parece que cuando se inicia el cambio de tamaño, el manejador de ese mensaje no regresa hasta que se realiza el cambio de tamaño.

¿Hay alguna manera de cambiar esto y llamar a IdleProcess() durante el cambio de tamaño varias veces por segundo?

Gracias Marc

EDIT:

Lo que quiero decir mediante la sustitución del tiempo interno con si es:

bool programcontinue = true; 
while(programcontinue) 
{ 
      if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) // <<<< 
      { 
        TranslateMessage(&Msg); 
        DispatchMessage(&Msg); 
      } 

      IdleProcess(); 
} 

Mi ventana Proc es un poco largo, pero me sale el mismo comportamiento con una pequeña aplicación de prueba. Esto es idéntico al wndproc que crea el Asistente de proyecto VS:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    int wmId, wmEvent; 
    PAINTSTRUCT ps; 
    HDC hdc; 

    switch (message) 
    { 
    case WM_COMMAND: 
     wmId = LOWORD(wParam); 
     wmEvent = HIWORD(wParam); 
     // Parse the menu selections: 
     switch (wmId) 
     { 
     case IDM_ABOUT: 
      DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 
      break; 
     case IDM_EXIT: 
      DestroyWindow(hWnd); 
      break; 
     default: 
      return DefWindowProc(hWnd, message, wParam, lParam); 
     } 
     break; 
    case WM_PAINT: 
     hdc = BeginPaint(hWnd, &ps); 
     // TODO: Add any drawing code here... 
     EndPaint(hWnd, &ps); 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 
+0

podría publicar su procedimiento de ventana WndProc (...) que recibirá mensajes de DispatchMessage() ya que su comentario sobre cambiar el tiempo en un si es un poco curioso –

+0

hecho + una explicación del "si" en lugar de " mientras que "cosita". – marc40000

+0

Según la actualización, es obvio que TranslateMessage o DispatchMessage no regresan inmediatamente. Su próxima tarea es averiguar cuál, y qué mensaje está desencadenando esto. –

Respuesta

14

Hay una serie de operaciones modales que ocurren en Windows. Las operaciones de Win32 Modal se refieren a funciones que ponen una aplicación en un "modo" iniciando su propio ciclo de procesamiento de eventos hasta que el modo finaliza. Los modos de aplicación comunes incluyen operaciones de arrastrar y soltar, operaciones de movimiento/tamaño, cada vez que aparece un cuadro de diálogo que necesita información antes de que la aplicación pueda continuar.

Entonces, lo que está sucediendo es: Su ciclo de mensajes NO se está ejecutando. Su ventana recibió un mensaje WM_LBUTTONDOWN que pasó a DefWindowProc. DefWindowProc determinó que el usuario intentaba dimensionar o mover la ventana de forma interactiva e ingresó una función modal de dimensionamiento/movimiento. Esta función se encuentra en un bucle de procesamiento de mensajes que observa los mensajes del mouse para poder interceptarlos y proporcionar una experiencia de dimensionamiento interactiva, y solo saldrá cuando finalice la operación de dimensionamiento, normalmente soltando el botón presionado o presionando escape.

Recibirá una notificación al respecto: DefWindowProc envía un mensaje WM_ENTERSIZEMOVE y WM_EXITSIZEMOVE cuando ingresa y sale del ciclo de procesamiento de evento modal.

Para continuar generando mensajes "inactivos", normalmente crea un temporizador (SetTimer) antes de llamar a una función modal - o cuando recibe un mensaje que DefWindowProc está ingresando a una función modal - el ciclo modal continuará despachando mensajes WM_TIMER .. y llamar al proceso inactivo desde el controlador de mensajes del temporizador.Destruye el temporizador cuando la función modal regrese.

1

Durante el cambio de tamaño Windows envía bastantes mensajes a su programa. No he probado esto, pero el comportamiento que describes es familiar. Yo sugeriría que llamar a su función IdleProcess() también dentro del bucle while (...) para ciertos eventos, como WM_SIZING cual su aplicación recibirá con frecuencia durante el redimensionamiento:

bool programcontinue = true; 
while(programcontinue) 
{ 
      while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) 
      { 
        TranslateMessage(&Msg); 
        DispatchMessage(&Msg); 
        if(Msg.message == WM_SIZING) 
         IdleProcess(); 
      } 

      IdleProcess(); 
} 

Ten en cuenta que esto supone , que IdleProcess() no crea ni consume ningún evento. Si ese es el caso, las cosas se vuelven mucho más complicadas.

+0

Eso no ayuda. Como escribí en mi pregunta "Intenté intercambiar el tiempo interior con un si" – marc40000

4

Cuando DefWindowProc maneja WM_SYSCOMMAND con SC_MOVE o SC_SIZE en el wParam, entra en un bucle hasta que el usuario lo detiene soltando el botón del mouse, o presionando enter o escape. Hace esto porque permite que el programa represente tanto el área del cliente (donde se dibujan sus widgets o juego o lo que sea) como los bordes y el área de los subtítulos al manejar los mensajes WM_PAINT y WM_NCPAINT (aún debe recibir estos eventos en su Procedimiento de ventana).

Funciona bien para las aplicaciones normales de Windows, que hacen la mayor parte de su procesamiento dentro de su Procedimiento de Ventana como resultado de recibir mensajes. Solo afecta a los programas que procesan fuera del Procedimiento de ventana, como los juegos (que generalmente son de pantalla completa y no se ven afectados de todos modos).

Sin embargo, hay una forma de evitarlo: manejar WM_SYSCOMMAND usted mismo, cambiar el tamaño o mover usted mismo. Esto requiere un gran esfuerzo, pero puede demostrar que vale la pena. Alternativamente, puede usar setjmp/longjmp para escapar del procedimiento de ventana cuando se envía WM_SIZING, o Windows Fibres en la misma línea; estas son soluciones hackish sin embargo.

Lo resolví (usando el primer método) este fin de semana pasado, si está interesado he lanzado el código al dominio público en sourceforge. Solo asegúrate de leer el archivo README, especialmente la sección de advertencia. Aquí está: https://sourceforge.net/projects/win32loopl/

1

todavía puede recibir el mensaje WM_PAINT, solo tienes que decir la API de Windows que desea que (visto en tutoriales NeHe OpenGL):

windowClass.style   = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws The Window For Any Movement/Resizing 

Será todavía bloquear su while/PeekMessage -loop sin embargo! WinAPI simplemente llama a su WndProc directamente.

Cuestiones relacionadas