(Win32 : WindowsProgramming-7) Window API 개념

Posted by : at

Category : win32   WindowsProgramming


Win32 API 개념

  • Windows OS에서 실행되는 프로그램을 개발하기 위한 SDK
#include <stdio.h>
#include <Windows.h>    // for Win32 API

int main()
{
    printf("Hello, C\n");

    MessageBoxA(0, "Hello", "API", MB_OK);
}
// Win32 API의 데이터 타입
DWORD   d;      // unsinged long
WORD    w;      // unsinged char
UINT    ui;     // unsinged int
POINT   pp;     // struct tagPOINT { LONG x; LONG y; }
PINT    pi;     // int*
PSTR    ps;     // char*
PCSTR   p3;     // const char*

API함수의 호출규약

  • C 표준 함수 : __cdecl
    • 호출자에서 스택파괴
  • Windows API 함수 : __stdcall
    • 피호출자에서 스택파괴
    • 시스템안정적, 코드양(용량)을 줄일 수 있음.
// ...
int
WINAPI  // __stdcall을 의미
MessageBox(
    // ...
)

동일한 함수가 3가지 형태로 제공되는 형태가 있다.

MessageBoxA(0, "Hello", "API", MB_OK);
MessageBoxW(0, L"Hello", L"API", MB_OK);
MessageBox (0, _T("Hello"), _T("API"), MB_OK);

자세한 설명은 UNICODE를 설명후 진행


UNICODE

#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "abcd가나다라";

    printf("%d\n", sizeof(s));  // 13(마지막에 null 포함)
    printf("%d\n", strlen(s));  // 12
}
  • SBCS(Single Byte Character Set) : 한 바이트를 사용해서 문자를 표현, 256개 밖에 사용불가 영어이외의 언어를 표현하지 못함.
  • MBCS(Multi Byte Character Set), 멀티바이트 문자집합 : 하나의 문자를 표현하기 위해 다양한 바이트 수를 사용, 윈도우OS에서 영어는 1바이트, 한글은 2바이트 (보통 2바이트 정로를 사용하기에 DBCS(Double Bytes Character Set)이라고도 한다.)

DBCS문제점

#include <stdio.h>
#include <string.h>
#include <string.h>

int main()
{
    char s[] = "abcd가나다라";  // DBCS

    char* p = s;

    while(*p != 0)
    {
        printf("%s\n", p);
        p = p + 1;
        /*
        영어는 1바이트만 추가해도 다음 문자열이 나오는데,
        한글은 2바이트를 추가해야 다음 문자열을 출력할 수 있다.
        */
    }
}

해결

#include <Windows.h>

// ...

while(*p != 0)
{
    printf("%s\n", p);
    if(IsDBCSLeadByte(*p))
        p = p + 2;
    else
        p = p + 1;
}

조금 더 편하게 해결하고 싶다면

#include <Windows.h>

// ...

while(*p != 0)
{
    printf("%s\n", p);
    p = CharNextA(p);
}
  • DBCS 문제점
    • 하나의 문자를 1, 2bytes로 표현하기에 프로그램 작성시 문제점이 있다.
    • 나라별로 표준(시스템 로캘을 변경시 달라진다)이 다르기에 호환성 문제가 있다.

결론 : 전세계 대부분의 문자를 동시에 표현할 수 있는 새로운 문자 집합이 필요하다 -> Unicode


Unicode

  • UTF-8 : 가변 길이로 인코딩, 1,2,3,4 바이트 모두 사용 언어에 따라 다름(영어 1, 한글 3바이트 사용)
    • 아무래도 영어를 많이 사용하는 환경에서 사용
  • UTF-16 : 가변 길이로 인코딩, 대부분 문자 2바이트 특정문자 4바이트(영어 2, 한글 2바이트 사용)
    • 한글과 영어가 비슷하게 사용되는 환경에서 사용
  • UTF-32 : 모든 문자를 4바이트로 인코딩, 단 메모리 사용량 증가
    • 리눅스에서 사용
  • Windows OS는 UTF-16을 사용
    • Linux의 경우 UTF-32임 참고
#include <stdio.h>
#include <string.h>

int main()
{
    char s1[] = "abcd가나다라"; // DBCS
    wchar_t s2[] = L"abcd가나다라";

    // L"" -> 문자열을 유니코드로 만들어 달라 -> Windows의 경우 UTF-16이고, 2byte

    /*
    whar_t :
        Windows : 2byte
        Linux : 4byte, 리눅스의 경우 UTF-32를 쓰니 그렇겠지?
    */

    printf("%d\n", sizeof(s2));         // 18(null포함)
    printf("%d\n", strlen((char*)s2));  // 1 -> why?
    // 제일 처음 나오는 문자 a는 아스키코드로 65이고 2바이트로 표현시 65, 00
    // little endian 표현으로 65, 00로 표현하게 되는데
    // strlen은 00(null)을 만날시 마지막 문자로 인식해서 길이를 리턴하게 된다.

    // 유니코드의 문자열 개수를 세는 새로운 len함수가 필요하다
}

유니코드용 c표준함수가 필요해진다.

printf("%d\n", wcslen(s2));

// 출력 또한 유니코드용이 있음
wprintf(L"AA\n");

MessageBoxA(0, "A", "B", MB_OK);
MessageBoxW(0, L"A", L"B", MB_OK);

유니코드 매크로

// 대략 매크로는 이러하다

#ifdef _UNICODE
    typedef wchar_t TCHAR;
    #define _T(x)       L##x
    #define _tcslen     wcslen
    #define _tprintf    wprintf
#else
    // ...
#endif
#include <tchar.h>

int main()
{
    TCHAR s[] = _T("abcd가나다라");

    // 유니코드일때는 유니코드로 쓰게해 달라

    _tprintf(_T("%d\n"), _tsclen(s));

    MessageBox(0, _T("A"), _T("B"), MB_OK);
}

(이건 참고)

int main(int argc, char** argv)
{
    // ...
}

main함수도 매개변수로 char** 유니코드가 아닌 문자열을 받지 않나?
main도 종류별로 나눠야 한다.

-> main, wmain, _tmain

물론 Unicode를 무조건 써야 한다는 것은 아니다. 다만 권장사항일 뿐!


GetLastError()

  • errno
    • C표준함수
    • 에러의 원인을 담은 변수
    • 구현에 따라 Thread-safe할 수 있고 아닐 수 있다는 단점이 있다.
int main()
{
    FILE* f = fopen("a.txt", "rt")

    if(f == 0)
        printf("실패 : %d\n", errno);
}
  • GetLastError()
    • Window API함수 호출이 실패 한 경우 실패의 원인을 담은 번호를 반환
    • Thread-safe하다
#define _CRT_SECURE_NO_WARNING
#include <stdio.h>
#include <Windows.h>

int main()
{
    FILE* f = fopen("a.txt", "rt"); // 없는 파일임

    if (f == 0)
        printf("실패 : %d\n", errno); // 2

    HWND hwnd = CreateWindow(0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0); // 의도적으로 Invalid한 값을 넘긺

    if(hwndd == 0)
        printf("실패 : %d, %d\n", errno, GetLastError()); // 2, 87 출력됨
    // errno이 잘못된 값을 리턴함을 알수있다.
}

참고로 도구 -> 오류조회 에서 에러타입에 대한 설명을 볼 수 있음.


About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links