Mixed-Mode APIs com reconhecimento de DPI e dimensionamento de DPI

Sub-Process Suporte à Conscientização de DPI

SetThreadDpiAwarenessContext permite o uso de diferentes modos de dimensionamento de DPI em um único processo. Antes da Atualização de Aniversário do Windows 10, uma janela de reconhecimento de DPI estava associada ao modo de conscientização de DPI em todo o processo (DPI sem reconhecimento, reconhecimento de DPI do Sistema ou Per-Monitor reconhecimento de DPI). Mas agora, com SetThreadDpiAwarenessContext, as janelas de nível superior podem ter um modo de reconhecimento de DPI diferente do modo de reconhecimento de DPI em todo o processo. Isso também afeta as janelas filho, pois elas sempre terão o mesmo modo de reconhecimento de DPI que a janela pai.

O uso de SetThreadDpiAwarenessContext permite que os desenvolvedores decidam onde desejam concentrar seus esforços de desenvolvimento ao definir o comportamento específico do DPI para aplicativos da área de trabalho. Por exemplo, a janela principal de nível superior de um aplicativo pode ser dimensionada por monitor, enquanto janelas secundárias de nível superior podem ser dimensionadas por meio do dimensionamento de bitmap pelo sistema operacional.

O contexto de reconhecimento de DPI

Antes da disponibilidade de SetThreadDpiAwarenessContext , o reconhecimento de DPI de um processo era definido no manifesto do binário do aplicativo ou por meio de uma chamada para SetProcessDpiAwareness durante a inicialização do processo. Com SetThreadDpiAwarenessContext, cada thread pode ter um contexto de reconhecimento de DPI individual que pode ser diferente do modo de reconhecimento de DPI em todo o processo. O contexto de reconhecimento de DPI de um thread é representado com o tipo DPI_AWARENESS_CONTEXT e se comporta das seguintes maneiras:

  • Um thread pode ter seu contexto de reconhecimento de DPI alterado a qualquer momento.
  • Todas as chamadas à API feitas após a alteração do contexto serão executadas no contexto de DPI correspondente (e podem ser virtualizadas).
  • Quando uma janela é criada, sua conscientização de DPI é definida como o reconhecimento de DPI do thread de chamada nesse momento.
  • Quando o procedimento de janela de uma janela é chamado, o thread é alternado automaticamente para o contexto de reconhecimento de DPI que estava em uso quando a janela foi criada.

Um cenário comum para o uso de SetThreadDpiAwarenessContext é o seguinte: Comece com um thread em execução com um contexto (como DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) alterne temporariamente para um contexto diferente (DPI_AWARENESS_CONTEXT_UNAWARE), crie uma janela e alterne imediatamente o contexto do thread de volta para seu estado anterior. A janela criada terá um contexto de DPI de DPI_AWARENESS_CONTEXT_UNAWARE, enquanto o contexto do thread de chamada será restaurado para DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE com uma chamada subsequente para SetThreadDpiAwarenessContext. Nesse cenário, a janela associada ao thread de chamada seria executada com um contexto por monitor (e, portanto, não seria ampliada por bitmap pelo sistema operacional), enquanto a janela recém-criada não estaria ciente de DPI (e, portanto, seria automaticamente o bitmap estendido em um conjunto de exibição para >dimensionamento de 100%).

A Figura 1 ilustra como o thread de processo main é executado com DPI_AWARENESS_CONTEXT_PER_MONITOR, alterna seu contexto para DPI_AWARENESS_CONTEXT_UNAWARE e cria uma nova janela. Em seguida, a janela recém-criada é executada com um contexto de reconhecimento de DPI de DPI_AWARENESS_CONTEXT_UNAWARE sempre que uma mensagem é enviada para ela ou chamadas à API são feitas a partir dela. Imediatamente após a criação da nova janela, o thread main é restaurado para seu contexto anterior de DPI_AWARENESS_CONTEXT_PER_MONITOR.

diagrama mostrando a conscientização de DPI por monitor em ação

Além do suporte para diferentes modos de reconhecimento de DPI em um único processo que SetThreadDpiAwarenessContext oferece, a seguinte funcionalidade específica de DPI foi adicionada para aplicativos da área de trabalho:

EnableNonClientDpiScaling

Observação

O modo de reconhecimento de DPI V2 por monitor habilita automaticamente essa funcionalidade e chamar EnableNonClientDpiScaling é, portanto, desnecessário em aplicativos que a usam.

Chamar EnableNonClientDpiScaling de dentro de um manipulador de WM_NCCREATE de janela resultará na área não cliente de uma janela de nível superior dimensionando automaticamente para DPI. Se a janela de nível superior estiver com reconhecimento de DPI por monitor (seja porque o processo em si tem reconhecimento de DPI por monitor ou porque a janela foi criada dentro de um thread com reconhecimento de DPI por monitor), a barra de legenda, barras de rolagem, menus e barras de menu dessas janelas serão dimensionadas em DPI sempre que a DPI da janela for alterada.

Observe que as áreas não cliente de uma janela filho, como barras de rolagem não cliente de um controle de edição filho, não serão dimensionadas automaticamente quando essa API for usada.

Observação

EnableNonClientDpiScaling deve ser chamado do manipulador de WM_NCCREATE .

As APIs *ForDpi
  • Várias APIs usadas com frequência, como GetSystemMetrics , não têm nenhum contexto de um HWND e, portanto, não têm como deduzê-lo para seus valores retornados. Chamar essas APIs de um thread que está em execução em um modo ou contexto de reconhecimento de DPI diferente pode retornar valores que não são dimensionados para o contexto do thread de chamada. GetSystemMetricForDpi, SystemParametersInfoForDpi e AdjustWindowRectExForDpi executarão a mesma funcionalidade que seus equivalentes sem reconhecimento de DPI, mas usarão uma DPI como argumento e inferirão o reconhecimento de dpi do contexto do thread atual.

  • GetSystemMetricForDpi e SystemParametersInfoForDpi retornarão valores de métrica do sistema dimensionados por DPI e valores de parâmetro do sistema de acordo com esta equação:

    GetSystemMetrics(...) @ dpi == GetSystemMetricsForDpi(..., dpi)

    Portanto, chamar GetSystemMetrics (ou SystemParametersInfoForDpi), ao executar em um dispositivo com um determinado valor de DPI do sistema, retornará o mesmo valor que suas variantes com reconhecimento de DPI (GetSystemMetricsForDpi e SystemParametersInfoForDpi) retornarão o mesmo valor de DPI que a entrada.

  • AdjustWindowRectExForDpi usa um HWND e calculará o tamanho necessário de um retângulo de janela de uma maneira sensível a DPI.

GetDpiForWindow
GetDpiForWindow retornará o DPI associado ao HWND fornecido. A resposta dependerá do modo de reconhecimento de DPI do HWND:
Modo de Reconhecimento de DPI do HWND Valor retornado
Inconscientes 96
Sistema O DPI do sistema
Per-Monitor A DPI de exibição na qual a janela de nível superior associada está localizada principalmente
(Se uma janela filho for fornecida, o DPI da janela pai de nível superior correspondente será retornado)
GetDpiForSystem

Chamar GetDpiForSystem é mais eficiente do que chamar GetDC e GetDeviceCaps para obter o DPI do sistema.

Qualquer componente que possa estar em execução em um aplicativo que usa o reconhecimento de DPI de subprocesso não deve assumir que o DPI do sistema está estático durante o ciclo de vida do processo. Por exemplo, se um thread em execução em DPI_AWARENESS_CONTEXT_UNAWARE contexto de conscientização consultar a DPI do sistema, a resposta será 96. No entanto, se esse mesmo thread mudar para DPI_AWARENESS_CONTEXT_SYSTEM contexto de conscientização e consultar o DPI do sistema novamente, a resposta poderá ser diferente. Para evitar o uso de um valor de DPI do sistema armazenado em cache (e possivelmente obsoleto), use GetDpiForSystem para recuperar o DPI do sistema em relação ao modo de reconhecimento de DPI do thread de chamada.