Usando o Windows

Os exemplos nesta seção descrevem como executar as seguintes tarefas:

Criando uma janela principal

A primeira janela que um aplicativo cria normalmente é a janela principal. Você cria a janela principal usando a função CreateWindowEx , especificando a classe da janela, o nome da janela, os estilos de janela, o tamanho, a posição, o identificador de menu, o identificador de instância e os dados de criação. Uma janela principal pertence a uma classe de janela definida pelo aplicativo, portanto, você deve registrar a classe de janela e fornecer um procedimento de janela para a classe antes de criar a janela principal.

A maioria dos aplicativos normalmente usa o estilo WS_OVERLAPPEDWINDOW para criar a janela principal. Esse estilo dá à janela uma barra de título, um menu de janela, uma borda de dimensionamento e botões de minimizar e maximizar. A função CreateWindowEx retorna um identificador que identifica exclusivamente a janela.

O exemplo a seguir cria uma janela principal pertencente a uma classe de janela definida pelo aplicativo. O nome da janela, Janela Principal, aparecerá na barra de título da janela. Ao combinar os estilos WS_VSCROLL e WS_HSCROLL com o estilo WS_OVERLAPPEDWINDOW , o aplicativo cria uma janela principal com barras de rolagem horizontais e verticais, além dos componentes fornecidos pelo estilo WS_OVERLAPPEDWINDOW . As quatro ocorrências da constante CW_USEDEFAULT definem o tamanho inicial e a posição da janela para os valores padrão definidos pelo sistema. Ao especificar NULL em vez de um identificador de menu, a janela terá o menu definido para a classe de janela.

HINSTANCE hinst; 
HWND hwndMain; 
 
// Create the main window. 
 
hwndMain = CreateWindowEx( 
    0,                      // no extended styles           
    "MainWClass",           // class name                   
    "Main Window",          // window name                  
    WS_OVERLAPPEDWINDOW |   // overlapped window            
             WS_HSCROLL |   // horizontal scroll bar        
             WS_VSCROLL,    // vertical scroll bar          
    CW_USEDEFAULT,          // default horizontal position  
    CW_USEDEFAULT,          // default vertical position    
    CW_USEDEFAULT,          // default width                
    CW_USEDEFAULT,          // default height               
    (HWND) NULL,            // no parent or owner window    
    (HMENU) NULL,           // class menu used              
    hinst,                  // instance handle              
    NULL);                  // no window creation data      
 
if (!hwndMain) 
    return FALSE; 
 
// Show the window using the flag specified by the program 
// that started the application, and send the application 
// a WM_PAINT message. 
 
ShowWindow(hwndMain, SW_SHOWDEFAULT); 
UpdateWindow(hwndMain); 

Observe que o exemplo anterior chama a função ShowWindow depois de criar a janela principal. Isso é feito porque o sistema não exibe automaticamente a janela principal depois de criá-la. Ao passar o sinalizador SW_SHOWDEFAULT para ShowWindow, o aplicativo permite que o programa que iniciou o aplicativo defina o estado de exibição inicial da janela principal. A função UpdateWindow envia à janela sua primeira mensagem WM_PAINT.

Criando, enumerando e dimensionando janelas filho

Você pode dividir a área do cliente de uma janela em diferentes áreas funcionais usando janelas filhas. Criar uma janela filho é como criar uma janela principal — você usa a função CreateWindowEx. Para criar uma janela de uma classe de janela definida pelo aplicativo, você deve registrar a classe de janela e fornecer um procedimento de janela antes de criar a janela filho. Você deve dar à janela filho o estilo WS_CHILD e especificar uma janela pai para a janela filho ao criá-la.

O exemplo a seguir divide a área do cliente da janela principal de um aplicativo em três áreas funcionais, criando três janelas filhas de tamanho igual. Cada janela filho tem a mesma altura que a área do cliente da janela principal, mas cada uma tem um terço de sua largura. A janela principal cria as janelas filho em resposta à mensagem WM_CREATE, que a janela principal recebe durante seu próprio processo de criação de janela. Como cada janela filho tem o estilo WS_BORDER , cada uma tem uma borda de linha fina. Além disso, como o estilo WS_VISIBLE não é especificado, cada janela filho é inicialmente ocultada. Observe também que a cada janela filho é atribuído um identificador de janela filha.

A janela principal dimensiona e posiciona as janelas filhas em resposta à mensagem WM_SIZE , que a janela principal recebe quando seu tamanho muda. Em resposta a WM_SIZE, a janela principal recupera as dimensões de sua área de cliente usando a função GetClientRect e, em seguida, passa as dimensões para a função EnumChildWindows . EnumChildWindows passa o identificador para cada janela filho, por sua vez, para a função de retorno de chamada EnumChildProc definida pelo aplicativo. Essa função dimensiona e posiciona cada janela filho chamando a função MoveWindow , o tamanho e a posição são baseados nas dimensões da área do cliente da janela principal e no identificador da janela filho. Depois, EnumChildProc chama a função ShowWindow para tornar a janela visível.

#define ID_FIRSTCHILD  100 
#define ID_SECONDCHILD 101 
#define ID_THIRDCHILD  102 
 
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    RECT rcClient; 
    int i; 
 
    switch(uMsg) 
    { 
        case WM_CREATE: // creating main window  
 
            // Create three invisible child windows. 

            for (i = 0; i < 3; i++) 
            { 
                CreateWindowEx(0, 
                               "ChildWClass", 
                               (LPCTSTR) NULL, 
                               WS_CHILD | WS_BORDER, 
                               0,0,0,0, 
                               hwnd, 
                               (HMENU) (int) (ID_FIRSTCHILD + i), 
                               hinst, 
                               NULL); 
            }
 
            return 0; 
 
        case WM_SIZE:   // main window changed size 
 
            // Get the dimensions of the main window's client 
            // area, and enumerate the child windows. Pass the 
            // dimensions to the child windows during enumeration. 
 
            GetClientRect(hwnd, &rcClient); 
            EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient); 
            return 0; 

        // Process other messages. 
    } 
    return DefWindowProc(hwnd, uMsg, wParam, lParam); 
} 
 
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam) 
{ 
    LPRECT rcParent; 
    int i, idChild; 
 
    // Retrieve the child-window identifier. Use it to set the 
    // position of the child window. 
 
    idChild = GetWindowLong(hwndChild, GWL_ID); 
 
    if (idChild == ID_FIRSTCHILD) 
        i = 0; 
    else if (idChild == ID_SECONDCHILD) 
        i = 1; 
    else 
        i = 2; 
 
    // Size and position the child window.  
 
    rcParent = (LPRECT) lParam; 
    MoveWindow(hwndChild, 
               (rcParent->right / 3) * i, 
               0, 
               rcParent->right / 3, 
               rcParent->bottom, 
               TRUE); 
 
    // Make sure the child window is visible. 
 
    ShowWindow(hwndChild, SW_SHOW); 
 
    return TRUE;
}

Destruindo uma janela

Você pode usar a função DestroyWindow para destruir uma janela. Normalmente, um aplicativo envia a mensagem WM_CLOSE antes de destruir uma janela, dando à janela a oportunidade de solicitar ao usuário a confirmação antes que a janela seja destruída. Uma janela que inclui um menu de janela recebe automaticamente a mensagem WM_CLOSE quando o usuário clica em Fechar no menu da janela. Se o usuário confirmar que a janela deve ser destruída, o aplicativo chama DestroyWindow. O sistema envia a mensagem de WM_DESTROY para a janela depois de removê-la da tela. Em resposta a WM_DESTROY, a janela salva seus dados e libera todos os recursos alocados. Uma janela principal conclui seu processamento de WM_DESTROY chamando a função PostQuitMessage para sair do aplicativo.

O exemplo a seguir mostra como solicitar a confirmação do usuário antes de destruir uma janela. Em resposta a WM_CLOSE, é exibida uma caixa de diálogo que contém os botões Sim, Não e Cancelar . Se o usuário clicar em Sim, DestroyWindow é chamado, caso contrário, a janela não será destruída. Como o aplicativo está manipulando a mensagem WM_CLOSE, 0 é retornado em todos os casos. Como a janela que está sendo destruída é uma janela principal, o exemplo chama PostQuitMessage em resposta a WM_DESTROY.

case WM_CLOSE: 
 
    // Create the message box. If the user clicks 
    // the Yes button, destroy the main window. 
 
    if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES) 
        DestroyWindow(hwndMain); 
    return 0; 
 
case WM_DESTROY: 
 
    // Post the WM_QUIT message to 
    // quit the application terminate. 
 
    PostQuitMessage(0); 
    return 0;

Usando janelas em camadas

Para que uma caixa de diálogo apareça como uma janela translúcida, primeiro crie a caixa de diálogo como de costume. Em seguida, em WM_INITDIALOG, defina o bit em camadas do estilo estendido da janela e chame SetLayeredWindowAttributes com o valor alfa desejado. O código pode ter esta aparência:

// Set WS_EX_LAYERED on this window 
SetWindowLong(hwnd, 
              GWL_EXSTYLE, 
              GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);

Observe que o terceiro parâmetro de SetLayeredWindowAttributes é um valor que varia de 0 a 255, com 0 tornando a janela completamente transparente e 255 tornando-a completamente opaca. Este parâmetro imita a função BLENDFUNCTION mais versátil da função AlphaBlend.

Para tornar essa janela completamente opaca novamente, remova o bit WS_EX_LAYERED chamando SetWindowLong e, em seguida, peça à janela para pintar novamente. A remoção do bit é desejada para que o sistema saiba que ele pode liberar alguma memória associada a camadas e redirecionamento. O código pode ter esta aparência:

// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd, 
              GWL_EXSTYLE,
              GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);

// Ask the window and its children to repaint
RedrawWindow(hwnd, 
             NULL, 
             NULL, 
             RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);

Para usar janelas filho em camadas, o aplicativo precisa se declarar compatível com o Windows 8 no manifesto.

Para o Windows 10/11, pode-se incluir este trecho de compatibilidade em seu app.manifest para torná-lo compatível com o Windows 10:

...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows 10 GUID -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
</compatibility>
...

Mais informações sobre como modificar o manifesto do aplicativo podem ser lidas aqui: Manifestos do aplicativo