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!