Win32 API programming with C - Using the file system

Ciprian FusaCiprian Fusa
5 min read

When we looked at common controls library we added an Edit control. Let's say we want to save the text that we write to a file on disk, for this we need to use the File System API.

First, we need a method to open the File Save dialog:

void OpenFileSaveDialog(HWND hwnd)
{
    OPENFILENAME ofn;
    char szFileName[MAX_PATH] = "";

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrDefExt = "txt";
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

    if (GetSaveFileName(&ofn))
    {
        HWND hEdit = GetDlgItem(hwnd, IDC_MY_EDIT);
        SaveTextFile(hEdit, szFileName);
    }
}

The SaveTextFile method reads the text inside the Edit control and creates a .txt file on disk. The CreateFile function can create a new file or open an existing file. You must specify the file name, creation instructions, and other attributes. When an application creates a new file, the operating system adds it to the specified directory.

The following example uses CreateFile to create a new file and open it for writing and WriteFile to write the content of the Edit control to the file.

BOOL SaveTextFile(HWND hEdit, LPCTSTR pszFileName)
{
    HANDLE hFile;
    BOOL bSuccess = FALSE;

    hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        DWORD dwTextLength;

        dwTextLength = GetWindowTextLength(hEdit);
        if (dwTextLength > 0)
        {
            LPSTR pszText;
            DWORD dwBufferSize = dwTextLength + 1;

            pszText = (LPSTR)GlobalAlloc(GPTR, dwBufferSize);
            if (pszText != NULL)
            {
                if (GetWindowText(hEdit, pszText, dwBufferSize))
                {
                    DWORD dwWritten;

                    if (WriteFile(hFile, pszText, dwTextLength, &dwWritten, NULL))
                        bSuccess = TRUE;
                }
                GlobalFree(pszText);
            }
        }
        CloseHandle(hFile);
    }
    return bSuccess;
}

Full code below:

#include <windows.h>
#include <commctrl.h>
#include "resource.h"

#pragma comment (lib, "comctl32")

// global variables
const char g_szClassName[] = "myWindowClass";

#define IDC_MY_EDIT        103
#define ID_MYBUTTON        104

// function declarations
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL SaveTextFile(HWND hEdit, LPCTSTR pszFileName);
void OpenFileSaveDialog(HWND hwnd);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;

    //Step 1: Register the Window Class
    wc.cbSize         = sizeof(WNDCLASSEX);                                                                           // The size, in bytes, of this structure
    wc.style         = 0;                                                                                            // The class style(s)
    wc.lpfnWndProc     = WndProc;                                                                                      // A pointer to the window procedure.
    wc.cbClsExtra     = 0;                                                                                            // The number of extra bytes to allocate following the window-class structure.
    wc.cbWndExtra     = 0;                                                                                            // The number of extra bytes to allocate following the window instance.
    wc.hInstance     = hInstance;                                                                                    // A handle to the instance that contains the window procedure for the class.
    wc.hIcon         = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON));                                 // A handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon.
    wc.hCursor         = LoadCursor(NULL, IDC_ARROW);                                                                  // A handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window.
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);                                                                     // A handle to the class background brush.
    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MYMENU);                                                                  // Pointer to a null-terminated character string that specifies the resource name of the class menu.
    wc.lpszClassName = g_szClassName;                                                                                // A string that identifies the window class.
    wc.hIconSm         = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON), IMAGE_ICON, 32, 32, 0);  // A handle to a small icon that is associated with the window class.

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        0,                      // Optional window styles.
        g_szClassName,          // Window class
        "My application",       // Window text
        WS_OVERLAPPEDWINDOW,    // Window style
        CW_USEDEFAULT,          // Position X
        CW_USEDEFAULT,          // Position Y
        800,                    // Width
        600,                    // Height
        NULL,                   // Parent window
        NULL,                   // Menu
        hInstance,              // Instance handle
        NULL                    // Additional application data
    );

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    INITCOMMONCONTROLSEX icex;
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC = ICC_STANDARD_CLASSES;  // Enables a set of common controls.
    InitCommonControlsEx(&icex);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
        {
            // Add Edit control
            HFONT hfDefault;
            HWND hEdit;

            hEdit = CreateWindowEx(
                WS_EX_CLIENTEDGE,                                      
                "EDIT",                                               
                "Default text to save on disk.",                                        
                WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
                0, 0, 400, 300, 
                hwnd,
                (HMENU)IDC_MY_EDIT,(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);
            if (hEdit == NULL)
                MessageBox(hwnd, "Could not create edit box.", "Error", MB_OK | MB_ICONERROR);

            hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
            SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));

            // Add Button control
            HWND hButton = CreateWindowEx(
                0,                                                     // Optional window styles.
                "BUTTON",                                              // Predefined class; Button.
                "Save",                                                // Button text.
                WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles.
                450, 50, 100,30,                                                    
                hwnd,                                                  // Parent window.
                (HMENU)ID_MYBUTTON,                                    // Button ID.
                (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
                NULL);                                                 // Pointer not needed.

            if (hButton == NULL)
                MessageBox(hwnd, "Could not create button.", "Error", MB_OK | MB_ICONERROR);

        }
        break;
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case ID_MYBUTTON:
                    OpenFileSaveDialog(hwnd);
                    break;
                case ID_FILE_ABOUT:
                    MessageBox(hwnd, "About menu item clicked", "Notice", MB_OK | MB_ICONINFORMATION);
                    break;
                case ID_FILE_EXIT:
                    DestroyWindow(hwnd);
                    break;
                }
            break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    return 0;
}

BOOL SaveTextFile(HWND hEdit, LPCTSTR pszFileName)
{
    HANDLE hFile;
    BOOL bSuccess = FALSE;

    hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        DWORD dwTextLength;

        dwTextLength = GetWindowTextLength(hEdit);
        if (dwTextLength > 0)
        {
            LPSTR pszText;
            DWORD dwBufferSize = dwTextLength + 1;

            pszText = (LPSTR)GlobalAlloc(GPTR, dwBufferSize);
            if (pszText != NULL)
            {
                if (GetWindowText(hEdit, pszText, dwBufferSize))
                {
                    DWORD dwWritten;

                    if (WriteFile(hFile, pszText, dwTextLength, &dwWritten, NULL))
                        bSuccess = TRUE;
                }
                GlobalFree(pszText);
            }
        }
        CloseHandle(hFile);
    }
    return bSuccess;
}

void OpenFileSaveDialog(HWND hwnd)
{
    OPENFILENAME ofn;
    char szFileName[MAX_PATH] = "";

    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrDefExt = "txt";
    ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

    if (GetSaveFileName(&ofn))
    {
        HWND hEdit = GetDlgItem(hwnd, IDC_MY_EDIT);
        SaveTextFile(hEdit, szFileName);
    }
}
0
Subscribe to my newsletter

Read articles from Ciprian Fusa directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ciprian Fusa
Ciprian Fusa

I am a .NET Developer and Consultant with over 15 years of experience, I specialize in crafting scalable, high-performance applications that drive business growth and innovation.