
◎ Part 1: 헤더 + 매크로 + 변환 함수들
| #ifndef MAIN_H #define MAIN_H #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> #include <string.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include <ctype.h> #include <limits.h> #include <stddef.h> #include <wincrypt.h> #include <commdlg.h> #include <wchar.h> #pragma comment(lib, "advapi32.lib") #pragma comment(linker, "/SUBSYSTEM:WINDOWS") #pragma comment(linker, "/ENTRY:WinMainCRTStartup") // Control IDs #define IDC_INPUT_TEXT 101 #define IDC_OUTPUT_TEXT 102 // Conversion buttons #define IDC_TEXT_TO_HEX 201 #define IDC_HEX_TO_TEXT 202 #define IDC_DEC_TO_HEX 203 #define IDC_HEX_TO_DEC 204 #define IDC_TEXT_TO_DEC 205 #define IDC_DEC_TO_TEXT 206 #define IDC_DEC_TO_OCTAL 207 #define IDC_OCTAL_TO_DEC 208 #define IDC_TEXT_TO_UTF7 209 #define IDC_UTF7_TO_TEXT 210 #define IDC_HEX_TO_UCS2 211 #define IDC_UCS2_TO_HEX 212 #define IDC_TEXT_TO_BINARY 213 #define IDC_BINARY_TO_TEXT 214 #define IDC_ESCAPE 215 #define IDC_UNESCAPE 216 #define IDC_ENCODE_HTML 217 #define IDC_DECODE_HTML 218 #define IDC_TEXT_TO_BASE64 219 #define IDC_BASE64_TO_TEXT 220 #define IDC_HEX_TO_BASE64 221 #define IDC_BASE64_TO_HEX 222 #define IDC_IP_TO_DEC 223 #define IDC_DEC_TO_IP 224 #define IDC_IP_TO_HEX 225 #define IDC_HEX_TO_IP 226 #define IDC_TEXT_TO_URL 227 #define IDC_URL_TO_TEXT 228 // Utility buttons #define IDC_COPY_OUTPUT 301 #define IDC_COPY_TO_INPUT 302 #define IDC_CLEAR_ALL 303 #define IDC_FILE_SELECT 304 #define IDC_TEXT_A 401 #define IDC_TEXT_B 402 #define IDC_REPLACE_GO 403 // SHA256 (32 bytes = 64 hex chars) #define SHA256_HASH_SIZE 32 #define SHA256_HEX_SIZE (SHA256_HASH_SIZE * 2 + 1) // 64 + '\0' // SHA512 (64 bytes = 128 hex chars) #define SHA512_HASH_SIZE 64 #define SHA512_HEX_SIZE (SHA512_HASH_SIZE * 2 + 1) // 128 + '\0' // Buffer sizes #define _CRT_SECURE_NO_WARNINGS #define MAX_BUFFER 8192 #define BUF_SIZE 8192 #define FILE_BUF_SIZE 65536 // 64KB: 다중 선택 경로 저장을 위한 큰 버퍼 #define BASE64_DECODE_MAX_SIZE(n) (((n) / 4) * 3 + 1) #define TEXT_TO_HEX_SIZE(n) (((n) == 0) ? 1 : (((n) * 3))) #define HEX_TO_TEXT_MAX_SIZE(n) (((n) / 2) + 1) #define TEXT_TO_DEC_SIZE(n) ((n) == 0 ? 1 : ((n) * 4 + 1)) #define DEC_TO_HEX_MAX_SIZE(count) ((count) * 9 + 1) #define TEXT_TO_BINARY_SIZE(n) ((n) == 0 ? 1 : ((n) * 9)) #define BINARY_TO_TEXT_MAX_SIZE(bits) (((bits) / 8) + 1) #define ESCAPE_STRING_MAX_SIZE(n) ((n) * 2 + 1) #define ENCODE_HTML_MAX_SIZE(n) ((n) * 6 + 1) // ============ 공통 헬퍼 함수들 ============ // Hex 문자를 숫자로 변환 static inline int hexCharToValue(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return -1; } // static inline size_t uintToStr(unsigned int val, char* buf) { if (!buf) return 0; if (val == 0) { buf[0] = '0'; return 1; } char temp[11]; // 최대 10자리 + null size_t len = 0; while (val > 0) { temp[len++] = '0' + (val % 10); val /= 10; } for (size_t i = 0; i < len; i++) { buf[i] = temp[len - 1 - i]; } return len; } // 숫자를 10진수 문자열로 변환 static inline size_t uintToDecimal(unsigned int val, char* buf) { if (val == 0) { buf[0] = '0'; return 1; } char temp[11]; size_t len = 0; while (val > 0) { temp[len++] = '0' + (val % 10); val /= 10; } for (size_t i = 0; i < len; i++) { buf[i] = temp[len - 1 - i]; } return len; } // 숫자를 16진수 문자열로 변환 static inline size_t uintToHex(unsigned int val, char* buf) { static const char hexDigits[] = "0123456789ABCDEF"; if (val == 0) { buf[0] = '0'; return 1; } char temp[9]; size_t len = 0; while (val > 0) { temp[len++] = hexDigits[val & 0x0F]; val >>= 4; } for (size_t i = 0; i < len; i++) { buf[i] = temp[len - 1 - i]; } return len; } // 안전한 2진수 파싱 static bool parseBinaryByte(const char* str, unsigned char* result) { if (!str || strlen(str) != 8) { return false; } // 모든 문자가 0 또는 1인지 확인 for (int i = 0; i < 8; i++) { if (str[i] != '0' && str[i] != '1') { return false; } } char* endptr; errno = 0; unsigned long val = strtoul(str, &endptr, 2); if (errno != 0 || endptr != str + 8 || val > 255) { return false; } *result = (unsigned char)val; return true; } // UTF-8로 인코딩 (간단한 버전) static size_t encodeUTF8(unsigned int codepoint, char* output) { if (codepoint <= 0x7F) { // 1바이트: 0xxxxxxx output[0] = (char)codepoint; return 1; } else if (codepoint <= 0x7FF) { // 2바이트: 110xxxxx 10xxxxxx output[0] = (char)(0xC0 | (codepoint >> 6)); output[1] = (char)(0x80 | (codepoint & 0x3F)); return 2; } else if (codepoint <= 0xFFFF) { // 3바이트: 1110xxxx 10xxxxxx 10xxxxxx output[0] = (char)(0xE0 | (codepoint >> 12)); output[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); output[2] = (char)(0x80 | (codepoint & 0x3F)); return 3; } else if (codepoint <= 0x10FFFF) { // 4바이트: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx output[0] = (char)(0xF0 | (codepoint >> 18)); output[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); output[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); output[3] = (char)(0x80 | (codepoint & 0x3F)); return 4; } return 0; // 잘못된 코드포인트 } // Dec to 8진수(Octal)로 변환 static size_t uintToOctal(unsigned int num, char* output) { if (num == 0) { output[0] = '0'; output[1] = '\0'; return 1; } char buffer[12]; // 최대 11자리 + 널 문자 int i = 11; buffer[i] = '\0'; while (num > 0 && i > 0) { i--; buffer[i] = (num % 8) + '0'; num /= 8; } size_t len = 11 - i; // 결과 복사 for (size_t j = 0; j < len; j++) { output[j] = buffer[i + j]; } output[len] = '\0'; return len; } // 8진수(Octal)에서 변환 static size_t uintToDec(unsigned int num, char* output) { // 32비트 unsigned int는 최대 10자리 (4294967295) + 널 문자 int len = snprintf(output, 11, "%u", num); if (len < 0 || len >= 11) { return 0; // 변환 실패 또는 버퍼 부족 } return (size_t)len; } // text to UTF7로 변환 static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static size_t encode_base64_block(const unsigned char* input, size_t inputLen, char* output, size_t outSize) { if (inputLen == 0 || inputLen > 3) { return 0; } size_t outPos = 0; unsigned int block = 0; // 바이트 조립 for (size_t i = 0; i < inputLen; i++) { block = (block << 8) | (unsigned char)input[i]; } // 패딩을 포함한 블록 생성 size_t padding = 3 - inputLen; block <<= (8 * padding); // Base64 문자 수 계산 size_t b64_char_count = (inputLen * 8 + 5) / 6; if (outSize < b64_char_count + 1) { return 0; } // 6비트씩 추출 size_t total_bits = (inputLen + padding) * 8; for (size_t j = 0; j < b64_char_count; j++) { int shift = (int)(total_bits - (j + 1) * 6); // 시프트는 int OK unsigned char index = (block >> shift) & 0x3F; output[outPos++] = base64_chars[index]; } return outPos; } // ============ 해시 관련 함수들 ============ // 헬퍼 함수: 바이트 배열을 16진수 문자열로 변환 (소문자) static void bytesToHex(const BYTE* bytes, size_t len, char* hexHash, size_t hexHashSize) { // 버퍼 크기 검증 (len * 2 + 1 필요) if (!bytes || !hexHash || hexHashSize < (len * 2 + 1)) { return; } for (size_t i = 0; i < len; i++) { sprintf_s(hexHash + (i * 2), hexHashSize - (i * 2), "%02x", bytes[i]); } hexHash[len * 2] = '\0'; // 명시적 널 종료 } /** * @brief 파일의 SHA 해시를 계산하는 일반 함수. * @param filePath 파일 경로 (와이드 문자) * @param hexHash 결과 버퍼 * @param hexHashSize 결과 버퍼 크기 (SHA256: 65, SHA512: 129 이상) * @param algId 사용할 해시 알고리즘 ID (CALG_SHA_256 또는 CALG_SHA_512) * @param expectedHashSize 예상되는 해시 출력 바이트 크기 (32 또는 64) * @return 성공 시 true, 실패 시 false */ static bool calculateFileHash( const wchar_t* filePath, char* hexHash, size_t hexHashSize, ALG_ID algId, DWORD expectedHashSize) { BOOL success = FALSE; HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; HANDLE hFile = INVALID_HANDLE_VALUE; BYTE* fileBuf = NULL; DWORD bytesRead = 0; BYTE hashVal[SHA512_HASH_SIZE] = { 0 }; // 최대 크기 (SHA512) DWORD hashSize = expectedHashSize; // 1. 입력 검증 if (!filePath || !hexHash || hexHashSize < (expectedHashSize * 2 + 1)) { return false; } // 결과 버퍼 초기화 memset(hexHash, 0, hexHashSize); // 2. 파일 버퍼 동적 할당 (스택 오버플로우 방지) fileBuf = (BYTE*)malloc(BUF_SIZE); if (!fileBuf) { return false; } // 3. 파일 열기 (CreateFileW) hFile = CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { free(fileBuf); return false; } // 4. 암호화 서비스 공급자 획득 (CryptAcquireContextW) if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { // PROV_RSA_AES 실패 시 PROV_RSA_FULL로 재시도 if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { goto cleanup; } } // 5. 해시 객체 생성 (CryptCreateHash) if (!CryptCreateHash(hProv, algId, 0, 0, &hHash)) { goto cleanup; } // 6. 파일 읽기 및 해시 계산 (CryptHashData) while (TRUE) { if (!ReadFile(hFile, fileBuf, BUF_SIZE, &bytesRead, NULL)) { // ReadFile 실패 goto cleanup; } if (bytesRead == 0) { // 파일 끝 정상 도달 break; } if (!CryptHashData(hHash, fileBuf, bytesRead, 0)) { goto cleanup; } } // 7. 해시값 획득 (CryptGetHashParam) if (!CryptGetHashParam(hHash, HP_HASHVAL, hashVal, &hashSize, 0)) { goto cleanup; } // 8. 해시 크기 검증 및 16진수 변환 if (hashSize == expectedHashSize) { bytesToHex(hashVal, hashSize, hexHash, hexHashSize); success = TRUE; } cleanup: if (hHash) CryptDestroyHash(hHash); if (hProv) CryptReleaseContext(hProv, 0); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (fileBuf) free(fileBuf); return success; } // SHA256 래퍼 함수 bool calculateFileHashSHA256(const wchar_t* filePath, char* hexHash, size_t hexHashSize) { return calculateFileHash(filePath, hexHash, hexHashSize, CALG_SHA_256, SHA256_HASH_SIZE); } // SHA512 래퍼 함수 bool calculateFileHashSHA512(const wchar_t* filePath, char* hexHash, size_t hexHashSize) { return calculateFileHash(filePath, hexHash, hexHashSize, CALG_SHA_512, SHA512_HASH_SIZE); } // --- 헬퍼 함수 1: Base64 문자에서 값 가져오기 --- // Base64 문자(0-63)의 인덱스 값을 반환합니다. static int base64_char_to_value(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; // UTF-7 Base64 블록 내에서는 패딩 문자 '='는 발생하지 않아야 합니다. return -1; // 유효하지 않은 Base64 문자 } // --- 헬퍼 함수 2: UTF-7 Modified Base64 디코딩 --- // Base64 문자열을 디코딩하여 바이트 스트림 (UTF-16 바이트)으로 변환합니다. // inputLen: Base64 문자열의 길이 (예: 'ICE'는 3) // outSize: 디코딩된 바이트를 저장할 버퍼 크기 static size_t decode_base64_block(const char* input, size_t inputLen, unsigned char* output, size_t outSize) { size_t outPos = 0; size_t i = 0; while (i < inputLen) { int val[4]; int val_count = 0; // Base64 문자 4개(또는 끝까지) 읽고 6비트 값으로 변환 for (int j = 0; j < 4 && i < inputLen; j++) { int current_val = base64_char_to_value(input[i++]); if (current_val != -1) { val[val_count++] = current_val; } else { // 유효하지 않은 Base64 문자는 무시하거나 오류 처리 가능. 여기서는 오류 처리 없이 진행. } } if (val_count == 0) break; // 6비트 4개를 묶어 24비트 블록 생성 (최대) unsigned int block = 0; if (val_count >= 1) block |= val[0] << 18; if (val_count >= 2) block |= val[1] << 12; if (val_count >= 3) block |= val[2] << 6; if (val_count >= 4) block |= val[3]; // 디코딩되는 바이트 수 계산 (val_count * 6 / 8의 내림) int byte_count = (val_count * 3) / 4; // 1 바이트 (상위 8비트) if (byte_count >= 1) { if (outPos >= outSize) return 0; // 버퍼 부족 output[outPos++] = (unsigned char)((block >> 16) & 0xFF); } // 2 바이트 (중간 8비트) if (byte_count >= 2) { if (outPos >= outSize) return 0; output[outPos++] = (unsigned char)((block >> 8) & 0xFF); } // 3 바이트 (하위 8비트) if (byte_count >= 3) { if (outPos >= outSize) return 0; output[outPos++] = (unsigned char)(block & 0xFF); } } return outPos; } #endif // MAIN_H HWND hInputEdit, hOutputEdit; HWND hTextA = NULL; HWND hTextB = NULL; |
1. #include 와 #define
- 전처리기와 헤더들
| #define WIN32_LEAN_AND_MEAN |
- #define은 “매크로 정의”입니다.
- WIN32_LEAN_AND_MEAN을 정의하면 windows.h 안에서 자주 안 쓰는 오래된 API들을 빼고 포함해서, 컴파일 속도와 충돌 가능성을 줄여 줍니다.
| #include <windows.h> |
- Windows API 함수와 자료형(예: HWND, HINSTANCE, CreateWindowEx 등)을 쓰기 위해 필요한 헤더입니다.
| #include <stdio.h> |
- 표준 입출력 함수들(printf, snprintf 등)을 사용하기 위한 헤더입니다.
| #include <string.h> |
- 문자열 관련 함수들(strlen, memcpy, strncmp 등)을 사용하기 위한 헤더입니다.
| #include <stdbool.h> |
- Boolean 타입(bool, true, false)을 사용하기 위한 헤더입니다.
| #include <stdint.h> |
- 고정 크기 정수 타입들(int8_t, uint16_t, int32_t, uint64_t 등)을 사용하기 위한 헤더입니다.
| #include <stdlib.h> |
- malloc, free, atoi, strtol 같은 메모리/숫자 변환 함수를 사용하기 위한 헤더입니다.
| #include <ctype.h> |
- isdigit, isxdigit 등의 문자 판별 함수를 사용하기 위한 헤더입니다.
- 링커·컴파일러 옵션
| #pragma comment(linker, "/SUBSYSTEM:WINDOWS") |
- 링커에게 “이 프로그램은 윈도우 GUI 프로그램이다”라고 알려 줍니다. 그래서 콘솔 창 없이 실행되게 됩니다.
| #pragma comment(linker, "/ENTRY:WinMainCRTStartup") |
- 링커에게 프로그램 시작 함수 이름을 지정합니다.
- 여기서는 C 런타임 초기화 함수 WinMainCRTStartup을 엔트리로 쓰겠다는 의미이고, 그 안에서 결국 사용자가 만든 WinMain이 호출됩니다.
- 컨트롤 ID 정의
| #define IDC_INPUT_TEXT 101 #define IDC_OUTPUT_TEXT 102 |
- 에디트 박스(입력/출력)에 붙일 ID 번호입니다.
- 나중에 WM_COMMAND에서 LOWORD(wParam)을 읽어 “어떤 컨트롤인지” 구분할 때 사용합니다.
| // Conversion buttons #define IDC_TEXT_TO_HEX 201 #define IDC_HEX_TO_TEXT 202 #define IDC_DEC_TO_HEX 203 #define IDC_HEX_TO_DEC 204 #define IDC_TEXT_TO_DEC 205 #define IDC_DEC_TO_TEXT 206 #define IDC_DEC_TO_OCTAL 207 #define IDC_OCTAL_TO_DEC 208 #define IDC_TEXT_TO_UTF7 209 #define IDC_UTF7_TO_TEXT 210 #define IDC_HEX_TO_UCS2 211 #define IDC_UCS2_TO_HEX 212 #define IDC_TEXT_TO_BINARY 213 #define IDC_BINARY_TO_TEXT 214 #define IDC_ESCAPE 215 #define IDC_UNESCAPE 216 #define IDC_ENCODE_HTML 217 #define IDC_DECODE_HTML 218 #define IDC_TEXT_TO_BASE64 219 #define IDC_BASE64_TO_TEXT 220 #define IDC_HEX_TO_BASE64 221 #define IDC_BASE64_TO_HEX 222 |
- 각각의 변환 기능 버튼에 대한 고유 ID입니다.
- 예를 들어 "Text to Hex" 버튼을 만들 때 IDC_TEXT_TO_HEX를 붙여 두고, 나중에 switch (btnId)에서 case IDC_TEXT_TO_HEX:이면 textToHex() 함수를 호출하는 식으로 연결합니다.
| // Utility buttons #define IDC_COPY_OUTPUT 226 #define IDC_COPY_TO_INPUT 227 #define IDC_CLEAR_ALL 228 |
- 유틸리티 기능(출력 복사, 출력→입력 복사, 전체 지우기) 버튼들의 ID입니다.
- 마찬가지로 WM_COMMAND에서 이 번호로 어떤 버튼인지 구분합니다.
- 버퍼 크기 상수
| #define MAX_BUFFER 8192 |
- 이 프로그램에서 문자열 버퍼를 만들 때 공통으로 사용할 최대 크기입니다.
- 예: char input[MAX_BUFFER]; 같이 사용해서, 한 번에 처리할 수 있는 최대 글자 수를 8192 바이트로 제한합니다.
2. 전역 변수
| HWND hInputEdit, hOutputEdit; |
- HWND = "Handle to Window" : “윈도우(컨트롤)를 가리키는 핸들(번호)”입니다.
- 윈도우 핸들 : 입력/출력 텍스트박스를 어디서나 접근할 수 있도록 전역변수를 선언합니다.
- hInputEdit : 입력용 에디트 박스, hOutputEdit : 출력용 에디트 박스를 가리킵니다.
'IT > Coding' 카테고리의 다른 글
| [C언어] Data Conversion Tool - RawVera #03 (0) | 2025.12.14 |
|---|---|
| [C언어] Data Conversion Tool - RawVera #02 (0) | 2025.12.14 |
| Python 3.14의 주요 개선사항 (0) | 2025.10.09 |
| [Python] 파이썬 2,3 가상환경 사용법 완전 가이드 (0) | 2025.10.03 |
| [Python] 파이썬 3 소개, 특징, 장단점, 개선점 등 (1) | 2025.10.03 |
