Desenvolvimento de aplicativos da área de trabalho com alta densidade de pixels (DPI) no Windows

Esse conteúdo é direcionado para desenvolvedores que estão procurando atualizar aplicativos da área de trabalho para lidar com alterações de fator de escala de exibição (pontos por polegada ou DPI) dinamicamente, permitindo que seus aplicativos sejam nítidos em qualquer exibição em que sejam renderizados.

Para começar, se você estiver criando um novo aplicativo do Windows do zero, é altamente recomendável criar um aplicativo UWP (Plataforma Universal do Windows). Os aplicativos UWP são dimensionados automaticamente e dinamicamente para cada exibição em que estão sendo executados.

Aplicativos de área de trabalho usando tecnologias de programação mais antigas do Windows (programação bruta do Win32, Windows Forms, WPF (Windows Presentation Framework), etc.) não é possível lidar automaticamente com o dimensionamento de DPI sem trabalho adicional do desenvolvedor. Sem esse trabalho, os aplicativos aparecerão desfocados ou de tamanho incorreto em muitos cenários de uso comuns. Este documento fornece contexto e informações sobre o que está envolvido na atualização de um aplicativo da área de trabalho para renderizar corretamente.

Fator de Escala da Tela & DPI

À medida que a tecnologia de exibição progrediu, os fabricantes de painéis de exibição empacotaram um número crescente de pixels em cada unidade de espaço físico em seus painéis. Isso fez com que os pontos por polegada (DPI) dos painéis de exibição modernos fossem muito mais altos do que historicamente. No passado, a maioria dos displays tinha 96 pixels por polegada linear de espaço físico (96 DPI); em 2017, as exibições com quase 300 DPI ou superior estão prontamente disponíveis.

A maioria dos frameworks legados de interface de usuário para desktop tem pressupostos embutidos de que o DPI da tela não mudará durante a execução do processo. Essa suposição não se sustenta mais, pois os DPIs da tela costumam mudar várias vezes ao longo do ciclo de vida de um processo de aplicativo. Alguns cenários comuns em que o fator de escala de exibição/as alterações de DPI são:

  • Configurações de vários monitores em que cada exibição tem um fator de escala diferente e o aplicativo é movido de uma exibição para outra (como uma exibição de 4K e 1080p)
  • Conectar e desconectar um laptop com DPI alto a um monitor externo com DPI baixo (ou vice-versa)
  • Conectar-se por Área de Trabalho Remota de um laptop/tablet com alta DPI a um dispositivo com baixa DPI (ou vice-versa)
  • Fazer com que as configurações de fator de escala de exibição sejam alteradas enquanto os aplicativos estão em execução

Nesses cenários, os aplicativos UWP são redesenhados automaticamente para o novo DPI. Por padrão e sem trabalho adicional do desenvolvedor, os aplicativos da área de trabalho não funcionam. Aplicativos de área de trabalho que não fazem esse trabalho extra para responder às alterações de DPI podem parecer desfocados ou de tamanho incorreto para o usuário.

Modo de Reconhecimento de DPI

Os aplicativos da área de trabalho devem informar ao Windows se dão suporte ao dimensionamento de DPI. Por padrão, o sistema considera os aplicativos da área de trabalho como não reconhecendo DPI e redimensiona suas janelas por bitmap. Ao definir um dos seguintes modos de reconhecimento de DPI disponíveis, os aplicativos podem dizer explicitamente ao Windows como desejam lidar com o dimensionamento de DPI:

DPI sem conhecimento

Aplicativos sem conhecimento de DPI são renderizados com um valor de DPI fixo de 96 (100%). Sempre que esses aplicativos forem executados em uma tela com uma escala de exibição maior que 96 DPI, o Windows estenderá o bitmap do aplicativo para o tamanho físico esperado. Isso faz com que o aplicativo pareça desfocado.

Reconhecimento de DPI do Sistema

Os aplicativos de área de trabalho que estão cientes do DPI do sistema normalmente recebem o DPI do monitor conectado primário a partir do momento da entrada do usuário. Durante a inicialização, eles dispõem a interface do usuário adequadamente (controles de dimensionamento, escolher tamanhos de fonte, carregar ativos etc.) usando esse valor de DPI do Sistema. Dessa forma, os aplicativos compatíveis com DPI do sistema não são escalados por DPI (com o bitmap ampliado) pelo Windows em telas que renderizam nessa única configuração de DPI. Quando o aplicativo é movido para uma tela com um fator de escala diferente, ou se o fator de escala da tela for alterado de outra forma, o Windows redimensionará por bitmap as janelas do aplicativo, fazendo com que pareçam borradas. Na prática, os aplicativos de área de trabalho com reconhecimento do DPI do sistema só são renderizados com nitidez em um único fator de escala da tela, ficando borrados sempre que o DPI muda.

Reconhecimento de DPI por monitor e por monitor (V2)

É recomendável que os aplicativos da área de trabalho sejam atualizados para usar o modo de reconhecimento de DPI por monitor, permitindo que eles sejam renderizados imediatamente corretamente sempre que o DPI for alterado. Quando um aplicativo informa ao Windows que quer ser executado nesse modo, o Windows não redimensionará o aplicativo por bitmap quando o DPI mudar; em vez disso, enviará WM_DPICHANGED para a janela do aplicativo. Nesse caso, passa a ser de inteira responsabilidade do aplicativo fazer o próprio redimensionamento para o novo DPI. A maioria das estruturas de interface do usuário usadas por aplicativos da área de trabalho (controles comuns do Windows (comctl32), Windows Forms, Windows Presentation Framework etc.) não dão suporte ao dimensionamento automático de DPI, exigindo que os desenvolvedores redimensionem e reposicionem o conteúdo de suas próprias janelas.

Há duas versões de Per-Monitor awareness para as quais um aplicativo pode se registrar: a versão 1 e a versão 2 (PMv2). Registrar um processo como em execução no modo de reconhecimento PMv2 resulta em:

  1. A notificação do aplicativo quando há alteração de DPI (tanto dos HWNDs de nível superior quanto dos filhos)
  2. O aplicativo que vê os pixels brutos de cada exibição
  3. O aplicativo nunca é redimensionado por bitmap pelo Windows
  4. Dimensionamento automático de DPI da área não cliente (barra de título da janela, barras de rolagem etc.) pelo Windows
  5. Caixas de diálogo Win32 (de CreateDialog) são dimensionadas automaticamente em DPI pelo Windows
  6. Recursos de bitmap desenhados pelo tema em controles comuns (caixas de seleção, fundos de botão etc.) sendo renderizados automaticamente com o fator de escala de DPI adequado

Quando executados no modo de reconhecimento Per-Monitor v2, os aplicativos são notificados quando o DPI muda. Se um aplicativo não se redimensionar para o novo DPI, a interface do usuário do aplicativo aparecerá muito pequena ou muito grande (dependendo da diferença nos valores de DPI anterior e novo).

Nota

O suporte ao Per-Monitor V1 (PMv1) é muito limitado. É recomendável que os aplicativos usem PMv2.

A tabela a seguir mostra como os aplicativos serão renderizados em cenários diferentes:

Modo de Reconhecimento de DPI Versão do Windows lançada Visão do aplicativo sobre o DPI Comportamento na alteração do DPI
Alheio N/A Todas as exibições são 96 DPI Esticamento de bitmap (borrado)
Sistema Vista Todas as exibições têm a mesma DPI (a DPI da exibição primária no momento em que a sessão do usuário atual foi iniciada) Estiramento de bitmap (borrado)
Per-Monitor 8.1 O DPI da tela em que a janela do aplicativo está localizada principalmente
  • O HWND de nível superior é notificado sobre a alteração do DPI
  • Sem dimensionamento de DPI em quaisquer elementos da interface do usuário.

Per-Monitor V2 Windows 10 Creators Update (1703) O DPI da tela onde a janela do aplicativo está localizada principalmente
  • Os HWNDs filho de e de nível superior são notificados sobre a alteração do DPI

Escalonamento automático de DPI de:
  • Área não cliente
  • Bitmaps desenhados por tema em controles comuns (comctl32 V6)
  • Caixas de diálogo (CreateDialog)

Reconhecimento de DPI por Monitor (V1)

O modo de reconhecimento de DPI por monitor V1 (PMv1) foi introduzido no Windows 8.1. Esse modo de reconhecimento de DPI é muito limitado e oferece apenas a funcionalidade listada abaixo. É recomendável que os aplicativos para desktop usem o modo de reconhecimento Per-Monitor v2, com suporte a partir do Windows 10, versão 1703.

O suporte inicial para reconhecimento por monitor só oferecia aos aplicativos o seguinte:

  1. HWNDs de nível superior são notificados sobre uma alteração de DPI e recebem um novo tamanho sugerido
  2. O Windows não fará o bitmap alongar a interface do usuário do aplicativo
  3. O aplicativo vê todas as exibições em pixels físicos (consulte virtualização)

No Windows 10 1607 ou superior, os aplicativos PMv1 também podem chamar EnableNonClientDpiScaling durante WM_NCCREATE para solicitar que o Windows dimensione corretamente a área não cliente da janela.

Suporte ao dimensionamento de DPI por monitor por UI Framework/Tecnologia

A tabela abaixo mostra o nível de suporte de reconhecimento de DPI por monitor oferecido por várias estruturas de interface do usuário do Windows a partir do Windows 10 1703:

Framework / Tecnologia Apoio Versão do sistema operacional Dimensionamento de DPI controlado por Leitura adicional
UWP (Plataforma Universal do Windows) Completo 1607 Estrutura de interface do usuário plataforma Universal do Windows (UWP)
Controles Nativos do Win32/Controles Comuns V6 (comctl32.dll)
  • Mensagens de notificação de alteração de DPI enviadas a todos os HWNDs
  • Os ativos desenhados por tema são renderizados corretamente em controles comuns
  • Dimensionamento automático de DPI para caixas de diálogo
1703 Aplicação Exemplo do GitHub
Windows Forms Dimensionamento automático de DPI por monitor limitado para alguns controles 1703 Estrutura de interface do usuário alto suporte a DPI no Windows Forms
Estrutura de Apresentação do Windows (WPF) Aplicativos WPF nativos ajustarão a escala de DPI do WPF hospedado em outros frameworks, e outros frameworks hospedados no WPF não serão ajustados automaticamente. 1607 Estrutura de interface do usuário Exemplo do GitHub
GDI Nenhum N/A Aplicação Consulte Dimensionamento GDI para High DPI
GDI+ Nenhum N/A Aplicação Consulte Dimensionamento High-DPI do GDI
MFC Nenhum N/A Aplicação N/A

Atualizando aplicativos existentes

Para atualizar um aplicativo de área de trabalho existente para lidar com o dimensionamento de DPI corretamente, ele precisa ser atualizado de modo que, no mínimo, as partes importantes de sua interface do usuário sejam atualizadas para responder às alterações de DPI.

A maioria dos aplicativos da área de trabalho é executada no modo de reconhecimento de DPI do sistema. Os aplicativos com reconhecimento de DPI do sistema normalmente são dimensionados para o DPI da exibição primária (a exibição em que a bandeja do sistema estava localizada no momento em que a sessão do Windows foi iniciada). Quando o DPI mudar, o Windows redimensionará por bitmap a interface do usuário desses aplicativos, o que geralmente faz com que eles fiquem borrados. Ao atualizar um aplicativo com reconhecimento de DPI do Sistema para se tornar ciente do DPI por monitor, o código que manipula o layout da interface do usuário precisa ser atualizado de modo que ele seja executado não apenas durante a inicialização do aplicativo, mas também sempre que uma notificação de alteração de DPI (WM_DPICHANGED no caso do Win32) for recebida. Isso normalmente envolve revisitar quaisquer suposições no código de que a interface do usuário só precisa ser dimensionada uma vez.

Além disso, no caso da programação win32, muitas APIs Win32 não têm nenhum contexto de DPI ou exibição, portanto, elas só retornarão valores relativos ao DPI do Sistema. Pode ser útil usar o grep no seu código para procurar por algumas dessas APIs e substituí-las por variantes compatíveis com DPI. Algumas das APIs comuns que têm variantes com reconhecimento de DPI são:

Versão com DPI único versão por monitor
GetSystemMetrics GetSystemMetricsForDpi
AdjustWindowRectEx AdjustWindowRectExForDpi
SystemParametersInfo SystemParametersInfoForDpi
GetDpiForMonitor GetDpiForWindow

Também é uma boa ideia procurar, na sua base de código, tamanhos codificados diretamente que pressupõem um valor de DPI constante, substituindo-os por código que leve corretamente em conta o dimensionamento por DPI. Veja abaixo um exemplo que incorpora todas essas sugestões:

Exemplo:

O exemplo a seguir mostra um caso Win32 simplificado de criação de um HWND filho. A chamada para CreateWindow pressupõe que o aplicativo está em execução em 96 DPI (USER_DEFAULT_SCREEN_DPI constante) e nem o tamanho nem a posição do botão estarão corretos em DPIs mais altos:

case WM_CREATE: 
{ 
    // Add a button 
    HWND hWndChild = CreateWindow(L"BUTTON", L"Click Me",  
        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,  
        50,  
        50,  
        100,  
        50,  
        hWnd, (HMENU)NULL, NULL, NULL); 
} 

O código atualizado abaixo mostra:

  1. O código de criação da janela dimensionando por DPI a posição e o tamanho do HWND filho de acordo com o DPI da janela pai
  2. Respondendo à alteração do DPI reposicionando e redimensionando o HWND filho
  3. Tamanhos codificados diretamente foram removidos e substituídos por código que se adapta a alterações de DPI
#define INITIALX_96DPI 50 
#define INITIALY_96DPI 50 
#define INITIALWIDTH_96DPI 100 
#define INITIALHEIGHT_96DPI 50 

// DPI scale the position and size of the button control 
void UpdateButtonLayoutForDpi(HWND hWnd) 
{ 
    int iDpi = GetDpiForWindow(hWnd); 
    int dpiScaledX = MulDiv(INITIALX_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI); 
    int dpiScaledY = MulDiv(INITIALY_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI); 
    int dpiScaledWidth = MulDiv(INITIALWIDTH_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI); 
    int dpiScaledHeight = MulDiv(INITIALHEIGHT_96DPI, iDpi, USER_DEFAULT_SCREEN_DPI); 
    SetWindowPos(hWnd, hWnd, dpiScaledX, dpiScaledY, dpiScaledWidth, dpiScaledHeight, SWP_NOZORDER | SWP_NOACTIVATE); 
} 
 
... 
 
case WM_CREATE: 
{ 
    // Add a button 
    HWND hWndChild = CreateWindow(L"BUTTON", L"Click Me",  
        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, 
        0, 
        0, 
        0, 
        0, 
        hWnd, (HMENU)NULL, NULL, NULL); 
    if (hWndChild != NULL) 
    { 
        UpdateButtonLayoutForDpi(hWndChild); 
    } 
} 
break; 
 
case WM_DPICHANGED: 
{ 
    // Find the button and resize it 
    HWND hWndButton = FindWindowEx(hWnd, NULL, NULL, NULL); 
    if (hWndButton != NULL) 
    { 
        UpdateButtonLayoutForDpi(hWndButton); 
    } 
} 
break; 

Ao atualizar um aplicativo com reconhecimento de DPI do sistema, algumas etapas comuns a seguir são:

  1. Marque o processo como V2 (reconhecimento de DPI por monitor) usando um manifesto do aplicativo (ou outro método, dependendo das estruturas de interface do usuário usadas).
  2. Torne a lógica de layout da interface do usuário reutilizável e mova-a para fora do código de inicialização do aplicativo, de modo que possa ser reutilizado quando ocorrer uma alteração de DPI (WM_DPICHANGED no caso da programação do Windows (Win32).
  3. Invalide qualquer código que pressupõe que os dados confidenciais de DPI (DPI/fontes/tamanhos/etc.) nunca precisam ser atualizados. É uma prática muito comum armazenar em cache tamanhos de fonte e valores de DPI na inicialização do processo. Ao atualizar um aplicativo para torná-lo compatível com DPI por monitor, os dados sensíveis ao DPI devem ser reavaliados sempre que um novo valor de DPI for detectado.
  4. Quando houver uma alteração de DPI, recarregue (ou rasterize novamente) quaisquer recursos de bitmap para o novo DPI ou, opcionalmente, redimensione por estiramento os recursos de bitmap atualmente carregados até o tamanho correto.
  5. Use grep para localizar APIs que não sejam compatíveis com DPI por monitor e substitua-as por APIs compatíveis com DPI por monitor (quando aplicável). Exemplo: substitua GetSystemMetrics por GetSystemMetricsForDpi.
  6. Teste seu aplicativo em um sistema com vários monitores e vários DPIs.
  7. Para qualquer janela de nível superior em seu aplicativo que você não consiga atualizar para a escala de DPI corretamente, use o dimensionamento de DPI de modo misto (descrito abaixo) para permitir o alongamento de bitmap dessas janelas de nível superior pelo sistema.

Dimensionamento de DPI em modo misto (dimensionamento de DPI por subprocesso)

Ao atualizar um aplicativo para dar suporte à conscientização de DPI por monitor, às vezes ele pode se tornar impraticável ou impossível atualizar todas as janelas do aplicativo de uma só vez. Isso pode ser simplesmente devido ao tempo e ao esforço necessários para atualizar e testar toda a interface do usuário ou porque você não possui todo o código da interface do usuário que precisa executar (se o aplicativo talvez carregue a interface do usuário de terceiros). Nessas situações, o Windows oferece uma maneira de fazer uma transição gradual para o DPI por monitor, permitindo que você execute algumas janelas do seu aplicativo (apenas as de nível superior) no modo original de reconhecimento de DPI, enquanto concentra seu tempo e energia em atualizar as partes mais importantes da interface do usuário.

Veja abaixo uma ilustração da aparência disso: atualize a interface do usuário do aplicativo principal ("Janela Principal" na ilustração) para ser executada com reconhecimento de DPI por monitor enquanto executa outras janelas no modo existente ("Janela Secundária").

diferenças no dimensionamento de DPI entre os modos de conscientização

Antes da atualização de aniversário do Windows 10 (1607), o modo de reconhecimento de DPI de um processo era uma propriedade aplicada a todo o processo. A partir da Atualização de Aniversário do Windows 10, essa propriedade agora pode ser definida para cada janela de nível superior. (Janelas filho devem continuar a corresponder ao tamanho de dimensionamento de seus pais.) Uma janela de nível superior é definida como uma janela sem pai. Normalmente, essa é uma janela "regular" com botões minimizar, maximizar e fechar. O cenário para o qual a percepção de DPI em subprocessos se destina é fazer com que a interface do usuário secundária seja dimensionada pelo Windows (bitmap ampliado), enquanto você concentra seu tempo e recursos em atualizar a interface do usuário primária.

Para habilitar o reconhecimento de DPI de subprocesso, chame SetThreadDpiAwarenessContext antes e depois de qualquer chamada de criação de janela. A janela criada será associada à consciência de DPI que você definiu por meio de SetThreadDpiAwarenessContext. Use a segunda chamada para restaurar o reconhecimento de DPI do thread atual.

Ao usar a escala de DPI por subprocesso, você pode contar com o Windows para realizar parte do ajuste de escala de DPI do seu aplicativo, mas isso pode aumentar a complexidade do seu aplicativo. É importante que você entenda as desvantagens dessa abordagem e da natureza das complexidades que ela apresenta. Para obter mais informações sobre a percepção de DPI de subprocessos, consulte Escalonamento de DPI em modo misto e APIs com percepção de DPI.

Testando suas alterações

Depois de atualizar seu aplicativo para se tornar ciente do DPI por monitor, é importante validar se o aplicativo responde corretamente às alterações de DPI em um ambiente de DPI misto. Alguns detalhes a serem testados incluem:

  1. Movendo janelas de aplicativos para frente e para trás entre exibições de diferentes valores de DPI
  2. Iniciando seu aplicativo em exibições de diferentes valores de DPI
  3. Alterando o fator de escala do monitor enquanto o aplicativo está em execução
  4. Alterando a exibição que você usa como exibição primária, sair do Windowse, em seguida, testar novamente seu aplicativo depois de entrar novamente. Isso é particularmente útil na localização de código que usa tamanhos/dimensões codificados em código.

Erros comuns (Win32)

Não usar o retângulo sugerido fornecido no WM_DPICHANGED

Quando o Windows envia uma mensagem WM_DPICHANGED à janela do aplicativo, essa mensagem inclui um retângulo sugerido que você deve usar para redimensionar sua janela. É fundamental que seu aplicativo use esse retângulo para se redimensionar, como isso fará:

  1. Verifique se o cursor do mouse permanecerá na mesma posição relativa na Janela ao arrastar entre exibições
  2. Impedir que a janela do aplicativo entre em um ciclo de alteração de dpi recursivo em que uma alteração de DPI dispara uma alteração de DPI subsequente, que dispara mais uma alteração de DPI.

Se você tiver requisitos específicos do aplicativo que impedem o uso do retângulo sugerido que o Windows fornece na mensagem WM_DPICHANGED, consulte WM_GETDPISCALEDSIZE. Essa mensagem pode ser usada para dar ao Windows um tamanho desejado que você gostaria de usar depois que a alteração de DPI ocorreu, evitando ainda os problemas descritos acima.

Falta de documentação sobre virtualização

Quando um HWND ou processo é executado sem reconhecimento de DPI ou com reconhecimento apenas do DPI do sistema, ele pode ser redimensionado por bitmap pelo Windows. Quando isso acontece, o Windows redimensiona e converte informações sensíveis ao DPI de algumas APIs para o espaço de coordenadas da thread chamadora. Por exemplo, se um thread sem conhecimento de DPI consultar o tamanho da tela durante a execução em uma exibição de alta DPI, o Windows virtualizará a resposta fornecida ao aplicativo como se a tela estivesse em 96 unidades de DPI. Como alternativa, quando um thread com reconhecimento de DPI do Sistema estiver interagindo com uma exibição em um DPI diferente do que estava em uso quando a sessão do usuário atual foi iniciada, o Windows dimensionará algumas chamadas de API para o espaço de coordenadas que o HWND estaria usando se estivesse em execução em seu fator de escala de DPI original.

Quando você atualiza o aplicativo da área de trabalho para a escala de DPI corretamente, pode ser difícil saber quais chamadas à API podem retornar valores virtualizados com base no contexto do thread; no momento, essas informações não estão documentadas suficientemente pela Microsoft. Esteja ciente de que, se você chamar qualquer API do sistema a partir de um contexto de thread não compatível com DPI ou compatível apenas com o DPI do sistema, o valor retornado poderá ser virtualizado. Dessa forma, verifique se o thread está em execução no contexto de DPI que você espera ao interagir com a tela ou janelas individuais. Ao alterar temporariamente o contexto de DPI de um thread usando SetThreadDpiAwarenessContext, certifique-se de restaurar o contexto antigo quando terminar de evitar causar comportamento incorreto em outro lugar do aplicativo.

Muitas APIs do Windows não têm um contexto de DPI

Muitas APIs herdadas do Windows não incluem um contexto DPI ou HWND como parte de sua interface. Como resultado, os desenvolvedores geralmente precisam fazer um trabalho adicional para lidar com o dimensionamento de qualquer informação confidencial de DPI, como tamanhos, pontos ou ícones. Por exemplo, os desenvolvedores que usam LoadIcon devem ou redimensionar por bitmap os ícones carregados, ou usar APIs alternativas para carregar ícones com o tamanho correto para o DPI adequado, como LoadImage.

A partir do Windows 11 (Build 22000), aplicativos que criam cursores de dados na memória podem usar SetThreadCursorCreationScaling para habilitar o dimensionamento automático de DPI por monitor, semelhante aos cursores carregados dos recursos do módulo.

Redefinição forçada do reconhecimento de DPI em todo o processo

Em geral, o modo de reconhecimento de DPI do processo não pode ser alterado após a inicialização do processo. No entanto, o Windows poderá alterar à força o modo de reconhecimento de DPI do seu processo se você tentar quebrar o requisito de que todos os HWNDs em uma árvore de janela tenham o mesmo modo de reconhecimento de DPI. Em todas as versões do Windows, a partir do Windows 10 1703, não é possível ter HWNDs diferentes em uma árvore de HWNDs operando em modos diferentes de reconhecimento de DPI. Se você tentar criar uma relação entre filho e pai que viole essa regra, a percepção de DPI de todo o processo poderá ser redefinida. Isso pode ser acionado por:

  1. Uma chamada a CreateWindow em que a janela pai passada está em um modo de reconhecimento de DPI diferente do da thread que faz a chamada.
  2. Uma chamada SetParent em que as duas janelas estão associadas a diferentes modos de reconhecimento de DPI.

A tabela a seguir mostra o que acontece se você tentar violar essa regra:

Operação Windows 8.1 Windows 10 (1607 e anterior) Windows 10 (1703 e posterior)
CreateWindow (In-Proc) N/A Filho herda (modo misto) Filho herda (modo misto)
CreateWindow (Cross-Proc) Reinicialização forçada (do processo do chamador) Filho herda (modo misto) Reinicialização forçada (do processo chamador)
SetParent (In-Proc) N/A Redefinição forçada (do processo atual) fail (ERROR_INVALID_STATE)
SetParent (Cross-Proc) Reinicialização forçada (do processo da janela filha) Reinicialização forçada (do processo da janela filha) Redefinição forçada (do processo da janela filha)

Referência da API de alta DPI

Dimensionamento de DPI em modo misto e APIs com reconhecimento de DPI.