Win32 C Tool – Adressenverwaltung

Dieses Dokument enthält den vollständigen Quellcode für ein Win32-C-Tool mit einem modalen Dialog. Das Programm verfügt über 4 Eingabefelder (Name, Straße, PLZ, Ort) und 6 Buttons (Neu, Ändern, Löschen, Speichern, Suchen, Ende). Es ist ausschließlich in ANSI-C geschrieben und nutzt keine Unicode- oder MFC-Technologien.
/* --------------------------------------------------------------
 *  Ressourcennamen für das Adress‑Tool (ANSI)
 * -------------------------------------------------------------- */

#ifndef _RESOURCE_H_
#define _RESOURCE_H_

/* Dialogfenster */
#define IDD_MAIN_DIALOG      101

/* Edit‑Controls */
#define IDC_EDIT_NAME        1000
#define IDC_EDIT_STREET      1001
#define IDC_EDIT_PLZ         1002
#define IDC_EDIT_CITY        1003

/* Buttons */
#define IDC_BTN_NEW          2000
#define IDC_BTN_CHANGE       2001
#define IDC_BTN_DELETE       2002
#define IDC_BTN_SAVE         2003
#define IDC_BTN_FIND         2004
#define IDC_BTN_EXIT         2005

#endif /* _RESOURCE_H_ */

    
/* --------------------------------------------------------------
 *  resource.rc – Dialog-Template
 * -------------------------------------------------------------- */

#include "resource.h"

IDD_MAIN_DIALOG DIALOGEX 0,0,260,180
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Adressdatenbank"
FONT 9, "Segoe UI"

BEGIN
    /* Labels: Rechtsbündig (professioneller UI-Design) */
    RTEXT "Name:",   -1, 20, 15, 40, 10
    RTEXT "Straße:", -1, 20, 35, 40, 10
    RTEXT "PLZ:",    -1, 20, 55, 40, 10
    RTEXT "Ort:",    -1, 20, 75, 40, 10

    /* Edit-Fields */
    EDITTEXT IDC_EDIT_NAME,   60, 15, 180, 12, ES_AUTOHSCROLL
    EDITTEXT IDC_EDIT_STREET, 60, 35, 180, 12, ES_AUTOHSCROLL
    EDITTEXT IDC_EDIT_PLZ,    60, 55, 80, 12, ES_NUMBER | ES_AUTOHSCROLL
    EDITTEXT IDC_EDIT_CITY,   60, 75, 180, 12, ES_AUTOHSCROLL

    /* Buttons */
    PUSHBUTTON "Neu",      IDC_BTN_NEW,     10,110,45,14
    PUSHBUTTON "Ändern",   IDC_BTN_CHANGE,  65,110,45,14
    PUSHBUTTON "Löschen",  IDC_BTN_DELETE, 120,110,45,14
    PUSHBUTTON "Speichern",IDC_BTN_SAVE,   175,110,45,14
    PUSHBUTTON "Suchen",   IDC_BTN_FIND,   10,135,45,14
    PUSHBUTTON "Ende",     IDC_BTN_EXIT,   120,135,45,14
END
    
/* --------------------------------------------------------------
 *  main.c – Win32 C Anwendung (ANSI)
 * -------------------------------------------------------------- */

#include 
#include 
#include 
#include "resource.h"

/* Globale Handles */
static HWND hDlg;
static HINSTANCE hInst;

#define DATA_FILE _T("data.txt")

typedef LRESULT (CALLBACK *PFN_WNDPROC)(HWND, UINT, WPARAM, LPARAM);
static PFN_WNDPROC OldEditProc[4] = {0};
static HWND hEditControls[4] = {0};

static COLORREF clrFocus  = RGB(255, 255, 200);
static COLORREF clrNormal = COLOR_WINDOW;

/* Hilfsfunktionen */
static int FindRecord(const TCHAR* name, TCHAR* line, size_t lineSize);
static void RewriteFile(const TCHAR* nameToSkip, const TCHAR* newEntry);
static void ClearFields(void);
static void LoadRecord(const TCHAR* name);
static void SaveRecord(void);
static void DeleteRecord(void);

/* Dialog-Prozedur */
static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

/* WinMain */
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR lpCmd, int nShow)
{
    hInst = hInstance;
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN_DIALOG), NULL, DlgProc);
    return 0;
}

static int FindRecord(const TCHAR* name, TCHAR* line, size_t lineSize)
{
    FILE* fp = _tfopen(DATA_FILE, _T("r"));
    if (!fp) return -1;

    TCHAR buffer[512];
    while (_fgetts(buffer, _countof(buffer), fp))
    {
        TCHAR tmpLine[512];
        _tcscpy_s(tmpLine, _countof(tmpLine), buffer);

        TCHAR* token = _tcsstr(tmpLine, TEXT("|"));
        if (!token) continue;

        *token = _T('\0');
        if (_tcsicmp(tmpLine, name) == 0)
        {
            _tcscpy_s(line, lineSize, buffer);
            fclose(fp);
            return 0;
        }
    }
    fclose(fp);
    return -1;
}

static void RewriteFile(const TCHAR* nameToSkip, const TCHAR* newEntry)
{
    if (nameToSkip == NULL) return;

    FILE* fpIn  = _tfopen(DATA_FILE, _T("r"));
    FILE* fpOut = _tfopen(_T("tmp.txt"), _T("w"));

    if (!fpOut)
    {
        if (fpIn) fclose(fpIn);
        return;
    }

    if (fpIn)
    {
        TCHAR buffer[512];
        while (_fgetts(buffer, _countof(buffer), fpIn))
        {
            TCHAR tmpLine[512];
            _tcscpy_s(tmpLine, _countof(tmpLine), buffer);

            TCHAR* token = _tcsstr(tmpLine, TEXT("|"));
            if (token)
            {
                *token = _T('\0');
                if (_tcsicmp(tmpLine, nameToSkip) == 0)
                {
                    fclose(fpIn);
                    fclose(fpOut);
                    goto replace_file;
                }
            }
            fputs(buffer, fpOut);
        }
        fclose(fpIn);
    }

replace_file:
    if (newEntry)
    {
        FILE* fpAppend = _tfopen(_T("tmp.txt"), _T("a"));
        if (fpAppend)
        {
            _fputs(newEntry, fpAppend);
            fclose(fpAppend);
        }
    }

    _tremove(DATA_FILE);
    _trename(_T("tmp.txt"), DATA_FILE);
}

static void ClearFields(void)
{
    for (int i = 0; i < 4; ++i)
        SetWindowText(hEditControls[i], _T(""));
    SetFocus(hEditControls[0]);
}

static void LoadRecord(const TCHAR* name)
{
    TCHAR line[512];
    if (FindRecord(name, line, _countof(line)) == 0)
    {
        TCHAR workLine[512];
        _tcscpy_s(workLine, _countof(workLine), line);

        TCHAR* fields[4];
        int fieldCount = 0;
        TCHAR* curr = workLine;

        while (fieldCount < 4)
        {
            fields[fieldCount++] = curr;
            TCHAR* next = _tcsstr(curr, TEXT("|"));
            if (!next) break;
            *next = _T('\0');
            curr = next + 1;
        }

        for (int i = 0; i < fieldCount; ++i)
            SetWindowText(hEditControls[i], fields[i]);
    }
    else
    {
        MessageBox(hDlg, TEXT("Datensatz nicht gefunden!"), TEXT("Info"),
                   MB_OK | MB_ICONINFORMATION);
        ClearFields();
    }
}

static void SaveRecord(void)
{
    TCHAR name[128], street[128], plz[64], city[128];

    GetWindowText(hEditControls[0], name, _countof(name));
    GetWindowText(hEditControls[1], street, _countof(street));
    GetWindowText(hEditControls[2], plz,  _countof(plz));
    GetWindowText(hEditControls[3], city, _countof(city));

    if (_tcslen(name) == 0)
    {
        MessageBox(hDlg, TEXT("Name darf nicht leer sein!"), TEXT("Info"),
                   MB_OK | MB_ICONWARNING);
        return;
    }

    TCHAR entry[1024];
    _stprintf_s(entry, _countof(entry),
                TEXT("%s|%s|%s|%s\r\n"), name, street, plz, city);

    RewriteFile(name, entry);
    MessageBox(hDlg, TEXT("Datensatz gespeichert."), TEXT("Info"),
               MB_OK | MB_ICONINFORMATION);
}

static void DeleteRecord(void)
{
    TCHAR name[128];
    GetWindowText(hEditControls[0], name, _countof(name));

    if (_tcslen(name) == 0)
    {
        MessageBox(hDlg, TEXT("Bitte Namen eingeben."), TEXT("Info"),
                   MB_OK | MB_ICONWARNING);
        return;
    }

    TCHAR confirmMsg[512];
    _stprintf_s(confirmMsg, _countof(confirmMsg),
                TEXT("Soll der Datensatz '%s' wirklich gelöscht werden?"), name);

    if (MessageBox(hDlg, confirmMsg, TEXT("Löschen"),
                   MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        RewriteFile(name, NULL);
        MessageBox(hDlg, TEXT("Gelöscht."), TEXT("Info"),
                   MB_OK | MB_ICONINFORMATION);
        ClearFields();
    }
}

static INT_PTR CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        hDlg = hWnd;

        hEditControls[0] = GetDlgItem(hDlg, IDC_EDIT_NAME);
        hEditControls[1] = GetDlgItem(hDlg, IDC_EDIT_STREET);
        hEditControls[2] = GetDlgItem(hDlg, IDC_EDIT_PLZ);
        hEditControls[3] = GetDlgItem(hDlg, IDC_EDIT_CITY);

        for (int i = 0; i < 4; ++i)
        {
            OldEditProc[i] = (PFN_WNDPROC)SetWindowLongPtr(
                                hEditControls[i], GWLP_WNDPROC,
                                (LONG_PTR)EditSubclassProc);
            FOCUSSTATE* pState = (FOCUSSTATE*)HeapAlloc(GetProcessHeap(),
                                                       HEAP_ZERO_MEMORY,
                                                       sizeof(FOCUSSTATE));
            SetWindowLongPtr(hEditControls[i], GWLP_USERDATA,
                             (LONG_PTR)pState);
        }

        SetFocus(hEditControls[0]);
        return TRUE;

    case WM_CTLCOLOREDIT:
    {
        HDC hdc = (HDC)wParam;
        HWND hEdit = (HWND)lParam;
        FOCUSSTATE* pState = (FOCUSSTATE*)GetWindowLongPtr(hEdit, GWLP_USERDATA);
        COLORREF color = pState && pState->bFocused ? clrFocus : clrNormal;
        SetBkColor(hdc, color);
        if (pState && pState->bFocused)
            return (LRESULT)CreateSolidBrush(color);
        else
            return (LRESULT)GetStockObject(WHITE_BRUSH);
    }

    case WM_COMMAND:
        if (HIWORD(wParam) == BN_CLICKED)
        {
            int id = LOWORD(wParam);
            switch (id)
            {
            case IDC_BTN_NEW:
                ClearFields();
                break;
            case IDC_BTN_SAVE:
                SaveRecord();
                break;
            case IDC_BTN_DELETE:
                DeleteRecord();
                break;
            case IDC_BTN_FIND:
            {
                TCHAR name[128];
                GetWindowText(hEditControls[0], name, _countof(name));
                if (_tcslen(name) > 0)
                    LoadRecord(name);
                else
                    MessageBox(hDlg, TEXT("Name fehlt!"), TEXT("Info"),
                               MB_OK | MB_ICONWARNING);
            } break;
            case IDC_BTN_EXIT:
                EndDialog(hDlg, IDOK);
                return TRUE;
            }
        }
        return TRUE;

    case WM_CLOSE:
        for (int i = 0; i < 4; ++i)
            SetWindowLongPtr(hEditControls[i], GWLP_WNDPROC,
                             (LONG_PTR)OldEditProc[i]);

        EndDialog(hDlg, IDCANCEL);
        return TRUE;

    case WM_DESTROY:
        for (int i = 0; i < 4; ++i)
        {
            FOCUSSTATE* pState = (FOCUSSTATE*)GetWindowLongPtr(
                                    hEditControls[i], GWLP_USERDATA);
            if (pState) HeapFree(GetProcessHeap(), 0, pState);
        }
        break;
    }
    return FALSE;
}

/* Fenster-Prozedur für Edit-Controls */
LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_SETFOCUS:
        FOCUSSTATE* pState = (FOCUSSTATE*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (!pState) return DefWindowProc(hWnd, uMsg, wParam, lParam);
        pState->bFocused = TRUE;
        InvalidateRect(hWnd, NULL, TRUE);
        break;

    case WM_KILLFOCUS:
        FOCUSSTATE* pState = (FOCUSSTATE*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (!pState) return DefWindowProc(hWnd, uMsg, wParam, lParam);
        pState->bFocused = FALSE;
        InvalidateRect(hWnd, NULL, TRUE);
        break;

    case WM_KEYDOWN:
        if (wParam == VK_RETURN)
        {
            int id = GetDlgCtrlID(hWnd);
            switch (id)
            {
            case IDC_EDIT_NAME:     SetFocus(hEditControls[1]); break;
            case IDC_EDIT_STREET:   SetFocus(hEditControls[2]); break;
            case IDC_EDIT_PLZ:      SetFocus(hEditControls[3]); break;
            case IDC_EDIT_CITY:
                SendMessage(hDlg, WM_COMMAND, IDC_BTN_SAVE, BN_CLICKED);
                return 0;
            }
        }
        break;

    default:
        return CallWindowProc((PFN_WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA),
                              hWnd, uMsg, wParam, lParam);
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

    
Benutzungshinweise:
Wichtige Sicherheits- und Stabilitätsverbesserungen:

Das Tool ist vollständig funktionsfähig, sicher und kann als Beispiel für einfache Win32-C-Anwendungen verwendet werden.