^(코딩캣)^ = @"코딩"하는 고양이;
[Win32 문자열 종류와 상호 변환 방법] Part 1 - C/C++ 기본 문자열
Win32 C/C++ 문자열 종류와 상호 변환 방법 Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수는 없고, 적절한 형태로 변환하는 과정이 일반적으로 쓰인다. 본 시리즈에서는 Win32 C/C++에서 사용하는 문자열의 종류에 대해 설명하고 상호 변환 방법에 대해 설명하겠다. 1. char, char *, const char * C/C++은 기본적으로 ASCII 코드로 인코딩된 ANSI 문자열을 지원한다. 가장 기본적인 문자와 문자열의 형태이다. charchar는 ASCII로 인코딩된 문자 하나를 표현하는 자료형으로서 1바이트의 크기를 갖는다. char *char *는 ASCII 문자로 구성되며 항상 끝이 1바..
Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 10:52

[Win32 문자열 종류와 상호 변환 방법] Part 1 - C/C++ 기본 문자열

Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 10:52

Win32 C/C++ 문자열 종류와 상호 변환 방법


Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수는 없고, 적절한 형태로 변환하는 과정이 일반적으로 쓰인다. 본 시리즈에서는 Win32 C/C++에서 사용하는 문자열의 종류에 대해 설명하고 상호 변환 방법에 대해 설명하겠다.

 

1. char, char *, const char *


C/C++은 기본적으로 ASCII 코드로 인코딩된 ANSI 문자열을 지원한다. 가장 기본적인 문자와 문자열의 형태이다.

char
char는 ASCII로 인코딩된 문자 하나를 표현하는 자료형으로서 1바이트의 크기를 갖는다.
char *
char *는 ASCII 문자로 구성되며 항상 끝이 1바이트의 NULL 문자로 끝나는 문자열을 나타내는 자료형이다.
const char *
const char *는 ASCII 문자로 구성되며 항상 끝이 1바이트의 NULL 문자로 끝나는 문자열 상수를 나타내는 자료형이다.

 

문자열과 문자열 상수에 대해 좀 더 설명하자면, 둘 다 끝이 NULL 문자로 끝나야 하는 특징이 있지만 문자열은 중간의 일부 문자를 변경할 수 있고 문자열 상수는 변경이 불가하다. 또한 문자열은 주로 버퍼에 복사된 문자열을 뜻하고, 문자열 상수는 주로 소스 코드를 작성할 때 처음부터 정해진 문자열을 뜻한다. 소스 코드에서부터 정해진 문자열은 실행파일 내에 내장되고, 프로그램이 적재될 때 함께 메모리에 로드되며 이는 프로그램이 진행되는 내내 변경될 수 없다.

 

1-1. Multibyte ANSI String


영어권 이외의 국가에서는 자국의 문자를 표현하기 위해 확장 ASCII 영역을 새롭게 정의해서 사용한다. 그러나 이것으로도 글자를 표현하기에 부족한 경우(예: 완성형 한글 및 한자)에는 확장 ASCII 문자 2개를 묶어서 자국의 문자를 할당하기도 하였다. 여기에서 영문자는 1바이트, 한글/한자는 2바이트 크기라는 규칙이 생겼다. 이러한 인코딩으로 된 문자열을 부르는 명칭에는 여러가지가 있겠으나 여기에서는 Multibyte ANSI String이라 하겠다.

Multibyte ANSI String도 컴퓨터의 입장에서는 ASCII 코드이므로 char *, const char * 형으로 표현가능하다. 단 자국 문자는 기본 2바이트이기 때문에 1 문자를 char 단독으로 저장할 수 없다. 예를 들어,

char a = 'A'; // 가능하다.
char b = '가'; // 불가능하다.

 

2. wchar_t, wchar_t *, const wchar_t *


영어권 이외의 국가를 고려하여 모든 문자가 n바이트의 크기를 갖는 Widechar 문자열을 지원하는 형식이다. Multibyte가 문자의 종류마다 1 문자당 크기(바이트)가 달라졌다면, Widechar는 모든 문자가 1 바이트 이상의 동일한 크기를 갖는 차이가 있다. 일반적으로 Windows 운영체제에서는 wchar_t의 크기가 2 바이트이며 UCS-2로 인코딩된다. Linux 운영체제에서는 wchar_t의 크기가 4 바이트이며 UCS-4로 인코딩되지만 컴파일러 설정에 따라 Windows 방식과 동일하게 변경 가능하다. (그러나 wchar_t를 쓸 경우 반드시 유니코드로 처리된다는 규칙은 없으므로 코딩에 참고하기 바란다.)

wchar_t
wchar_t는 Widechar로 표현된 1개 문자이다.
wchar_t *
wchar _t *는 Widechar로 표현된 문자열이며 1바이트 이상의 크기를 갖는 NULL 문자로 끝난다.
const wchar_t *
const wchar_t *wchar_t *와 동일하지만 상수 특성을 갖는다.

 

2-1. 문자열 리터럴 접두어


문자열 상수를 유니코드 문자열로 정의하기 위해서는 문자열 앞에 접두어를 붙이면 된다. C++11에서 정의된 문자열 리터럴literal 접두어의 종류로는 아무것도 안 붙임, L, u8, u, U의 5가지가 있다.

"문자열"
표준 ASCII 문자 및 Multibyte 문자로 구성된 ANSI 문자열이다.
L"문자열"
Widechar 문자로 구성된 문자열이다. 일반적으로 유니코드로 인코드된다.

 

이외의 문자열에 대해서는 다음 문단에서 이어진다.

 

3. 유니코드 문자로서의 char, char16_t, char32_t


char는 경우에 따라 ASCII 문자를 나타낼 때도 쓰이지만 유니코드(UTF-8) 문자를 나타낼 때도 쓰인다. 이는 일반적인 로마자에 대하여 ASCII와 Unicode가 호환되기 때문이다. 물론 로마자 이외의 문자는 유니코드로 표현된 char라고 해도 문자의 종류에 따라 Multibyte처럼 여러 바이트에 걸쳐져서 표현된다.

char16_tchar32_t는 C++11부터 도입된 자료형으로 uchar.h 또는 cuchar에 정의되어 있습니다. 각각 UTF-16과 UTF-32로 인코드된 문자 및 문자열을 표현할 때 사용된다.

 

2-1. 유니코드 문자열 리터럴의 접두어


문자열 상수를 유니코드 문자열로 인코드 하고자 할 때는 u8, u, U의 세가지 중 하나를 사용한다.

u8"문자열"
문자열 상수를 UTF-8로 저장한다. 이 때는 char 자료형으로 문자와 문자열을 표현한다.
u"문자열"
문자열 상수를 UTF-16으로 저장한다. 이 때는 char16_t 자료형으로 문자와 문자열을 표현한다.
U"문자열"
문자열 상수를 UTF-32로 저장한다. 이 때는 char32_t 자료형으로 문자와 문자열을 표현한다.

 

4. Multibyte - Widechar간 상호 변환


지금까지는 C/C++ 표준에 정의된 자료형에 대한 설명이었다. 위 세 종류의 자료형으로된 문자열 및 문자열 상수를 상호변환해 보겠다.

Multibyte(ANSI 문자의 char 형)와 Widechar(wchar_t 형)의 상호 변환은 wctomb, mbtowc, wcstombs 또는 mbstowcs를 사용한다. 전자는 1개 문자를 변환하고 후자는 문자열을 변환하며 stdlib.h 또는 cstdlib 헤더파일에 정의되어 있다.

int mbtowc (wchar_t * pwc, const char * pmb, size_t max); // Multibyte에서 Widechar로 1개 문자를 변환
size_t mbstowcs (wchar_t * dest, const char * src, size_t max); // Multibyte에서 Widechar로 문자열을 변환
int wctomb(char * pmb, wchar_t wc); // Multibyte에서 Widechar로 1개 문자를 변환
size_t wcstombs(char * dest, const wchar_t * src, size_t max); // Multibyte에서 Widechar로 문자열을 변환

 

4-1. Wide Character에서 Multibyte Character로 변환


다음은 1개의 문자를 Widechar에서 Multibyte로 변환하는 예이다.

/* C++ Source */
#include <cstdlib>
#include <cassert>
#include <clocale>

using namespace std;

/* ... */

wchar_t wc = L'가'; // 변환할 1개의 Wide Character
char mbc[8] = { 0, }; // 인코딩을 고려할 때 1개의 Wide Character에 대한 충분한 Multi Byte 공간을 제공합니다. 대체로 1개 문자당 8 바이트를 부여하면 충분하다.
int mbn = 0; // 해당 문자가 Multibyte Character로 변환시 실제로 몇 바이트를 차지하는지 확인한다.

setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.

/* 1개의 Wide Character를 Multibyte Character로 변환한다. */
/* 이 때 변환된 Multibyte Character의 바이트 수를 반환받는다. */
/* 지정한 Wide Character가 현재의 로케일에서 표현할 수 없는 문자일 때, -1이 반환된다. */
assert((mbn = wctomb(mbc, wc)) != -1);

printf("Wide Character = %lc [%d byte(s)]\n", wc, sizeof(wc));
printf("Multibyte Character = %s [%d byte(s)]\n", mbc, mbn);

/* ... */

 

4-2. Wide String에서 Multibyte String으로 변환


다음은 Wide 문자열(wchar_t *)에서 Multibyte 문자열(char */ANSI)로 변환하는 예이다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>

using namespace std;

/* ... */

wchar_t wcs[] = L"안녕하세요."; // 변환할 Wide String
char mbs[112] = { 0, }; // 인코딩을 고려할 때 1개의 Wide Character에 대한 충분한 Multi Byte 공간을 제공합니다. 대체로 1개 문자당 8 바이트를 부여하면 남아돕니다.
int mbn = 0; // 해당 문자가 Multibyte Character로 변환시 실제로 몇 바이트를 차지하는지 확인합니다.

setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋합니다.

/* Wide String을 Multibyte String으로 변환한다. */
/* 이 때 변환된 Multibyte String의 바이트 수를 반환받는다. */
/* 지정한 Wide Character가 현재의 로케일에서 표현할 수 없는 문자일 때, ((size_t)(-1))이 반환된다. */
/* ((size_t)(-1))은 컴파일러에 따라 0xFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF 중 하나를 말한다. */
assert((mbn = wcstombs(mbs, wcs, sizeof(mbs))) != ((size_t)(-1)));

printf("Wide String = %ls [%d byte(s)]\n", wcs, sizeof(wcs));
printf("Multibyte Character = %s [%d byte(s)]\n", mbs, mbn);

/* ... */

 

4-3. Multibyte Character에서 Wide Character으로 변환


다음은 Multibyte Character를 Wide Character로 변환하는 예이다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>

using namespace std;

/* ... */

char mbc[] = "가"; // 1개의 문자를 n바이트에 저장
size_t mbn = sizeof(mbc) - 1; // 끝의 NULL을 제외한 순수 1글자의 크기(바이트)
wchar_t wc[4] = { L'\0', }; // 1개의 문자를 1개의 Widechar에 저장, Surrogate로 변환될 것을 가정하여 1문자당 최대 4개의 wchar_t를 확보해 놓으면 넉넉하다.

setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.

/* Multibyte Character를 Wide Character로 변환한다. */
/* 이 로케일에서 정의하는 문자가 Wide Character로 표현될 수 없을 경우 -1을 반환한다. */
assert(mbtowc(wc, mbc, mbn) != -1); 

printf("Multibyte Character = %s [%d byte(s)]\n", mbc, mbn);
printf("Wide Character = %ls [%d byte(s)]\n", wc, sizeof(wc));

/* ... */

 

4-4. Multibyte String에서 Wide String으로 변환


다음은 Multibyte 문자열을 Widechar 문자열로 변환하는 예이다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>

/* ... */

char mbs[] = "안녕하세요."; // 변환할 Multibyte String
wchar_t wcs[28] = { L'\0', }; // 인코딩을 고려할 때 1개의 Multibyte Character에 대한 충분한 Wide String 공간을 제공한다. Surrogate로 변환될 것을 가정하여 1개 문자당 최대 4개의 wchar_t를 확보해 놓으면 넉넉하다. 
size_t wcn = 0; // 실제로 변환되어 생성된 문자 수를 받아온다.

setlocale(LC_ALL, ""); // 변환에 앞서 프로그램의 로케일을 시스템에서 기본으로 설정한 로케일로 리셋한다.

/* Multibyte String를 Wide String으로 변환한다. */
/* 이 로케일에서 정의하는 문자가 Wide String으로 표현될 수 없을 경우 -1을 반환한다. */	
assert((wcn = mbstowcs(wcs, mbs, sizeof(wcs) / sizeof(wchar_t))) != ((size_t)(-1)));

/* ... */

 

4-5. (덧) Multibyte String과 Wide String의 문자 개수 세는 방법


strlen 함수는 문자열을 구성하는 모든 문자가 표준 ASCII 코드로 표현된 ANSI 문자일 경우에 유효하다. 그러나 KS 완성형 코드나 JIS 코드 등 특정 인코딩을 사용하는 시스템일 경우 strlen 함수가 반환하는 문자의 수가 실제와 다르다. Multibyte String일 때 사용하는 함수는 mblen이다.

int mblen (const char* pmb, size_t max);
pmb
문자의 개수를 구하고자 하는 Multibyte String이다.
max
pmb 버퍼의 최대 크기이다.

 

Wide String의 문자수를 세는 함수는 wcslen이다. 사용법은 strlen과 같다.

 

5. Wide Character - Unicode간 상호 변환


Wide Character/String과 Unicode간 변환을 지원하는 표준 함수는 이 포스트가 작성된 현재 없다. 이 경우 다음에 설명되는 Windows API를 사용한다.

 

6. Multibyte Character - Unicode간 상호 변환


Multibyte Character와 UTF-16/UTF-32간 변환은 표준 함수로 제공된다. 그 외 UTF-7/UTF-8은 이 포스트가 작성된 현재 표준에 없다. 이 경우 다음에 설명되는 Windows API를 사용한다.

 

6-1. Multibyte Character에서 UTF-16으로 변환


Multibyte Character에서 UTF-16 Character로 변환하기 위해서는 mbrtoc16 함수를 사용합니다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>

using namespace std;

/* ... */

char mbc[8] = "가";
char16_t uc[8] = { '\0', };
mbstate_t mbstate = { 0 };

setlocale(LC_ALL, "");
assert((mbn = mbrtoc16(uc, mbc, sizeof(mbc), &mbstate)) != (size_t)(-1));

/* ... */

 

6-2. UTF-16에서 Multibyte Character으로 변환


UTF-16 Character에서 Multibyte Character로 변환하기 위해서는 c16rtomb 함수를 사용한다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>

using namespace std;

/* ... */

char16_t uc = u'가';
char mbc[8] = { '\0', };
size_t mbn = 0;
mbstate_t mbstate = { 0 };

setlocale(LC_ALL, "");

assert((mbn = c16rtomb(mbc, uc, &mbstate)) != (size_t)(-1));

/* ... */

 

6-3. Multibyte Character에서 UTF-32으로 변환


Multibyte Character에서 UTF-32 Character로 변환하기 위해서는 mbrtoc32 함수를 사용한다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>

using namespace std;

/* ... */

char mbc[8] = "가";
char32_t uc[8] = { U'\0', };
mbstate_t mbstate = { 0 };

setlocale(LC_ALL, "");
assert((mbn = mbrtoc32(uc, mbc, sizeof(mbc), &mbstate)) != (size_t)(-1));

/* ... */

 

6-4. UTF-32에서 Multibyte Character으로 변환

UTF-32 Character에서 Multibyte Character로 변환하기 위해서는 c32rtomb 함수를 사용한다.

/* C++ Source */
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <clocale>
#include <cuchar>

using namespace std;

/* ... */

char32_t uc = U'가';
char mbc[8] = { '\0', };
size_t mbn = 0;
mbstate_t mbstate = { 0 };

setlocale(LC_ALL, "");

assert((mbn = c32rtomb(mbc, uc, &mbstate)) != (size_t)(-1));

/* ... */

 

7. 마무리

여기까지 해서 Win32 C/C++ 문자열 중 C/C++ 표준 문자열 타입에 대해 알아보았다. 다음 포스트에서는 Windows API에서 정의된 기본 문자열 타입에 대해 알아보겠다.

 

 

카테고리 “Microsoft Windows/Win32 문자열 처리”
more...
썸네일 이미지
[단막 Windows NT 서버] 딥웹 .onion 익명 사이트 개설(호스팅)하기 #1
단막 Windows NT 서버 딥웹 .onion 익명 사이트 개설(호스팅)하기 (Tor를 사용한 .onion 주소 할당 방법) 일반적인 검색엔진(구글, 네이버, 다음 등)으로 검색되지 않는 웹 사이트들이 존재하는 통신망을 딥웹(Deep Web), 심층 웹, 깊은 웹 등이라 부른다. Tor 네트워크는 딥 웹의 한 종류이며 전용 브라우저인 Tor Browser를 사용하여 접속할 경우 여러 프록시 서버들을 거치며 그 내용 또한 암호화되어 전송되기 때문에 익명으로 인터넷을 이용할 수 있고, warning.or.kr처럼 특정 국가에서 차단해 놓은 사이트들을 접근할 수도 있다. Tor 네트워크에 존재하는 웹 사이트들은 통상적인 도메인이 아닌 .onion으로 끝나는 해쉬hash 형태의 주소를 가지는데, 이번 포스팅에..
Operating System/Windows NT
2019. 2. 26. 21:48

[단막 Windows NT 서버] 딥웹 .onion 익명 사이트 개설(호스팅)하기 #1

Operating System/Windows NT
2019. 2. 26. 21:48

단막 Windows NT 서버


 

딥웹 .onion 익명 사이트 개설(호스팅)하기 (Tor를 사용한 .onion 주소 할당 방법)


일반적인 검색엔진(구글, 네이버, 다음 등)으로 검색되지 않는 웹 사이트들이 존재하는 통신망을 딥웹(Deep Web), 심층 웹, 깊은 웹 등이라 부른다.

Tor 네트워크는 딥 웹의 한 종류이며 전용 브라우저인 Tor Browser를 사용하여 접속할 경우 여러 프록시 서버들을 거치며 그 내용 또한 암호화되어 전송되기 때문에 익명으로 인터넷을 이용할 수 있고, warning.or.kr처럼 특정 국가에서 차단해 놓은 사이트들을 접근할 수도 있다. Tor 네트워크에 존재하는 웹 사이트들은 통상적인 도메인이 아닌 .onion으로 끝나는 해쉬hash 형태의 주소를 가지는데, 이번 포스팅에서는 Windows 상에서 간단하게 웹 서버를 구축하고 .onion 주소를 할당받는 방법을 소개하겠다.

 

1 단계. 웹 서버 구성하기


포트가 충돌할 우려가 있으니 기존 사이트를 운영하던 서버에 Deep Web 사이트를 설정하는 것보다는 Deep Web 사이트만을 위한 서버를 따로 마련하는 것이 권장된다. 그리고 이 항목은 Apache나 IIS 등의 서버를 설치하고 구성한 경우라면 건너뛰어도 된다. 여기에서는 XAMPP(Apache + MySQL + PHP + Perl)을 이용해 서버를 구성해보겠다.

XAMPP 공식 홈 페이지(https://www.apachefriends.org/index.html)에 접속하여 최신 버전의 XAMPP를 다운로드 받는다. Windows XP 사용자는 최신버전이 지원되지 않으므로 1.8.x 이하 버전을 설치하여야 한다.

UAC 때문에 원활히 작동되지 않을 수 있으므로 Program Files 폴더는 피해서 설치할 것을 권장한다.
[Next >] 버튼을 누른다.
구성 요소를 선택한다. 특별히 고칠 것이 없으므로 [Next >] 버튼을 누른다.
설치 경로는 그대로 두고 [Next >] 버튼을 누른다.
[Next >] 버튼을 누른다.
설치가 진행되고 있다. 시간이 다소 걸리므로 완료될 때까지 기다린다.
제어판을 실행해야 하므로 선택 후 [Next >] 버튼을 누른다.
언어 선택하고...

 

Onion 사이트의 페이지는 80번 포트를 통해 Tor 네트워크로 나갈 것이다. 이는 Apache가 내보내는 포트의 기본값과 같으므로 충돌을 방지하기 위해 Apache가 사이트를 내보내는 포트를 다른 번호로 바꿔줘야 하다. 다음과 같이 XAMPP 제어판이 실행되었다면, Apache에서 [Config] 버튼 -> [Apache (httpd.conf)]를 클릭한다.

XAMPP 제어판
[Apache (httpd.conf)]를 클릭
80번 포트에서 8080 포트로 수정
방화벽 예외에 추가

 

다음과 같이 XAMPP 제어판에서 8080번 포트로 웹 사이트가 게시되고 있음을 확인할 수 있다.

8080번 포트로 게시되고 있음

 

xampp\htdocs 폴더에 웹 사이트로 게시되는 파일들을 저장한다. 기본 제공되는 테스트 파일들은 모두 제거한 후 다음과 같은 내용의 html 파일을 작성하여 index.html로 저장한다.

<!doctype html>
<html lang="en">
    <head>
        <title>Hello, Onion!</title>
        <meta charset="utf-8" />
    </head>
    <body>
        <p>Hello, Onion!</p>
        <p>^(코딩캣)^ = @"코딩"하는 <strong>고양이</strong></p>
    </body>
</html>

 

기본 제공된 파일을 제거한다
웹 브라우저로 localhost 접속됨을 확인

 

2 단계. Tor로 Onion 주소 얻어 Deep Web 사이트 개설하기


구 버전의 Tor에서는 Tor Browser라는 Tor 네트워크 브라우저와 Vidalia라는 Tor 네트워크 제어판이 분리되었는데, 현재 나오는 버전의 Tor는 Tor Browser로 일원화되어있다. 따라서 Tor Browser만 종료하지 않고 실행해 두면 항상 Tor 네트워크 접속 상태가 유지될 것이다. 서버의 유동 IP/고정 IP 여부도 염려할 필요가 없다. 외부에서 접속할 때는 IP가 아닌 onion 주소를 사용하며, IP 주소와 onion 주소의 대응은 Tor 네트워크에서 알아서 해 줄 것이다.

먼저 Tor Project 홈 페이지(https://www.torproject.org/)에 접속하여 Tor Browser를 다운로드 받는다.

설치 경로를 지정한다.
설치 완료.

 

Tor Browser가 설치된 경로(\Tor Browser\Browser\TorBrowser\Data\Tor)에 들어가면 torrc라는 0 바이트짜리 파일이 보일 것이다. Tor Browser를 처음으로 실행한 후 이 파일을 다시 찾을 것이다.

torrc가 0바이트이다.

 

설치된 경로(\Tor Browser\Start Tor Browser.lnk)를 실행한다.

[Connect] 버튼을 눌러 기본값으로 Tor 네트워크에 접속한다.
Tor 네트워크에 접속되었다면 다시 닫는다.

 

3 단계. 포트 설정하기


앞서 확인했던 경로(\Tor Browser\Browser\TorBrowser\Data\Tor)를 다시 열면 0 바이트였던 torrc 파일의 크기가 늘어난 것을 확인할 수 있다. 텍스트 편집기로 이를 열어서 내용을 확인한다.

크기가 증가한 torrc 파일을 연다.

 

아래의 두 줄을 새로 추가한다. 첫 번째 줄은 xampp로 게시되는 로컬 경로를 Tor 네트워크에게 알려주는 것이고, 두 번째 줄은 서버의 아이피와 xampp로 게시되는 일반 웹의 포트번호(여기서는 8080)를 딥 웹으로는 80번 포트로 재게시 하는 것이다.

HiddenServiceDir 사이트의로컬경로
HiddenServicePort 80 서버의아이피:서버의포트번호

 

torrc 파일을 수정한다.

 

Tor Browser를 다시 실행한다. 이 때부터는 htdocs의 로컬 경로가 하나의 Deep Web 사이트로서 Tor 네트워크에 내보내지고 있으므로 닫으면 외부에서 접속이 안 된다.

 

4 단계. onion 주소 확인하기


torrc 파일에서 HiddenServiceDir 항목으로 지정한 로컬 경로로 들어가보면 몇 개의 파일이 생성된 것을 볼 수 있다. 이 중 hostname 파일을 열어보면 이 사이트의 onion 주소를 확인할 수 있다.

서버 경로에 생성된 몇 개의 파일
hostname에 기록된 현 서버의 onion 주소

 

Tor Browser를 실행하고 약 1분이 지난 후 이 주소를 접속하면 일반 웹 브라우저에서 http://localhost:8080로 보았던 것과 동일한 웹 페이지를 볼 수 있다. 즉, 외부 사용자도 이 onion 주소를 통해 같은 페이지를 보고 있다는 뜻이다. 이렇게 해서 하나의 Deep Web 사이트 구성이 완료되었다.

 

Tor Browser를 통해 접속한 웹 사이트의 모습

 

마무리


이번 포스팅에서는 Tor Browser와 xampp를 활용하여 딥 웹 사이트를 운영하는 서버를 구축하여 보았다. 다음 포스팅에서는 커스텀 onion 주소를 할당받는 방법에 대해 설명하겠다.

카테고리 “Operating System/Windows NT”
more...
[단막 DirectX 9.0 활용] DirectX 9.0를 WM_PAINT 이벤트에 적용하기
단막 DirectX 9.0 활용 DirectX 9.0를 WM_PAINT 이벤트에 적용하기 WM_PAINT(MFC: OnPaint) 이벤트는 창의 내용을 그릴 때 호출되는 이벤트이다. 이 이벤트에 DirectX 9.0을 적용하여 보겠다. IDirect3DDevice9::TestCooperativeLevel 로스트 상태는, DirectX 객체와 실제 그래픽 어댑터 사이의 연결이 끊어진 상태를 뜻한다. DirectX 객체가 생성된 시점과 이 객체를 이용하여 실제로 그리는 시각 시점의 상대적으로 긴 시간이 있는 경우 이 사이에 여러가지 이유(예: 로그오프, 절전모드 등)로 객체와 장치간 연결이 끊어질 수 있는데 이는 IDirect3DDevice9::TestCooperativeLevel함수로 체크할 수 있다. 만..
API/DirectX
2019. 2. 26. 19:30

[단막 DirectX 9.0 활용] DirectX 9.0를 WM_PAINT 이벤트에 적용하기

API/DirectX
2019. 2. 26. 19:30

단막 DirectX 9.0 활용


 

DirectX 9.0를 WM_PAINT 이벤트에 적용하기


WM_PAINT(MFC: OnPaint) 이벤트는 창의 내용을 그릴 때 호출되는 이벤트이다. 이 이벤트에 DirectX 9.0을 적용하여 보겠다.

 

IDirect3DDevice9::TestCooperativeLevel


로스트 상태는, DirectX 객체와 실제 그래픽 어댑터 사이의 연결이 끊어진 상태를 뜻한다. DirectX 객체가 생성된 시점과 이 객체를 이용하여 실제로 그리는 시각 시점의 상대적으로 긴 시간이 있는 경우 이 사이에 여러가지 이유(예: 로그오프, 절전모드 등)로 객체와 장치간 연결이 끊어질 수 있는데 이는 IDirect3DDevice9::TestCooperativeLevel함수로 체크할 수 있다.

만일 DirectX와 장치간 연결이 정상이 아닐 경우 그 원인에 따라 D3DERR_DRIVERINTERNALERROR, D3DERR_DEVICELOST, D3DERR_DEVICENOTRESET 중 하나를 반환하는데, 이 때는 IDirect3DDevice9::CreateDevice에 사용되었던 D3DPRESENT_PARAMETERS 구조체를 다시 사용하여 IDirect3DDevice9::Reset 함수를 실행하여 장치와 객체의 연결을 리셋한다.

/* C++ Source */
IDirect3DDevice * pDirect3DDevice;
D3DPRESENT_PARAMETERS d3dPresentParameters;
HRESULT hResult;
// ...
hResult = pDirect3DDevice->TestCooperativeLevel();
switch (hResult)
{
case D3DERR_DRIVERINTERNALERROR:
    pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
    // Reset 실패 시 각종 조치
    break;
case D3DERR_DEVICELOST:
    // Reset 실패 시 각종 조치
    pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
    break;
case D3DERR_DEVICENOTRESET:
    // Reset 실패 시 각종 조치
    pDirect3DDevice->Reset(&d3dPresentParameters); // D3DPRESENT_PARAMETERS *
    break;
}

 

IDirect3DDevice9::Clear

장치의 로스트 상태 확인 및 조치를 완료하였다면 지정된 위치의 버퍼를 특정 색상으로 지운다.

/* C++ Source */
IDirect3DDevice * pDirect3DDevice;
D3DPRESENT_PARAMETERS d3dPresentParameters;
DWORD dwRects;
D3DRECT * pRects;

HRESULT hResult;
// ...
hResult = pDirect3DDevice->Clear
(
    0,                                // DWORD Count : pRects 배열의 원소 수
    NULL,                            // const D3DRECT * pRects
    D3DCLEAR_TARGET,                 //
    D3DCOLOR_XRGB(0x00, 0xFF, 0xFF), // D3DCOLOR Color : 리셋 후 버퍼의 색상
    0.0f,                            // float Z
    0                                // DWORD Stencil
);

if (FAILED(hResult))
{
    // Clear 실패 시 각종 조치
}

 

IDirect3DDevice9::BeginScene, IDirect3DDevice9::EndScene, IDirect3DDevice9::Present


그리기 단위인 '장면scene'을 시작하고, 끝내는 함수이고 IDirect3DDevice9::Present를 통해 버퍼 스왑을 한다.

/* C++ Source */
IDirect3DDevice * pDirect3DDevice;
D3DPRESENT_PARAMETERS d3dPresentParameters;
HRESULT hResult;
// ...
pDirect3DDevice->BeginScene();
if (FAILED(hResult))
{
    // BeginScene 실패 시 각종 조치
}

// 각종 그리기 연산

pDirect3DDevice->EndScene();
if (FAILED(hResult))
{
    // EndScene 실패 시 각종 조치
}

pDirect3DDevice->Present
(
    NULL, // const RECT * pSourceRect
    NULL, // const RECT * pDestRect
    NULL, // HWND hDestWindowOverride
    NULL  // const RGNDATA *pDirtyRegion
);

if (FAILED(hResult))
{
    // Present 실패 시 각종 조치
}

 

카테고리 “API/DirectX”
more...
썸네일 이미지
[단막 DirectX 9.0 활용] DirectX 9.0 객체의 생성과 해제
단막 DirectX 9.0 활용 DirectX 9.0 객체의 생성과 해제 DirectX 9.0을 시작하기 위해 객체의 생성을 하고 창을 닫을 때의 해제는 다음과 같이 한다. DirectX 객체는 COMComponent Object Model으로 제공되므로 C++ 언어를 사용한다. Direct3DCreate9, IDirect3D9::Release DirectX 9.0의 객체 생성은 Direct3DCreate9 함수를 실행한다. 성공하면 LPDIRECT3D9(struct IDirect3D9 *) 형식의 객체를 반환하고, 실패하면 NULL을 반환한다. /* C++ Source */ #pragma comment(lib, "d3d9.lib") // DirectX 9.0 라이브러리를 링크 #include // Wi..
API/DirectX
2019. 2. 12. 20:42

[단막 DirectX 9.0 활용] DirectX 9.0 객체의 생성과 해제

API/DirectX
2019. 2. 12. 20:42

단막 DirectX 9.0 활용


 

DirectX 9.0 객체의 생성과 해제


DirectX 9.0을 시작하기 위해 객체의 생성을 하고 창을 닫을 때의 해제는 다음과 같이 한다. DirectX 객체는 COMComponent Object Model으로 제공되므로 C++ 언어를 사용한다.

 

Direct3DCreate9, IDirect3D9::Release


DirectX 9.0의 객체 생성은 Direct3DCreate9 함수를 실행한다. 성공하면 LPDIRECT3D9(struct IDirect3D9 *) 형식의 객체를 반환하고, 실패하면 NULL을 반환한다.

/* C++ Source */
#pragma comment(lib, "d3d9.lib") // DirectX 9.0 라이브러리를 링크
#include <Windows.h> // Windows API에서 제공되는 기본 자료형들을 불러오기
#include <d3d9.h> // DirectX 9.0 API를 불러오기

LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체

/* 창을 생성하거나 그래픽을 리셋하는 단계에서 */

lpDirect3D = Direct3DCreate9(D3D_SDK_VERSION); // DirectX 9.0 버전으로 객체를 생성한다.
if (!lpDirect3D) 
{
    // 생성에 실패한 경우 실행 작업
}

/* 생성에 성공한 경우 실행 작업 */

 

DirectX 9.0 객체의 해제는 IDirect3D9::Release를 호출한다.

/* C++ Source */
#pragma comment(lib, "d3d9.lib") // DirectX 9.0 라이브러리를 링크
#include <Windows.h> // Windows API에서 제공되는 기본 자료형들을 불러오기
#include <d3d9.h> // DirectX 9.0 API를 불러오기

LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체

/* 창을 닫거나 그래픽을 종료하는 단계에서 */

lpDirect3D->Release(); // 객체 해제

/* 이후 작업 */

 

실제 코드에 적용


다음은 MFC에서 DirectX 객체를 생성하고 해제하는 예이다. CTestFrameCWndFrame에서 상속된 클래스이고 m_pDirect3D는 사전에 클래스 헤더에 선언된 멤버 변수이다.

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] CTestFrame::OnCreate.\n"));
        return ret;
    }
    
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    return ret;
}

void CTestFrame::OnDestroy()
{
    TRACE(TEXT("[SUCCESS] CTestFrame::OnDestroy called.\n"));
    
    if (this->m_pDirect3D != NULL)
    {
        this->m_pDirect3D->Release();
    }
    
    CFrameWnd::OnDestroy();
}

 

MFC 프로그램의 실행 결과 (아직 빈 창)
OnCreate와 OnDestroy이 실행된 흔적

 

어댑터 종류 열거하기


DirectX를 사용하기 위해 컴퓨터에서 지원 가능한 그래픽 어댑터의 종류를 모두 가져오려면 CreateDXGIFactory 함수로 생성된 IDXGIFactory형 객체를 사용한다.

 

CreateDXGIFactory


/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")

#include <d3d9.h>
#include <dxgi.h>

IDXGIFactory * pDXGIFactory = NULL;
IDXGIAdapter * pDXGIAdapter = NULL;
SIZE_T nDXGIAdapter = 0;

/* 창을 생성하거나 그래픽을 리셋하는 단계에서 */
hResult = CreateDXGIFactory
(
    __uuidof(IDXGIFactory),                  // REFIID riid
    reinterpret_cast<void **>(&pDXGIFactory) // void ** ppFactory
);

// 해당 인덱스 번호에 해당하는 어댑터가 없다면 DXGI_ERROR_NOT_FOUND를 반환하기 시작한다.
// 0번 어댑터부터 검색을 시작한다.
nDXGIAdapter = 0;
while (pDXGIFactory->EnumAdapter(nDXGIAdapter, &pDXGIAdapter) != DXGI_ERROR_NOT_FOUND)
{
nDXGIAdapter++; // 다음 번호의 어댑터를 확인한다.

}

 

실제 코드에 적용


그래픽 어댑터의 종류를 열거하는 위 코드를 응용하여 디버그 문자열로 그래픽 어댑터의 이름을 출력해보겠다.

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
        return ret;
    }
    
    // DirectX 객체를 생성
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    // 어댑터를 열거할 수 있는 IDXGIFactory 객체 생성
    IDXGIFactory * pDXGIFactory = NULL;
    hResult = CreateDXGIFactory
    (
        __uuidof(IDXGIFactory),                  // REFIID riid
        reinterpret_cast<void **>(&pDXGIFactory) // void ** ppFactory
    );
    if (FAILED(hResult))
    {
        TRACE(TEXT("[FAILURE] CreateDXGIFactory.\n"));
        return ret;
    }
    
    // 열거된 어댑터를 하나씩 배열에 보관
    CArray<IDXGIAdapter *> dxgiAdapters;
    IDXGIAdapter * pDXGIAdapter = NULL;
    UINT i = 0;
    while (TRUE)
    {
        hResult = pDXGIFactory->EnumAdapters
        (
            i++,          // UINT Adapter
            &pDXGIAdapter // IDXGIAdapter ** ppAdapter
        );
        
        if (hResult == DXGI_ERROR_NOT_FOUND) break;
        
        dxgiAdapters.Add(pDXGIAdapter);
        pDXGIAdapter = NULL;
    }
    
    // 배열에 보관된 어댑터의 이름을 디버그 문자열로 출력
    for (INT_PTR i = 0; i < dxgiAdapters.GetCount(); i++)
    {
        DXGI_ADAPTER_DESC dxgiAdapterDesc;
        TCHAR szBuffer[256];
        
        dxgiAdapters[i]->GetDesc(&dxgiAdapterDesc);
        TRACE(TEXT("[%2d] DXGI_ADAPTER_DESC.Description = %s\n"), i, dxgiAdapterDesc.Description);
    }
    
    // 사용이 끝난 어댑터 객체와 Factory 객체 해제
    for (INT_PTR i = 0; i < dxgiAdapters.GetCount(); i++)
    {
        dxgiAdapters[i]->Release();
    }
    dxgiAdapters.RemoveAll();
    pDXGIFactory->Release();
    
    return ret;
}

 

본 컴퓨터에 장착된 그래픽 어댑터의 종류가 열거되는 것을 확인할 수 있다. 다만 [장치 관리자]에는 없는 Microsoft Basic Render Driver가 마지막에 있을 수도 있다.

컴퓨터에 장착된 그래픽 어댑터의 종류가 열거되는 결과

 

그래픽 어댑터의 색상 포맷의 지원 여부 확인


ARGB, RGB, HSV 등등의 색상 포맷을 사용하고자 할 때 그래픽 어댑터가 이를 지원하는지 여부를 확인한다. 여기서 XRGB는 RGB와 같은 것을 의미한다.

 

IDirect3D9::EnumAdapterModes


 

/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")

#include <d3d9.h>
#include <dxgi.h>

LPDIRECT3D9 lpDirect3D = (LPDIRECT3D9)NULL; // DirectX 9.0 객체
UINT uAdapterMode = 0;

/* 창 또는 그래픽을 리셋하는 단계에서 */

uAdapterMode = lpDirect3D->GetAdapterModeCount
(
    D3DADAPTER_DEFAULT, // UINT Adapter : 시스템 기본 어댑터를 사용하겠다
    D3DFMT_X8R8G8B8     // D3DFORMAT Format : 픽셀당 32비트 색상 포맷을 쓴다. 단, 실제 사용되는 비트는 R, G, B 24비트이다. 
);

 

실제 코드에 적용


그래픽 어댑터에서 해당 색상 포맷으로 몇 개의 모드를 지원하는지를 확인해 보겠다.

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
        return ret;
    }
    
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    IDXGIFactory * pDXGIFactory = NULL;
    hResult = CreateDXGIFactory
    (
        __uuidof(IDXGIFactory),                 // REFIID riid
        reinterpret_cast<void **>(&pDXGIFactory) // void ** ppFactory
    );
    if (FAILED(hResult))
    {
        TRACE(TEXT("[FAILURE] CreateDXGIFactory.\n"));
        return ret;
    }
    
    UINT uAdapterMode = this->m_pDirect3D->GetAdapterModeCount
    (
        D3DADAPTER_DEFAULT, // UINT Adapter
        D3DFMT_X8R8G8B8     // D3DFORMAT Format
    );
    TRACE(TEXT("[SUCCESS] GetAdapterModeCount = %u\n"), uAdapterMode);
    
    return ret;
}

 

본 컴퓨터의 경우 RGB(X8R8G8B8) 색상 포맷으로 43가지의 어댑터 모드를 지원한다.

 

IDirect3D9::EnumAdapterModes


IDirect3D9::EnumAdapterModes 함수는 그래픽 어댑터가 지원 가능한 모드를 상세히 열거하는 함수이다. 지원 모드는 D3DDISPLAYMODE 구조체를 통해 반환된다.

/* C++ Source */
UINT uAdapterMode = this->m_pDirect3D->GetAdapterModeCount
(
    D3DADAPTER_DEFAULT, // UINT Adapter
    D3DFMT_X8R8G8B8     // D3DFORMAT Format
);

for (UINT i = 0; i < uAdapterMode; i++)
{
    D3DDISPLAYMODE d3dDisplayMode;
    hResult = this->m_pDirect3D->EnumAdapterModes
    (
        D3DADAPTER_DEFAULT, // UINT Adapter
        D3DFMT_X8R8G8B8,    // D3DFORMAT Format
        i,                  // UINT Mode : 어댑터에 대해 얻고자 하는 모드의 번호
        &d3dDisplayMode     // D3DDISPLAYMODE * pMode : 반환 정보
    );
}

 

실제 코드에 적용


기본 어댑터(D3DADAPTER_DEFAULT)의 RGB 컬러(D3DFMT_X8R8G8B8)로 몇 가지의 어댑터 모드가 지원 가능한지 조회해 보겠다. 실행 결과는 컴퓨터마다 상이하다.

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
        return ret;
    }
    
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    IDXGIFactory * pDXGIFactory = NULL;
    hResult = CreateDXGIFactory
    (
        __uuidof(IDXGIFactory),                  // REFIID riid
        reinterpret_cast(&pDXGIFactory) // void ** ppFactory
    );
    if (FAILED(hResult))
    {
        TRACE(TEXT("[FAILURE] CreateDXGIFactory.\n"));
        return ret;
    }
    
    UINT uAdapterMode = this->m_pDirect3D->GetAdapterModeCount
    (
        D3DADAPTER_DEFAULT, // UINT Adapter
        D3DFMT_X8R8G8B8     // D3DFORMAT Format
    );
    TRACE(TEXT("[SUCCESS] GetAdapterModeCount = %u\n"), uAdapterMode);
    
    for (UINT i = 0; i < uAdapterMode; i++)
    {
        D3DDISPLAYMODE d3dDisplayMode;
        hResult = this->m_pDirect3D->EnumAdapterModes
        (
            D3DADAPTER_DEFAULT, // UINT Adapter
            D3DFMT_X8R8G8B8,    // D3DFORMAT Format
            i,                  // UINT Mode : 어댑터에 대해 얻고자 하는 모드의 번호
            &d3dDisplayMode     // D3DDISPLAYMODE * pMode : 반환 정보
        );
        if (SUCCEEDED(hResult))
        {
            TRACE(TEXT("[%2u] EnumAdapterModes\n"), i);
            TRACE(TEXT("     D3DDISPLAYMODE.Format = %x\n"), d3dDisplayMode.Format);
            TRACE(TEXT("     D3DDISPLAYMODE.RefreshRate = %u\n"), d3dDisplayMode.RefreshRate);
            TRACE(TEXT("     D3DDISPLAYMODE.Width = %u\n"), d3dDisplayMode.Width);
            TRACE(TEXT("     D3DDISPLAYMODE.Height = %u\n"), d3dDisplayMode.Height);
        }
    }
    
    return ret;
}

 

RGB 모드로 지원 가능한 43개의 어댑터 모드에 대해 프레임 속도와 해상도가 열거되고 있다.

 

어댑터 장치의 성능 확인하기


장치의 성능 중에서 특히 자주하게 확인할 것은 하드웨어로 버텍스 처리가 가능한지 여부이다. 하드웨어에서 버텍스 프로세싱이 가능하면 그대로 이용하고, 그렇지 않을 경우 소프트웨어(CPU)로 버텍스를 연산해야 한다. 이 경우 체감 속도가 다소 느릴 수 있다.

 

IDirect3D9::GetDeviceCaps


IDirect3D9::GetDeviceCaps 함수가 반환하는 구조체인 D3DCAPS9DevCaps 멤버를 통해 확인이 가능하며 D3DDEVCAPS_HWTRANSFORMANDLIGHT 비트의 설정 여부로 하드웨어의 버텍스 처리 성능을 판단할 수 있다.

/* C++ Source */
D3DCAPS9 d3dCaps;
ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
hResult = this->m_pDirect3D->GetDeviceCaps
(
    D3DADAPTER_DEFAULT, // UINT Adapter
    D3DDEVTYPE_HAL,     // D3DDEVTYPE DeviceType
    &d3dCaps            // D3DCAPS9 * pCaps
);
if (FAILED(hResult))
{
    // 장치 정보 얻기에 실패시 조치
}
if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
{
    // 하드웨어로 버텍스 처리가 가능하다.
}
else
{
    // 소프트웨어로 버텍스 처리가 가능하다. (속도 느림)
}

 

실제 코드에 적용


실제 코드에 적용하여 보겠다.

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
        return ret;
    }
    
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    D3DCAPS9 d3dCaps;
    ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
    hResult = this->m_pDirect3D->GetDeviceCaps
    (
        D3DADAPTER_DEFAULT, // UINT Adapter
        D3DDEVTYPE_HAL,     // D3DDEVTYPE DeviceType
        &d3dCaps            // D3DCAPS9 * pCaps
    );
    if (FAILED(hResult))
    {
        TRACE(TEXT("[FAILURE] IDirect3D9::GetDeviceCaps = %u"), hResult);
        return ret;
    }
    if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
    {
        TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps has D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
    }
    else
    {
        TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps not D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
    }
    
    return ret;
}
실행 결과 하드웨어 버텍스 처리가 가능하면 위와 같이 출력될 것이다.

 

IDirect3DDevice9 객체 생성


그래픽 어댑터를 호출할 수 있는 장치 인터페이스인 IDirect3DDevice9 객체를 생성한다. 앞서 확인한 각종 하드웨어 사항과 본인이 선정한 색상 포맷, 기타 필요한 사항들을 조합하여 하나의 컨텍스트를 만든 다음 이를 IDirect3D9::CreateDevice함수에 D3DPRESENT_PARAMETERS 구조체로 전달한다.

IDirect3D9::CreateDevice


IDirect3D9::CreateDevice의 사용 방법은 다음과 같다.

/* C++ Source */
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "dxgi.lib")

#include <d3d9.h>
#include <dxgi.h>

LPDIRECT3D9 lpDirect3D9 = NULL;
LPDIRECT3DDEVICE9 lpDirect3DDevice9 = NULL;
HRESULT hResult;

/* 그래픽을 리셋하는 부분에서 */

hResult = lpDirect3D9->CreateDevice
(
    D3DADAPTER_DEFAULT, // UINT Adapter
    D3DDEVTYPE_HAL,     // D3DDEVTYPE DeviceType
    &d3dCaps            // D3DCAPS9 * pCaps
);

 

실제 코드에 적용


본 함수를 시험하기 전에 새 멤버변수를 클래스의 헤더에 선언하였다.

static const DWORD CTestFrame::GRAPHIC_WIDTH = 800;
윈도우의 폭이면서 그래픽 버퍼의 폭이다. 앞서 그래픽 어댑터에서 지원 가능한 것으로 확인된 800 픽셀을 상수로 지정한 것이다.
static const DWORD CTestFrame::GRAPHIC_HEIGHT = 600;
윈도우의 높이면서 그래픽 버퍼의 높이이다. 앞서 그래픽 어댑터에서 지원 가능한 것으로 확인된 600 픽셀을 상수로 지정한 것이다.
IDirect3DDevice * m_pDirect3DDevice = NULL
새로 생성될 장치 객체이다.

 

/* MFC Source */
int CTestFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    HRESULT hResult = NULL;
    int ret = 0;
    
    TRACE(TEXT("[SUCCESS] CTestFrame::OnCreate called.\n"));
    
    ret = CFrameWnd::OnCreate(lpCreateStruct);
    if (ret != 0)
    {
        TRACE(TEXT("[FAILURE] OnCreate::OnCreate.\n"));
        return ret;
    }
    
    this->m_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (this->m_pDirect3D == NULL)
    {
        TRACE(TEXT("[FAILURE] Direct3DCreate9.\n"));
        return ret;
    }
    
    // 윈도우 크기를 조정한다.
    RECT rect;
    this->GetWindowRect(&rect);
    this->SetWindowPos(NULL, rect.left, rect.top, CTestFrame::GRAPHIC_WIDTH, CTestFrame::GRAPHIC_HEIGHT, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
    
    D3DCAPS9 d3dCaps;
    D3DPRESENT_PARAMETERS d3dPresentParameters;
    ZeroMemory(&d3dCaps, sizeof(D3DCAPS9));
    ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
    hResult = this->m_pDirect3D->GetDeviceCaps
    (
        D3DADAPTER_DEFAULT, // UINT Adapter
        D3DDEVTYPE_HAL,     // D3DDEVTYPE DeviceType
        &d3dCaps            // D3DCAPS9 * pCaps
    );
    if (FAILED(hResult))
    {
        TRACE(TEXT("[FAILURE] IDirect3D9::GetDeviceCaps = %u"), hResult);
        return ret;
    }
    if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
    {
        TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps has D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
        
        // 그래픽 어댑터가 하드웨어 버텍스 처리를 지원하는 경우
        ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
        d3dPresentParameters.BackBufferWidth = CTestFrame::GRAPHIC_WIDTH;
        d3dPresentParameters.BackBufferHeight = CTestFrame::GRAPHIC_HEIGHT;
        d3dPresentParameters.BackBufferFormat = D3DFMT_X8R8G8B8;
        d3dPresentParameters.BackBufferCount = 1;
        d3dPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dPresentParameters.hDeviceWindow = this->m_hWnd; // 윈도우 핸들
        d3dPresentParameters.Windowed = TRUE;  // 어플리케이션을 창 모드로 연다.
        d3dPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
        
        hResult = m_pDirect3D->CreateDevice
        (
            D3DADAPTER_DEFAULT,
            D3DDEVTYPE_HAL,
            this->m_hWnd,
            D3DCREATE_HARDWARE_VERTEXPROCESSING, // 하드웨어 버텍스 처리
            &d3dPresentParameters,
            &this->m_pDirect3DDevice
        );
        if (FAILED(hResult))
        {
            TRACE(TEXT("[FAILURE] IDirect3D9::CreateDevice = %x\n"), hResult);
            return ret;
        }
        TRACE(TEXT("[SUCCESS] IDirect3D9::CreateDevice\n"));
    }
    else
    {
        TRACE(TEXT("[DEVCAPS] D3DCAPS9.DevCaps not D3DDEVCAPS_HWTRANSFORMANDLIGHT\n"));
        
        // 그래픽 어댑터가 하드웨어 버텍스 처리를 지원하지 않는 경우
        ZeroMemory(&d3dPresentParameters, sizeof(D3DPRESENT_PARAMETERS));
        d3dPresentParameters.BackBufferWidth = CTestFrame::GRAPHIC_WIDTH;
        d3dPresentParameters.BackBufferHeight = CTestFrame::GRAPHIC_HEIGHT;
        d3dPresentParameters.BackBufferFormat = D3DFMT_X8R8G8B8;
        d3dPresentParameters.BackBufferCount = 1;
        d3dPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dPresentParameters.hDeviceWindow = this->m_hWnd; // 윈도우 핸들
        d3dPresentParameters.Windowed = TRUE; // 어플리케이션을 창 모드로 연다.
        d3dPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
        
        // 장치 객체를 생성한다.
        hResult = m_pDirect3D->CreateDevice
        (
            D3DADAPTER_DEFAULT,
            D3DDEVTYPE_HAL,
            this->m_hWnd,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING, // 소프트웨어로 버텍스 연산한다. (느림)
            &d3dPresentParameters,
            &this->m_pDirect3DDevice
        );
        if (FAILED(hResult))
        {
            TRACE(TEXT("[FAILURE] IDirect3D9::CreateDevice = %x\n"), hResult);
            return ret;
        }
        TRACE(TEXT("[SUCCESS] IDirect3D9::CreateDevice\n"));
    }
    
    return ret;>>
}
크기가 조정된 DirectX 윈도우
장치 객체 생성에 성공한 흔적

 

카테고리 “API/DirectX”
more...
[단막 C++ 문법] C++ 배열 반복문 사용 방법
C++ 배열 반복문 사용 방법 C++에서 배열 반복문을 사용하는 방법은 다음과 같이 정리할 수 있다. 1. 전통적인 배열 반복문 전통적인 배열 반복문은 역시 인덱스 변수를 선언하고 그 변수를 증감하면서 배열의 원소에 순차적으로 접근하는 방식이다. /* C++ source */ #include // std::array #include // std::cout using namespace std; int main(int argc, char * argv[]) { std::array integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 }; for (size_t i = 0; i < integers.size(); i++) { cout
Language/C & C++
2019. 2. 7. 16:06

[단막 C++ 문법] C++ 배열 반복문 사용 방법

Language/C & C++
2019. 2. 7. 16:06

C++ 배열 반복문 사용 방법


C++에서 배열 반복문을 사용하는 방법은 다음과 같이 정리할 수 있다.

 

1. 전통적인 배열 반복문


전통적인 배열 반복문은 역시 인덱스 변수를 선언하고 그 변수를 증감하면서 배열의 원소에 순차적으로 접근하는 방식이다.

/* C++ source */
#include <array>     // std::array
#include <iostream>  // std::cout

using namespace std;

int main(int argc, char * argv[])
{
    std::array<int, 10> integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 };
    
    for (size_t i = 0; i < integers.size(); i++)
    {
        cout<<integers[i]<<'\t';
    }
    return 0;
}

 

2. 반복자 사용하기


STL Container 클래스내의 반복자(iterator)를 자동변수로 선언하여 배열의 원소에 순차적으로 접근할 수도 있다. 여기에서 반복자의 자료형은 std::array<int, 10>::iterator이지만 작성의 편의를 위해 auto 키워드로 대체하여 반복자를 선언하는 것이 보통이다.

/* C++ source */
#include <array>     // std::array
#include <iostream>  // std::cout

using namespace std;

int main(int argc, char * argv[])
{
    std::array<int, 10> integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 };
    
    for (std::array<int, 10>::iterator = integers.begin(); integer != integers.end(); integer++)
    {
        cout<<*integers<<'\t';
    }
    
    return 0;
}

 

3. std::for_each 사용하기


std::for_each는 앞서 작성한 반복자 선언을 좀 더 키워드화한 것으로 algorithm 헤더 파일에 다음과 같이 선언되어 있다.

/* C++ source */
template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function fn);

 

여기서 fn 매개변수는 람다식이 될 수도 있고 함수 포인터가 될 수도 있다. 다음은 함수 포인터를 사용한 예이다.

/* C++ source */
#include <algorithm> // std::for_each
#include <array>     // std::array
#include <iostream>  // std::cout

using namespace std;

void myloop(int);

int main(int argc, char * argv[])
{
    std::array<int, 10> integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 };
    
    std::for_each
    (
        integers.begin(), // InputIterator first
        integers.end(), // InputIterator last
        myloop // Function fn
    );
    
    return 0;
}

void myloop(int integer)
{
    cout<<integer<<'\t';
}

 

다음은 람다식을 사용한 예이다.

/* C++ source */
#include <algorithm> // std::for_each
#include <array>     // std::array
#include <iostream>  // std::cout

using namespace std;

int main(int argc, char * argv[])
{
    std::array<int, 10> integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 };
    
    std::for_each
    (
        integers.begin(), // InputIterator first
        integers.end(), // InputIterator last
        [](int integer) { cout<<integer<<'\t'; } // Function fn
    );
    
    return 0;
}

 

4. Range based for 구문 사용하기


Range based for가 바로 파이썬, 베이직 등 타 스크립트 언어에서 지원하는 foreach 구문이다. C++에서 foreach 구문은 다음과 같은 형태를 갖는다.

/* C++ source */
#include <array>     // std::array
#include <iostream>  // std::cout

using namespace std;

int main(int argc, char * argv[])
{
    std::array<int, 10> integers = { 2, 4, 6, 8, 0, 9, 7, 5, 3, 1 };
    
    for (int & integer : integers)
    {
        cout<<integer<<'\t';
    }
    
    return 0;
}

 

카테고리 “Language/C & C++”
more...
썸네일 이미지
OpenCL 기본 실행 예 (1)
OpenCL 기본 실행 예 (1) OpenCL은 실행을 위한 세팅 과정이 너무 많다. 따라서 본 포스팅을 통해 OpenCL을 실행하는 예시 코드를 정리해 둔다. 0 단계. 사용 가능한 플랫폼 개수 확인하기. OpenCL을 사용할 수 있는 플랫폼(CPU와 GPU 등 디바이스 조합)이 총 몇 개인지를 확인하기 위해 clGetPlatformIDs 함수를 사용한다. 2 번째 매개변수인 cl_platform_id * platforms에 NULL을 대입하면 플랫폼의 총 개수가 반환된다. 이 때 플랫폼의 개수는 1 번째 매개변수인 cl_uint num_entries 이하로 반환한다. 그러므로 최대의 개수를 얻고자 하면 1 번째 매개변수에 -1을 대입하면 된다. 이 매개변수의 자료형은 부호 없는 정수(cl_uint)이..
API/OpenCL
2019. 2. 2. 22:25

OpenCL 기본 실행 예 (1)

API/OpenCL
2019. 2. 2. 22:25

OpenCL 기본 실행 예 (1)


OpenCL은 실행을 위한 세팅 과정이 너무 많다. 따라서 본 포스팅을 통해 OpenCL을 실행하는 예시 코드를 정리해 둔다.

 

0 단계. 사용 가능한 플랫폼 개수 확인하기.


OpenCL을 사용할 수 있는 플랫폼(CPU와 GPU 등 디바이스 조합)이 총 몇 개인지를 확인하기 위해 clGetPlatformIDs 함수를 사용한다. 2 번째 매개변수인 cl_platform_id * platformsNULL을 대입하면 플랫폼의 총 개수가 반환된다. 이 때 플랫폼의 개수는 1 번째 매개변수인 cl_uint num_entries 이하로 반환한다. 그러므로 최대의 개수를 얻고자 하면 1 번째 매개변수에 -1을 대입하면 된다. 이 매개변수의 자료형은 부호 없는 정수(cl_uint)이기 때문에 -10xFFFF, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF 중에 하나이고 부호 없는 정수의 최댓값과 같다.

사용 가능한 플랫폼의 수는 3 번째 매개변수인 cl_uint * num_platforms으로 반환된다. 함수 자체가 반환하는 값은 함수의 실행 결과로서, 정상 작동했다면 CL_SUCCESS를 반환한다.

/* C source */
// 새로 추가된 변수
cl_int errNo;
cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

/* ... */

// 0 단계. 시스템으로부터 OpenCL이 사용 가능한 플랫폼의 수를 확인한다.
errNo = clGetPlatformIDs
(
    (cl_uint)(-1),  // cl_uint num_entries
     NULL,          // cl_platform_id * platforms
    &nPlatformId    // cl_uint * num_platforms
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clGetPlatformIDs = %d\n", errNo);
    // 실패 시 작업
}
else if (nPlatformId <= 0)
{
    printf("[FAILURE] clGetPlatformIDs: nPlatformId = %u", nPlatformId);
    // 실패 시 작업
}
else
{
    printf("[SUCCEED] clGetPlatformIDs: nPlatformId = %u\n", nPlatformId);
}

/* ... */

 

사용 가능한 플랫폼의 수(여기서는 1개)를 확인할 수 있다.

 

1 단계. 사용 가능한 플랫폼 목록 가져오기.


앞서 0 단계에서 플랫폼 수를 확인하였다면, 메모리를 동적할당하여 플랫폼 목록을 가져온다. 마찬가지로 clGetPlatformIDs를 사용한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

/* ... */

// nPlatformId개의 구조체를 동적할당한다.
assert(lpPlatformId = (cl_platform_id *)calloc(nPlatformId, sizeof(cl_platform_id)));

// 1 단계. 플랫폼 목록을 가져온다.
errNo = clGetPlatformIDs
(
    nPlatformId,  // cl_uint num_entries
    lpPlatformId, // cl_platform_id * platforms
    NULL          // cl_uint * num_platforms
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clGetPlatformIDs = %d\n", errNo);
    // 실패 시 작업
}
else
{
    printf("[SUCCEED] clGetPlatformIDs: lpPlatformId = %p\n", lpPlatformId);
}

/* ... */

free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

사용 가능한 플랫폼 목록을 가져온다.

 

2 단계. 컨텍스트 생성


컨텍스트context는 OpenCL에서 사용 가능한 CPU + GPU + 메모리 객체 등등을 아우르는 개념이다. OpenCL은 컨텍스트 속에서 작동하는 구조로 되어 있으므로 이를 얻는 것이 가장 중요하다.

본 포스팅에서는 컨텍스트를 얻기 위해서 clCreateContextFromType 함수를 사용하겠다.

 

2-1 단계. 컨텍스트 프로퍼티 구성


컨텍스트 프로퍼티context property clCreateContextFromType에서 요구하는 매개변수로서 다음과 같은 구조의 배열이다.

{ CL_CONTEXT_PLATFORM, 플랫폼 1, CL_CONTEXT_PLATFORM, 플랫폼 2, ..., 0 }

 

마지막 원소는 항상 0이어야 한다.

다음은 앞서 얻은 플랫폼 배열을 통해 컨텍스트 프로퍼티 배열을 구성하는 예이다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

// 새로 추가된 변수
size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

/* ... */

assert((nContextProperties = nPlatformId * 2 + 1) > 1);
assert(lpContextProperties = (cl_context_properties *)calloc(nContextProperties, sizeof(cl_context_properties)));
for (i = 0, j = 0; i < nContextProperties;)
{
    if (i < nContextProperties - 1)
    {
        // 마지막 원소가 아니라면...
        // 플랫폼에 대해 질의한다.
        lpContextProperties[i++] = (cl_context_properties)CL_CONTEXT_PLATFORM;
        // 질의하고 싶은 플랫폼을 지정한다.
        lpContextProperties[i++] = (cl_context_properties)lpPlatformId[j++];
    }
    else
    {
        // 마지막 원소는 항상 zero
        lpContextProperties[i] = (cl_context_properties)0;
        break;
    }
}

/* ... */

free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

2-2 단계. 컨텍스트 객체 생성


앞서 구성한 컨텍스트 프로퍼티 배열을 가지고 OpenCL 컨텍스트 객체를 생성한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

// 새로 추가된 변수
size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

/* ... */

context = clCreateContextFromType
(
    lpContextProperties, // cl_context_properties * properties
    CL_DEVICE_TYPE_ALL,  // cl_device_type device_type
    NULL,                // void  (*pfn_notify) (const char *errinfo, const void  *private_info, size_t  cb, void  *user_data)
    NULL,                // void * user_data
    &errNo               // cl_int * errcode_ret
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clCreateContextFromType = %d\n", errNo);
    free(lpContextProperties); // 컨텍스트 얻기에 사용된 배열 해제
    // 실패 시 작업
}
else
{
    printf("[SUCCEED] clCreateContextFromType: context = %p\n", context);
}

/* ... */

clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

컨텍스트 객체 생성에 성공한 화면

 

3 단계. 컨텍스트에 포함된 장치들 얻기


컨텍스트에는 CPU, GPU를 포함한 가속 장치들이 포함되어 있다. 현재 컨텍스트에 몇 개의 장치들이 있고, 각각의 장치가 무엇인지 확인하기 위해 다음과 같이 코드를 작성한다.

 

3-1. 장치 정보를 얻기 위한 버퍼의 크기 구하기


컨텍스트에 포함된 장치들의 목록을 얻기 위해 총 몇 바이트의 버퍼가 필요한지 확인하는 과정이다. clGetContextInfo를 사용하며 매개변수에 param_value_size = 0, param_value = NULL을 하고 param_value_size_ret =&변수를 하면 변수를 통해 메모리의 크기가 반환된다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

// 새로 추가된 변수
size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

/* ... */

errNo = clGetContextInfo
(
    context,            // cl_context context
    CL_CONTEXT_DEVICES, // cl_context_info param_name
    0,                  // size_t param_value_size
    NULL,               // void * param_value
    &cbDeviceBuffer     // size_t * param_value_size_ret
);
if (errNo != CL_SUCCESS)
{
    fprintf(stderr, "[FAILURE] clGetContextInfo = %d\n", errNo);
    // 실패 시 작업
}
else if (cbDeviceBuffer <= 0)
{
    fprintf(stderr, "[FAILURE] deviceBufferSize = %zu\n", cbDeviceBuffer);
    // 실패 시 작업
}
else
{
    fprintf(stderr, "[SUCCESS] clGetContextInfo: cbDeviceBuffer = %zu\n", cbDeviceBuffer);
}

/* ... */

clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 얻기에 사용된 배열 해제
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

3-2. 컨텍스트에 있는 장치들의 정보 가져오기


장치 목록들을 얻기 위해 몇 바이트가 필요한지를 위와 같이 clGetContextInfo 함수로 확인하였다. 다시 호출하여 장치 목록을 복사해온다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

// 새로 추가된 변수
size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

/* ... */

assert(nDeviceId = (cl_uint)(cbDeviceBuffer / sizeof(cl_device_id)));
assert(lpDeviceId = (cl_device_id *)calloc(nDeviceId, sizeof(cl_device_id)));
errNo = clGetContextInfo
(
    context,            // cl_context context
    CL_CONTEXT_DEVICES, // cl_context_info param_name
    cbDeviceBuffer,     // size_t param_value_size
    lpDeviceId,         // void * param_value
    NULL                // size_t * param_value_size_ret
);
if (errNo != CL_SUCCESS)
{
    fprintf(stderr, "[FAILURE] clGetContextInfo = %d\n", errNo);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clGetContextInfo: nDeviceId = %zu\n", nDeviceId);
}

/* ... */

free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

이 PC에서는 컨텍스트에 2개의 장치가 있음을 확인 가능하다.

 

3-3. 컨텍스트에 있는 장치들에 대한 커맨드 큐 객체 생성


커맨드 큐command queue는 연산장치가 수행할 연산들이 대기하는 장소이다. clCreateCommandQueue 함수로 컨텍스트에 있는 각 장치들의 커맨트 큐 객체를 생성한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

// 새로 추가된 변수
size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

/* ... */

assert((nCommandQueue = nDeviceId) > 0);
assert(lpCommandQueue = (cl_command_queue *)calloc(nCommandQueue, sizeof(cl_command_queue)));
for (i = 0; i < nCommandQueue; i++)
{
    lpCommandQueue[i] = clCreateCommandQueue
    (
        context,       //cl_context context
        lpDeviceId[i], // cl_device_id device
        0,           // cl_command_queue_properties properties
        &errNo         // cl_int * errcode_ret
    );
    if (errNo != CL_SUCCESS)
    {
        printf("[FAILURE] clCreateCommandQueue = %d\n", errNo);
        // 실패 시 작업
    }
    else if (lpCommandQueue[i] == NULL)
    {
        printf("[FAILURE] commandQuene == %p @ %zu\n", lpCommandQueue[i], i);
        // 실패 시 작업
    }
    else
    {
        printf("[SUCCESS] clCreateCommandQueue: commandQueue = %p @ %zu\n", lpCommandQueue[i], i);
    }
}

/* ... */

free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

커맨드 큐 객체 생성 결과

 

4 단계. OpenCL 소스 파일을 읽어서 컴파일


OpenCL 소스는 기본적으로 텍스트 상태로 배포된다. 사용자의 다양한 아키텍처에 맞추어 즉석으로 컴파일하기 때문이다. 이 때 사용되는 함수는 clCreateProgramWithSource, clBuildProgram이다.

 

4-1 단계. OpenCL 소스 작성 후 읽기


OpenCL 소스를 작성한다. 파일 이름은 hello.cl로 정한다.

/* OpenCL source */
// ptr1 배열의 각 원소와 ptr2 배열의 각 원소를 더하여 out으로 리턴한다.
__kernel void hello(__global float * out, __global const float * ptr1, __global const float * ptr2)
{
    int global_id = 0; // 배열의 몇 번째 원소를 참조할 것인지를 나타내는 인덱스 변수이다.
    
    global_id = get_global_id(0);
    out[global_id] = (ptr1[global_id] + ptr2[global_id]);
    
    return;
}

 

위 소스 파일을 C로 읽어들인다.

/* C source */
FILE * fp = NULL;
size_t cbSource = 0;
char * lpSource = NULL;

if (fp = fopen("./hello.cl", "rt")) // 파일을 읽기 모드로 연다
{
    fseek(fp, 0, SEEK_END); // 파일이 몇 바이트인지를 체크한다.
    cbSource = ftell(fp) + 1;
    lpSource = (char *)calloc(cbSource, sizeof(char));
    fseek(fp, 0, SEEK_SET); // 파일을 처음부터 끝까지 읽는다.
    fread(lpSource, sizeof(char), cbSource, fp);
}
fclose(fp);

 

4-2 단계. OpenCL 소스로부터 프로그램 객체를 생성


위의 단계에서 읽어들인 소스 파일을 컴파일한다. 컴파일에 앞서 소스 문자열로부터 프로그램 객체를 생성해야 한다. 소스 문자열로부터 프로그램 객체를 생성하는 함수는 clCreateProgramWithSource이다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

// 새로 추가된 변수
cl_program program;

/* ... */

program = clCreateProgramWithSource
(
    context,                       // cl_context context
    1,                             // cl_uint count
    (const char **)&sourceContent, // const char **strings
    &sourceContentSize,            // const size_t *lengths
    &errNum                        // cl_int *errcode_ret
);
if (errNum != CL_SUCCESS)
{
    printf("[FAILURE] clCreateProgramWithSource == %d\n", errNum);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clCreateProgramWithSource\n");
}

/* ... */

clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

프로그램 객체의 생성 결과

 

4-3 단계. OpenCL 소스 컴파일


앞서 생성한 프로그램 객체를 컴파일한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

/* ... */

errNo = clBuildProgram
(
    program, // cl_program program : 컴파일하고자 하는 프로그램 객체
    0,       // cl_uint num_devices : 컴파일 대상이 될 장치의 개수 (0개 = 컨텍스트에 있는 기본 장치들)
    NULL,    // const cl_device_id * device_list : 컴파일 대상이 될 장치 (NULL = 컨텍스트에 있는 기본 장치들)
    NULL,    // const char * options : 빌드 옵션
    NULL,    // void (*pfn_notify)(cl_program, void * user_data) : 콜백 함수
    NULL     // void * user_data : 콜백함수에 전달할 사용자 데이터
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clBuildProgram == %d\n", errNo);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clBuildProgram\n");
}

/* ... */

clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

OpenCL 소스 컴파일 결과

 

5 단계. OpenCL에서 선언한 함수 실행하기


커널kernel은 OpenCL 소스에서 선언한 각 함수들의 진입점에 해당하는 개념이다. 앞서 컴파일한 OpenCL 소스에서 특정 함수의 이름을 통해 그 커널을 가져와 호출한다. 함수가 매개변수를 필요로 한다면 메모리 객체를 별도로 구성해야 한다.

 

5-1 단계. 커널 얻기


hello.cl 소스 파일에서 선언한 한 함수 hello를 호출해보도록 하겠다. 이를 위해 커널 객체를 생성한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

// 새로 추가된 변수
cl_kernel kernel;

/* ... */

kernel = clCreateKernel
(
    program, // cl_program program
    "hello", // const char * kernel_name
    &errNo   // cl_int * errcode_ret
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clCreateKernel = %d\n", errNo);
    // 실패 시 작업
}
else if (kernel == NULL)
{
    printf("[FAILURE] clCreateKernel: kernel = %p\n", kernel);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clCreateKernel\n");
}

/* ... */

clReleaseKernel(kernel); // 커널 객체 해제
clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

5-2 단계. 매개변수 구성하기


OpenCL 소스에서 선언한 hello 함수에서는 float * 형식의 3개의 매개변수(out, ptr1, ptr2)가 필요하다. 이를 구성해보겠다.

먼저 다음과 같이 테스트 배열을 마련한다. 하나는 1부터 50까지이고 다른 하나는 51부터 100까지의 각 50개의 실수로 구성된 배열이다.

/* C source */
float out[50] = { 0.0f, };
float ptr1[50] = { 0.0f, };
float ptr2[50] = { 0.0f, };
size_t i = 0;
for (i = 0; i < 50; i++) 
{
    ptr1[i] = (1.0f + (float)i);
    ptr2[i] = (51.0f + (float)i);
}

 

C의 배열을 OpenCL 소스에서 작성한 함수로 보내기 위해서는 메모리 객체를 생성한다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

cl_kernel kernel;

// 새로 추가된 변수
cl_mem memOut;
cl_mem memPtr1;
cl_mem memPtr2;

/* ... */

memOut = clCreateBuffer
(
    context,                                  // cl_context context : 배열을 사용할 컨텍스트
    CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, // cl_mem_flags flags : 각종 속성
    sizeof(out),                              // size_t size : 배열의 크기 (단위 = 바이트)
    out,                                      // void * host_ptr : 배열
    &errNo                                    // cl_int * errcode_ret : 오류 코드
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clCreateBuffer = %d", errNo);
    // 실패 시 작업
}
else if (memOut == NULL)
{
    printf("[FAILURE] clCreateBuffer: memOut = %p", memOut);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clCreateBuffer\n");
}

memOut = clCreateBuffer
(
    context,                                  // cl_context context
    CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,  // cl_mem_flags flags
    sizeof(ptr1),                             // size_t size
    ptr1,                                     // void * host_ptr
    &errNo                                    // cl_int * errcode_ret
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clCreateBuffer = %d", errNo);
    // 실패 시 작업
}
else if (memOut == NULL)
{
    printf("[FAILURE] clCreateBuffer: memOut = %p", memOut);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clCreateBuffer\n");
}

memOut = clCreateBuffer
(
    context,                                  // cl_context context
    CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,  // cl_mem_flags flags
    sizeof(ptr2),                             // size_t size
    ptr2,                                     // void * host_ptr
    &errNo                                    // cl_int * errcode_ret
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clCreateBuffer = %d", errNo);
    // 실패 시 작업
}
else if (memOut == NULL)
{
    printf("[FAILURE] clCreateBuffer: memOut = %p", memOut);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clCreateBuffer\n");
}

/* ... */

clReleaseMemObject(memOut); // float out[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr1); // float ptr1[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr2); // float ptr2[50]에 대한 메모리 객체 해제
clReleaseKernel(kernel); // 커널 객체 해제
clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

메모리 객체의 생성 결과

 

5-3 단계. 커널에 매개변수 전달하기


함수 진입점을 '커널'의 형태로 얻었고, 함수에 전달할 매개변수도 '메모리'의 형태로 구성하였다. 이제 커널과 매개변수를 연계해보겠다. OpenCL 소스에서 (out, ptr1, ptr2)의 순서로 선언하였으므로 이 순서대로 메모리 객체를 커널에 연계한다. 이 때 쓰이는 함수는 clSetKernelArg이다.

/* C source */

cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

cl_kernel kernel;

// 새로 추가된 변수
cl_mem memOut;
cl_mem memPtr1;
cl_mem memPtr2;

/* ... */

errNo = clSetKernelArg
(
    kernel,         // cl_kernel kernel : 실행하고 싶은 커널
    0,              // cl_uint arg_index : 커널에 선언된 매개변수 순서 (0번째 = out)
    sizeof(cl_mem), // size_t arg_size : 메모리 객체의 크기
    &memOut         // const void * arg_value : 버퍼
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clSetKernelArg = %d\n", errNo);
}
else
{
    printf("[SUCCESS] clSetKernelArg\n");
}
    
errNo = clSetKernelArg
(
    kernel,         // cl_kernel kernel
    1,              // cl_uint arg_index : 1번째 = ptr1
    sizeof(cl_mem), // size_t arg_size
    &memPtr1        // const void * arg_value
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clSetKernelArg = %d\n", errNo);
}
else
{
    printf("[SUCCESS] clSetKernelArg\n");
}
    
errNo = clSetKernelArg
(
    kernel,         // cl_kernel kernel
    2,              // cl_uint arg_index : 2번째 = ptr2
    sizeof(cl_mem), // size_t arg_size
    &memPtr2        // const void * arg_value
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clSetKernelArg = %d\n", errNo);
}
else
{
    printf("[SUCCESS] clSetKernelArg\n");
}

/* ... */

clReleaseMemObject(memOut); // float out[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr1); // float ptr1[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr2); // float ptr2[50]에 대한 메모리 객체 해제
clReleaseKernel(kernel); // 커널 객체 해제
clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

커널에 매개변수 지정한 결과

 

5-4 단계. 커널을 호출하기


이제 커널을 실행한다. 커맨드 큐에 커널을 추가하는 것으로 커널은 실행 준비 상태가 된다. 앞서 장치의 수 만큼 여러개의 커맨드 큐를 얻었는데 0번째 커맨드 큐로 커널을 추가해 보겠다. 이 때 사용되는 함수는 clEnqueueNDRangeKernel이다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

cl_kernel kernel;

cl_mem memOut;
cl_mem memPtr1;
cl_mem memPtr2;

// 새로 추가된 변수
// 커널이 수행할 총 작업량을 어떻게 분배할 것인지 그 크기를 지정한다. 이 분배는 최적화 방식에 따라 달라질 수 있다.
size_t globalWorkSize[1] = { 50 }; // 각 배열이 가지고 있는 50개의 원소
size_t localWorkSize[1] = { 1 }; // 하나의 디바이스에 몰아서 처리

/* ... */

errNo = clEnqueueNDRangeKernel
(
    lpCommandQueue[0], // cl_command_queue command_queue
    kernel,            // cl_kernel kernel
    1,                 // cl_uint work_dim
    NULL,              // const size_t * global_work_offset
    globalWorkSize,    // const size_t * global_work_size
    localWorkSize,     // const size_t * local_work_size
    0,                 // cl_uint num_events_in_wait_list
    NULL,              // const cl_event * event_wait_list
    NULL               // cl_event * event
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clEnqueueNDRangeKernel = %d\n", errNo);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clEnqueueNDRangeKernel\n");
}

/* ... */

clReleaseMemObject(memOut); // float out[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr1); // float ptr1[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr2); // float ptr2[50]에 대한 메모리 객체 해제
clReleaseKernel(kernel); // 커널 객체 해제
clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

커널을 큐에 추가한 결과

 

6 단계. 커널의 실행 결과 읽기


커널에서는 out 매개 변수를 통해 값을 함수 밖으로 전달한다. 이 매개변수는 C 소스에서 cl_mem 형식으로 전달된다. cl_mem으로부터 배열을 읽어올 때 사용되는 함수는 clEnqueueReadBuffer이다.

/* C source */
cl_int errNo;

cl_uint nPlatformId = 0;
cl_platform_id * lpPlatformId = NULL;

size_t i, j;
cl_uint nContextProperties = 0;
cl_context_properties * lpContextProperties = NULL;
cl_context context;

size_t cbDeviceBuffer = 0;
size_t nDeviceId = 0;
cl_device_id * lpDeviceId = NULL;

size_t nCommandQueue = 0;
cl_command_queue * lpCommandQueue = NULL;

cl_program program;

cl_kernel kernel;

float out[50] = { 0.0f, };
float ptr1[50] = { 0.0f, };
float ptr2[50] = { 0.0f, };
cl_mem memOut;
cl_mem memPtr1;
cl_mem memPtr2;

size_t globalWorkSize[1] = { 50 }; 
size_t localWorkSize[1] = { 1 }; 

/* ... */

errNo = clEnqueueReadBuffer
(
    lpCommandQueue[0], // cl_command_queue command_queue
    memOut,            // cl_mem buffer
    CL_TRUE,           // cl_bool blocking_read
    0,                 // size_t offset
    sizeof(out),       // size_t cb
    out,               // void * ptr
    0,                 // cl_uint num_events_in_wait_list
    NULL,              // const cl_event * event_wait_list
    NULL               // cl_event * event
);
if (errNo != CL_SUCCESS)
{
    printf("[FAILURE] clEnqueueReadBuffer = %d\n", errNo);
    // 실패 시 작업
}
else
{
    printf("[SUCCESS] clEnqueueReadBuffer\n");
}

/* ... */

clReleaseMemObject(memOut); // float out[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr1); // float ptr1[50]에 대한 메모리 객체 해제
clReleaseMemObject(memPtr2); // float ptr2[50]에 대한 메모리 객체 해제
clReleaseKernel(kernel); // 커널 객체 해제
clReleaseProgram(program); // 프로그램 객체 해제
free(lpDeviceId); // 장치 버퍼 메모리 해제
clReleaseContext(context); // 컨텍스트를 해제한다.
free(lpContextProperties); // 컨텍스트 속성 배열을 해제한다.
free(lpPlatformId); // 플랫폼 구조체 동적할당을 해제한다.

 

메모리 객체로부터 배열을 읽은 결과

 

6-1. 결과 확인하기


OpenCL 커널의 연산 결과는 clEnqueueReadBuffer를 통해 float out[50];로 복사되었다. 출력해보면 OpenCL 소스에서 작성하였던 ptr1[x] * 100.0f + ptr2[x]의 결과가 출력됨을 확인할 수 있다.

/* C source */
/* ... */
for (i = 0; i < 50; i++)
{
    printf("%6.1f ", out[i]);
    if ((i + 1) % 8 == 0) printf("\n");
}
    
printf("\n");
/* ... */

 

OpenCL 소스 코드의 실행 결과

 

썸네일 이미지
[VMware × Windows 3.1] 그래픽 드라이버 업그레이드
[VMware × Windows 3.1] 그래픽 드라이버 업그레이드 본 포스팅에서는 VMware에서 Windows 3.1을 설치한 경우, 그래픽 드라이버를 업그레이드하여 보다 큰 화면으로 작업할 수 있는 방법에 대하여 소개한다. 주의! 본 그래픽 드라이버는 영문 버전의 MS-DOS 및 Windows 3.1(https://blog.codingcat.kr/40)에서만 작동을 보장한다. 한글 버전의 MS-DOS 및 Windows 3.1에 설치할 경우 정상적으로 작동되지 않는다. 1 단계. SVGA 드라이버 여부 확인하기. 먼저 용량이 충분한 드라이브에 파일(, 압축 해제 암호: blog.codingcat.kr)을 다운로드 받아 압축을 풀어놓는다. 그 다음 Windows가 설치된 경로(예: C:\Windows\..
Operating System/MS-DOS & Windows 3.x
2019. 1. 31. 18:12

[VMware × Windows 3.1] 그래픽 드라이버 업그레이드

Operating System/MS-DOS & Windows 3.x
2019. 1. 31. 18:12

[VMware × Windows 3.1] 그래픽 드라이버 업그레이드


본 포스팅에서는 VMware에서 Windows 3.1을 설치한 경우, 그래픽 드라이버를 업그레이드하여 보다 큰 화면으로 작업할 수 있는 방법에 대하여 소개한다.

주의! 본 그래픽 드라이버는 영문 버전의 MS-DOS 및 Windows 3.1(https://blog.codingcat.kr/40)에서만 작동을 보장한다. 한글 버전의 MS-DOS 및 Windows 3.1에 설치할 경우 정상적으로 작동되지 않는다.

 

1 단계. SVGA 드라이버 여부 확인하기.


먼저 용량이 충분한 드라이브에 파일(VGAPATCH.rar, 압축 해제 암호: blog.codingcat.kr)을 다운로드 받아 압축을 풀어놓는다.

그 다음 Windows가 설치된 경로(예: C:\Windows\System)에 SVGA256.DRV 파일이 있는지 확인한다.

> C:
> cd C:\WINDOWS\SYSTEM
> dir svga256.drv
SVGA256.DRV가 없는 경우...

 

2 단계. SVGA 드라이버와 관련 파일 생성하기


없으면 압축을 푼 경로에서 SVGA.EXE 파일을 실행하여 이를 생성한다.

> svga.exe
svga.exe를 실행하여 드라이버 파일들을 빈 디렉터리에 생성한다.

 

경고문에도 나와있듯, 이 파일은 C:\WINDOWS 또는 C:\WINDOWS\SYSTEM 디렉터리에 놓고 실행하면 안 된다. 'y' 키를 눌러 진행한다.

'y' 키를 눌러 진행한다.

 

실행이 완료되면 여러 파일들이 생성되었음을 확인할 수 있다.

실행 완료 후 모습

 

3 단계. SVGA 드라이버를 패치하기


생성된 파일 중에 svga256.drv가 있다. 첨부한 vgapatch.com으로 이를 패치한다. 다음과 같이 완료되었음을 확인한다.

> vgapatch.com p
드라이버 파일을 패치한다.

 

4 단계. SVGA 드라이버를 시스템 디렉터리에 복사하기


작업한 디렉터리에서 이제 다운받았던 파일들을 제거한 후 생성한 파일들만 시스템 디렉터리에 복사한다.

> del svga.exe
> del vgapatch.com
> del vgapatch.txt
> copy *.* C:\WINDOWS\SYSTEM

 

이 때, 덮어쓰기 질문이 뜨면 'a'를 눌러 시스템 파일들을 모두 덮어쓰기한다.

새로 패치된 파일로 모두 덮어쓰기한다.

 

정확히 44개의 파일이 복사되었다.

복사 완료.

 

5 단계. 드라이버 적용하기


C:\WINDOWS\SETUP.EXE를 실행하여 윈도우 설치 화면으로 들어간다.

> C:
> cd C:\WINDOWS
> setup

 

셋업 화면으로 들어가기.

 

화살표 키를 이용하여 Display: VGA를 선택한 후 [Enter]를 누른다.

Display: VGA 선택

 

다시 화살표 키를 이용하여 Super VGA 1024x768 256 Large/Small 중 하나를 선택 후 [Enter]를 누른다.

Super VGA 1024x768 선택

 

처음 화면으로 돌아왔다. [Enter]를 눌러 확인한다.

Accept the configuration shown above를 선택

 

드라이버 패치가 완료되었으므로 This driver for Display is already installed...라고 뜬다. [Enter]를 누르고 설치 화면에서 나간다.

[Enter]를 눌러 설치 화면에서 나간다.

 

win을 실행해본다. 다음과 같이 큰 화면이 나타나면 성공이다.

SVGA의 큰 화면을 보여주는 Windows 3.1

 

카테고리 “Operating System/MS-DOS & Windows 3.x”
more...
썸네일 이미지
[단막 Windows API 활용법] Windows API 폰트 변경하기
단막 Windows API 활용법 Windows API 폰트 변경하기 Windows API로 UI를 만들 경우 폰트가 다음과 같이 투박하게 보여짐을 확인할 수 있다. 기본적으로 투박한 폰트로 보여지는 Windows API 윈도우 본 포스팅에서는 Windows API로 작성한 윈도우에 폰트를 적용하는 방법에 대해 정리하겠다. 시스템 정의 기본 폰트 사용하기 시스템 정의 기본 폰트는 제목 표시줄, 메시지 박스, 메뉴, 상태 표시줄, 캡션 표시줄 등에 사용되기 위해 미리 정의된 폰트이다. 이를 불러오기 위해서는 SystemParametersInfo 함수를 SPI_GETNONCLIENTMETRICS 매개변수를 적용하여 호출한다. WndProc 콜백 프로시저의 WM_CREATE 섹션에 다음과 같이 적는다. /* ..
API/Windows API
2019. 1. 20. 20:51

[단막 Windows API 활용법] Windows API 폰트 변경하기

API/Windows API
2019. 1. 20. 20:51

단막 Windows API 활용법


Windows API 폰트 변경하기


Windows API로 UI를 만들 경우 폰트가 다음과 같이 투박하게 보여짐을 확인할 수 있다.

기본적으로 투박한 폰트로 보여지는 Windows API 윈도우

 

본 포스팅에서는 Windows API로 작성한 윈도우에 폰트를 적용하는 방법에 대해 정리하겠다.

 

시스템 정의 기본 폰트 사용하기


시스템 정의 기본 폰트는 제목 표시줄, 메시지 박스, 메뉴, 상태 표시줄, 캡션 표시줄 등에 사용되기 위해 미리 정의된 폰트이다. 이를 불러오기 위해서는 SystemParametersInfo 함수를 SPI_GETNONCLIENTMETRICS 매개변수를 적용하여 호출한다.

WndProc 콜백 프로시저의 WM_CREATE 섹션에 다음과 같이 적는다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
// 새로 지정할 폰트 핸들
static HFONT s_hFont = (HFONT)NULL;
// 시스템 정의 폰트는 NONCLIENTMETRICS라는 구조체를 통해 전달된다.
NONCLIENTMETRICS nonClientMetrics;
/* ... */
// 구조체의 내용을 0으로 리셋한다.
ZeroMemory(&nonClientMetrics, sizeof(NONCLIENTMETRICS));
// cbSize 필드만 특별히 구조체의 크기로 지정한다.
nonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);

// 구조체를 통해 전달되는 폰트는 LOGFONT 형식이다.
// GDI에서 사용하기 위해 LOGFONT에서 기술된 폰트 정보를 바탕으로 HFONT를 생성한다.
s_hFont = CreateFontIndirect(&nonClientMetrics.lfCaptionFont);

// 창 스스로에게 WM_SETFONT 메시지를 전달한다.
SendMessage(hWnd, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));

 

자기 자신에게 WM_SETFONT를 전달했으므로 WndProc에서 WM_SETFONT 섹션을 추가하여 이벤트를 처리해보겠다. 다음은 그 예이다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
// 새로 지정할 폰트 핸들
static HFONT s_hFont = (HFONT)NULL;
// 시스템 정의 폰트는 NONCLIENTMETRICS라는 구조체를 통해 전달된다.
NONCLIENTMETRICS nonClientMetrics;
/* ... */
lResult = DefWindowProc(hWnd, uMessage, wParam, lParam);
// Static 윈도우에 폰트 변경 메시지를 보낸다.
SendMessage(s_hwndStatic, WM_SETFONT, wParam, lParam);
/* ... */

 

다음과 같이 윈도우의 폰트가 변경되었음을 확인할 수 있다.

시스템 정의 기본 폰트로 변경된 모습

 

LOGFONT 직접 지정하기


시스템 기본 폰트 외에 다른 폰트를 지정해보겠다. LOGFONT 구조체의 각 멤버들을 직접 지정하면 다양한 폰트들을 얻어서 윈도우에 적용 가능하다.

이번에는 굴림, 10 포인트를 LOGFONT를 활용해 적용한다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
HDC hDC = (HDC)NULL;
LOGFONT logFont;
/* ... */

/* 굴림, 10 포인트로 폰트 설정 */
ZeroMemory(&logFont, sizeof(LOGFONT));
hDC = GetDC(hWnd);
\logFont.lfHeight = -MulDiv(10, GetDeviceCaps(hDC, LOGPIXELSY), 72);
logFont.lfWeight = FW_NORMAL;
_tcscpy(logFont.lfFaceName, TEXT("Gulim"));
s_hFont = CreateFontIndirect(&logFont);
ReleaseDC(hDC);

/* ... */

 

10 포인트의 굴림으로 적용된 모습

 


카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API 활용법] OpenFolderDialog 열기
단막 Windows API 활용법 OpenFolder Dialog 열기 [폴더 찾아보기...] 종류의 버튼을 클릭할 때 나오는 다이얼로그를 Windows API에서 어떻게 호출하고, 그 선택된 폴더 경로를 가져오는 방법에 대해 설명한다. 이 기능은 쉘shell에 대한 기능이므로 쉘 함수를 사용하기 위하여 #include 를 포함한다. 레이아웃 설정 빈 화면에 다음과 같이 윈도우들을 배치하도록 WndProc을 작성한다. /* WndProc: HWND, UINT, WPARAM, LPARAM */ static HWND s_hwndStatic1 = (HWND)NULL; static HWND s_hwndStatic2 = (HWND)NULL; static HWND s_hwndStatic3 = (HWND)NULL; ..
API/Windows API
2019. 1. 20. 16:15

[단막 Windows API 활용법] OpenFolderDialog 열기

API/Windows API
2019. 1. 20. 16:15

단막 Windows API 활용법


OpenFolder Dialog 열기


[폴더 찾아보기...] 종류의 버튼을 클릭할 때 나오는 다이얼로그를 Windows API에서 어떻게 호출하고, 그 선택된 폴더 경로를 가져오는 방법에 대해 설명한다.

이 기능은 쉘shell에 대한 기능이므로 쉘 함수를 사용하기 위하여

#include <ShlObj.h>

를 포함한다.

 

레이아웃 설정


빈 화면에 다음과 같이 윈도우들을 배치하도록 WndProc을 작성한다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
static HWND s_hwndStatic1 = (HWND)NULL;
static HWND s_hwndStatic2 = (HWND)NULL;
static HWND s_hwndStatic3 = (HWND)NULL;
static HWND s_hwndStatic4 = (HWND)NULL;
static HWND s_hwndEdit = (HWND)NULL;
static HWND s_hwndButton = (HWND)NULL;
/* ... */
case WM_CREATE:
	s_hwndStatic1 = CreateWindow
	(
		TEXT("STATIC"),
		TEXT("폴더 경로를 입력하시오."),
		WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
		0, 0, 300, 30,
		hWnd,
		(HMENU)ID_STATIC1,
		g_hInstance,
		(LPVOID)NULL
	);
	s_hwndEdit = CreateWindow
	(
		TEXT("EDIT"),
		TEXT(""),
		WS_CHILD | WS_VISIBLE | WS_THICKFRAME,
		0, 30, 180, 30,
		hWnd,
		(HMENU)ID_EDIT,
		g_hInstance,
		(LPVOID)NULL
	);
	s_hwndButton = CreateWindow
	(
		TEXT("BUTTON"),
		TEXT("찾아보기(&B)..."),
		WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
		180, 30, 105, 30,
		hWnd,
		(HMENU)ID_BUTTON,
		g_hInstance,
		(LPVOID)NULL
	);
	s_hwndStatic2 = CreateWindow
	(
		TEXT("STATIC"),
		TEXT("폴더 이름은 :"),
		WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
		0, 60, 150, 30,
		hWnd,
		(HMENU)ID_STATIC2,
		g_hInstance,
		(LPVOID)NULL
	);
	s_hwndStatic3 = CreateWindow
	(
		TEXT("STATIC"),
		TEXT(""),
		WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
		150, 60, 150, 30,
		hWnd,
		(HMENU)ID_STATIC3,
		g_hInstance,
		(LPVOID)NULL
	);
	s_hwndStatic4 = CreateWindow
	(
		TEXT("STATIC"),
		TEXT("^(코딩캣)^ = @\"코딩\"하는 고양이."),
		WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
		0, 90, 300, 30,
		hWnd,
		(HMENU)ID_STATIC3,
		g_hInstance,
		(LPVOID)NULL
	);
	break;
/* ... */

 

WM_CREATE 메시지 처리에서 지정한 레이아웃의 실행 결과

 

폴더 다이얼로그 띄우기


위와 같이 레이아웃을 구성한 다음 [찾아보기(B)...] 버튼(ID_BUTTON, s_hwndButton)을 클릭하여 폴더 다이얼로그를 띄우도록 하겠다. WM_COMMAND에서 LOWORD(wParam) 값이 ID_BUTTON인 경우에 다음과 같이 이벤트를 처리한다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
case WM_COMMAND:
	switch (LOWORD(wParam))
	{
	case ID_BUTTON:
		{
			BROWSEINFO browseInfo;
			ITEMIDLIST * pidlBrowse = NULL;
			TCHAR szPath[MAX_PATH];
			TCHAR szDisplayName[MAX_PATH];

			ZeroMemory(&browseInfo, sizeof(BROWSEINFO));

			// 본 다이얼로그를 소유한 창의 핸들이다.
			browseInfo.hwndOwner = hWnd;

			// 여기서 지정한 특정 폴더 이하의 경로만 선택 가능하다.
			browseInfo.pidlRoot = (LPCITEMIDLIST)NULL;

			// 폴더의 실제 이름과 보여지는 이름이 다를 때 지정한 버퍼로 보여지는 이름을 전달한다.
			browseInfo.pszDisplayName = szDisplayName; 

			// 폴더 다이얼로그를 통해 사용자에게 보여질 메시지를 지정한다.
			browseInfo.lpszTitle = TEXT("선택하고 싶은 폴더를 선택합니다...");
				
			// 각종 옵션을 지정한다.
			browseInfo.ulFlags =
				BIF_NEWDIALOGSTYLE | // 새로운 형식의 다이얼로그([새 폴더 만들기] 버튼 있는 창)
				BIF_RETURNONLYFSDIRS | // 로컬 경로의 디렉토리만 선택 가능하도록 함
				BIF_EDITBOX; // 폴더 이름을 타자로도 입력할 수 있도록 텍스트 상자 제공함

			// 다이얼로그 창에 대한 콜백을 지정한다.
			browseInfo.lpfn = BrowseProc;

			// 콜백 함수에 전달할 추가적인 데이터가 있다면 이것을 통해 전달한다.
			browseInfo.lParam = (LPARAM)NULL;

			// 폴더 열기 다이얼로그를 실행한다.
			pidlBrowse = SHBrowseForFolder(&browseInfo);
			if (pidlBrowse != NULL)
			{
				// 다이얼로그에서 선택한 폴더 항목을 문자열 경로로 변환한다.
				if (SHGetPathFromIDList(pidlBrowse, szPath))
				{
					// 경로를 Edit로 출력하고
					SetWindowText(s_hwndEdit, szPath);
					// 선택한 폴더의 DisplayName도 Static으로 출력한다.
					SetWindowText(s_hwndStatic3, szDisplayName);
				}
				else
				{
					OutputDebugString(TEXT("Invalid Path\n"));
				}
			}
		}
		break;
/* ... */

 

BROWSEINFO 구조체의 ulFlags에 지정 가능한 옵션은 MSDN(https://docs.microsoft.com/ko-kr/windows/desktop/api/shlobj_core/ns-shlobj_core-_browseinfoa)을 참조하면 된다.

 

BIF_NEWDIALOGSTYLE를 적용한 다이얼로그
BIF_NEWDIALOGSTYLE를 적용하지 않은 다이얼로그

 

콜백 프로시저


다이얼로그의 콜백 프로시저는 다음과 같이 선언한다.

int CALLBACK BrowseProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)

 

다음은 콜백 함수의 구현 예시이다.

/* BrowseProc: HWND, UINT, LPARAM, LPARAM */
int CALLBACK BrowseProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	static TCHAR s_szPath[MAX_PATH];
	int iResult = 0;

	switch (uMsg)
	{
	case BFFM_INITIALIZED:
		// 다이얼로그 상자가 이제 막 떴을 때 처리할 내용은 이 곳에 적는다.

		// BFFM_SETSELECTION: 기본 값으로 선택하고 있을 폴더가 있을 때 사용한다.
		// lParam은 선택하고 있을 폴더이다.
		// lParam이 텍스트 형식의 경로라면 wParam = TRUE
		// lParam이 PIDL 형식의 객체라면 wParam = FALSE
		SendMessage(hWnd, BFFM_SETSELECTION, TRUE, lParam);

		// BFFM_SEROKTEXT: [확인] 버튼의 텍스트를 변경한다.
		// lParam은 [확인] 버튼에 새로 넣을 텍스트이다.
		SendMessage(hWnd, BFFM_SETOKTEXT, 0, (LPARAM)TEXT("이걸로"));
		break;
	case BFFM_SELCHANGED:
		
		if (SHGetPathFromIDList((LPITEMIDLIST)lParam, s_szPath))
		{
			if (_tcsstr(s_szPath, TEXT("blocked")))
			{
				// 예를 들어,
				// 경로 중 일부 문자에 "blocked"가 포함되면 확인버튼 비활성화.

				// BFFM_ENABLEOK: [확인] 버튼을 활성화/비활성화 한다.
				// lParam은 [확인] 버튼의 활성화(TRUE) / 비활성화(FALSE) 여부이다.
				SendMessage(hWnd, BFFM_ENABLEOK, 0, FALSE);
			}
			else
			{
				SendMessage(hWnd, BFFM_ENABLEOK, 0, TRUE);

				// BFFM_SETSTATUSTEXT: 현재 선택한 폴더 경로를 다이얼로그의 Text 속성으로 지정함.
				// lParam은 새로 선택된 폴더 경로
				SendMessage(hWnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)s_szPath);
			}
		}
		break;
	case BFFM_VALIDATEFAILED:
		// 다이얼로그에 전달된 문자열이 잘못되었을 때 처리할 내용은 이 곳에 적는다.

		// 다이얼로그를 계속 띄우고 있으려면 콜백이 Non-zero를 반환한다.
		// 다이얼로그를 닫으려면 콜백이 0을 반환한다.
		break;
	}

	return 0;
}

 

실행 결과

 

위 그림은 C:\Users를 선택한 후 [확인] 버튼을 누른 결과이다. 해당 폴더의 실제 이름은 Users이지만 한국어 모드에서는 사용자라는 이름으로 표시된다. 이것은 BROWSERINFO.pszDisplayName멤버로 전달된다.

카테고리 “API/Windows API”
more...
썸네일 이미지
국규 9566(KPS-9566) 문자셋을 지원하는 북한 폰트(조선 서체)
국규 9566(KPS-9566) 문자셋을 지원하는 북한 폰트(조선 서체) 다음은 국규 9566(KPS-9566) 문자셋(http://codingcatkr.tistory.com/33, http://codingcatkr.tistory.com/33)을 지원하는 서체이다. Below are some font files supporting KPS-9566(http://codingcatkr.tistory.com/33, http://codingcatkr.tistory.com/33) Hangul charset. '붉은별' 2.0에서 추출한 서체들 / Font files extracted from RedStar 2.0 Linux 1. 천리마 (KP CheonRiMa) 2. 청봉 (KP CheongPong) 3. 광명 (..
Extras/Charset
2018. 12. 27. 20:15

국규 9566(KPS-9566) 문자셋을 지원하는 북한 폰트(조선 서체)

Extras/Charset
2018. 12. 27. 20:15

국규 9566(KPS-9566) 문자셋을 지원하는 북한 폰트(조선 서체)


다음은 국규 9566(KPS-9566) 문자셋(http://codingcatkr.tistory.com/33, http://codingcatkr.tistory.com/33)을 지원하는 서체이다.

Below are some font files supporting KPS-9566(http://codingcatkr.tistory.com/33, http://codingcatkr.tistory.com/33) Hangul charset.

 

'붉은별' 2.0에서 추출한 서체들 / Font files extracted from RedStar 2.0 Linux


 

1. 천리마 (KP CheonRiMa)


kpcholim.tar.gz
다운로드

 

2. 청봉 (KP CheongPong)


kpchopom.tar.gz
다운로드

 

3. 광명 (KP KwangMyeong)


kpkwamym.tar.gz
다운로드

 

4. 붓글 (KP PusKul)


kppuskum.tar.gz
다운로드

 

'붉은별' 3.0에서 추출한 서체들 / Font files extracted from RedStar 3.0 Linux


붉은별 3.0에 내장된 원본 폰트 파일은 일부 글자glyph에서 버텍스vertex 관련 오류가 있어서 FontForge 등의 글꼴 제작 프로그램으로 열어 무결성 검사를 할 경우 "Self Intersection" 오류를 볼 수 있다. 이는 본 폰트의 개발자가 실수한 것으로 보인다. 때문에 Microsoft Windows에서 원본 파일을 열 경우 "올바른 글꼴 파일이 아닙니다."라는 오류 메시지를 볼 수 있다. 물론 유닉스 및 호환 운영체제에서는 원본 파일을 오류 메시지 없이 사용 가능하다. Microsoft Windows에서 사용하고자 하는 경우 원본 파일이 아닌 수정본 파일만을 사용 가능하다. 수정본 파일은 FontForge로 원본 파일을 열어서 True Type 포맷으로 다시 Export하는 방법으로 재생성되었다.

When opening original font files from RedStar 3.0 Linux by font development softwares, You may see "Self Intersection" error message. Maybe it is a mistake of developer of this fonts. So you may see "Invalid font format." error message, if you open the original files in Microsoft Windows. Of course, it is possible to use in Unix and its compatible OS. However, in Microsoft Windows, Only modified versions are possible to be used. Modified files attatched below are re-created by opening and exporting again in FontForge.

 

1. 천리마 (KCC-KP-CheonRiMa-Normal-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcholin.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheonRiMa-Normal-KP-2011KPS.zip
다운로드

 

2. 천리마 굵은 (KCC-KP-CheonRiMa-Bold-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcholiB.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheonRiMa-Bold-KP-2011KPS.zip
다운로드

 

3. 천리마 중간(KCC-KP-CheonRiMa-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcholim.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheonRiMa-Medium-KP-2011KPS.zip
다운로드

 

4. 천리마 가는 (KCC-KP-CheonRiMa-Light-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcholiL.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheonRiMa-Light-KP-2011KPS.zip
다운로드

 

5. 천리마 변체 굵은 (KCC-KP-CR_PyeonChe-Bold-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcrpyeB.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CR_PyeonChe-Bold-KP-2011KPS.zip
다운로드

 

6. 천리마 둥근 (KCC-KP-CR_Tungkeun-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpcrtunm.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CR_Tungkeun-Medium-KP-2011KPS.zip
다운로드

 

7. 청봉 (KCC-KP-CheongPong-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpchopom.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheongPong-Medium-KP-2011KPS.zip
다운로드

 

8. 청봉 굵은 (KCC-KP-CheongPong-Bold-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpchopoB.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheongPong-Bold-KP-2011KPS.zip
다운로드

 

9. 청봉 가는 (KCC-KP-CheongPong-Light-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpchopoL.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-CheongPong-Light-KP-2011KPS.zip
다운로드

 

10. 광명 (KCC-KP-KwangMyeong-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpkwamym.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-KwangMyeong-Medium-KP-2011KPS.zip
다운로드

 

11. 광명 굵은 (KCC-KP-KwangMyeong-Bold-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kpkwamyB.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-KwangMyeong-Bold-KP-2011KPS.zip
다운로드

 

12. 붓글 (KCC-KP-PusKul-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

KCC-KP-PusKul-Medium-KP-2011KPS.zip
다운로드

윈도우 버전(for Windows):

kppuskum.tar.gz
다운로드

 

13. 붓글 궁체 (KCC-KP-PK_KungChe-Medium-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kppkkunM.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-PK_KungChe-Medium-KP-2011KPS.zip
다운로드

 

14. 붓글 예서 굵은 (KCC-KP-PK_Yeso-Bold-KP-2011KPS)


유닉스 버전(for Unix/Compatible OS):

kppkyesB.tar.gz
다운로드

윈도우 버전(for Windows):

KCC-KP-PK_Yeso-Bold-KP-2011KPS.zip
다운로드

 

카테고리 “Extras/Charset”
more...
썸네일 이미지
Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) [完]
Windows NT 서버 운영 본 포스팅은 Windows NT 서버를 운영하는 각종 예에 대한 정리이다. RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) 리얼플레이어(RealPlayer)는 자체적인 파일 포맷과 스트리밍 프로토콜을 지원해서 2000년대 초반까지 Windows Media Player와 함께 흔하게 쓰이던 오디오 및 비디오 플레이어이다. 이번 포스팅에서는 Windows NT 서버에 RealMedia 형식의 미디어를 게시하기 위해 기존 파일 형식에서 RealMedia로 변환하는 방법에 대해 소개한다. 오래된 프로그램이다보니 현재 사용되는 .mp4, .mkv 등의 코덱은 지원하지 않는다. Microsoft MPEG-4 Video v3 이하의 코덱을 ..
Operating System/Windows NT
2018. 12. 21. 23:11

Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) [完]

Operating System/Windows NT
2018. 12. 21. 23:11

Windows NT 서버 운영


본 포스팅은 Windows NT 서버를 운영하는 각종 예에 대한 정리이다.

 

RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2)


리얼플레이어(RealPlayer)는 자체적인 파일 포맷과 스트리밍 프로토콜을 지원해서 2000년대 초반까지 Windows Media Player와 함께 흔하게 쓰이던 오디오 및 비디오 플레이어이다. 이번 포스팅에서는 Windows NT 서버에 RealMedia 형식의 미디어를 게시하기 위해 기존 파일 형식에서 RealMedia로 변환하는 방법에 대해 소개한다.

오래된 프로그램이다보니 현재 사용되는 .mp4, .mkv 등의 코덱은 지원하지 않는다. Microsoft MPEG-4 Video v3 이하의 코덱을 사용하는 .avi 포맷을 기준으로 작업한다.

 

  1. Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1)
  2. Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) [完]

 

4. 미디어 변환하기


클라이언트에서 rd3en200.exe 또는 rd3en851.exe를 실행하면 RealProducer 설치 마법사가 실행된다. 마법사에 따라 RealProducer를 설치한다.

설치를 마치고 처음 실행 시 설정 화면이 나타난다. 아래 화면은 출력 파일에 녹화 기능을 적용(=복사방지기능 비활성화)해서 접속자가 다운로드해 갈 수 있게 하겠느냐는 질문인데 나중에 [Options] 메뉴에서도 설정할 수 있으니 우선 [Next >]를 누른다.

다음 나타나는 화면에서는 사용자의 인적사항을 적는다. 어차피 지금 와서는 안 되는 기능이니 이메일 주소를 대충 적고 [Finish]를 누른다.

 

앞으로 RealProducer를 실행하면 이 화면이 보여질 것이다.

Record From File
기존 소리, 동영상 파일로부터 RealMedia 파일(*.rm 파일)로 변환하는 메뉴이다.
Record From Media Device
카메라, 마이크 등의 영상/음성 장치로부터 녹화 또는 녹음하는 메뉴이다.
Live Broadcast
RealServer로 실시간 스트리밍 전송하는 기능이다.

 

 

2, 3번째를 수행하기 위한 호환되는 하드웨어가 이제는 없으므로 여기에서는 첫 번째만 해 보도록 하겠다.

 

[Browse...] 버튼을 열어 인코딩할 파일을 연다.

 

파일에 대한 메타데이터를 입력한다.

 

대역폭을 설정하는 것인데, 첫 번째 옵션은 속도가 다른 다양한 접속환경에 맞춰 알아서 전송하는 미디어를 생성하는 것이고 두 번째는 전송속도가 고정적인 미디어를 생성한다. 둘 중 하나를 선택한다.

 

어떤 속도에 맞춰 제작할 것인지를 묻는다. 아래 나오는 속도들은 현재 관점에서 보았을 때 충분히 전송하고도 남는 속도들이므로 아무거나 선택한다.

 

소리 포맷을 설정합니다. 아래쪽으로 갈수록 고음질입니다.

 

화질 설정이다. Normal이나 Smoothese를 선택하면 무난하다.

 

출력될 미디어의 경로를 지정한다.

 

지금까지의 설정을 확인 후 [마침]을 누른다.

 

왼쪽 화면은 원본 동영상, 오른쪽 화면은 rm 파일로 인코드된 영상이 보여질 것이다. 화면 아래 [Start] 버튼을 눌러 인코딩을 시작한다.

 

영상이 매우 빠르게 지나가며 인코딩이 될 것이다.

 

인코딩이 끝난 후 원본영상이 있는 폴더에 변환된 미디어가 생성되었다. 이를 Windows NT 서버에 게시한다. C:\InetPub\mediaroot 위치에 전송하면 된다.

 

클라이언트의 RealPlayer를 실행하여 pnm://192.168.2.9/mediaroot/psy-gangnamstyle.rm을 접속한다. 잘 재생됨을 확인할 수 있다.

 

카테고리 “Operating System/Windows NT”
more...
썸네일 이미지
Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1)
Windows NT 서버 운영 본 포스팅은 Windows NT 서버를 운영하는 각종 예에 대한 정리이다. RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1) 리얼플레이어(RealPlayer)는 자체적인 파일 포맷과 스트리밍 프로토콜을 지원해서 2000년대 초반까지 Windows Media Player와 함께 흔하게 쓰이던 오디오 및 비디오 플레이어이다. 이번 포스팅에서는 Windows NT 서버에 RealServer 8.0을 설치하여 스트리밍 서버를 구성하고 클라이언트에서 스트리밍으로 동영상으로 재생해보는 방법에 대해 소개한다. Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1) Win..
Operating System/Windows NT
2018. 12. 21. 20:30

Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1)

Operating System/Windows NT
2018. 12. 21. 20:30

Windows NT 서버 운영


본 포스팅은 Windows NT 서버를 운영하는 각종 예에 대한 정리이다.

 

RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1)


리얼플레이어(RealPlayer)는 자체적인 파일 포맷과 스트리밍 프로토콜을 지원해서 2000년대 초반까지 Windows Media Player와 함께 흔하게 쓰이던 오디오 및 비디오 플레이어이다. 이번 포스팅에서는 Windows NT 서버에 RealServer 8.0을 설치하여 스트리밍 서버를 구성하고 클라이언트에서 스트리밍으로 동영상으로 재생해보는 방법에 대해 소개한다.

 

  1. Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 1)
  2. Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) [完]

 

0. 준비하기


Windows NT 서버에 RealServer를 운영하기 위해서 준비해야 할 것들이다.

  • 1. Windows NT가 설치된 실제 서버 또는 가상 서버
  • 2. Windows 98, Me 등이 설치된 실제 클라이언트 또는 가상 클라이언트
  • 3. 서버에 설치할 RealServer 8.0와 클라이언트에 설치할 RealPlayer 8.0

 

1, 2번의 경우 설치파일은 토렌트 또는 WinWorld PC(https://winworldpc.com/home) 등의 아카이브 사이트를 쉽게 구할 수 있다. 3번의 경우 구하기 힘들다. 특히 RealServer 8.0은 극히 레어한 자료이므로 더욱 그렇다. 하지만 본 블로그는 어렵게 준비하여 올린다.

아래의 압축 파일을 다운로드하여 해제한다. 압축 해제 암호는 codingCat.kr이다.

  • RealMedia.part1.rar

    MD5
    a134b8236fe5baf9b7f08a88ccc1c9ec
    SHA1
    4a157b76de13fdb048b136fb1ca73bb3c544db26
    SHA256
    175c2446a0763d598b9cd9a44ae15984bf8967a866b278c41150ee361e5c2f5a
    SHA512
    660fc26fc7304d9645ffee4502e93d84c123e92bf7b8f287cfec241eb3eca8bd12972d275be446f7fe543d35564e3251e84868c6685ddddae88cf3f02f3bfc5c
  • RealMedia.part2.rar

    MD5
    1117bcbae5ccd49e3fe9a6c6ccd5fccc
    SHA1
    6c5b71dfe6442feaf3ba81cfd84c50e240c242e9
    SHA256
    582950bf7e6adcae0552ce00516eaf1709c56922ed7cce1cfd5e520576a890e6
    SHA512
    b331173a3a1e3bd5a082b095ece6e5b11f956dffa8066574cf7d95407254af9eecc2ce22f18658f6fff5d80f5d362e68b2403db0642a49a58b9420a7fc93ab10
  • RealMedia.part3.rar

    MD5
    8e0a91caa7685e7c471859a963e77c02
    SHA1
    17525a1ae3cef6652b4306e3f057cbdea9cc7885
    SHA256
    057c201d2dfb5d78110782afe4d59f8ae8e75601abd39c7f1a90c1709a8d0651
    SHA512
    e6ea14d4492d2f5e9e266b677df8f04fbabe49664a16a5b3461924b7ebb54553cd8c65984ebce8f1e00b7b3d97751dca7fddb02f88e646206990be0224585af5
  • RealMedia.part4.rar

    MD5
    ae4f071008afa308311c3bdeafa757ed
    SHA1
    1664933efd1015f05dddfa9dc6527fd3fa9f4c6b
    SHA256
    2b9b4e41687eb794dc99849a5b7fa8dd6667966f024891a63ad276ba681c3861
    SHA512
    ff45856fd3e73c370d5bbdaccbc42118721b59f544a5ed852f87a504ebc1f246fcbca55e42795c40a27a2fb5e0ecdfa9ce4bafae1d9e78ff5e236b52aef90607

 

압축을 해제하면 총 8개의 파일이 있다.

license1.lic
1997년 1월 1일 ~ 2010년 3월 8일 사이에서 사용 가능한 RealServer 8.0의 라이선스 파일이다.
license2.lic
2000년 8월 1일 ~ 2030년 12월 31일 사이에서 사용 가능한 RealServer 8.0의 라이선스 파일이다.
rs801winnt.exe
Windows NT용 RealServer 8.0의 설치파일이다.
rp8en584.exe
RealPlayer 8.0 영어 버전의 설치파일이다.
rp8kr450.exe
RealPlayer 8.0 한국어 버전의 설치 파일이다. 영어 또는 한국어 둘 중 하나만 설치하면 된다.
rd8en200.exe
RealProducer 8.5 Basic 설치파일이다. 녹화, 녹음, 파일 변환 등의 작업을 여기에서 수행하면 된다. (일부 기능은 제한됨.)
rd8en851.exe
RealProducer 8.51 Plus 설치파일이다. 녹화, 녹음, 파일 변환 등의 작업을 여기에서 수행하면 된다. (기능 제한 없음.)
rd8en851.txt
RealProducer 8.51 Plus의 시리얼이다.

 

날짜가 경과하여도 다음과 같이 Windows NT 서버의 날짜를 변경한다면, 라이선스 파일을 계속 사용할 수 있다.

 

1. Windows NT 서버에 RealServer 8.0 설치하기


첨부한 파일의 압축을 풀어 rs801winnt.exe를 실행한다. 설치 마법사가 뜨면 [Next]를 클릭하여 설치를 시작한다.

 

라이선스 파일의 위치를 묻는다. [Browse...]를 눌러 동봉된 lic 파일을 선택한다.

 

라이선스 파일의 정보를 확인 후 [Accept >]를 누른다.

 

RealServer 8.0이 설치될 경로를 지정한다.

 

설치 후 관리 화면에 접속하기 위한 ID와 암호를 지정한다.

 

PNA 방식으로 스트리밍 서비스를 제공하기 위한 포트를 지정한다. 기본 값은 7070이다.

 

RTSP 방식으로 스트리밍을 제공하기 위한 포트 번호를 지정한다. 기본 값은 554이다.

 

HTTP 방식으로 접속하여 미디어를 제공하기 위한 포트를 설정한다. 기본 값은 8080이다.

 

RealServer 8.0의 관리 화면은 웹 브라우저를 통해 접속이 가능하다. 이 관리 화면에 접속하기 위한 포트를 지정한다. 이 값은 임의로 지정되며, 기존에 사용중인 포트와 충돌하면 안 된다.

 

RealServer 8.0을 NT 서비스로 시작할 것인지 여부를 묻는다. 서비스의 형태로 실행하면, 별도의 콘솔 창이 떠 있지 않고도 RealServer가 실행을 유지할 수 있다. 서버를 시작할 때마다 서비스 형태로 실행되게 하려면 이를 체크한다.

 

설정한 사항이 맞는지 확인 후 [Continue]를 누른다.

 

설치가 완료되었다 [OK]를 누른다.

 

시작메뉴와 바탕화면에 RealServer 항목이 추가되었다. 이제 관리자 화면으로 접속하기 위해 [RealServer 8.0 Administrator]를 클릭한다.

 

로그인 화면이 나타나면 설치 마법사에서 입력했던 관리자 아이디와 암호를 입력한다.

 

2. 관리자 화면 둘러보기 및 마운트 지점 설정하기


바탕화면의 RealServer 아이콘 또는 시작메뉴를 통해 RealServer 8.0 아이콘을 실행하면, 콘솔창만 떠 있거나 아무 반응도 없을 것이다. RealServer 그 자체는 백그라운드로 실행되기 때문에 그렇다. RealServer 8.0의 관리화면을 접속하면 다음과 같은 화면이 나타난다.

 

설치 당시 지정했던 포트를 바꾸고 싶다면 왼쪽 메뉴에서 [General Setup] > [Ports] 항목을 클릭한다. 아래와 같이 포트를 수정하고 [Apply] 버튼을 누르면 된다.

 

[General Setup] > [Mount Points] 메뉴를 접속하면 스트리밍 서버의 마운트 위치를 설정할 수 있다. 적당한 폴더를 마운트하여 미디어 서버를 시험한다.

 

C:\InetPubpnmroot라는 폴더를 하나 만든다.

 

C:\Program Files\Real\Content에 샘플 오디오와 비디오 파일들이 있는데 이들을 C:\InetPub\mediaroot에 복사한다.

 

다시 관리화면으로 돌아온다. "Mount Points" 부분에 있는 [Add New]를 클릭하면 마운트 위치가 하나 생긴다. 오른쪽에서 "Edit Mount Point"의 텍스트 박스를 /pnmroot로 수정 후 [Edit]를 누르면 마운트 지점이 변경되는 것을 확인할 수 있다. 그 다음 "Description"에는 간단한 설명을 붙이고, "BasePath"에는 실제 경로를 지정한다. 여기에서는 C:\InetPub\pnmroot를 스트리밍으로 내보낼 것이므로 그대로 경로를 적는다. 그 후 [Apply]를 누른다.

 

이런 화면이 뜨고 상태가 "Succeeded"라고 나타나면 마운트 지점 편집에 성공한 것이다. [Close]를 누른다.

 

변경 상태를 적용하려면 화면상단의 [Pending Changes]를 눌러서 단순 적용만 할 수도 있고 [Restart Server]를 눌러서 서버 재시작을 할 수도 있다. 여기서는 서버 재시작을 해 보겠다.

 

접속자가 0명임을 확인 후 [OK]를 클릭한다.

 

서버가 재시작되고 있다. 약 20초 후에 관리 화면으로 돌아간다.

 

3. 테스트하기


이제 관리 화면은 닫는다. 단 Windows NT 서버는 끄지 말고 계속 켜 둔다. 이 상태에서 클라이언트로 자리를 옮긴다. RealPlayer를 설치 후, 아래의 주소를 입력하고 [Enter]를 눌러 접속한다. 마운트 지점에 게시한 샘플 미디어 파일을 열어보는 과정이다.

pnm://서버주소:포트번호/pnmroot/real8video.rm

 

포트 번호는 관리화면에서 지정한 포트번호이며 기본값으로 둔 경우 생략 가능하다. 서버 주소는 Windows NT 서버의 콘솔창을 열고 "ipconfig /all"을 실행하면 확인 가능하다. 여기에서는 pnm://192.168.204.153:7070/pnmroot/realvideo8.rm으로 접속한다.

아래와 같이 약간의 버퍼링 후 동영상이 뜨면 스트리밍 서버가 정상적으로 작동됨을 확인할 수 있습니다.

 

Epilogue


이번 포스트에서는 Windows NT 서버에 RealServer를 설치하고 미디어를 게시한 후 클라이언트에서 이를 시험해보았다. 다음 포스트(Windows NT 서버 운영 - RealServer 8.0 설치하여 RealPlayer 스트리밍 서버 구성하기 (Part 2) [完])에서는 번외편으로 다른 미디어 형식을 RealMedia 포맷으로 변환하여 게시해보겠다.

 

카테고리 “Operating System/Windows NT”
more...
썸네일 이미지
단막 CentOS 활용법 - sudo 패스워드 입력 시 별표('*') 나타내기
단막 CentOS 활용법 본 포스팅은 CentOS 서버를 운영하는 각종 예에 대한 정리이다. sudo 패스워드 입력 시 별표('*') 나타내기 sudo는 관리자 권한 상승 명령이다. 이 명령을 실행할 때 관리자 계정으로 접근하기 위한 암호를 요구하는데, 특별한 설정을 고치지 않았다면 키보드를 누를 때마다 별표('*')가 나타나지 않아 다소 불편할 수 있다. 이에 본 포스팅에서는 sudo로 관리자 암호를 입력할 때 키보드 터치에 의해 별표('*')가 나타나도록 설정하는 방법을 정리한다. sudoer 파일 백업하기 /etc/sudoers의 내용을 수정함으로써 sudo로 암호 입력 시 별표가 나타나게 할 수 있다. 먼저 다음의 명령을 실행하여 sudoers 파일을 백업한다. $ sudo cp /etc/sudo..
Operating System/Unix × RedHat
2018. 12. 20. 10:42

단막 CentOS 활용법 - sudo 패스워드 입력 시 별표('*') 나타내기

Operating System/Unix × RedHat
2018. 12. 20. 10:42

단막 CentOS 활용법


본 포스팅은 CentOS 서버를 운영하는 각종 예에 대한 정리이다.

sudo 패스워드 입력 시 별표('*') 나타내기


sudo는 관리자 권한 상승 명령이다. 이 명령을 실행할 때 관리자 계정으로 접근하기 위한 암호를 요구하는데, 특별한 설정을 고치지 않았다면 키보드를 누를 때마다 별표('*')가 나타나지 않아 다소 불편할 수 있다. 이에 본 포스팅에서는 sudo로 관리자 암호를 입력할 때 키보드 터치에 의해 별표('*')가 나타나도록 설정하는 방법을 정리한다.

 

sudoer 파일 백업하기


/etc/sudoers의 내용을 수정함으로써 sudo로 암호 입력 시 별표가 나타나게 할 수 있다. 먼저 다음의 명령을 실행하여 sudoers 파일을 백업한다.

$ sudo cp /etc/sudoers /etc/sudoers.bak

 

sudoer 파일 수정하기


다음 관리자 권한으로 visudo를 실행한다. visudovi 에디터이기는 하나 sudoers 파일을 안전하게 편집하기 위한 특별한 명령어이다.

$ sudo visudo

 

sudoers 파일을 백업한 뒤 visudo로 이를 편집한다.

 

Preserving HOME has security...로 시작하는 영역을 찾아보면, 아래 그림과 같이 Defaults env_reset 항목이 있다. 이 곳을 수정한다.

편집할 부분이다.

 

이 줄의 끝에 , pwfeedback를 추가한다. 즉,

Defaults env_reset, pwfeedback

이 되도록 수정하면 된다.

줄 끝에 pwfeedback을 추가함.

 

쉘을 리셋하기


visudo를 종료하고 쉘shell을 리셋한다.

$ sudo reset
쉘을 리셋함.

 

설정 수정의 효과 확인


이후 sudo를 실행하면 다음과 같이 관리자 암호를 입력할 때 키보드 터치마다 별표('*')가 나타남을 확인할 수 있다.

sudoer 파일 수정 후 관리자 암호를 입력할 때마다 별표가 표시됨

 

썸네일 이미지
한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 3.0
한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) 한국(대한민국)에서는 전산처리 과정에서 한글을 부호화하기 위하여 유니코드, KS X 1001(구 KS C 5601), KS X 1002(구 KS C 5657) 등의 문자집합charset이 쓰인다면 북한('조선민주주의인민공화국')에서는 유니코드와 KPS-9566(국규 9566) 코드가 쓰인다. 한글 자모 영역에 특정 한글 음절('김'A4E8, '일'A4E9, '성'A4EA, '김'A4EB, '정'A4EC, '일'A4ED)이 중복하여 따로 배정(북에서는 이 공간을 "존(엄?)함" 영역이라 별도로 부름)되어 있다고 해서 일명 "김일성김정일" 코드라고도 부르는 이 한글 코드는 한글 자모의 순서가 남한과 다르기 때문에 '가'를 제외한 모든 글자가 한국..
Extras/Charset
2018. 12. 12. 21:53

한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 3.0

Extras/Charset
2018. 12. 12. 21:53

한글(조선글자) 완성형 코드표 KPS-9566(국규 9566)


한국(대한민국)에서는 전산처리 과정에서 한글을 부호화하기 위하여 유니코드, KS X 1001(구 KS C 5601), KS X 1002(구 KS C 5657) 등의 문자집합charset이 쓰인다면 북한('조선민주주의인민공화국')에서는 유니코드와 KPS-9566(국규 9566) 코드가 쓰인다.

한글 자모 영역에 특정 한글 음절('김'A4E8, '일'A4E9, '성'A4EA, '김'A4EB, '정'A4EC, '일'A4ED)이 중복하여 따로 배정(북에서는 이 공간을 "존(엄?)함" 영역이라 별도로 부름)되어 있다고 해서 일명 "김일성김정일" 코드라고도 부르는 이 한글 코드는 한글 자모의 순서가 남한과 다르기 때문에 '가'를 제외한 모든 글자가 한국의 완성형 코드와 호환되지 않는다는 가장 중요한 특징을 갖고 있다.

한국의 한글 자음: ㄱ, ㄲ, ㄴ, ㄷ, ㄸ, ㄹ, ㅁ, ㅂ, ㅃ, ㅅ, ㅆ, ㅇ, ㅈ, ㅉ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ

한국의 한글 모음: ㅏ, ㅐ, ㅑ, ㅒ, ㅓ, ㅔ, ㅕ, ㅖ, ㅗ, ㅘ, ㅙ, ㅚ, ㅛ, ㅜ, ㅝ, ㅞ, ㅟ, ㅠ, ㅡ, ㅢ, ㅣ

북한의 한글(조선글자) 자음: ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ, ㄲ, ㄸ, ㅃ, ㅆ, ㅉ, ㅇ

북한의 한글(조선글자) 모음: ㅏ, ㅑ, ㅓ, ㅕ, ㅗ, ㅛ, ㅜ, ㅠ, ㅡ, ㅣ, ㅐ, ㅒ, ㅔ, ㅖ, ㅚ, ㅟ, ㅢ, ㅘ, ㅝ, ㅙ, ㅞ

이 외에도 특수 문자 중 일부는 북한 내에서나 통용될 법한 문자(예: 조선로동당 마크(ACA1, ACA2))들이 배정되어있고, 한자의 순서도 우리나라와는 많이 다름을 알 수 있다.

 

  1. 한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 2.0
  2. 한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 3.0 [完]

 

붉은별 3.0에서 지원하는 국규 9566 코드


본 포스트에서는 북한의 자체개발(?)로 만들었다는 운영체제인 붉은별 3.0(RedStar 3.0)을 기준으로 하여 이 운영체제에서 지원하는 국규 9566 한글 코드에 대해 다룬다. 보통의 2바이트 문자셋과 마찬가지로 0xA0 - 0xFF행과 0xA0 - 0xFF열에서 정의되며 실질적으로 문자가 배치되는 영역은 총 94행 x 94열로 되어 있다.

이전 버전인 붉은별 2.0에 내장된 문자코드와 비교했을 때 큰 차이는 없다. 특수 문자 영역에서 AEA0-AEAF 부분에 있던 문자가 삭제되었고, '존함' 영역에 선대 국왕의 이름을 이어 '그 사람' 이름이 추가된 것이 특징이다.

 

국규 9566 한글 코드 표 (Part 1/4: A0A0 - CFCF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 2/4: A0D0 - CFFF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 3/4: D0A0 - FFCF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 4/4: D0D0 - FFFF)


클릭하면 큰 화면으로 볼 수 있음.

 

카테고리 “Extras/Charset”
more...
썸네일 이미지
한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 2.0
한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) 한국(대한민국)에서는 전산처리 과정에서 한글을 부호화하기 위하여 유니코드, KS X 1001(구 KS C 5601), KS X 1002(구 KS C 5657) 등의 문자집합charset이 쓰인다면 북한('조선민주주의인민공화국')에서는 유니코드와 KPS-9566(국규 9566) 코드가 쓰인다. 한글 자모 영역에 특정 한글 음절('김'A4E8, '일'A4E9, '성'A4EA, '김'A4EB, '정'A4EC, '일'A4ED)이 중복하여 따로 배정(북에서는 이 공간을 "존 엄 함" 영역이라 별도로 부름)되어 있다고 해서 일명 "김일성김정일" 코드라고도 부르는 이 한글 코드는 한글 자모의 순서가 남한과 다르기 때문에 '가'를 제외한 모든 글자가 한국의..
Extras/Charset
2018. 12. 12. 21:11

한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 2.0

Extras/Charset
2018. 12. 12. 21:11

한글(조선글자) 완성형 코드표 KPS-9566(국규 9566)


한국(대한민국)에서는 전산처리 과정에서 한글을 부호화하기 위하여 유니코드, KS X 1001(구 KS C 5601), KS X 1002(구 KS C 5657) 등의 문자집합charset이 쓰인다면 북한('조선민주주의인민공화국')에서는 유니코드와 KPS-9566(국규 9566) 코드가 쓰인다.

한글 자모 영역에 특정 한글 음절('김'A4E8, '일'A4E9, '성'A4EA, '김'A4EB, '정'A4EC, '일'A4ED)이 중복하여 따로 배정(북에서는 이 공간을 "존

함" 영역이라 별도로 부름)되어 있다고 해서 일명 "김일성김정일" 코드라고도 부르는 이 한글 코드는 한글 자모의 순서가 남한과 다르기 때문에 '가'를 제외한 모든 글자가 한국의 완성형 코드와 호환되지 않는다는 가장 중요한 특징을 갖고 있다.

한국의 한글 자음: ㄱ, ㄲ, ㄴ, ㄷ, ㄸ, ㄹ, ㅁ, ㅂ, ㅃ, ㅅ, ㅆ, ㅇ, ㅈ, ㅉ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ

한국의 한글 모음: ㅏ, ㅐ, ㅑ, ㅒ, ㅓ, ㅔ, ㅕ, ㅖ, ㅗ, ㅘ, ㅙ, ㅚ, ㅛ, ㅜ, ㅝ, ㅞ, ㅟ, ㅠ, ㅡ, ㅢ, ㅣ

북한의 한글(조선글자) 자음: ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅈ, ㅊ, ㅋ, ㅌ, ㅍ, ㅎ, ㄲ, ㄸ, ㅃ, ㅆ, ㅉ, ㅇ

북한의 한글(조선글자) 모음: ㅏ, ㅑ, ㅓ, ㅕ, ㅗ, ㅛ, ㅜ, ㅠ, ㅡ, ㅣ, ㅐ, ㅒ, ㅔ, ㅖ, ㅚ, ㅟ, ㅢ, ㅘ, ㅝ, ㅙ, ㅞ

이 외에도 특수 문자 중 일부는 북한 내에서나 통용될 법한 문자(예: 조선로동당 마크(ACA1, ACA2))들이 배정되어있고, 한자의 순서도 우리나라와는 많이 다름을 알 수 있다.

 

  1. 한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 2.0
  2. 한글(조선글자) 완성형 코드표 KPS-9566(국규 9566) - 붉은별 3.0 [完]

 

붉은별 2.0에서 지원하는 국규 9566 코드


본 포스트에서는 북한의 자체개발(?)로 만들었다는 운영체제인 붉은별 2.0(RedStar 2.0)을 기준으로 하여 이 운영체제에서 지원하는 국규 9566 한글 코드에 대해 다룬다. 보통의 2바이트 문자셋과 마찬가지로 0xA0 - 0xFF행과 0xA0 - 0xFF열에서 정의되며 실질적으로 문자가 배치되는 영역은 총 94행 x 94열로 되어 있다.

 

국규 9566 한글 코드 표 (Part 1/4: A0A0 - CFCF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 2/4: A0D0 - CFFF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 3/4: D0A0 - FFCF)


클릭하면 큰 화면으로 볼 수 있음.

 

국규 9566 한글 코드 표 (Part 4/4: D0D0 - FFFF)


클릭하면 큰 화면으로 볼 수 있음.

 

카테고리 “Extras/Charset”
more...

“분류 전체보기” (134건)