Questo esercizio è stato il primo che ho fatto dopo aver imparato a creare le finestre con l’SDK di Windows e mi è sembrato incredibile poter sviluppare delle finestre con dei buchi, si avete capito bene, dei buchi dai quali poter vedere sotto. Tutto questo è possibile grazie ad una semplice funzione
int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
che permette di associare ad una finestra, e non ad un DC, un’area di clipping, questo significa che è possibile definire un’area di qualsiasi forma contenuta in un HRGN, assegnarla alla finestra e vedere tutto l’output della finestra stessa tagliato entro i limiti della regione. Prendiamo allora il codice di una semplice finestra ed inseriamogli le modifiche per bucarla, in particolare inseriremo un buco che assomiglia a quello delle chiavi, così possiamo sbirciare tutto quello che sta sotto di essa.
// File di intestazione CorsoVC.h
#include <windows.h>
#define APP_NAME "Finestra con il buco"
#define IDM_EXIT 102
#define IDM_ABOUT 103
LRESULT CALLBACK AppWndProc(HWND, UINT, WPARAM, LPARAM);
// File sorgente CorsoVC.cpp
#define WIN32_LEAN_AND_MEAN
#include "CorsoVC.h"
static HWND m_hWndMain;
static HINSTANCE m_hInst;
static HRGN m_hRgnView;
static RECT m_rcHole;
static int InitApplication(HINSTANCE);
static int InitInstance(HINSTANCE);
static int SetClipArea(HWND);
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPreInst,
LPSTR lpszCmdLine, int iShowCmd)
{
MSG msg;
if(!InitApplication(hInst))
return FALSE;
if (!InitInstance(hInst))
return FALSE;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (m_hRgnView)
DeleteObject(m_hRgnView);
return TRUE;
}
LRESULT CALLBACK AppWndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_EXIT:
SendMessage(m_hWndMain, WM_CLOSE, 0, 0L);
break;
}
break;
case WM_SIZE:
case WM_MOVE:
DefWindowProc(hWnd, uMsg, wParam, lParam);
SetClipArea(hWnd);
return 0;
case WM_CLOSE:
DestroyWindow(m_hWndMain);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
DrawEdge(ps.hdc, &m_rcHole, EDGE_SUNKEN, BF_RECT);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int InitApplication(HINSTANCE hInst)
{
WNDCLASS wc;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)AppWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszClassName = "HOLE_CLASS";
wc.lpszMenuName = NULL;
return RegisterClass(&wc);
}
int InitInstance(HINSTANCE hInst)
{
m_hInst = hInst;
m_hWndMain = CreateWindowEx(0, "HOLE_CLASS", APP_NAME,
WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500,
NULL, NULL, hInst, NULL);
if (!m_hWndMain)
return FALSE;
SetClipArea(m_hWndMain);
ShowWindow(m_hWndMain, SW_SHOW);
UpdateWindow(m_hWndMain);
return TRUE;
}
int SetClipArea(HWND hWnd)
{
RECT rcHole, rcArea, rcCirc;
POINT pt[3];
HRGN hRgnHole, hRgnWnd, hRgnCirc, hRgnTriangle;
POINT ptOffset;
GetWindowRect(hWnd, &rcArea);
rcArea.right -= rcArea.left;
rcArea.bottom -= rcArea.top;
rcArea.left = 0;
rcArea.top = 0;
CopyRect(&rcHole, &rcArea);
rcHole.top += GetSystemMetrics(SM_CYCAPTION);
ptOffset.x = GetSystemMetrics(SM_CXSIZEFRAME) +
GetSystemMetrics(SM_CXEDGE);
ptOffset.y = GetSystemMetrics(SM_CYSIZEFRAME) +
GetSystemMetrics(SM_CYEDGE);
InflateRect(&rcHole, ptOffset.x, ptOffset.y);
InflateRect(&rcHole, -rcArea.right / 6, -rcArea.bottom / 8);
m_hRgnView = CreateRectRgn(0, 0, 10, 10);
hRgnWnd = CreateRectRgnIndirect(&rcArea);
hRgnHole = CreateRectRgnIndirect(&rcHole);
CopyRect(&rcCirc, &rcHole);
InflateRect(&rcCirc, -
(rcHole.right - rcHole.left) / 4, 0);
rcCirc.bottom = rcHole.top +
(rcHole.bottom - rcHole.top) / 3;
hRgnCirc = CreateEllipticRgnIndirect(&rcCirc);
pt[0].x = rcCirc.left +
(rcHole.right - rcHole.left) / 4;
pt[0].y = rcCirc.top +
(rcCirc.bottom - rcCirc.top) / 2;
pt[1].x = rcHole.right;
pt[1].y = rcHole.bottom;
pt[2].x = rcHole.left;
pt[2].y = rcHole.bottom;
hRgnTriangle = CreatePolygonRgn((LPPOINT)&pt,
3, ALTERNATE);
CombineRgn(hRgnHole, hRgnCirc, hRgnTriangle, RGN_OR);
CombineRgn(m_hRgnView, hRgnWnd, hRgnHole, RGN_DIFF);
DeleteObject(hRgnHole);
DeleteObject(hRgnCirc);
DeleteObject(hRgnTriangle);
DeleteObject(hRgnWnd);
SetWindowRgn(hWnd, m_hRgnView, TRUE);
return TRUE;
}
Occorre creare una regione di forma ellittica, un’altra triangolare e sommarle, quindi sottrarre la regione della chiave e richiamare la funzione SetWindowRgn per fare in modo che la parte interna non ci sia nell’area di clipping della finestra, il risultato è una finestra con un buco al centro a forma di chiave.

In questi esempi che vi propongo cerco di semplificare la programmazione di applicazioni anche complicate, ma l’SDK di Windows è talmente vasto che non bastano 10 anni per conoscerlo tutto, occorre sapere a grandi linee il compito delle funzioni e poi andarsele a cercare nella guida in linea del compilatore. Se comunque avete bisogno di chiarimenti o fare delle semplici domande potete iscrivervi al forum, nella sezione dedicata alla programmazione.