본문으로 건너뛰기
Previous
Next
Windows API 파일 I/O·레지스트리 | 파일 처리 완벽 가이드

Windows API 파일 I/O·레지스트리 | 파일 처리 완벽 가이드

Windows API 파일 I/O·레지스트리 | 파일 처리 완벽 가이드

이 글의 핵심

Windows에서 파일 처리는 CreateFile로 핸들을 얻고 ReadFile/WriteFile로 읽고 씁니다. 레지스트리는 RegOpenKeyEx로 키를 열고 RegQueryValueEx로 읽습니다. 파일 매핑으로 대용량 파일을 메모리처럼 처리할 수 있습니다.

들어가며

Windows 파일 시스템 API는 1990년대 Windows NT부터 제공된 강력한 파일 처리 인터페이스입니다. C 표준 라이브러리(fopen, fread)보다 저수준이지만, 비동기 I/O, 파일 잠금, 메모리 매핑 같은 고급 기능을 제공합니다.

// Windows API 파일 쓰기 기본
HANDLE hFile = CreateFile(L"test.txt", GENERIC_WRITE, 0, NULL,
                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

const char* data = "Hello, Windows!";
DWORD written;
WriteFile(hFile, data, (DWORD)strlen(data), &written, NULL);

CloseHandle(hFile);

레지스트리는 Windows의 중앙 설정 데이터베이스로, 애플리케이션 설정, 사용자 환경, 시스템 구성을 저장합니다.


1. 파일 열기와 닫기

1.1 CreateFile - 모든 것의 시작

HANDLE CreateFile(
    LPCWSTR lpFileName,               // 파일 경로
    DWORD dwDesiredAccess,            // 접근 권한
    DWORD dwShareMode,                // 공유 모드
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // 보안 속성
    DWORD dwCreationDisposition,      // 생성 방식
    DWORD dwFlagsAndAttributes,       // 플래그와 속성
    HANDLE hTemplateFile              // 템플릿 파일 (보통 NULL)
);

// 읽기 전용으로 열기
HANDLE hFile = CreateFile(
    L"C:\\data.txt",
    GENERIC_READ,          // 읽기
    FILE_SHARE_READ,       // 다른 프로세스도 읽기 가능
    NULL,                  // 기본 보안
    OPEN_EXISTING,         // 파일이 있어야 함
    FILE_ATTRIBUTE_NORMAL, // 일반 파일
    NULL
);

if (hFile == INVALID_HANDLE_VALUE) {
    DWORD err = GetLastError();
    wchar_t buf[256];
    swprintf_s(buf, L"파일 열기 실패! 에러 코드: %d", err);
    MessageBox(NULL, buf, L"오류", MB_ICONERROR);
    return;
}

// 사용 후 반드시 닫기
CloseHandle(hFile);

1.2 CreateFile 파라미터 상세

// dwDesiredAccess (접근 권한)
GENERIC_READ           // 읽기
GENERIC_WRITE          // 쓰기
GENERIC_READ | GENERIC_WRITE  // 읽기+쓰기

// dwShareMode (공유 모드)
0                      // 독점 (다른 프로세스 접근 불가)
FILE_SHARE_READ        // 다른 프로세스 읽기 허용
FILE_SHARE_WRITE       // 다른 프로세스 쓰기 허용
FILE_SHARE_DELETE      // 다른 프로세스 삭제 허용
FILE_SHARE_READ | FILE_SHARE_WRITE  // 읽기+쓰기 허용

// dwCreationDisposition (생성 방식)
CREATE_NEW             // 새 파일 생성 (이미 있으면 실패)
CREATE_ALWAYS          // 항상 새 파일 생성 (덮어쓰기)
OPEN_EXISTING          // 기존 파일 열기 (없으면 실패)
OPEN_ALWAYS            // 있으면 열고, 없으면 생성
TRUNCATE_EXISTING      // 기존 파일 열고 크기를 0으로

// dwFlagsAndAttributes (플래그와 속성)
FILE_ATTRIBUTE_NORMAL     // 일반 파일
FILE_ATTRIBUTE_READONLY   // 읽기 전용
FILE_ATTRIBUTE_HIDDEN     // 숨김
FILE_FLAG_OVERLAPPED      // 비동기 I/O
FILE_FLAG_NO_BUFFERING    // 버퍼링 없음 (직접 I/O)
FILE_FLAG_SEQUENTIAL_SCAN // 순차 스캔 최적화
FILE_FLAG_RANDOM_ACCESS   // 랜덤 액세스 최적화

1.3 파일 존재 확인

bool FileExists(const wchar_t* path)
{
    DWORD attr = GetFileAttributes(path);
    return (attr != INVALID_FILE_ATTRIBUTES &&
            !(attr & FILE_ATTRIBUTE_DIRECTORY));
}

// 사용 예
if (FileExists(L"C:\\config.ini")) {
    MessageBox(NULL, L"파일이 존재합니다.", L"알림", MB_OK);
}

2. 파일 읽기와 쓰기

2.1 ReadFile - 파일 읽기

// 텍스트 파일 읽기
HANDLE hFile = CreateFile(L"data.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, L"파일 열기 실패!", L"오류", MB_ICONERROR);
    return;
}

// 파일 크기 얻기
LARGE_INTEGER fileSize;
GetFileSizeEx(hFile, &fileSize);

// 버퍼 할당
char* buffer = new char[(size_t)fileSize.QuadPart + 1];

// 읽기
DWORD bytesRead;
if (!ReadFile(hFile, buffer, (DWORD)fileSize.QuadPart, &bytesRead, NULL)) {
    MessageBox(NULL, L"파일 읽기 실패!", L"오류", MB_ICONERROR);
    delete[] buffer;
    CloseHandle(hFile);
    return;
}

buffer[bytesRead] = '\0';  // null terminator

// 사용
MessageBoxA(NULL, buffer, "파일 내용", MB_OK);

// 정리
delete[] buffer;
CloseHandle(hFile);

2.2 WriteFile - 파일 쓰기

// 텍스트 파일 쓰기
HANDLE hFile = CreateFile(L"output.txt", GENERIC_WRITE, 0, NULL,
                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, L"파일 생성 실패!", L"오류", MB_ICONERROR);
    return;
}

const char* data = "Hello, Windows!\nLine 2\nLine 3";
DWORD bytesWritten;

if (!WriteFile(hFile, data, (DWORD)strlen(data), &bytesWritten, NULL)) {
    MessageBox(NULL, L"파일 쓰기 실패!", L"오류", MB_ICONERROR);
    CloseHandle(hFile);
    return;
}

CloseHandle(hFile);

wchar_t buf[256];
swprintf_s(buf, L"%d 바이트 기록됨", bytesWritten);
MessageBox(NULL, buf, L"완료", MB_OK);

2.3 유니코드 파일 처리

// UTF-16 (유니코드) 파일 쓰기
HANDLE hFile = CreateFile(L"unicode.txt", GENERIC_WRITE, 0, NULL,
                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

// BOM (Byte Order Mark) 쓰기
WORD bom = 0xFEFF;
DWORD written;
WriteFile(hFile, &bom, sizeof(WORD), &written, NULL);

// 유니코드 문자열 쓰기
const wchar_t* text = L"안녕하세요, 세계!\nHello, World!";
WriteFile(hFile, text, (DWORD)(wcslen(text) * sizeof(wchar_t)), &written, NULL);

CloseHandle(hFile);

2.4 파일 포인터 이동

HANDLE hFile = CreateFile(L"data.bin", GENERIC_READ | GENERIC_WRITE,
                          0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

// 파일 처음으로
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);

// 파일 끝으로
SetFilePointer(hFile, 0, NULL, FILE_END);

// 상대 위치 (현재 위치에서 100바이트 앞으로)
SetFilePointer(hFile, 100, NULL, FILE_CURRENT);

// 현재 위치 얻기
DWORD pos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);

// 대용량 파일 (64비트)
LARGE_INTEGER distance;
distance.QuadPart = 1000000000LL;  // 1GB
SetFilePointerEx(hFile, distance, NULL, FILE_BEGIN);

CloseHandle(hFile);

3. 파일 정보와 속성

3.1 파일 정보 얻기

HANDLE hFile = CreateFile(L"data.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

BY_HANDLE_FILE_INFORMATION fileInfo;
if (GetFileInformationByHandle(hFile, &fileInfo)) {
    wchar_t buf[1024];
    swprintf_s(buf,
        L"파일 크기: %u 바이트\n"
        L"생성 시간: %08X %08X\n"
        L"수정 시간: %08X %08X\n"
        L"속성: %08X",
        fileInfo.nFileSizeLow,
        fileInfo.ftCreationTime.dwHighDateTime,
        fileInfo.ftCreationTime.dwLowDateTime,
        fileInfo.ftLastWriteTime.dwHighDateTime,
        fileInfo.ftLastWriteTime.dwLowDateTime,
        fileInfo.dwFileAttributes
    );
    MessageBox(NULL, buf, L"파일 정보", MB_OK);
}

CloseHandle(hFile);

3.2 파일 시간 변환

// FILETIME을 SYSTEMTIME으로 변환
BY_HANDLE_FILE_INFORMATION fileInfo;
GetFileInformationByHandle(hFile, &fileInfo);

SYSTEMTIME st;
FileTimeToSystemTime(&fileInfo.ftLastWriteTime, &st);

wchar_t buf[256];
swprintf_s(buf, L"수정 시간: %04d-%02d-%02d %02d:%02d:%02d",
    st.wYear, st.wMonth, st.wDay,
    st.wHour, st.wMinute, st.wSecond);
MessageBox(NULL, buf, L"시간", MB_OK);

// 로컬 시간으로 변환
FILETIME ftLocal;
FileTimeToLocalFileTime(&fileInfo.ftLastWriteTime, &ftLocal);
FileTimeToSystemTime(&ftLocal, &st);

swprintf_s(buf, L"로컬 시간: %04d-%02d-%02d %02d:%02d:%02d",
    st.wYear, st.wMonth, st.wDay,
    st.wHour, st.wMinute, st.wSecond);
MessageBox(NULL, buf, L"로컬 시간", MB_OK);

3.3 파일 속성 변경

// 읽기 전용 설정
SetFileAttributes(L"data.txt", FILE_ATTRIBUTE_READONLY);

// 숨김 파일 설정
SetFileAttributes(L"secret.dat", FILE_ATTRIBUTE_HIDDEN);

// 여러 속성 조합
SetFileAttributes(L"important.txt",
    FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);

// 속성 읽기
DWORD attr = GetFileAttributes(L"data.txt");
if (attr != INVALID_FILE_ATTRIBUTES) {
    if (attr & FILE_ATTRIBUTE_READONLY) {
        MessageBox(NULL, L"읽기 전용 파일입니다.", L"알림", MB_OK);
    }
}

4. 디렉토리 작업

4.1 디렉토리 생성과 삭제

// 디렉토리 생성
if (CreateDirectory(L"C:\\MyFolder", NULL)) {
    MessageBox(NULL, L"폴더 생성 성공!", L"알림", MB_OK);
} else {
    DWORD err = GetLastError();
    if (err == ERROR_ALREADY_EXISTS) {
        MessageBox(NULL, L"폴더가 이미 존재합니다.", L"알림", MB_OK);
    } else {
        MessageBox(NULL, L"폴더 생성 실패!", L"오류", MB_ICONERROR);
    }
}

// 디렉토리 삭제 (비어있어야 함)
if (RemoveDirectory(L"C:\\MyFolder")) {
    MessageBox(NULL, L"폴더 삭제 성공!", L"알림", MB_OK);
} else {
    MessageBox(NULL, L"폴더 삭제 실패! (비어있지 않음?)", L"오류", MB_ICONERROR);
}

4.2 디렉토리 탐색

#include <vector>
#include <string>

std::vector<std::wstring> ListFiles(const wchar_t* directory)
{
    std::vector<std::wstring> files;
    
    wchar_t searchPath[MAX_PATH];
    swprintf_s(searchPath, L"%s\\*", directory);
    
    WIN32_FIND_DATA findData;
    HANDLE hFind = FindFirstFile(searchPath, &findData);
    
    if (hFind == INVALID_HANDLE_VALUE) {
        return files;
    }
    
    do {
        // "."와 ".." 제외
        if (wcscmp(findData.cFileName, L".") != 0 &&
            wcscmp(findData.cFileName, L"..") != 0) {
            
            files.push_back(findData.cFileName);
            
            // 디렉토리 여부 확인
            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 디렉토리
            } else {
                // 파일
            }
        }
    } while (FindNextFile(hFind, &findData));
    
    FindClose(hFind);
    return files;
}

// 사용 예
std::vector<std::wstring> files = ListFiles(L"C:\\Windows");
std::wstring list;

for (const auto& file : files) {
    list += file + L"\n";
}

MessageBox(NULL, list.c_str(), L"파일 목록", MB_OK);

4.3 재귀적 디렉토리 탐색

void ListFilesRecursive(const wchar_t* directory, std::vector<std::wstring>& files)
{
    wchar_t searchPath[MAX_PATH];
    swprintf_s(searchPath, L"%s\\*", directory);
    
    WIN32_FIND_DATA findData;
    HANDLE hFind = FindFirstFile(searchPath, &findData);
    
    if (hFind == INVALID_HANDLE_VALUE) return;
    
    do {
        if (wcscmp(findData.cFileName, L".") != 0 &&
            wcscmp(findData.cFileName, L"..") != 0) {
            
            wchar_t fullPath[MAX_PATH];
            swprintf_s(fullPath, L"%s\\%s", directory, findData.cFileName);
            
            if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                // 서브 디렉토리 탐색
                ListFilesRecursive(fullPath, files);
            } else {
                // 파일 추가
                files.push_back(fullPath);
            }
        }
    } while (FindNextFile(hFind, &findData));
    
    FindClose(hFind);
}

5. 파일 매핑 (Memory-Mapped Files)

5.1 기본 파일 매핑

// 파일 열기
HANDLE hFile = CreateFile(L"large.dat", GENERIC_READ | GENERIC_WRITE,
                          0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

// 파일 매핑 객체 생성
HANDLE hMapping = CreateFileMapping(
    hFile,
    NULL,
    PAGE_READWRITE,  // 읽기+쓰기
    0,               // 최대 크기 (상위 32비트)
    0,               // 최대 크기 (하위 32비트, 0 = 파일 크기)
    NULL             // 이름 (NULL = 익명)
);

if (hMapping == NULL) {
    CloseHandle(hFile);
    return;
}

// 메모리에 매핑
void* pView = MapViewOfFile(
    hMapping,
    FILE_MAP_ALL_ACCESS,  // 접근 권한
    0,                    // 오프셋 (상위 32비트)
    0,                    // 오프셋 (하위 32비트)
    0                     // 크기 (0 = 전체)
);

if (pView == NULL) {
    CloseHandle(hMapping);
    CloseHandle(hFile);
    return;
}

// 메모리처럼 사용!
char* data = (char*)pView;
data[0] = 'A';  // 파일의 첫 바이트 수정

// 정리
UnmapViewOfFile(pView);
CloseHandle(hMapping);
CloseHandle(hFile);

5.2 프로세스 간 공유 메모리

// 프로세스 A (생성자)
HANDLE hMapping = CreateFileMapping(
    INVALID_HANDLE_VALUE,  // 파일 없음 (메모리만)
    NULL,
    PAGE_READWRITE,
    0,
    4096,  // 4KB
    L"MySharedMemory"  // ★ 이름 지정
);

void* pView = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

// 데이터 쓰기
strcpy_s((char*)pView, 4096, "Hello from Process A!");

// ... 프로세스 A 계속 실행

UnmapViewOfFile(pView);
CloseHandle(hMapping);

// 프로세스 B (읽기)
HANDLE hMapping = OpenFileMapping(
    FILE_MAP_ALL_ACCESS,
    FALSE,
    L"MySharedMemory"  // ★ 같은 이름
);

if (hMapping == NULL) {
    MessageBox(NULL, L"공유 메모리 열기 실패!", L"오류", MB_ICONERROR);
    return;
}

void* pView = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

// 데이터 읽기
MessageBoxA(NULL, (char*)pView, "Process B", MB_OK);

UnmapViewOfFile(pView);
CloseHandle(hMapping);

6. 레지스트리 (Registry)

6.1 레지스트리 읽기

// HKEY_CURRENT_USER\Software\MyApp\Settings\UserName 읽기

HKEY hKey;
LONG result = RegOpenKeyEx(
    HKEY_CURRENT_USER,
    L"Software\\MyApp\\Settings",
    0,
    KEY_READ,
    &hKey
);

if (result != ERROR_SUCCESS) {
    MessageBox(NULL, L"레지스트리 키 열기 실패!", L"오류", MB_ICONERROR);
    return;
}

wchar_t userName[256];
DWORD dataSize = sizeof(userName);
DWORD dataType;

result = RegQueryValueEx(
    hKey,
    L"UserName",
    NULL,
    &dataType,
    (LPBYTE)userName,
    &dataSize
);

if (result == ERROR_SUCCESS && dataType == REG_SZ) {
    MessageBox(NULL, userName, L"사용자 이름", MB_OK);
} else {
    MessageBox(NULL, L"값 읽기 실패!", L"오류", MB_ICONERROR);
}

RegCloseKey(hKey);

6.2 레지스트리 쓰기

// HKEY_CURRENT_USER\Software\MyApp\Settings 키 생성

HKEY hKey;
DWORD disposition;

LONG result = RegCreateKeyEx(
    HKEY_CURRENT_USER,
    L"Software\\MyApp\\Settings",
    0,
    NULL,
    REG_OPTION_NON_VOLATILE,
    KEY_WRITE,
    NULL,
    &hKey,
    &disposition
);

if (result != ERROR_SUCCESS) {
    MessageBox(NULL, L"레지스트리 키 생성 실패!", L"오류", MB_ICONERROR);
    return;
}

if (disposition == REG_CREATED_NEW_KEY) {
    MessageBox(NULL, L"새 키 생성됨", L"알림", MB_OK);
} else if (disposition == REG_OPENED_EXISTING_KEY) {
    MessageBox(NULL, L"기존 키 열림", L"알림", MB_OK);
}

// 문자열 값 쓰기
const wchar_t* userName = L"홍길동";
RegSetValueEx(
    hKey,
    L"UserName",
    0,
    REG_SZ,
    (LPBYTE)userName,
    (DWORD)((wcslen(userName) + 1) * sizeof(wchar_t))
);

// 정수 값 쓰기
DWORD age = 30;
RegSetValueEx(
    hKey,
    L"Age",
    0,
    REG_DWORD,
    (LPBYTE)&age,
    sizeof(DWORD)
);

RegCloseKey(hKey);

6.3 레지스트리 삭제

// 값 삭제
HKEY hKey;
RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\MyApp\\Settings", 0, KEY_WRITE, &hKey);
RegDeleteValue(hKey, L"UserName");
RegCloseKey(hKey);

// 키 삭제 (비어있어야 함)
RegDeleteKey(HKEY_CURRENT_USER, L"Software\\MyApp\\Settings");

// 키 및 하위 키 모두 삭제 (Windows Vista+)
RegDeleteTree(HKEY_CURRENT_USER, L"Software\\MyApp");

6.4 레지스트리 열거

HKEY hKey;
RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\MyApp\\Settings", 0, KEY_READ, &hKey);

// 값 개수와 하위 키 개수 얻기
DWORD numValues, numSubKeys;
RegQueryInfoKey(hKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL,
                &numValues, NULL, NULL, NULL, NULL);

wchar_t valueName[256];
DWORD valueNameSize;
DWORD dataType;
BYTE data[1024];
DWORD dataSize;

std::wstring list;

// 모든 값 열거
for (DWORD i = 0; i < numValues; i++) {
    valueNameSize = 256;
    dataSize = sizeof(data);
    
    if (RegEnumValue(hKey, i, valueName, &valueNameSize, NULL,
                     &dataType, data, &dataSize) == ERROR_SUCCESS) {
        
        wchar_t buf[512];
        
        if (dataType == REG_SZ) {
            swprintf_s(buf, L"%s = %s\n", valueName, (wchar_t*)data);
        } else if (dataType == REG_DWORD) {
            swprintf_s(buf, L"%s = %u\n", valueName, *(DWORD*)data);
        }
        
        list += buf;
    }
}

MessageBox(NULL, list.c_str(), L"레지스트리 값들", MB_OK);

RegCloseKey(hKey);

7. INI 파일

7.1 INI 파일 읽기/쓰기

// config.ini 파일 쓰기
WritePrivateProfileString(
    L"Settings",       // 섹션
    L"UserName",       // 키
    L"홍길동",         // 값
    L"C:\\config.ini"  // 파일 경로
);

WritePrivateProfileInt(
    L"Settings",
    L"Age",
    30,
    L"C:\\config.ini"
);

// config.ini 파일 읽기
wchar_t userName[256];
GetPrivateProfileString(
    L"Settings",
    L"UserName",
    L"기본값",         // 기본값 (키가 없을 때)
    userName,
    256,
    L"C:\\config.ini"
);

int age = GetPrivateProfileInt(
    L"Settings",
    L"Age",
    0,                 // 기본값
    L"C:\\config.ini"
);

wchar_t buf[512];
swprintf_s(buf, L"이름: %s\n나이: %d", userName, age);
MessageBox(NULL, buf, L"INI 파일", MB_OK);

7.2 INI 섹션 열거

// 모든 섹션 이름 얻기
wchar_t sections[4096];
GetPrivateProfileSectionNames(sections, 4096, L"C:\\config.ini");

// NULL로 구분된 문자열 파싱
std::wstring list;
const wchar_t* p = sections;

while (*p) {
    list += p;
    list += L"\n";
    p += wcslen(p) + 1;
}

MessageBox(NULL, list.c_str(), L"섹션 목록", MB_OK);

8. 실전 예제: 로그 파일 관리자

#include <windows.h>
#include <string>
#include <ctime>

class Logger {
private:
    HANDLE m_hFile;
    CRITICAL_SECTION m_cs;
    
public:
    Logger(const wchar_t* filename) {
        m_hFile = CreateFile(
            filename,
            GENERIC_WRITE,
            FILE_SHARE_READ,
            NULL,
            OPEN_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            NULL
        );
        
        if (m_hFile != INVALID_HANDLE_VALUE) {
            // 파일 끝으로 이동 (추가 모드)
            SetFilePointer(m_hFile, 0, NULL, FILE_END);
        }
        
        InitializeCriticalSection(&m_cs);
    }
    
    ~Logger() {
        if (m_hFile != INVALID_HANDLE_VALUE) {
            CloseHandle(m_hFile);
        }
        DeleteCriticalSection(&m_cs);
    }
    
    void Log(const char* level, const char* message) {
        if (m_hFile == INVALID_HANDLE_VALUE) return;
        
        EnterCriticalSection(&m_cs);
        
        // 타임스탬프
        time_t now = time(NULL);
        struct tm timeinfo;
        localtime_s(&timeinfo, &now);
        
        char timestamp[64];
        strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
        
        // 로그 형식: [TIMESTAMP] [LEVEL] MESSAGE\n
        char logLine[1024];
        sprintf_s(logLine, "[%s] [%s] %s\r\n", timestamp, level, message);
        
        DWORD written;
        WriteFile(m_hFile, logLine, (DWORD)strlen(logLine), &written, NULL);
        
        LeaveCriticalSection(&m_cs);
    }
    
    void Info(const char* message) { Log("INFO", message); }
    void Warning(const char* message) { Log("WARN", message); }
    void Error(const char* message) { Log("ERROR", message); }
};

// 사용 예
Logger logger(L"C:\\app.log");

logger.Info("Application started");
logger.Warning("Low memory warning");
logger.Error("Failed to connect to server");
logger.Info("Application terminated");

9. 실전 예제: 설정 관리 클래스

class Config {
private:
    std::wstring m_iniPath;
    
public:
    Config(const wchar_t* iniPath) : m_iniPath(iniPath) {}
    
    // 문자열 읽기
    std::wstring GetString(const wchar_t* section, const wchar_t* key,
                           const wchar_t* defaultValue = L"") {
        wchar_t buf[1024];
        GetPrivateProfileString(section, key, defaultValue, buf, 1024, m_iniPath.c_str());
        return buf;
    }
    
    // 정수 읽기
    int GetInt(const wchar_t* section, const wchar_t* key, int defaultValue = 0) {
        return GetPrivateProfileInt(section, key, defaultValue, m_iniPath.c_str());
    }
    
    // 불린 읽기
    bool GetBool(const wchar_t* section, const wchar_t* key, bool defaultValue = false) {
        return GetInt(section, key, defaultValue ? 1 : 0) != 0;
    }
    
    // 문자열 쓰기
    void SetString(const wchar_t* section, const wchar_t* key, const wchar_t* value) {
        WritePrivateProfileString(section, key, value, m_iniPath.c_str());
    }
    
    // 정수 쓰기
    void SetInt(const wchar_t* section, const wchar_t* key, int value) {
        wchar_t buf[32];
        swprintf_s(buf, L"%d", value);
        WritePrivateProfileString(section, key, buf, m_iniPath.c_str());
    }
    
    // 불린 쓰기
    void SetBool(const wchar_t* section, const wchar_t* key, bool value) {
        SetInt(section, key, value ? 1 : 0);
    }
};

// 사용 예
Config config(L"C:\\app.ini");

// 읽기
std::wstring userName = config.GetString(L"User", L"Name", L"Guest");
int volume = config.GetInt(L"Audio", L"Volume", 50);
bool autoSave = config.GetBool(L"Options", L"AutoSave", true);

// 쓰기
config.SetString(L"User", L"Name", L"홍길동");
config.SetInt(L"Audio", L"Volume", 75);
config.SetBool(L"Options", L"AutoSave", false);

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글이 도움이 되셨나요? Windows 파일 I/O와 레지스트리를 마스터하는 데 도움이 되었기를 바랍니다!

다음 글에서는 Windows API 멀티스레딩을 다루겠습니다. 🧵