2017-06-18 15 views
3

Je tente de définir mes propriétés de contexte de périphérique préférées dans le message WM_CREATE, puis de l'utiliser dans le message WM_PAINT. Ma méthode consiste à utiliser les fonctions SaveDC et RestoreDC respectivement dans les messages WM_CREATE et WM_PAINT. Mais le résultat ne répond pas à mes besoins. Ce dont j'ai vraiment besoin, c'est de montrer un cercle au centre de la zone client.La fonction "SaveDC" et "RestoreDC" ne peut-elle pas être utilisée dans un code de traitement de message différent?

#include <windows.h> 

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 
        PSTR szCmdLine, int iCmdShow) 
{ 
    static TCHAR szAppName[] = TEXT ("HelloWin") ; 
    HWND   hwnd ; 
    MSG   msg ; 
    WNDCLASS  wndclass ; 

    wndclass.style   = CS_HREDRAW | CS_VREDRAW ; 
    wndclass.lpfnWndProc = WndProc ; 
    wndclass.cbClsExtra = 0 ; 
    wndclass.cbWndExtra = 0 ; 
    wndclass.hInstance  = hInstance ; 
    wndclass.hIcon   = LoadIcon (NULL, IDI_APPLICATION) ; 
    wndclass.hCursor  = LoadCursor (NULL, IDC_ARROW) ; 
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 
    wndclass.lpszMenuName = NULL ; 
    wndclass.lpszClassName = szAppName ; 

    if (!RegisterClass (&wndclass)) 
    { 
      MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
         szAppName, MB_ICONERROR) ; 
      return 0 ; 
    } 

    hwnd = CreateWindow (szAppName,     // window class name 
          TEXT ("The Hello Program"), // window caption 
          WS_OVERLAPPEDWINDOW,  // window style 
          CW_USEDEFAULT,    // initial x position 
          CW_USEDEFAULT,    // initial y position 
          CW_USEDEFAULT,    // initial x size 
          CW_USEDEFAULT,    // initial y size 
          NULL,      // parent window handle 
          NULL,      // window menu handle 
          hInstance,     // program instance handle 
          NULL) ;      // creation parameters 

    ShowWindow (hwnd, iCmdShow) ; 
    UpdateWindow (hwnd) ; 

    while (GetMessage (&msg, NULL, 0, 0)) 
    { 
      TranslateMessage (&msg) ; 
      DispatchMessage (&msg) ; 
    } 
    return msg.wParam ; 
} 

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    HDC   hdc ; 
    PAINTSTRUCT ps ; 
    RECT rect; 
    static int nSavedDC; 

    switch (message) 
    {   
    case WM_CREATE: 
     hdc = GetDC(hwnd); 
     SetMapMode(hdc, MM_LOMETRIC); 
     GetClientRect(hwnd, &rect); 
     SetViewportOrgEx(hdc, rect.right/2, rect.bottom/2, NULL); 
     nSavedDC = SaveDC(hdc);   //I want to save the current state of device context to be used in WM_PAINT message. 
     ReleaseDC(hwnd, hdc); 
     return 0; 

    case WM_PAINT: 
      hdc = BeginPaint (hwnd, &ps) ; 
      RestoreDC(hdc, nSavedDC);  //Restore the the state of device context which is saved in WM_CREATE message. 
      Ellipse(hdc, -100, 100, 100, -100); 
      EndPaint (hwnd, &ps) ; 
      return 0 ; 

    case WM_DESTROY: 
      PostQuitMessage (0) ; 
      return 0 ; 
    } 
    return DefWindowProc (hwnd, message, wParam, lParam) ; 
} 
+1

Ce code ne fonctionnera jamais. Vous ne pouvez pas appeler 'ReleaseDC (hwnd, hdc);'; l'appel approprié serait 'ReleaseDC (hwnd, nSavedDc);', ce qui, bien entendu, n'aurait aucun sens. Pourquoi pensez-vous que vous devez sauvegarder '' SaveDC' dans 'WM_CREATE' en premier lieu? Il n'y a aucune utilisation dans le code que vous avez écrit pour n'importe quel but raisonnable, alors que pensez-vous exactement que vous accomplissez ici? Qu'essayez-vous de faire? –

+3

Ce n'est pas comme ça que fonctionnent les DC. Techniquement, vous êtes supposé charger manuellement le DC avec son état à chaque fois, parce que vous n'êtes pas sûr d'avoir le même DC à chaque fois - ils sont dans une piscine, ou quelque chose comme ça. (Il y a des façons d'exiger un DC spécifique pour chaque fenêtre, mais ce sont des retards de Windows 16 bits qui ont beaucoup d'embûches de nos jours qui peuvent produire des résultats spectaculaires dans des programmes suffisamment compliqués.) Raymond Chen a beaucoup sur le sujet. Chargez simplement votre état chaque fois que vous appelez 'BeginPaint()' ou 'GetDC()' et réinitialisez-le avant de l'annuler. – andlabs

+1

@KenWhite 'ReleaseDC (hwnd, nSavedDc);' est complètement faux car 'nSavedDc' est un entier. Le code 'ReleaseDC (hwnd, hdc);' d'origine est correct. – VTT

Répondre

1

Votre code ne gère pas les erreurs. Vous devez inspecter toutes les valeurs renvoyées par chaque appel de fonction GDI.

Comme décrit sur MSDN page le but de SaveDC/RestoreDC est principalement de restaurer l'état à l'original après avoir fini de dessiner. Et c'est exactement ce que vous ne faites pas dans les gestionnaires de messages WM_CREATE et WM_PAINT. Vous laissez toujours DC dans un état modifié.

Quant à l'utilisation SaveDC/RestoreDC pour mettre en place état en courant continu une fois, puis restaurer rapidement à toutes les opérations de peinture au lieu de mettre en place à partir de zéro chaque fois que je pense d'au moins un obstacle: Si une autre fonction appelle RestoreDC entre les gestionnaires DC restaure l'élément qui n'était pas au sommet de la pile des états DC alors votre état enregistré qui était au sommet de la pile sera détruit comme décrit here.