Creazione, Apertura e Chiusura di un File in Windows

In questa lezione vedremo come creare, aprire e chiudere un file in Windows attraverso il linguaggio C con le funzioni CreateFile e CloseHandle.

Creazione ed Apertura di un File

La prima funzione dell'API di Windows che andremo ad utilizzare è la funzione CreateFile che ci permette di creare o aprire un file. La funzione CreateFile è definita nel file Windows.h e ha la seguente firma:

HANDLE CreateFile(
    LPCSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
);

Attraverso questa funzione possiamo iniziare a vedere alcune delle convenzioni che vengono utilizzate quando si scrivono programmi C per Windows.

In particolare, osservando i parametri, possiamo notare che il prefisso dw viene utilizzato per indicare che il parametro è un valore di tipo DWORD (ovvero un intero a 32 bit). Inoltre, il prefisso lp viene utilizzato per indicare che il parametro è un puntatore ad un oggetto di tipo LPCSTR (ovvero un puntatore ad una stringa di caratteri).

Adesso vediamo nel dettaglio i parametri della funzione CreateFile:

  • lpFileName: è il nome del file da creare o aprire. Questo parametro è un puntatore ad una stringa di caratteri terminata dal carattere nullo.

  • dwDesiredAccess: è il tipo di accesso che si vuole avere al file. Questo parametro può assumere i seguenti valori:

  • GENERIC_READ: indica che si vuole avere accesso in lettura al file.
  • GENERIC_WRITE: indica che si vuole avere accesso in scrittura al file.
  • GENERIC_READ | GENERIC_WRITE: indica che si vuole avere accesso in lettura e scrittura al file.

  • dwShareMode: è il tipo di condivisione che si vuole avere sul file. Questo parametro può assumere i seguenti valori:

    • 0: indica che il file non può essere condiviso con altri processi.
    • FILE_SHARE_READ: indica che il file può essere condiviso in lettura con altri processi.
    • FILE_SHARE_WRITE: indica che il file può essere condiviso in scrittura con altri processi.
    • FILE_SHARE_READ | FILE_SHARE_WRITE: indica che il file può essere condiviso in lettura e scrittura con altri processi.
  • lpSecurityAttributes: è un puntatore ad un oggetto di tipo SECURITY_ATTRIBUTES che contiene informazioni sulle proprietà di sicurezza del file. Questo parametro può essere NULL se non si vogliono specificare particolari proprietà di sicurezza. Nelle lezioni più avanti vedremo come utilizzare questo parametro per specificare le proprietà di sicurezza di un file. Per il momento possiamo lasciare questo parametro a NULL.

  • dwCreationDisposition: è il tipo di azione da intraprendere nel caso in cui il file non esista. Questo parametro può assumere i seguenti valori:

    • CREATE_NEW: indica che il file deve essere creato. Se il file esiste già, la funzione CreateFile fallisce.
    • CREATE_ALWAYS: indica che il file deve essere creato. Se il file esiste già, il file viene sovrascritto.
    • OPEN_EXISTING: indica che il file deve essere aperto. Se il file non esiste, la funzione CreateFile fallisce.
    • OPEN_ALWAYS: indica che il file deve essere aperto. Se il file non esiste, il file viene creato.
    • TRUNCATE_EXISTING: il file verrà aperto e la sua dimensione verrà impostata a 0. In altre parole, il contenuto del file verrà eliminato. Se il file non esiste, la funzione CreateFile fallisce.
  • dwFlagsAndAttributes: è un valore che specifica gli attributi del file. Esistono svariati attributi che possono essere specificati. Per il momento possiamo lasciare questo parametro a 0. Più avanti vedremo come utilizzare questo parametro per specificare gli attributi di un file per applicazioni specifiche.

  • hTemplateFile: è un handle ad un file di cui si vogliono copiare gli attributi. Per il momento possiamo lasciare questo parametro a NULL.

La funzione CreateFile restituisce un handle al file creato o aperto. Questo handle può essere utilizzato per accedere al file.

Esempi

Nell'esempio seguente viene mostrato come utilizzare la funzione CreateFile per creare un file chiamato test.txt nella directory corrente. Nel caso in cui il file esista già, il file viene sovrascritto.

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
    HANDLE hFile = CreateFile("test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    CloseHandle(hFile);
    return 0;
}

In questo esempio viene mostrato come utilizzare la funzione CreateFile per aprire un file chiamato test.txt nella directory corrente. Nel caso in cui il file non esista, la funzione CreateFile fallisce.

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
    HANDLE hFile = CreateFile("test.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

    if (hFile == INVALID_HANDLE_VALUE) {
        MessageBox(NULL, "Impossibile aprire il file", "Errore", MB_OK | MB_ICONERROR);
        return 1;
    }

    CloseHandle(hFile);
    return 0;
}

Differenze rispetto alle funzioni standard del C

La funzione CreateFile è molto simile alla funzione fopen del C. Tuttavia, esistono alcune differenze tra queste due funzioni.

La prima cosa da notare è che la funzione CreateFile richiede una serie di parametri in più rispetto alla funzione fopen. La motivazione sta nel fatto che la funzione CreateFile è una funzione di più basso livello rispetto alla funzione fopen. Infatti, la funzione CreateFile permette di specificare in modo più dettagliato il tipo di accesso che si vuole avere al file, il tipo di condivisione che si vuole avere sul file e il tipo di azione da intraprendere nel caso in cui il file non esista.

In generale, ci si potrebbe chiedere perché utilizzare la funzione CreateFile invece della funzione fopen. La risposta è che CreateFile consente un maggior controllo sul file rispetto alla funzione fopen.

La seconda principale differenza rispetto alla funzione fopen è che la funzione CreateFile restituisce un handle al file creato o aperto a differenza di un puntatore a FILE restituito dalla funzione fopen.

Nella realtà, quando si scrive un programma C per windows usando la libreria standard del C, la funzione fopen viene implementata utilizzando la funzione CreateFile internamente.

Quando si scrive un programma per Windows bisogna scegliere se renderlo portabile e quindi utilizzare la libreria standard del C oppure utilizzare le API di Windows. Se si sceglie di utilizzare la libreria standard del C, il programma sarà portabile ma si avrà un minor controllo sul file. Se si sceglie di utilizzare le API di Windows, il programma non sarà portabile ma si avrà un maggior controllo sul file.

Chiusura di un File

Adesso che sappiamo come creare e aprire un file, vediamo come chiudere un file. Per chiudere un file utilizziamo la funzione CloseHandle che è definita nel file Windows.h e ha la seguente firma:

BOOL CloseHandle(
    HANDLE hObject
);

Questa funzione prende in ingresso un handle ad un oggetto e restituisce un valore booleano che indica se la chiusura dell'oggetto è avvenuta con successo o meno.

Negli esempi di sopra abbiamo visto che, dopo aver creato o aperto un file, è necessario chiudere l'handle del file utilizzando la funzione CloseHandle.

Nota

Dopo aver chiuso un file, non è più possibile accedervi.

Bisogna stare attenti a non richiamare funzioni che utilizzano l'handle del file dopo averlo chiuso, ossia dopo aver richiamato la funzione CloseHandle. In tal caso, infatti, l'handle non sarà più valido.

In sintesi

In questa lezione abbiamo visto come creare, aprire e chiudere un file in Windows attraverso il linguaggio C con le funzioni CreateFile e CloseHandle.

Nella prossima lezione vedremo come leggere e scrivere un file in Windows attraverso il linguaggio C con le funzioni ReadFile e WriteFile.