Este artigo foi traduzido por máquina.

Windows com C++

Escreva aplicativos com alto DPI para o Windows 8.1

Kenny Kerr

Kenny Kerr
Windows 8 introduziu um novo modelo de programação para aplicativos Windows, com base no Windows Runtime (WinRT), que permite aos usuários alterar dinamicamente o tamanho dos elementos da tela com uma configuração de PC. Você pode ver o que parece essa opção nas configurações do PC em Figura 1. Na minha área de trabalho, as opções são padrão e Larger. Na minha Surface Pro, as opções são menores e padrão. Depende do dispositivo e, em especial, a resolução vertical da mostra anexada. Mais importante, apps da loja do Windows recebem um evento sempre que esta opção é alterada e, portanto, pode atualizar dinamicamente o seu código de renderização para refletir o fator de escala atual.

Windows 8.1 PC Setting Affecting Windows Store Apps
Figura 1 configuração de PC Windows 8.1 afetando Apps da loja do Windows

O desktop no Windows 8, no entanto, manteve-se estática. Aplicações desktop continuaram a ser servidos por uma configuração de DPI do sistema, quaisquer alterações que só entrem em vigor depois que o usuário assina e volta de novo, forçando eficazmente todos os aplicativos para desligar e reiniciar. Você pode ver esta opção, que está ainda disponível em Windows 8.1, no Figura 2, com suas opções mais granulares de menor, médio, maior e Extra grande. Lembra-me de uma viagem para o café local. Aqui, também, as opções que podem estar disponíveis dependem o exibe anexado. Meu Surface Pro, por exemplo, inclui apenas menor, médio e Larger.

Pre-Windows 8.1 PC Setting Affecting Desktop Applications
Figura 2 configuração de PC pré-Windows 8.1 que afetam aplicativos de Desktop

Escusado será dizer, esta dividida personalidade — como muitas coisas no Windows 8 — pode ser um pouco confuso para o desenvolvedor, quanto mais o usuário. Enquanto Windows 8.1 realmente não lidar com a confusão de forma significativa, que finalmente permite que aplicativos de desktop, da mesma forma, lidar com DPI dimensionamento dinamicamente, assim, o usuário já não é forçado a fechar tudo e abrir uma nova sessão de logon. Mas Windows 8.1 vai muito mais longe e realmente traz nova vida para configurações de vários monitores.

Enquanto a janela no Figura 2parece bastante semelhante ao que estava disponível no Windows 8, agora ostenta uma pequena caixa de seleção que foi adicionada no Windows 8.1. Embora esteja marcada Figura 2, o padrão é desmarcado. O título da caixa de seleção, "Deixe-me escolher um nível de dimensionamento para todos os meus monitores," aponta para a capacidade de outra que há de nova no Windows 8.1: a capacidade de monitores diferentes ter diferentes fatores de escala. O título da caixa de seleção é um pouco confuso, como limpar esta caixa de seleção ainda oferece valor para os usuários que têm apenas um único monitor. Nesse caso, ele ainda oferece ao usuário a opção de mudar o fator de escala dinamicamente. Se assinalar esta opção realmente representa um modo de legado ou compatibilidade para comportamento DPI. Então, se você tiver vários monitores — ou, mais importante, independentemente de quantos monitora os usuários normalmente podem usar — você vai querer vir aos apertos com essas novas opções. Eles vai afetar seus aplicativos, quer gostes ou não. Desmarcando esta opção revela a janela no Figura 3. Novamente, este agora é o padrão no Windows 8.1.

Windows 8.1 PC Setting Affecting Dynamic and Per-Monitor Scaling for the Desktop
Figura 3 configuração de PC Windows 8.1 afetando dinâmico e dimensionamento para o Desktop por Monitor

Se você pensar sobre isso, não há realmente nenhuma diferença entre Figura 2 e Figura 3. O primeiro utiliza quatro botões de rádio e o último usa um controle deslizante com quatro posições possíveis. A única diferença é que as alterações para o controle deslizante efeito imediatamente, ou em menos, logo que você tocar no botão Apply. Isto é muito a mesma experiência, pelo menos para o usuário, como a opção de dimensionamento para aplicativos Windows Store. Altera para a seleção do botão de rádio, no entanto, só terão efeito na próxima vez que o usuário assina em.

Os quatro valores possíveis, para o controle deslizante ou botões de rádio, correspondem ao DPI quatro fatores de escala e são ilustrados em Figura 4. Como um desenvolvedor, aviso para não ler muito para os valores DPI específicos. Eles devem refletir a densidade de pixel ou de resolução da tela, mas na realidade, muitos fatores influenciam o valor DPI — como fator de forma e distância para a tela — para que o valor efetivo de DPI você acabar usando tem pouco a ver com uma polegada real. Estas quatro opções também representam todo o espectro de possibilidades, mas o que pode oferecer um PC particular depende da resolução vertical de suas exposições.

Figura 4 sistema fatores de escala

DPI Porcentagem
96 100
120 125
144 150
192 200

Isso é ilustrado no Figura 5. Estes limites destinam-se a manter elementos de interface do usuário de ficar cortada fora da parte inferior do visor. Se seu monitor tem menos de 900 linhas de resolução vertical, então você não terá todas as opções e os 100% fator de escala será tudo que existe. À medida que aumenta a resolução vertical do monitor, você é apresentado com mais opções, até chegar a 1.440 linhas de resolução vertical, no ponto em que você experimenta todos os quatro possíveis opções. Essas opções não, no entanto, afeta igualmente a escala em todos os monitores. Este é de onde vem o conceito de dimensionamento de DPI por monitor. Não é inteiramente óbvio à primeira vista porque o sistema operacional leva em conta tanto a resolução vertical, bem como o DPI nativo para a exibição física.

Figura 5 dimensionamento opções em relação à resolução Vertical

Resolução Opções de dimensionamento
… – 900 100%
900 – 1079 100% – 125%
1080 – 1439 100% – 125% – 150%
1440 – ... 100% – 125% – 150% – 200%

Como um desenvolvedor de aplicações desktop, é importante perceber que agora existem dois fatores de escala que podem estar em jogo. Há o fator de escala de DPI de sistema e então há um fator de escala DPI por monitor. O fator de escala do sistema DPI corresponde a um dos valores de Figura 4— com excepção dos dispositivos tais como Windows Phone — e permanece constante durante a duração da sessão de logon. O valor DPI do sistema baseia-se no botão de rádio inicial mostrado na Figura 2ou a posição do controle deslizante mostrado na Figura 3.

Para recuperar o sistema, o valor DPI, comece por se apoderar do contexto de dispositivo de área de trabalho. Sim, isso se resume a antiga API GDI , mas não tem nada a ver com o renderização GDI e é apenas uma nota de rodapé histórica. Primeiro, para obter um identificador que representa o contexto de dispositivo de desktop, você chamar a função GetDC com um nullptr em vez de um identificador de janela. Este valor especial indica que você quer o contexto de dispositivo para o desktop como um todo, em vez de uma janela específica:

auto dc = GetDC(nullptr);

Naturalmente, você deve lembrar de liberar este identificador quando terminar:

ReleaseDC(nullptr, dc);

O primeiro parâmetro é o identificador para a janela a que se refere o contexto de dispositivo. Novamente, um valor de nullptr representa a área de trabalho. Agora, dado o contexto de dispositivo, você pode usar a função GetDeviceCaps para recuperar o sistema DPI fator de escala para o x e y eixos da seguinte forma:

auto x = GetDeviceCaps(dc, LOGPIXELSX);
auto y = GetDeviceCaps(dc, LOGPIXELSY);

Ter um valor diferente para o x e y eixos datas de volta à idade média quando impressoras rotineiramente ofereceram dimensionamento diferentes fatores horizontalmente e verticalmente. Eu nunca me deparei com uma exposição que oferece pixels não quadrados, mas ouvi dizer que eles existem em algumas indústrias para as quais foram desenvolvidas placas gráficas especiais. LOGPIXELSX e LOGPIXELSY representam o número de pixels por polegada lógica ao longo da largura e altura da área de trabalho. Novamente, isso é um pouco de lógica e não se destina a reflectir a realidade. Além disso, tendo em conta estes são os valores do sistema DPI, significa que eles são as mesmas para todos os monitores que abrangem a área de trabalho independentemente de como relativamente grande ou pequeno como podem ser. Aí que reside o problema.

Se você conectar seu tablet Dell local Pro 8 com sua tela de 8 polegadas em uma matriz de monitores Dell UltraSharp de 30 polegadas, você vai ter que fazer uma escolha difícil. Eu rotineiramente ligar dois ou três monitores vastamente diferentes na placa de vídeo do meu desktop. Um fator de escala de DPI de todo o sistema apenas não cortá-la. O que é necessário é que cada monitor tem um fator de escala DPI, ideal para seu tamanho relativo ou resolução. Isto é exatamente o que Windows 8.1 oferece com seu DPI por monitor dimensionamento de suporte.

8.1 Windows oferece três diferentes níveis de reconhecimento de DPI. Isto é óbvio quando você olha para a janela do Process Explorer, mostrado na Figura 6. Você pode ver que algumas aplicações são completamente DPI-inconsciente, como no caso do prompt de comando. A maioria dos aplicativos que foram escritos para o Windows 7 e Windows 8 estão ciente do sistema, ou pelo menos pretendem ser. Exemplos incluem o Microsoft Outlook e a calculadora. Reconhecimento de DPI por monitor é o ideal e terceiro nível de consciência. Exemplos em Figura 6 incluem o Internet Explorer e Microsoft Word. Curiosamente, a palavra não é realmente por monitor ciente no momento, mas eu ter desativado a exibição de dimensionamento para o Word evitar manchas. Isto tem o efeito de substituir o reconhecimento de DPI de processo para que o desktop window manager não vai escalar a janela. Isto foi feito com uma opção de compatibilidade.

Process Explorer Showing Purported DPI Awareness
Figura 6 processo Explorer apresentando supostas reconhecimento de DPI

O ponto principal é que é melhor ter certeza de seus apps estão ciente por monitor e escala em conformidade. Como exatamente você pode fazer isso? Continue lendo.

Diferentes aplicações fazem várias afirmações sobre seu nível de consciência DPI e em seguida a janela do desktop manager — o serviço responsável por compor as várias janelas de aplicativo juntos — determina como a escala diferente de aplicativo do windows com base em suas reivindicações individuais de DPI, tal que todas as janelas são apresentadas em uma escala consistente. Se um aplicativo afirma ser ciente por monitor, o desktop window manager não vai escalar a janela em tudo e assume que o aplicativo sabe que ele está fazendo. Se um aplicativo está ciente do sistema, o gerente de janela desktop dimensionará a janela com base na suposição de que ele foi processado para o fator de escala de DPI do sistema retornado pela função GetDeviceCaps que mencionei anteriormente. E se um aplicativo é sem reconhecimento de DPI, o gerente de janela desktop assume a janela foi processada para o tradicional 96 DPI e dimensiona-lo nesse sentido.

Antes que eu considero como realmente escrever um bem comportamento, per-­monitor, aplicativo ciente, falarei sobre o que é preciso para fazer tal afirmação. Windows Vista introduziu a função SetProcessDPIAware durante para marcar o processo de chamada como ciente. Esta função levou sem argumentos e simplesmente virou-se no reconhecimento de DPI, por assim dizer. Isso foi antes de sensibilização por monitor, então isso foi um estado binário simples. Ou você é ciente ou você não está. Windows 8.1 introduziu uma nova função chamada SetProcessDpiAwareness que fornece mais controle sobre este nível de consciência:

VERIFY_(S_OK, SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE));

Isso define o reconhecimento de DPI do processo para o determinado nível. Constantes estão disponíveis para cobrir os três estados possíveis: DPI-inconscientes, sistema ciente e ciente por monitor. Há também uma correspondente função GetProcessDpiAwareness para consultar esse valor para um determinado processo. Mas usar o código para definir o nível de consciência tem uma série de inconvenientes. Você precisa ser cuidadoso para chamar essas funções no início de vida do seu aplicativo. Às vezes isto não é prático e faz com que uma série de questões, ao misturar arquivos executáveis e DLLs. Uma solução melhor é chamada para.

Aplicativos podem incorporar ou distribuir um arquivo de manifesto — juntamente com seus binários — que contém dicas para a shell ou o carregador do Windows para usar na determinação de como preparar o processo de aplicação, antes de qualquer código começa a correr. O shell do Windows usa isto para uma variedade de finalidades, incluindo recursos de segurança, informações de assembly de dependência e, sim, afirma DPI-consciência.

Esses arquivos de manifesto são apenas arquivos de texto. Eles estão incorporados dentro do executável como um recurso do Win32 tradicional ou eles simplesmente são enviados juntamente com o executável. Você pode, por exemplo, dar uma olhada no arquivo de manifesto para o Word usando a ferramenta de manifesto incluída com o Windows SDK usando o seguinte comando:

mt -inputresource:"C:\ ...
\WINWORD.EXE" -out:manifest.xml

Eu tenho omitidos o caminho completo para conservar o espaço. Se tudo correr bem, você será recompensado com um documento XML no diretório atual. Você pode ver o que parece Figura 7. No meio de alguma outra forma informações independentes sobre o executável do Word são o elemento XML de dpiAware e seu valor de true.

The Microsoft Word Manifest File
Figura 7 o arquivo de manifesto da Microsoft Word

Se um aplicativo não tem um arquivo de manifesto e não usa a abordagem programática de ajustar o seu nível de consciência, então presume ser sem reconhecimento de DPI. Se um aplicativo tem um arquivo de manifesto, mas não contém o elemento dpiAware, então novamente presume ser sem reconhecimento de DPI. No Windows 7, a presença do elemento dpiAware é suficiente para que o sistema assume que está ciente. Não importa qual o valor que contém este elemento, ou se ele mesmo tem um valor. Janela 8 é um pouco mais específica. Ele espera um valor começando com T, no pressuposto de que diz "É verdade". Caso não importa.

Então que lida com DPI-inconsciente e sistema de aplicativos com reconhecimento de DPI. Quanto a aplicativos por monitor ciente? Bem, quando este recurso foi desenvolvido, um novo valor foi escolhido para o elemento de dpiAware: Por Monitor. O desejo de permitir que os desenvolvedores de aplicativo produzir um binário simples capaz de atingir tanto o Windows 7 e o Windows 8 com suporte para reconhecimento de DPI do sistema, bem como o reconhecimento de DPI por monitor no Windows 8, levou a um novo valor a ser escolhido: True/PM. Isso permite que o Windows 7 e Windows 8 aceitar o binário como sistema ciente, e Windows 8.1 aceita-lo como por monitor ciente. Você pode, naturalmente, continuar a usar por Monitor, se você só precisar dar suporte Windows 8.1.

Infelizmente, Visual C++ 2013 ainda não oferece suporte a esse novo valor, mas há uma maneira de fazer acontecer. A ferramenta de manifesto que é parte da compilação Visual C++ projeto tem uma opção para mesclar em arquivos de manifesto adicionais. Tudo o que você precisa fazer é criar um arquivo de texto com o elemento dpiAware correto e o valor e fundi-lo para sua compilação. O código a seguir mostra o XML que você precisa:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
  manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns=
        "https://schemas.microsoft.com/SMI/2005/WindowsSettings">
        True/PM</dpiAware>
    </windowsSettings>
  </application>
</assembly>

Uma vez que você criou o arquivo de manifesto, você pode simplesmente atualizar as configurações do seu projeto, conforme ilustrado no Figura 8. O manifesto é definido como um arquivo de manifesto adicional e o reconhecimento de DPI é definido como None.

Manifest Tool Options
Figura 8 opções de ferramenta de manifesto

Quando se trata de renderização a janela do seu aplicativo, você precisa certificar-se de que você tem o fator de escala DPI certo. Se você está cuidando de um aplicativo herdado com raízes profundas no GDI gráfica e controles de usuário, então você provavelmente vai experimentar um monte de desafios na tentativa de fazê-lo por-­monitorar ciente. Estes são os mesmos desafios enfrentados pelas equipes de Windows e Office da Microsoft. Mas se você estiver seguindo minha coluna, sabe que o Direct2D é o caminho a percorrer. Direct2D cuida de todos os seu DPI dimensionamento de necessidades e apresenta-lhe com um sistema de coordenadas lógico independente dos pixels físicos e o fator de dimensionamento DPI. Claro, para Direct2D ser capaz de fazer isso de forma eficaz, você precisa contar que valores DPI para usar para um determinado alvo processam.

Tradicionalmente, aplicações de Direct2D simplesmente obtido os valores DPI a fábrica Direct2D, que por sua vez simplesmente chamado GetDeviceCaps, como eu anteriormente ilustrado. Mas isto já não é suficiente. Como mostrei, os valores DPI agora podem mudar rapidamente, e o valor fornecido pelo GetDeviceCaps só é útil se você está tentando se encaixar com o renderização de um sistema aplicativo ciente.

Em vez disso, logo depois que você criou seu Direct2D processar alvo, você precisa consultar o valor DPI para o monitor mais próximo de sua janela:

auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);

Dado esse identificador de monitor, você pode chamar a função GetDpiForMonitor para recuperar os valores DPI para este monitor específico:

auto x = unsigned {};
auto y = unsigned {};
VERIFY_(S_OK, GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y));

O valor DPI efetivo para um determinado monitor não necessariamente corresponde exatamente à opções apresentadas no Figura 4. Mais uma vez depende de uma série de fatores, incluindo a resolução, o DPI física do visor e a distância assumida para a superfície de exibição. Finalmente, você pode chamar o método de SetDpi do destino o Direct2D e Direct2D vai cuidar de dimensionamento corretamente seu conteúdo, conforme necessário.

Eu mencionei que isto pode acontecer dinamicamente. Seu aplicativo pode ser executado e de repente o usuário altera a escala com a janela mostrada na Figura 3 ou arrasta a janela para outro monitor com um fator de escala diferente DPI. Nesses casos, o shell do Windows irá enviar janelas de aplicativos por monitor ciente uma nova janela de mensagem chamada WM_DPICHANGED.

Dentro de seu manipulador de mensagem, pode chamar mais uma vez o MonitorFromWindow e GetDpiForMonitor funções para obter os valores DPI efetivo para o monitor atual e então simplesmente atualizar seu Direct2D processam destino com seu método de SetDpi novamente. Alternativamente, WPARAM a mensagem pacotes de x e y valores DPI então isso se torna uma simples questão de extrair as palavras de baixa e alta ordem:

auto x = LOWORD(wparam);
  auto y = HIWORD(wparam);

LPARAM a mensagem WM_DPICHANGED é, no entanto, indispensável. Ele fornece uma nova posição sugerida e o tamanho de sua janela baseada o novo fator de escala que está agora em vigor. Isso garante que enquanto Direct2D cuida-se de dimensionar seu conteúdo, também é possível dimensionar o tamanho real da sua janela no desktop e posicioná-lo adequadamente. LPARAM a mensagem é apenas um ponteiro para uma estrutura RECT:

auto rect = *reinterpret_cast<RECT *>(lparam);

Você pode então simplesmente chamar a função SetWindowPos para atualizar a posição e o tamanho da sua janela:

VERIFY(SetWindowPos(window,
                    0, // No relative window
                    rect.left,
                    rect.top,
                    rect.right - rect.left,
                    rect.bottom - rect.top,
                    SWP_NOACTIVATE | SWP_NOZORDER));

E isso é o suficiente. Se você investiu em Direct2D, está a poucos passos de distância sendo ciente por monitor. Alguns aplicativos têm feito o salto, para que seu aplicativo está vinculado a destacar-se! Se você tem um aplicativo Windows Presentation Foundation (WPF) existente, há também uma amostra disponível em bit.ly/IPDN3p que mostra como integrar estes mesmos princípios descritos neste artigo em sua base de código do WPF.

Figura 9 fornece um exemplo de um aplicativo por monitor ciente, Dimensionando automaticamente tanto o seu conteúdo e o seu tamanho de janela com base nas mensagens WM_DPICHANGED. O emoticon indica qual monitor a janela atualmente está posicionada em. Para um exemplo mais interativo, Confira meu curso Pluralsight no bit.ly/1fgTifi, onde você também pode obter o código de exemplo para esta aplicação.

Per-Monitor DPI-Aware Application
Figura 9 por Monitor aplicativo ciente

Kenny Kerr é um programador de computador com base no Canadá, bem como um autor para Pluralsight e MVP da Microsoft. Blogs de He em kennykerr.ca e você pode segui-lo no Twitter em twitter.com/kennykerr.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: James Clarke (Microsoft)