sabato, novembre 09, 2024

5 Esempi Pratici di Applicazioni Windows Semplici in C++

Se sei interessato a imparare la programmazione per Windows in C++, sei nel posto giusto. In questo articolo esploreremo cinque esempi pratici di applicazioni semplici, che possono aiutarti a comprendere meglio le basi delle API Win32 e come costruire piccole utility utili. Dall'iconica "Hello World" a un piccolo editor di testo, ogni esempio rappresenta un ottimo punto di partenza per i programmatori in erba. Vediamo insieme queste app!

Istruzioni Generali per la Compilazione

Per compilare questi esempi su un sistema Windows, è necessario avere un ambiente di sviluppo C++. Puoi usare uno dei seguenti strumenti:

  • Visual Studio: Uno dei migliori ambienti per la programmazione Windows. Basta creare un nuovo progetto di tipo "Windows Desktop Application" e incollare il codice.
  • g++ (MinGW): Un'opzione open source. Installa MinGW, apri il terminale e usa i comandi che forniamo per ogni esempio.

Di seguito trovi i comandi specifici per compilare ogni applicazione utilizzando g++ di MinGW:

1. Finestra "Hello World"

Il primo esempio di qualsiasi programmatore che si avvicina alla programmazione per Windows è creare una finestra con un semplice messaggio "Hello World". Questo esempio aiuta a comprendere le basi delle finestre di Windows, tra cui la registrazione di una finestra e la gestione dei messaggi.

Codice

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            TextOut(hdc, 50, 50, "Hello, World!", 13);
            EndPaint(hwnd, &ps);
            return 0;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "HelloWorldWindow";
    
    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    
    RegisterClass(&wc);
    
    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "Hello, World!", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 500, 300,
        NULL, NULL, hInstance, NULL
    );
    
    if (hwnd == NULL) {
        return 0;
    }
    
    ShowWindow(hwnd, nCmdShow);
    
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

Comando di Compilazione

g++ -o HelloWorld.exe HelloWorld.cpp -lgdi32

Questo comando crea un eseguibile chiamato HelloWorld.exe. Il flag -lgdi32 serve per linkare la libreria GDI, necessaria per il rendering del testo.

2. Calcolatrice Base

Questa applicazione è una semplice calcolatrice con le operazioni di base come addizione, sottrazione, moltiplicazione e divisione. Permette agli utenti di fare semplici calcoli.

Codice

La calcolatrice usa i pulsanti per ricevere input dall'utente e un campo di testo per mostrare il risultato. Vediamo una versione semplificata:

#include <windows.h>
#include <string>

HWND hEdit;
HWND hAddButton, hSubButton, hMulButton, hDivButton, hResultButton;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static double num1 = 0, num2 = 0;
    static char operation = 0;
    char buffer[256];

    switch (uMsg) {
        case WM_CREATE:
            hEdit = CreateWindowEx(0, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL,
                                  10, 10, 260, 25, hwnd, (HMENU)1, NULL, NULL);
            hAddButton = CreateWindow("BUTTON", "+", WS_VISIBLE | WS_CHILD, 10, 50, 50, 30, hwnd, (HMENU)2, NULL, NULL);
            hSubButton = CreateWindow("BUTTON", "-", WS_VISIBLE | WS_CHILD, 70, 50, 50, 30, hwnd, (HMENU)3, NULL, NULL);
            hMulButton = CreateWindow("BUTTON", "*", WS_VISIBLE | WS_CHILD, 130, 50, 50, 30, hwnd, (HMENU)4, NULL, NULL);
            hDivButton = CreateWindow("BUTTON", "/", WS_VISIBLE | WS_CHILD, 190, 50, 50, 30, hwnd, (HMENU)5, NULL, NULL);
            hResultButton = CreateWindow("BUTTON", "=", WS_VISIBLE | WS_CHILD, 250, 50, 50, 30, hwnd, (HMENU)6, NULL, NULL);
            return 0;
        
        case WM_COMMAND:
            if (HIWORD(wParam) == BN_CLICKED) {
                switch (LOWORD(wParam)) {
                    case 2: // Addizione
                        GetWindowText(hEdit, buffer, 256);
                        num1 = atof(buffer);
                        operation = '+';
                        SetWindowText(hEdit, "");
                        break;
                    case 3: // Sottrazione
                        GetWindowText(hEdit, buffer, 256);
                        num1 = atof(buffer);
                        operation = '-';
                        SetWindowText(hEdit, "");
                        break;
                    case 4: // Moltiplicazione
                        GetWindowText(hEdit, buffer, 256);
                        num1 = atof(buffer);
                        operation = '*';
                        SetWindowText(hEdit, "");
                        break;
                    case 5: // Divisione
                        GetWindowText(hEdit, buffer, 256);
                        num1 = atof(buffer);
                        operation = '/';
                        SetWindowText(hEdit, "");
                        break;
                    case 6: // Risultato
                        GetWindowText(hEdit, buffer, 256);
                        if (strlen(buffer) > 0 && operation != 0) {
                            num2 = atof(buffer);
                            double result = 0;
                            switch (operation) {
                                case '+': result = num1 + num2; break;
                                case '-': result = num1 - num2; break;
                                case '*': result = num1 * num2; break;
                                case '/': result = num2 != 0 ? num1 / num2 : 0; break;
                            }
                            sprintf(buffer, "%lf", result);
                            SetWindowText(hEdit, buffer);
                            operation = 0; // Resetta l'operazione dopo il calcolo
                        }
                        break;
                }
            }
            return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "CalculatorWindow";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "Calcolatrice Base", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 330, 150,
        NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

Comando di Compilazione

g++ -o Calculator.exe Calculator.cpp -lgdi32

Lo stesso comando di base utilizzato per le altre app, con link alla libreria gdi32.

3. Editor di Testo Minimalista

Questo progetto è un piccolo editor di testo, simile a un Blocco Note, che ti consente di leggere e salvare file di testo. Utilizza i controlli di modifica per l'inserimento del testo e l'API di Windows per gestire il caricamento e il salvataggio dei file.

Codice

#include <windows.h>
#include <commdlg.h>
#include <fstream>
#include <string>

// Dichiarazione globale dei controlli
HWND hEdit;
HWND hButtonSave;
HWND hButtonOpen;

// Funzioni di supporto per apertura e salvataggio file
void SaveTextFile(HWND hwnd);
void OpenTextFile(HWND hwnd);

// Window Procedure
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_CREATE:
            // Creazione del campo di testo
            hEdit = CreateWindowEx(0, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE,
                                   10, 10, 400, 300, hwnd, (HMENU)1, NULL, NULL);

            // Creazione del pulsante "Salva"
            hButtonSave = CreateWindowEx(0, "BUTTON", "Salva", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                         420, 10, 100, 30, hwnd, (HMENU)2, NULL, NULL);

            // Creazione del pulsante "Apri"
            hButtonOpen = CreateWindowEx(0, "BUTTON", "Apri", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                         420, 50, 100, 30, hwnd, (HMENU)3, NULL, NULL);
            return 0;

        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case 2:  // Pulsante "Salva" premuto
                    SaveTextFile(hwnd);
                    break;

                case 3:  // Pulsante "Apri" premuto
                    OpenTextFile(hwnd);
                    break;
            }
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// Funzione per salvare il contenuto del campo di testo in un file
void SaveTextFile(HWND hwnd) {
    OPENFILENAME ofn;
    char szFile[260] = { 0 };
    
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = "Text Files\0*.txt\0All Files\0*.*\0";
    ofn.lpstrDefExt = "txt";
    ofn.lpstrTitle = "Save Text File";
    ofn.Flags = OFN_OVERWRITEPROMPT;

    if (GetSaveFileName(&ofn)) {
        int length = GetWindowTextLength(hEdit);
        char *buffer = new char[length + 1];
        GetWindowText(hEdit, buffer, length + 1);

        std::ofstream outFile(ofn.lpstrFile);
        if (outFile.is_open()) {
            outFile << buffer;
            outFile.close();
        }

        delete[] buffer;
    }
}

// Funzione per aprire un file di testo e caricarlo nel campo di testo
void OpenTextFile(HWND hwnd) {
    OPENFILENAME ofn;
    char szFile[260] = { 0 };

    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    ofn.lpstrFilter = "Text Files\0*.txt\0All Files\0*.*\0";
    ofn.lpstrDefExt = "txt";
    ofn.lpstrTitle = "Open Text File";
    ofn.Flags = OFN_FILEMUSTEXIST;

    if (GetOpenFileName(&ofn)) {
        std::ifstream inFile(ofn.lpstrFile);
        if (inFile.is_open()) {
            std::string content((std::istreambuf_iterator<char>(inFile)), std::istreambuf_iterator<char>());
            SetWindowText(hEdit, content.c_str());
            inFile.close();
        }
    }
}

// Entry point per la GUI di Windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "SampleWindowClass";

    WNDCLASS wc = { };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        "Text Editor",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 550, 400,
        NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

Comando di Compilazione

g++ -o TextEditor.exe TextEditor.cpp -lgdi32 -lcomdlg32

Questo comando crea un editor di testo chiamato TextEditor.exe.

4. Timer con Avviso

Un timer con avviso consente all'utente di impostare un conto alla rovescia e di essere avvisato una volta terminato. Può essere utile per ricordare scadenze o piccole pause.

Codice

#include <windows.h>
#include <string>

// Funzione di callback per la gestione degli eventi della finestra
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static UINT_PTR timerId = 0;
    static int counter = 0; // Inizialmente il contatore è a zero
    static HWND hCounterDisplay;
    static HWND hButtonStart, hButtonStop, hEditAlarmInput;
    static int alarmTime = -1; // Valore dell'allarme inizialmente non impostato (-1)

    switch (uMsg) {
        case WM_CREATE:
            // Creazione del pulsante per avviare il timer
            hButtonStart = CreateWindow("BUTTON", "Start Timer", WS_VISIBLE | WS_CHILD, 10, 10, 100, 30, hwnd, (HMENU)1, NULL, NULL);
            
            // Creazione del pulsante per fermare il timer
            hButtonStop = CreateWindow("BUTTON", "Stop Timer", WS_VISIBLE | WS_CHILD, 120, 10, 100, 30, hwnd, (HMENU)2, NULL, NULL);

            // Creazione dell'area di visualizzazione del contatore del timer
            hCounterDisplay = CreateWindow("STATIC", "0", WS_VISIBLE | WS_CHILD | SS_CENTER, 10, 90, 210, 30, hwnd, (HMENU)3, NULL, NULL);

            // Creazione del campo di input per l'inserimento del tempo di allarme
            hEditAlarmInput = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER, 10, 50, 60, 30, hwnd, (HMENU)4, NULL, NULL);
            CreateWindow("STATIC", "Set Alarm (sec)", WS_VISIBLE | WS_CHILD, 80, 55, 120, 20, hwnd, (HMENU)5, NULL, NULL);
            return 0;

        case WM_COMMAND:
            if (LOWORD(wParam) == 1) { // Pulsante "Start Timer" premuto
                if (timerId == 0) { // Avvia il timer solo se non è già in esecuzione
                    // Ottieni il valore inserito dall'utente per l'allarme (se presente)
                    char buffer[10];
                    GetWindowText(hEditAlarmInput, buffer, 10);
                    if (strlen(buffer) > 0) {
                        alarmTime = std::stoi(buffer);
                    } else {
                        alarmTime = -1; // Nessun allarme impostato
                    }

                    counter = 0; // Inizializza il contatore a 0
                    SetWindowText(hCounterDisplay, std::to_string(counter).c_str());
                    timerId = SetTimer(hwnd, 1, 1000, NULL); // Timer di 1 secondo
                }
            } else if (LOWORD(wParam) == 2) { // Pulsante "Stop Timer" premuto
                if (timerId != 0) {
                    KillTimer(hwnd, timerId);
                    timerId = 0;
                    MessageBox(hwnd, "Timer fermato.", "Avviso", MB_OK);
                }
            }
            return 0;

        case WM_TIMER:
            if (wParam == 1) {
                counter++;
                SetWindowText(hCounterDisplay, std::to_string(counter).c_str());

                // Controlla se l'allarme è stato impostato e se è stato raggiunto
                if (alarmTime != -1 && counter == alarmTime) {
                    MessageBeep(MB_ICONEXCLAMATION); // Riproduci un suono di allarme
                    MessageBox(hwnd, "Hai raggiunto il tempo impostato per l'allarme!", "Allarme", MB_OK);
                }
            }
            return 0;

        case WM_DESTROY:
            if (timerId != 0) {
                KillTimer(hwnd, timerId);
            }
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// Funzione di ingresso per applicazioni GUI Windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const char CLASS_NAME[] = "SampleWindowClass";

    // Registrazione della classe della finestra
    WNDCLASS wc = { };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Creazione della finestra principale
    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        "Timer Example",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 350, 200,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    // Ciclo dei messaggi
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

Comando di Compilazione

g++ -o TimerAlert.exe TimerAlert.cpp -lgdi32

Il comando crea un eseguibile chiamato TimerAlert.exe che gestisce un timer e mostra un messaggio di avviso.

5. Contatore di Click

Il contatore di click tiene traccia del numero di click effettuati su un pulsante. È un esempio utile per imparare a gestire gli eventi con WinAPI.

Codice

#include <windows.h>
#include <cstdio>

// Prototipo della funzione callback della finestra principale
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // Classe della finestra
    const char CLASS_NAME[] = "ClickCounterWindow";

    WNDCLASS wc = {};

    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Creazione della finestra principale
    HWND hwnd = CreateWindowEx(
        0,                            // Stili estesi
        CLASS_NAME,                   // Nome della classe
        "Click Counter",             // Titolo della finestra
        WS_OVERLAPPEDWINDOW,          // Stili della finestra
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,                         // Finestra genitore
        NULL,                         // Menu
        hInstance,                    // Gestore dell'istanza
        NULL                          // Parametri di creazione
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    // Ciclo dei messaggi
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static int clickCount = 0;
    char buffer[50];
    switch (uMsg) {
        case WM_CREATE:
            CreateWindow(
                "BUTTON",                // Classe del controllo
                "Click Me",              // Testo del pulsante
                WS_VISIBLE | WS_CHILD,    // Stili del pulsante
                10, 10, 100, 30,          // Posizione e dimensioni
                hwnd,                     // Finestra principale come genitore
                (HMENU)1,                 // ID del controllo
                (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
                NULL                      // Parametri di creazione
            );
            return 0;

        case WM_COMMAND:
            if (LOWORD(wParam) == 1) {  // Se l'ID del pulsante è 1
                clickCount++;
                sprintf(buffer, "Click Count: %d", clickCount);
                SetWindowText(hwnd, buffer);  // Aggiorna il titolo della finestra
            }
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Comando di Compilazione

g++ -o ClickCounter.exe ClickCounter.cpp -lgdi32

Questo comando compila l'applicazione contatore di click, creando un eseguibile chiamato ClickCounter.exe.

Conclusioni

Questi cinque esempi ti danno un'idea di come iniziare a sviluppare applicazioni Windows con C++. Sebbene siano semplici, forniscono una base solida per capire come funziona la programmazione Windows usando l'API Win32. Puoi prendere spunto da questi esempi per creare applicazioni più complesse, migliorando progressivamente le tue abilità di programmazione.

Se hai domande o vuoi esplorare ulteriori dettagli di uno di questi progetti, non esitare a chiedere!