Question

Suis-je autorisé à utiliser un extérieur continu d'un cycle de peinture? DC est de ma fenêtre garantie pour être valable pour toujours?

Je suis en train de comprendre combien de temps le contexte de périphérique de mon contrôle (DC) est valide.

Je sais que je peux appeler:

GetDC(hWnd);

pour obtenir le contexte de périphérique de la fenêtre de mon contrôle, mais est-ce permis?

Lorsque Windows me envoie un message WM_PAINT, je suis censé appeler BeginPaint / EndPaint pour reconnaître correctement que Je l'ai peint, et dans la région invalide interne claire:

BeginPaint(hWnd, {out}paintStruct);
try
   //Do my painting
finally
   EndPaint(hWnd, paintStruct);
end;

Mais appeler BeginPaint aussi me retourne un DC intérieur de la structure de PAINTSTRUCT. C'est le DC que i devrait être sur la peinture.

Je ne trouve rien dans la documentation qui dit que le DC est revenu par BeginPaint () est le même DC que je recevrais de GetDC ().

Surtout maintenant, dans les jours de composition du bureau, est-il valide pour peindre sur un DC que j'obtenir en dehors du BeginPaint?

Il semble y avoir 2 façons je peux obtenir un courant continu à peindre durant un cycle de peinture:

  1. GetDC (hWND);

  2. BeginPaint (& PAINTSTRUCT);

Il y a une 3ème voie, mais il semble être un bug avec Borland Delphi que je développe avec.

WM_PAINT traitement, croit Delphi que le wParam est un courant continu, et procède à la peinture sur elle. Alors que le MSDN dit que le wParam d'un message WM_PAINT est utilisé.

Pourquoi

Mon véritable objectif est d'essayer de garder un GDI persistant + objet graphique contre un HDC, afin que je puisse utiliser certaines fonctionnalités performantes de mieux GDI + qui dépendent d'avoir un courant continu persistant.

Au cours de la gestion des messages WM_PAINT je veux dessiner un GDI + image sur la toile. La version suivante Nieve est très lent:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);
   Graphics g = new Graphics(ps.hdc);
   g.DrawImage(m_someBitmap, 0, 0);
   g.Destroy();
   EndPaint(h_hwnd, ps);
}

GDI contient un bitmap plus rapide d'exécution, un CachedBitmap. Mais l'utiliser sans penser donne aucun avantage de performance:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);

   Graphics g = new Graphics(ps.hdc);
   CachedBitmap bm = new CachedBitmap(m_someBitmap, g);
   g.DrawCachedBitmap(m_bm, 0, 0);
   bm.Destroy();
   g.Destroy();
   EndPaint(h_hwnd, ps);
}

Le gain de performance est de créer une fois que le CachedBitmap, donc l'initialisation du programme:

m_graphics = new Graphics(GetDC(m_hwnd));
m_cachedBitmap = new CachedBitmap(b_someBitmap, m_graphcis);

Et maintenant sur le cycle de peinture:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);
   m_graphics.DrawCachedBitmap(m_cachedBitmap, 0, 0);
   EndPaint(h_hwnd, ps);
}        

Sauf que maintenant je suis confiant que le DC i obtenu après initializtion du programme sera le même courant continu pour ma fenêtre tant que l'application est en cours d'exécution. Cela signifie qu'il survit grâce à:

  • utilisateur passe rapide
  • composition
  • activé / désactivé
  • commutation de thème
  • thème invalidantes

Je ne trouve rien dans MSDN qui garantit que le même courant continu sera utilisé pour une fenêtre particulière aussi longtemps que la fenêtre existe.

Remarque: Je ne suis pas en utilisant une double mémoire tampon, parce que je veux être un bon développeur, et faire la bonne chose . Parfois, cela signifie que vous double mise en mémoire tampon est mauvais.

Était-ce utile?

La solution

Il y a des exceptions, mais en général, vous pouvez obtenir un autre DC chaque fois que vous appelez GetDC ou BeginPaint. Ainsi, vous ne devriez pas essayer d'enregistrer l'état dans le DC. (Si vous devez le faire pour la performance, il y a des contrôleurs de domaine spéciaux que vous pouvez créer pour une classe de fenêtres ou d'une instance de fenêtre particulière, mais il ne semble pas comme ça est ce que vous avez vraiment besoin ou si vous voulez.)

La plupart du temps, cependant, les contrôleurs de domaine seront compatibles. Ils représenteront le même mode graphique, de sorte que votre bitmap compatible devrait fonctionner, même si vous obtenez un autre DC.

Il y a des messages Windows qui vous indiquent quand le mode graphique des changements, comme WM_DISPLAYCHANGE et WM_PALETTECHANGED. Vous pouvez écouter ces derniers, et de recréer votre bitmap mis en cache. Étant donné que ce sont des événements rares, vous n'aurez pas à vous soucier de l'impact sur les performances de recréer votre bitmap mis en cache à ce point.

Vous pouvez également obtenir des notifications pour des choses comme les changements de thème. Ceux-ci ne change pas le mode graphique - ils sont un concept de niveau supérieur - de sorte que votre bitmap mise en cache doit être toujours compatible avec tous les DC que vous obtenez. Mais si vous voulez changer bitmap lorsque le thème change, vous pouvez écouter WM_THEMECHANGED aussi bien.

Autres conseils

La seule façon que je connaisse qui peut (ou non) faire ce que vous cherchez est de créer la fenêtre avec le CS_OWNDC de style de classe.

Qu'est-ce qui fait est alloue un contexte de dispositif unique pour chaque fenêtre de la classe.

Modifier

De l'article MSDN lié:

  

Un contexte de dispositif est un ensemble spécial de   les valeurs utilisées par les applications pour   dessin dans la zone client de leur   les fenêtres. Le système nécessite un dispositif   contexte pour chaque fenêtre sur l'affichage   mais permet une certaine souplesse dans la façon dont la   système stocke et traite ce dispositif   contexte.

     

Si aucun style appareil contexte est   explicitement donnée, le système suppose   chaque fenêtre utilise un contexte de dispositif   récupérées à partir d'un pool de contextes   maintenu par le système. dans ce   cas, chaque fenêtre doit récupérer et   initialiser le contexte de périphérique avant   peinture et libre après la peinture.

     

Pour éviter de récupérer un contexte de périphérique   chaque fois qu'il a besoin de peindre l'intérieur d'un   fenêtre, une application peut spécifier le   le style CS_OWNDC pour la classe de fenêtre.   Ce style de classe dirige le système   créer un contexte de périphérique privé - que   est, pour attribuer un dispositif unique,   contexte pour chaque fenêtre de la classe.   L'application doit seulement récupérer la   contexte une fois, puis l'utiliser pour tous   peinture ultérieure.

     

Windows 95/98 / Me: Bien que la   le style CS_OWNDC est pratique, utiliser   soigneusement, parce que chaque contexte de périphérique   utilise une partie significative de 64K GDI   tas.

Peut-être cet exemple illustre l'utilisation de CS_OWNDC mieux:

#include <windows.h>

static TCHAR ClassName[] = TEXT("BitmapWindow");
static TCHAR WindowTitle[] = TEXT("Bitmap Window");

HDC m_hDC;
HWND m_hWnd;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static PAINTSTRUCT ps;

    switch (msg)
    {
    case WM_PAINT:
        {
            BeginPaint(hWnd, &ps);

            if (ps.hdc == m_hDC)
                MessageBox(NULL, L"ps.hdc == m_hDC", WindowTitle, MB_OK);
            else
                MessageBox(NULL, L"ps.hdc != m_hDC", WindowTitle, MB_OK);

            if (ps.hdc == GetDC(hWnd))
                MessageBox(NULL, L"ps.hdc == GetDC(hWnd)", WindowTitle, MB_OK);
            else
                MessageBox(NULL, L"ps.hdc != GetDC(hWnd)", WindowTitle, MB_OK);

            RECT r;
            SetRect(&r, 10, 10, 50, 50);
            FillRect(m_hDC, &r, (HBRUSH) GetStockObject( BLACK_BRUSH ));

            EndPaint(hWnd, &ps);
            return 0;
        }
    case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{   
    WNDCLASSEX wcex;

    wcex.cbClsExtra = 0;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.cbWndExtra = 0;
    wcex.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH );
    wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
    wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wcex.hIconSm = NULL;
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = WndProc;
    wcex.lpszClassName = ClassName;
    wcex.lpszMenuName = NULL;
    wcex.style = CS_OWNDC;

    if (!RegisterClassEx(&wcex))
        return 0;

    DWORD dwExStyle = 0;
    DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;

    m_hWnd = CreateWindowEx(dwExStyle, ClassName, WindowTitle, dwStyle, 0, 0, 300, 300, NULL, NULL, hInstance, NULL);

    if (!m_hWnd)
        return 0;

    m_hDC = GetDC(m_hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

Le drapeau CS_OWNDC est pas confondre avec le drapeau CS_CLASSDC qui:

  
    

Alloue un contexte de périphérique à partager par toutes les fenêtres de la classe. Parce que les classes de fenêtre sont processus spécifiques, il est possible pour plusieurs threads d'une application pour créer une fenêtre de la même classe. Il est également possible pour les fils pour tenter d'utiliser le contexte de l'appareil en même temps. Lorsque cela se produit, le système ne permet qu'un seul fil pour terminer avec succès son opération de dessin.

  

Si tout le reste échoue juste Reconstruire le CachedBitmap.

  
    

Lorsque vous créez un objet de CachedBitmap, vous devez passer l'adresse d'un objet graphique au constructeur. Si l'écran associé à cet objet Graphics a sa profondeur de bit changé après le bitmap mis en cache est construit, la méthode de DrawCachedBitmap échouera, et vous devez reconstruire le bitmap mis en cache. Vous pouvez également brancher le message de notification de changement d'affichage et de reconstruire le bitmap mis en cache à ce moment-là.

  

Je ne dis pas que CS_OWNDC est la solution parfaite, mais un pas vers une meilleure solution.

Modifier

Le programme d'échantillonnage semblait conserver le même DC lors de la résolution d'écran / profondeur bit changement test avec le drapeau CS_OWNDC, cependant, quand le drapeau a été enlevé, de la DC étaient différentes (Windows 7 64 bits Édition Intégrale) ( devrait fonctionnent de la même sur differn versions OS ... bien qu'il ne serait pas mal à tester).

Edit2

Cet exemple ne remet pas GetUpdateRect pour vérifier si la fenêtre doit être peinte pendant la WM_PAINT. C'est une erreur.

Vous pouvez dessiner sur la fenêtre selon cc vous plaît. Ils sont tous les deux valides. Une fenêtre n'a pas seulement un courant continu qui peut représenter à la fois. Donc, chaque fois que vous appelez GetDC - et BeginPaint ne interne, vous obtiendrez une nouvelle, dc unique qui représente néanmoins la même zone d'affichage. Juste ReleaseDC (ou EndPaint) lorsque vous avez terminé avec eux. Dans les jours des contextes de périphériques Windows 3.1 sont une ressource limitée du système, ou très coûteux, les applications ont été encouragés à ne jamais tenir sur eux, mais pour les récupérer à partir du cache GetDC. aujourd'hui son tout à fait acceptable de créer un courant continu à la création de la fenêtre et le cache pour la durée de vie de la fenêtre.

Le seul « problème » est, lors de la manipulation WM_PAINT, dc retourné par BeginPaint sera coupée à la rect invalide, et celle enregistrée ne sera pas.


Je ne comprends cependant pas ce que vous essayez d'atteindre avec gdiplus. En général, si un objet est ... sélectionné en courant continu pendant une longue période de temps, que cc est un courant continu de mémoire, pas un courant continu de la fenêtre.


Chaque fois que GetDC est appelé, vous obtiendrez un nouveau HDC représentant un contexte de dispositif distinct avec son propre état. Ainsi, les objets, les couleurs d'arrière-plan, les modes de texte, etc. mis sur un courant continu n'affectera pas cet état d'un autre DC récupéré par un autre appel à GetDC ou BeginPaint.

Le système ne peut invalider au hasard récupérés par le HDC client, et il fait beaucoup de travail en arrière-plan pour faire en sorte que devant un récupéré HDC commutateur de mode d'affichage, continuent de fonctionner. Même changer la profondeur de bit, qui fait techniquement en aucune façon, incompatible, de la DC ne sera pas, empêcher une application de continuer à utiliser hdc blit.

Cela dit, il est sage de regarder au moins pour WM_DISPLAYCHANGE, libérer les contrôleurs de domaine et mises en cache bitmaps de l'appareil, et les recréer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top