2016-09-04 13 views
8

Problem opis

Chcę utworzyć aplikację Windows API w C, który renderuje menu i przyciski z napisami w tym samym obszarze non-klient, podobnie jak Firefox notice the menu and caption buttonsczęści wyciągnięte z DrawThemeBackground na Windows 10 nie są poprawne

w tym celu, już ustalił, że rozwiązanie musi:

  • być typu WS_POPUP, w celu menu, aby być wyrównane do górnej
  • Przyswojenie spoza strefy klienta (gdzie menu jest renderowany)
  • ręcznie uczynić zminimalizować/zmaksymalizować/przyciski zamknięcia

Rozwiązanie musi działać w systemie Windows 7, 8 i 10 (a najlepiej również przyszłe wersje).

Jak to wygląda teraz

I have a test program available on GitHub.

W moim app, mam przesłonięte odpowiednie wydarzenia:

WM_NCCALCSIZE, WM_NCHITTEST, WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMOUSEMOVE, WM_NCPAINT

a potem przemalować obszarów non-klienckie na tych wydarzeń: WM_NCACTIVATE, WM_SETTEXT

Oto przykład tego, jak wykonuję renderowanie:

// globals set elsewhere 
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect; 
BOOL maximized; 

// ... 

LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) { 
    RECT windowRect; 
    HRGN hRgn = NULL; 
    GetWindowRect(hWnd, &windowRect); 
    if (wParam == 1) { 
    hRgn = CreateRectRgnIndirect(&windowRect); 
    } else { 
    hRgn = (HRGN)wParam; 
    } 

    if (hRgn) { 
    // Carve out the area for custom content 
    HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect); 
    CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR); 
    DeleteObject(captionButtonRgn); 

    // Force default painting for non-client area 
    LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0); 

    // black background covering part of menu, behind buttons 
    HDC hDC = GetWindowDC(hWnd); 
    FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); 

    HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW")); 

    DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL); 
    DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL); 
    DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL); 

    CloseThemeData(hTheme); 
    } 
} 

wytopione wynik wygląda tak: screenshot showing what I have now Niestety, style używane do części (minimalizować, maksymalizować/przywracania zamknij) wyglądać style for Windows 7/8, a nie natywnego systemu Windows 10 steruje. Szukałem sposobu, aby zrobić to przez kilka dni bez powodzenia. Potrzebuję pomocy w zrozumieniu sposobu renderowania tych przycisków w systemie Windows 10 przy użyciu interfejsu API systemu Windows.

Obecny status (i co starałem tak daleko)

Moje pierwsze przeczucie, że muszę prawidłowo włączyć style wizualne.

  • Per this article wzywa sprawdzania wersji systemu operacyjnego Windows 8 dostanie chyba że wyraźnie podsystemie Windows 10 poprzez oczywistym. Click here to view my manifest. to działa:

    • Przed GetVersionEx powrócił głównym = 6, poboczny = 2, budować = 9200
    • Teraz GetVersionEx zwraca główną = 10, minor = 0, budować = 10586
  • Per the official "Enabling Visual Styles" article, Upewniłem się, że używam Common Controls w wersji 6.

    • Dodany łącznik wejściowy dla Comctl32.lib
    • nawiązane połączenie do InitCommonControls na starcie programu
    • Dodano zależność dla wersji 6 do application manifest

Oto kilka screenów z istotnych ustawienia projektu, które próbowałem: target platform version

embed manifest

Inne pomysły

biegnę mało rzeczy spróbować. Przed wyrzuceniem w ręcznik, było kilka rzeczy, które miałem zamiar spróbować:

  • Pomysł 1: using GetThemeStream który pozwala odzyskać rozmiar/bitmapę do kontroli. złożyć

    • aero obciążenia msstyles tak:

    HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);

    • Uzyskaj bitmapy dla części (przycisk minimalizacji, przycisk maksymalizacji, etc) jak tak (przechodząc załadowanego pliku theme) :

    GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);

    • Załaduj bitmapę; wydaje się być w formacie PNG (I nie dostał tak daleko)
    • Draw bitmapę
  • Pomysł 2: skopiuj część dla klienta z ukrytym oknie, które ma powierzchnię napisów (i zminimalizować , zmaksymalizuj, zamknij przyciski).

    • Utwórz okno z przyciskami caption i min/max, nigdy go nie aktywując.
    • w farbie nie klienckim uzyskać DC do tego okna i uchwycić piksele na min/max przycisku zamykania/
    • uznać je za pomocą bitblt
+1

Czy możemy zobaczyć plik manifestu, który utworzyłeś (i połączyć z plikiem binarnym)? Chociaż nie widzę żadnego wskazania, renderowanie motywu było powiązane z manifestem aplikacji. Skąd masz te informacje? – IInspectable

+0

Manifest dodany :) Oto link do strony MSDN, gdzie przeczytałem, że manifest jest powiązany z Visual Styles: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773175(v=vs .85) .aspx –

+1

Pliki manifestu kontrolują wiele aspektów aplikacji (na przykład zgodność z platformami, serwery COM itp.). Link w poprzednim komentarzu wyjaśnia, w jaki sposób kontrolować używanie funkcji Common Controls w wersji 6. Jest to wymagane do używania stylów wizualnych (ale brakuje ich w manifeście). Istnieją również dwa sposoby rozmieszczania plików manifestu: jako zasób osadzony, a także oddzielny plik obok obrazu wykonywalnego. Jeśli oba są obecne, manifest wbudowany ma pierwszeństwo. Czy sprawdziłeś, że plik, który utworzyłeś, jest również tym, który zostanie użyty? – IInspectable

Odpowiedz

0

Myślę, że problem wynika z próbuje użyć WM_NCPAINT w wersji OS> = Win Vista. Odtąd całe renderowanie NC jest kontrolowane przez DWM (menedżer okien pulpitu).Jeśli nadal śmie obsłużyć WM_NCPAINT, DWM renderowania zostanie wyłączony i masz „old-school” wygląd:

From the Shell Revealed Blog:

DWM robi żadnych ofert dużych zmartwień, ponieważ aplikacje nie mogą remis Wewnątrz szklaną ramę, ponieważ jej renderowanie i zarządzanie nią jest zupełnie innym procesem. Jeśli aplikacja spróbuje to zrobić, system Windows wykryje go i całkowicie usunie szklaną ramkę (a tym samym powróci do ramki podstawowej), aby aplikacja mogła narysować to, co chce narysować.

Aby uzyskać odpowiednie rezultaty, co musisz zrobić, to „DWM way” (w szczególności odcinków „Wyjmowanie standardowej ramy” i „rysunek w rozszerzonej ramy okna”). Działa to przez umożliwienie DWM renderowania ramki w obszarze roboczym, aby można było malować przez to ponad. Dzięki temu rozwiązaniu nie trzeba samemu narysować przycisków napisów. This answer summarizes the required steps (w sekcji "Obsługiwane rozwiązanie Aero").

Najważniejsze jest to, że prawdopodobnie musisz narysować menu na własną rękę i nie możesz użyć do tego większego interfejsu API GDI, ponieważ GDI ignoruje kanał alfa i rzeczy będą wyglądać brzydko, jeśli ramka jest półprzezroczysta (Vista i Wygraj 7 domyślnie, Win8 + z rozszerzeniami). BitBlt() działa, jeśli źródłem jest DC z pamięcią, która zawiera mapę bitową o wielkości 32bpp z kanałem alfa. GDI + działa również.