RSS구독하기:SUBSCRIBE TO RSS FEED
즐겨찾기추가:ADD FAVORITE
글쓰기:POST
관리자:ADMINISTRATOR
HTML  2007/05/25 14:46

메타 태그(Meta Tag)란?

이름에서 짐작할 수 있듯 (x)html 태그 중 하나이고 '메타', 즉, 문서 그 자체를 설명하는 태그를 메타 태그라 합니다. 일반 (x)html 태그는 문서 내용물을 어떻게 보이게 하는가를 규정하기 위해 마크 업을 합니다. 이와 달리 메타 태그는 (x)html 문서가 어떤 내용을 담고 있고, 그 문서의 핵심어는 무엇이며, 누가 만들었는지, 문자 세트는 어떤 것을 사용하는지 등의 메타 정보를 담고 있습니다.

메타 태그는,

  1. "empty tag" 중의 하나입니다. <b>볼드체</b>처럼 열고 닫는 태그가 다 있는것이 아니라 닫는 태그는 없습니다. 즉, </meta>라는 태그는 없습니다. 그러므로 xhtml의 경우 <meta ... />처럼 자체 내에서 /로 닫으면서 마무리되어야 합니다.
  2. 반드시 <head>~</head> 사이에 놓입니다.

메타 태그의 형태

메타 태그는,

<meta 속성="값" content="내용물" />

의 형태를 갖습니다.

예를 들면,

  • <meta name="author" content="이명헌" />
  • <meta name="title" content="메타 태그란?" />
  • <meta http-equiv="refresh" content="5;url=http://www.abc.com/" />

"속성 = 값", 그리고 "content = 내용물"입니다. 자주 들르는 웹 페이지의 '소스 보기'를 해보면 문서 서두에 있는 메타 태그를 확인해 볼 수 있습니다. 지금 이 페이지를 확인해 볼 수도 있습니다.

메타 태그의 용도

기본적으로 (x)html 문서를 만들 때는 항상 메타 태그를 써주는 것이 좋습니다. 그 문서가 어떤 내용을 담고 있는지, 키워드는 무엇인지를 요약해 둔 내용이 있다면 검색을 하는 데 많은 도움이 되기 때문입니다. 대개 다음의 용도로 많이 쓰입니다.

검색 엔진에 등록할 때


<meta name="keywords" content="코미디 역사 코메디 발생 기원 희극" />
<meta name="description" content="코미디의 역사를 소개하는 글" />

예를 들어, 코미디의 역사를 소개하는 웹페이지라면 위와 같이 그 문서의 키워드인 '코미디', '역사' 같은 단어를 "keywords" 메타 태그에 담고 '코미디의 역사를 소개하는 글'이라는 (x)html 문서 소개를 "description" 메타 태그에 담습니다. 이들 메타 태그를 그 (x)html 문서의 <head>~</head> 사이에 넣어 두는 것입니다. 검색 엔진이 웹 문서를 검색할 때 이들 "keywords", "description" 메타 태그를 통해 검색하는 경우가 있기 때문입니다. 검색엔진에서 검색어를 입력하고 엔터를 치면 검색엔진은 입력한 검색어와 같은 단어가 keywords, description 메타 태그 내에 등장하는 문서를 찾아내서 검색 결과를 통해 보여줍니다.

성인물을 다루는 사이트의 경우 메타 태그를 남용해서 keywords에 sex 관련 단어를 많이 넣어 두기도 합니다. 그렇게 하면 sex 관련 단어 검색 시 자신의 페이지가 결과로 뜰 것이라고 믿기 때문입니다. 하지만 이제는 이런 용도의 메타 테그 활용은 거의 의미가 없습니다. 메타 태그를 상업적으로 '악용'한 페이지가 너무 많아서 제대로 된 검색 결과를 얻지 못한다는 점을 깨달은 검색엔진 회사들이(특히 구글처럼 링크 구조(Link structure) 기반의 자동화된 검색엔진) 메타 태그를 거의 무시하고 있기 때문입니다.

그런 한계에도 불구하고 메타 태그가 없는 것보다는 있는 쪽이 검색에 더 도움이 되므로 적절히 사용하는 것이 좋습니다. 이런 점은 주의해야 합니다. description 메타 태그 내에 페이지 요약을 넣을 때 가급적 20단어를 넘지 않아야 합니다. 검색 엔진은 description을 적당한 크기까지만 읽고 나머지는 임의로 무시합니다. 그리고, 검색 엔진은 메타 태그와 함께 웹 페이지의 제목(<title>에 담긴) 역시 중요하게 취급합니다. (x)html 문서 제목을 결정할 때 가급적 신중하게 하세요. URL도 마찬가지입니다. 한글 싸이트와 직접적 연관은 없긴 하지만 keywords 메타 태그의 경우 대소문자를 별개로 취급하기 때문에 구분해서 실을수록 좋습니다.

검색 엔진 피하기

검색엔진 회사가 사용하는 웹 크롤러(Web crawler)는 웹 상의 여러 문서들을 긁어서 문서를 적당한 형태로 저장합니다. 이 때 keywordsdescription 등이나 문서 길이, url, 제목 등의 메타 정보를 따로 관리하며 저장하지만 (x)html 문서 자체도 모두 긁어서 저장합니다.

일반적인 램보다 속도가 훨씬 빠른 캐쉬 메모리(cache memory)가 자주 하는 작업을 저장하고 있다가 cpu에서 요청이 오면 즉각 보내서 컴퓨터 작업 속도를 늘려주는 것처럼, 웹 크롤러가 긁어 온 웹 페이지 내용 역시 검색엔진의 모처에 캐쉬 형태로 저장해서 매 검색 시마다 웹 전체를 뒤지지 않고 저장된 캐쉬를 검색해서 빠른 속도로 검색 결과를 보여줍니다.(구글의 '캐쉬'가 그것입니다.) 검색엔진 회사는 이렇게 웹 페이지를 긁어 오는 작업을 정기적으로 하고 있으며 크롤링을 자주 하는 곳일수록 캐쉬 내에 최근 내용이 저장되어 있으므로 보다 최신의 검색결과를 보여줍니다.

문제는 웹에 이런 웹 크롤러(web crawler)들이 아주 많이 떠돌아 다니고 있어서 외부에 노출하고 싶지 않은 문서까지 공개되고 심지어 저장될 수 있다는 사실입니다. 그것을 해결하는 것이 robots라는 메타 태그입니다.

<meta name="robots" content="noindex, nofollow" />

name= 자리에 robots가 들어가 있고 content에는 noindex, nofollow라고 되어 있습니다. 이것에 대해 자세하게 알아 보기 전에 웹 크롤러가 어떻게 동작하는지 간단하게 알아 봅시다. 웹 크롤러가 최초 웹 서버에 도착하면 그 웹 서버 문서 디렉토리의 최상위(/)에 "robots.txt" 라는 문서가 있는지 점검 합니다. 만약 robots.txt라는 문서가 있고, 그 문서 안에


User-agent: *
Disallow: /

라는 내용이 담겨 있다면 /(문서 루트 디렉토리) 밑으로는 disallow, 즉, 허락하지 않는 것이므로 그 사이트의 문서를 긁지 않고 떠납니다.

그런데 이것은 실용성이 떨어집니다. 감추고 싶은 문서도 있긴 하지만 검색 엔진에 올려 두고 싶은 문서가 훨씬 더 많을 수 있으니까요. 제일 상위인 / 디렉토리에서 웹 크롤러를 차단하는 것은 사이트 전체를 건너 뛰게 하므로 실용성이 없습니다. 또 하나 문제가 되는 것은 독립적인 웹 서버를 사용하지 않고 웹호스팅 서비스나 계정을 사용하고 있는 경우 "Document Root"에 robots.txt 문서를 넣어두기는 힘들다는 점입니다.

그럴 때 사용하는 것이 robots 메타 태그입니다. robots 메타 태그는 웹크롤러가 긁어갈 것인지 말 것인지를 개별 문서 별로 설정할 수 있습니다. 사용법은 간단합니다. robots 메타 태그의 contentindex이면 그 페이지는 긁어 갑니다. contentfollow면 그 페이지에 나온 모든 링크를 찾아 가서 링크된 문서도 긁어 갑니다. noindex면 그 페이지는 긁어가지 않습니다. nofollow면 링크를 확인해서 긁어가는 것을 건너 뜁니다.

즉, 다음과 같은 네 가지의 조합이 나옵니다. 목적에 맞게 사용하면 됩니다.


<meta name="robots" content="index,follow" />
: 이 문서도 긁어가고 링크된 문서도 긁어감.
<meta name="robots" content="noindex,follow" />
: 이 문서는 긁어가지 말고 링크된 문서만 긁어감.
<meta name="robots" content="index,nofollow" />
: 이 문서는 긁어가되, 링크는 무시함.
<meta name="robots" content="noindex,nofollow" />
: 이 문서도 긁지 않고, 링크도 무시함.

웹 문서 주소가 바뀐 경우

<meta http-equiv="refresh" content="5;url=http://www.abc.com/" />

이런 것 많이 보았을 것입니다.

"이 페이지는 10초 후에 자동으로 새 페이지로 이동합니다. 아무 변화가 없으면 밑의 링크를 클릭하세요."

이것을 할 때 메타 태그를 사용합니다. 위 코드에서 content= 다음에 몇 초만에 넘어가게 할 것인가 숫자를 적고 콜론(;)을 한 다음 url=에 이동할 페이지의 url을 적으면 됩니다. 간단하죠? 이 한줄을 예전 (x)html 문서 <head>~</head> 사이에 넣어 두면 지정된 시간 경과후 새 url로 자동으로 넘어갑니다. 시간은 0.1, 0.5등 1초 미만도 가능합니다. 즉시 넘어 가게 할 때는 1초 미만으로 하면 됩니다.

만약 url을 원래 페이지와 똑같이 해 놓으면 어떻게 될까요? 몇 초마다 그 페이지가 '다시보기'(reload)됩니다. 그럴 필요가 있을 지는 모르겠지만 원한다면 그렇게 사용할 수도 있습니다.

한글 페이지라는 사실을 알릴 때

<meta http-equiv="content-type" content="text/html; charset=euc-kr" />

웹 페이지를 디스플레이 하는 문자 세트(character set)를 지정할 때 메타 태그를 사용합니다. 사용자 웹 브라우져에서 인코딩 방식을 특별히 한글로 해놓지 않더라도 위와 같은 메타 태그가 들어있는 웹 페이지는 한글 문자 세트를 이용해서 한글로 보여집니다. 이 메타 태그 역시 필수적으로 넣어두면 좋습니다. 특히 외국에서 영문 웹 브라우져로 접속하는 사람들을 위해서입니다. 영어 웹 사이트를 위주로 써핑하는 사람의 경우 브라우져 문자 세트를 영어로 해 놓고 있을 가능성이 높습니다.

http-equiv란?

위에서 http-equiv라는 것이 나왔는데 이게 무엇일까요? http로 나누는 대화라는 글에서 http 헤더에 관해 자세하게 설명할 때, 서버가 클라이언트로부터 온 http request를 접수한 다음 http response를 다시 웹 브라우져로 보내 준다는 얘기가 있었습니다. 그 http response의 헤더를 각 (x)html 문서 별로 따로 설정할 수 있는 것이 http-equiv 메타 태그라고 이해하면 됩니다.

일반적인 순서는 다음과 같습니다.

사용자가 특정 문서로의 링크를 클릭
--> 웹 브라우져에서 생성된 http request를 웹 서버로 보냄
--> 웹 서버에서 접수한 뒤 http response와 함께 (x)html 파일(또는 그래픽, 싸운드...)을 TCP/IP를 이용해 전달
--> 웹 브라우져에서는 http response에 설정된 대로 전달받은 파일을 염.

이 때 전달받은 파일을 http 리스판스 헤더에 설정된 대로 열면서 개별 (x)html 문서에서 설정해 둔 http-equiv 메타 태그에 있는 내용 역시 써버에서 보낸 http 헤더에 첨가합니다.

<meta http-equiv="content-type" content="text/html; charset=euc-kr" />

라면 웹 서버에서 클라이언트로 보내는 http 헤더에 있는 Content-type 이라는 마임 타입을 설정하는 부분을 http-equiv 메타 태그를 활용해서 개별 문서 별로 설정하는 것입니다.

2007/05/25 14:46 2007/05/25 14:46
이 글에는 트랙백을 보낼 수 없습니다
HTML  2007/05/25 12:36

HTML이나 XHTML문서는 문서 상단에 Document Type Definition을 넣어 문서의 형을 선언하게 된다. 기본적으로 이 DTD에는 문서에서 사용하는 태그나 속성들이 명시되어 있고 Validator는 이 DTD정보를 기본으로 하여 문서의 문법을 검사하게 된다. 모든 마크업 문서는 이 DTD선언을 명시해 주어야만 한다.

DTD의 종류

웹에서 주로 사용하는 DTD는 HTML 4.01과 XHTML 1.0, XHTML 1.1이 있다. XHTML DTD는 HTML의 문법에 XML의 문법 규칙을 적용한 DTD이다. Transitional DTD는 디자인적인 속성이 포함된 DTD이고 Strict DTD는 디자인 적인 속성이 모두 제거된 형식이다. <a>에 target속성을 이용하거나 td에 width, height등의 속성을 사용하기 위해서는 transitional DTD를 사용해야 한다. Frameset DTD는 transitional DTD에 프레임을 사용할 수 있게 프레임 관련된 선언이 포함된 DTD이다.

HTML 4.01 Strict DTD
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
HTML 4.01 Transitional DTD
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
HTML 4.01 Frameset DTD
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
XHTML 1.0 Strict DTD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
XHTML 1.0 Transitional DTD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
XHTML 1.0 Frameset DTD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
XHTML 1.1 DTD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

DTD의 선택 기준

문 서의 DTD를 정하는 것은 특별한 기준이 있는 것은 아니고 전적으로 저자의 선택을 따르게 된다. 문서를 저작함에 있어서 목적과 향후 관리 측면을 고려하여 DTD를 정하면 된다. 예를 들어서 디자인 적인 요소가 필요하고 XML문법을 완벽하게 따라서 운영, 관리가 될 수 없는 페이지라면 XHTML DTD를 선언하는 것은 무의미 하므로 HTML DTD를 선언하여 사용하는 것이 바람직하다.

HTML 4.01 Transitaional이라고 해서 XHTML 1.1보다 못하거나 수준이 낮은, 또는 웹표준이 아니라고 말할 수는 없다. DTD 선택에 제약을 두는 것 보다는 상황에 맞는 DTD를 선택하고 선택한 DTD를 완벽하게 준수하는 것이 더욱 중요하다.

처음 접근하기 쉬운 DTD

웹표준을 처음 접하는 입장에서는 XHTML을 선택 하는 것 보다는 HTML 4.01 Transitional을 선택하고 validation에 신경을 쓰는 것이 더 좋은 선택이다.

mime-type과 DTD

XHTML의 경우 문서의 mime-type이 application/xhtml+xml로 제공이 되어야 한다. XHTML 1.0에서는 하위 호환성을 위해서 text/html로 전송하는 것도 허용하고는 있지만 XHTML1.1은 반드시 application/xhtml+xml로 배포 되어야 한다. 이러한 의미에서 본다면 XHTML보다는 HTML 4.01 Strict가 더 좋은 선택이 될 수도 있다.

2007/05/25 12:36 2007/05/25 12:36
이 글에는 트랙백을 보낼 수 없습니다
0 0x0000 작업을 완료했습니다.
1 0x0001 올바르지 않은 함수입니다.
2 0x0002 지정한 파일을 찾을 수 없습니다.
3 0x0003 지정한 경로를 찾을 수 없습니다.
4 0x0004 파일을 열 수 없습니다.
5 0x0005 접근이 거부되었습니다.
6 0x0006 잘못된 핸들입니다.
7 0x0007 저장 컨트롤 블록이 손상되었습니다.
8 0x0008 저장 공간이 부족해서 이 명령을 수행할 수 없습니다.
9 0x0009 저장 컨트롤 블록 주소가 올바르지 않습니다.
10 0x000A 환경이 올바르지 않습니다.
11 0x000B 잘못된 형식의 프로그램을 로드하려고 했습니다.
12 0x000C 액세스 코드가 올바르지 않습니다.
13 0x000D 데이터가 올바르지 않습니다.
14 0x000E 저장 공간이 부족해서 이 작업을 완료할 수 없습니다.
15 0x000F 지정한 드라이브를 찾을 수 없습니다.
16 0x0010 디렉터리를 삭제할 수 없습니다.
17 0x0011 파일을 다른 디스크 드라이브로 이동할 수 없습니다.
18 0x0012 더 이상 파일이 없습니다.
19 0x0013 매체가 쓰기 금지되었습니다.
20 0x0014 지정한 장치를 찾을 수 없습니다.
21 0x0015 장치가 준비되지 않았습니다.
22 0x0016 장치가 명령을 인식하지 않습니다.
23 0x0017 데이터 오류 (주기적 중복 검사)
24 0x0018 프로그램에서 명령을 내렸으나 명령 길이가 올바르지 않습니다.
25 0x0019 드라이브에서 디스크의 특정 영역이나 트랙의 위치를 지정할 수 없습니다.
26 0x001A 지정한 디스크나 디스켓을 액세스할 수 없습니다.
27 0x001B 드라이브에서 요청한 섹터를 찾을 수 없습니다.
28 0x001C 프린터에 용지가 떨어졌습니다.
29 0x001D 지정한 장치에 쓸 수 없습니다.
30 0x001E 지정한 장치로부터 읽을 수 없습니다.
31 0x001F 시스템에 부착된 장치가 작동하지 않습니다.
32 0x0020 다른 프로세스에서 이 파일을 사용하고 있기 때문에 이 파일을 액세스할 수 없습니다.
33 0x0021 다른 프로세스에서 이 파일의 일부를 잠궜기 때문에 이 파일을 액세스할 수 없습니다.
34 0x0022 드라이브에 잘못된 디스켓이 있습니다. Ŵ (볼륨 일련 번호: ŵ)을(를) inų 드라이브에 넣으십시오.
36 0x0024 공유하기 위해 연 파일이 너무 많습니다.
38 0x0026 파일의 끝입니다.
39 0x0027 디스크가 가득 차 있습니다.
50 0x0032 네트워크 요청이 지원되지 않습니다.


51 0x0033 원격 컴퓨터를 사용할 수 없습니다.
52 0x0034 네트워크에 중복된 이름이 있습니다.
53 0x0035 네트워크 경로를 찾을 수 없습니다.
54 0x0036 네트워크가 사용 중입니다.
55 0x0037 지정한 네트워크 리소스 또는 장치를 더 이상 사용할 수 없습니다.
56 0x0038 네트워크 BIOS 명령 한계에 도달했습니다.
57 0x0039 네트워크어댑터 하드웨어 오류가 발생했습니다.
58 0x003A 지정한 서버에서 요청한 작업을 수행할 수 없습니다.
59 0x003B 예기치 않은 네트워크 오류가 발생했습니다.
60 0x003C 원격 어댑터가 호환되지 않습니다.
61 0x003D 프린터 대기열이 가득 찼습니다.
62 0x003E 서버에 인쇄 대기 중인 파일을 저장할만한 공간이 없습니다.
63 0x003F 인쇄 대기 중인 파일이 삭제되었습니다.
64 0x0040 지정한 네트워크 이름을 더 이상 사용할 수 없습니다.
65 0x0041 네트워크 접근이 거부되었습니다.
66 0x0042 네트워크 리소스 종류가 올바르지 않습니다.
67 0x0043 네트워크 이름을 찾을 수 없습니다.
68 0x0044 지역 컴퓨터 네트워크 어댑터 카드의 이름 한계를 초과했습니다.
69 0x0045 네트워크 BIOS 세션 한계를 초과했습니다.
70 0x0046 원격 서버가 일시 중지되었거나 프로세스가 시작 중입니다.
71 0x0047 수용할 수 있는 최대 개수의 연결이 이미 있으므로 더 이상 연결을 작성 할 수 없습니다.
72 0x0048 지정한 프린터나 디스크 장치가 일시 중지되었습니다.
80 0x0050 파일이 존재합니다.
82 0x0052 디렉터리나 파일을 작성할 수 없습니다.
83 0x0053 INT 24에서 오류
84 0x0054 이 요청을 처리할만한 저장 공간이 없습니다.
85 0x0055 지역 장치 이름이 이미 사용 중입니다.
86 0x0056 지정한 네트워크 암호가 올바르지 않습니다.
87 0x0057 매개 변수가 올바르지 않습니다.
88 0x0058 네트워크에서 쓰기 오류가 발생했습니다.
89 0x0059 지금 다른 프로세스를 시작할 수 없습니다.
100 0x0064 다른 시스템 신호기를 작성할 수 없습니다.


101 0x0065 단독 신호기를 다른 프로세스가 소유하고 있습니다.
102 0x0066 신호기가 설정되었으므로 닫을 수 없습니다.
103 0x0067 신호기를 다시 설정할 수 없습니다.
104 0x0068 인터럽트 시간에 단독 신호기를 요청할 수 없습니다.
105 0x0069 이 신호기의 이전 소유권이 끝났습니다.
106 0x006A ų 드라이브에 디스켓을 넣으십시오.
107 0x006B 선택 디스켓을 넣지 않았으므로 프로그램이 중지되었습니다.
108 0x006C 디스크가 사용 중이거나 다른 프로세스에서 잠궜습니다.
109 0x006D 파이프가 종료되었습니다.
110 0x006E 지정한 장치나 파일을 열 수 없습니다.
111 0x006F 파일 이름이 너무 깁니다.
112 0x0070 디스크에 충분한 공간이 없습니다.
113 0x0071 사용할 수 있는 내부 파일 식별자가 이제 없습니다.
114 0x0072 대상 내부 파일 식별자가 올바르지 않습니다.
117 0x0075 응용 프로그램의 IOCTL 호출이 올바르지 않습니다.
118 0x0076 쓰기 확인(verify-on-write) 스위치 매개 변수 값이 올바르지 않습니다.
119 0x0077 요청한 명령이 지원되지 않습니다.
120 0x0078 이 함수는 Win32 모드에서만 유효합니다.
121 0x0079 신호기 시간 초과 기간이 만료되었습니다.
122 0x007A 시스템 호출에 전달된 데이터 영역이 너무 작습니다.
123 0x007B 파일 이름, 디렉터리 이름, 또는 디스크 이름의 구문이 올바르지 않습니다.
124 0x007C 시스템 호출 수준이 올바르지 않습니다.
125 0x007D 디스크에 디스크 이름이 없습니다.
126 0x007E 지정한 모듈을 찾을 수 없습니다.
127 0x007F 지정한 프로시저를 찾을 수 없습니다.
128 0x0080 대기 중인 하위 프로세스가 없습니다.
129 0x0081 ų 응용 프로그램을 Win32 모드에서 실행할 수 없습니다.
130 0x0082 원시 디스크 I/O 이외의 작업에 열려 있는 디스크 분할 영역의 파일 핸들을 사용하려고 했습니다.
131 0x0083 파일의 시작 부분 앞으로 파일 포인터를 옮기려고 했습니다.
132 0x0084 지정한 장치나 파일에 설정할 수 없는 파일 포인터입니다.
133 0x0085 JOIN이나 SUBST 명령을 이전에 결합된 드라이브를 포함하는 드라이브에 사용할 수 없습니다.
134 0x0086 이미 결합된 드라이브에 JOIN이나 SUBST 명령을 사용하려고 했습니다.
135 0x0087 이미 대체된 드라이브에 JOIN이나 SUBST 명령을 사용하려고 했습니다.
136 0x0088 결합되지 않은 드라이브의 JOIN을 삭제하려고 했습니다.
137 0x0089 대체되지 않은 드라이브의 대체품을 삭제하려고 했습니다.
138 0x008A 결합된 드라이브의 디렉터리로 드라이브를 결합하려고 했습니다.
139 0x008B 대체된 드라이브의 디렉터리로 드라이브를 대체하려고 했습니다.
140 0x008C 대체된 드라이브의 디렉터리로 드라이브를 결합하려고 했습니다.
141 0x008D 결합된 드라이브의 디렉터리로 드라이브를 SUBST하려고 했습니다.
142 0x008E 지금 JOIN이나 SUBST를 수행할 수 없습니다.
143 0x008F 같은 드라이브의 디렉터리로 드라이브를 결합하거나 대체할 수 없습니다.
144 0x0090 루트 디렉터리의 하위 디렉터리가 아닙니다.
145 0x0091 디렉터리가 비어 있지 않습니다.
146 0x0092 지정한 경로가 대체용으로 사용 중입니다.
147 0x0093 사용할 수 있는 리소스가 부족하므로 이 명령을 처리할 수 없습니다.
148 0x0094 지금 지정한 경로를 사용할 수 없습니다.
149 0x0095 드라이브의 디렉터리가 이전 대체 대상인 드라이브를 결합하거나 대체하려고 했습니다.
150 0x0096 CONFIG.SYS 파일에 시스템 추적 정보가 지정되지 않았거나 추적이 허용되지 않습니다.


151 0x0097 DosMuxSemWait용으로 지정한 신호기 사건의 개수가 올바르지 않습니다.
152 0x0098 DosMuxSemWait가 실행되지 않았습니다. 너무 많은 신호기가 이미 설정되었습니다.
153 0x0099 DosMuxSemWait 목록이 올바르지 않습니다.
154 0x009A 입력한 디스크 이름이 대상 파일 시스템의 이름 한계를 넘었습니다.
155 0x009B 다른 스레드를 작성할 수 없습니다.
156 0x009C 수신측 프로세스에서 신호를 거부했습니다.
157 0x009D 세그먼트가 이미 삭제되었거나 잠글 수 없습니다.
158 0x009E 세그먼트가 이미 잠금이 해제되었습니다.
159 0x009F 스레드 ID의 주소가 올바르지 않습니다.
160 0x00A0 DosExecPgm에 전달된 인수 문자열이 올바르지 않습니다.
161 0x00A1 지정한 경로가 올바르지 않습니다.
162 0x00A2 신호가 이미 대기 중입니다.
164 0x00A4 시스템에서 더 이상의 스레드를 작성할 수 없습니다.
167 0x00A7 파일의 영역을 잠글 수 없습니다.
170 0x00AA 요청한 리소스가 사용 중입니다.
173 0x00AD 잠금 요청이 제공된 취소 영역에서 두드러지지 않습니다.
174 0x00AE 파일 시스템에서 잠금 유형의 자동 변경을 지원하지 않습니다.
180 0x00B4 올바르지 않은 세그먼트 번호가 검색되었습니다.
182 0x00B6 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
183 0x00B7 그 파일이 이미 존재하면 파일을 작성할 수 없습니다.
186 0x00BA 전달된 플래그가 올바르지 않습니다.
187 0x00BB 지정한 시스템 신호기 이름을 찾을 수 없습니다.
188 0x00BC 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
189 0x00BD 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
190 0x00BE 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
191 0x00BF Win32 모드에서 ų 프로그램을 실행할 수 없습니다.
192 0x00C0 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
193 0x00C1 ų은 올바른 Win32 응용 프로그램이 아닙니다.
194 0x00C2 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
195 0x00C3 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
196 0x00C4 운영 체제에서 이 응용 프로그램을 실행할 수 없습니다.
197 0x00C5 운영 체제가 현재 이 응용 프로그램을 실행하도록 구성되어 있지 않습니다.
198 0x00C6 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
199 0x00C7 운영 체제에서 이 응용 프로그램을 실행할 수 없습니다.
200 0x00C8 코드 세그먼트가 64KB 이상일 수 없습니다.


201 0x00C9 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
202 0x00CA 운영 체제에서 ų 프로그램을 실행할 수 없습니다.
203 0x00CB 입력한 환경 옵션을 찾을 수 없습니다.
205 0x00CD 명령 하위 트리에 신호 핸들러를 갖고 있는 프로세스가 없습니다.
206 0x00CE 파일 이름이나 확장명이 너무 깁니다.
207 0x00CF 링 2 스택이 사용 중입니다.
208 0x00D0 전역 파일 이름 문자, * 또는 ?가 잘못 입력되었거나 너무 많은 전역 파일 이름 문자가 지정되었습니다.
209 0x00D1 통지된 신호가 올바르지 않습니다.
210 0x00D2 신호 핸들러를 설정할 수 없습니다.
212 0x00D4 세그먼트가 잠겼으므로 다시 할당할 수 없습니다.
214 0x00D6 이 프로그램이나 동적 연결 모듈에 너무 많은 동적 연결 모듈이 부착되었습니다.
215 0x00D7 LoadModule로의 호출을 중첩할 수 없습니다.
230 0x00E6 파이프 상태가 올바르지 않습니다.
231 0x00E7 모든 파이프 인스턴스가 사용 중입니다.
232 0x00E8 파이프를 닫고 있습니다.
233 0x00E9 파이프의 다른 쪽 끝에 프로세스가 없습니다.
234 0x00EA 사용 가능한 데이터가 더 있습니다.
240 0x00F0 세션이 취소되었습니다.
254 0x00FE 지정한 확장된 속성 이름이 올바르지 않습니다.
255 0x00FF 확장된 속성에 일관성이 없습니다.
259 0x0103 사용 가능한 데이터가 더 이상 없습니다.
266 0x010A 복사 API를 사용할 수 없습니다.
267 0x010B 디렉터리 이름이 올바르지 않습니다.
275 0x0113 확장된 속성이 버퍼에 맞지 않습니다.
276 0x0114 탑재된 파일 시스템의 확장된 속성 파일이 손상되었습니다.
277 0x0115 확장된 속성표 파일이 가득 찼습니다.
278 0x0116 지정한 확장 속성 핸들이 올바르지 않습니다.
282 0x011A 탑재된 파일 시스템에서 확장된 속성을 지원하지 않습니다.
288 0x0120 호출자가 소유하지 않는 mutex를 해제하려고 했습니다.
298 0x012A 신호기에 너무 많이 통지했습니다.
299 0x012B Read/WriteProcessMemory 요청의 일부만 완료되었습니다.


317 0x013D Ŵ용 메시지 파일에서 메시지 번호 0xų의 메시지를 찾을 수 없습니다.
487 0x01E7 잘못된 주소를 액세스하려고 했습니다.
534 0x0216 계산 결과가 32비트를 초과했습니다.
535 0x0217 파이프의 다른 쪽 끝에 프로세스가 있습니다.
536 0x0218 프로세스에서 파이프의 다른 쪽 끝을 열기를 기다리는 중입니다.
994 0x03E2 확장된 속성의 액세스가 거부되었습니다.
995 0x03E3 스레드 종료나 응용 프로그램 요청때문에 I/O 연산이 취소되었습니다.
996 0x03E4 중복된 I/O 사건에 대해 신호가 없습니다.
997 0x03E5 중복된 I/O 연산이 진행 중입니다.
998 0x03E6 메모리 위치의 액세스가 올바르지 않습니다.
999 0x03E7 Inpage 연산을 수행하는 중에 오류가 발생했습니다.


1001 0x03E9 되돌이 수준이 너무 깊어서 스택 오버플로가 발생했습니다.
1002 0x03EA 전송된 메시지에 대한 작업을 창에서 할 수 없습니다.
1003 0x03EB 이 함수를 완료할 수 없습니다.
1004 0x03EC 잘못된 플래그입니다.
1005 0x03ED 볼륨에 인식된 파일 시스템이 없습니다. 모든 필요한 파일 시스템 드라이버를 로드하였고
볼륨이 손상되지 않았는지 확인해 주십시오.
1006 0x03EE 파일의 볼륨이 표면상 변경되었으므로 열려진 파일은 더 이상 유효하지 않습니다
1007 0x03EF 전체 화면 모드에서 요청한 작업을 수행할 수 없습니다.
1008 0x03F0 존재하지 않는 토큰을 참조하려고 했습니다.
1009 0x03F1 구성 레지스트리 데이터베이스가 손상되었습니다.
1010 0x03F2 구성 레지스트리 키가 올바르지 않습니다.
1011 0x03F3 구성 레지스트리 키를 열 수 없습니다.
1012 0x03F4 구성 레지스트리 키를 읽을 수 없습니다.
1013 0x03F5 구성 레지스트리 키에 쓸 수 없습니다.
1014 0x03F6 레지스트리 데이터베이스의 파일 중 하나를 기록 또는 선택 복사를 사용하여 회복해야 할 경우였습니다. 회복이 완료되었습니다.
1015 0x03F7 레지스트리가 손상되었습니다.
레지스트리 데이터를 포함하는 파일 중 하나의 구조가 손상되었거나 메모리에 있는 파일의 시스템
이미지가 손상되었거나, 사본이나 로그 파일이 없거나 손상된 이유로 파일을 복구할 수 없습니다.
1016 0x03F8 레지스트리에서 초기화된 I/O 작업에 복구 불가능한 오류가 발생했습니다.
레지스트리의 시스템 이미지를 포함하는 파일 중의 하나를 읽거나 쓰거나 내용을 지우는 것이
불가능합니다.
1017 0x03F9 파일을 레지스트리로 로드하거나 복구하려고 했으나 지정한 파일이 레지스트리 파일 형식이 아닙니다.
1018 0x03FA 삭제 표시가 된 레지스트리 키에서 잘못된 연산을 하려고 했습니다.
1019 0x03FB 레지스트리 기록에 필요한 공간을 할당할 수 없습니다.
1020 0x03FC 이미 서브키나 값을 갖고 있는 레지스트리 키에 기호 연결을 작성할 수 없습니다.
1021 0x03FD 소멸성 상위 키 하에서 안정된 서브키를 작성할 수 없습니다.
1022 0x03FE 통지 변경 요청이 수행 중이며 그 정보가 호출자의 버퍼에 반환되지 않았습니다.
호출자는 지금 변경 사항을 알기 위해 파일을 이뉴머레이트할 필요가 있습니다.
1051 0x041B 정지 컨트롤이 다른 실행 중인 서비스가 의존하는 서비스로 전송되었습니다.
1052 0x041C 요청한 컨트롤이 이 서비스에 유효하지 않습니다.
1053 0x041D 서비스가 시작이나 컨트롤 요청에 적시에 반응하지 못했습니다.
1054 0x041E 서비스용으로 스레드를 작성할 수 없습니다.
1055 0x041F 서비스 데이터베이스가 잠겼습니다.
1056 0x0420 서비스의 인스턴스가 이미 실행 중입니다.
1057 0x0421 계정 이름이 올바르지 않거나 존재하지 않습니다.
1058 0x0422 지정한 서비스가 실행 불능 상태이거나 시작할 수 없습니다.
1059 0x0423 원형 서비스 종속성이 지정되었습니다.
1060 0x0424 지정한 서비스가 설치되어 있지 않습니다.
1061 0x0425 지금 서비스에서 컨트롤 메시지를 받아들일 수 없습니다.
1062 0x0426 서비스가 시작되지 않았습니다.
1063 0x0427 서비스 프로세스를 서비스 컨트롤러에 연결할 수 없습니다.
1064 0x0428 서비스에서 컨트롤 요청을 처리할 때 오류가 발생했습니다.
1065 0x0429 지정한 데이터베이스가 존재하지 않습니다.
1066 0x042A 서비스에서 서비스 고유의 오류 코드를 돌려주었습니다.
1067 0x042B 프로세스가 예기치 않게 종료되었습니다.
1068 0x042C 종속성 서비스나 그룹을 시작할 수 없습니다.
1069 0x042D 로그온할 수 없으므로 서비스를 시작할 수 없습니다.
1070 0x042E 시작한 후 서비스가 시작 대기 상태에 걸려 있습니다.
1071 0x042F 지정한 서비스 데이터베이스 잠금이 올바르지 않습니다.
1072 0x0430 지정한 서비스가 삭제 표시되었습니다.
1073 0x0431 지정한 서비스가 이미 존재합니다.
1074 0x0432 시스템이 현재 가장 좋은 시스템 구성으로 실행 중입니다.
1075 0x0433 종속성 서비스가 존재하지 않거나 삭제 표시되었습니다.
1076 0x0434 현재 시동은 `마지막으로 인식된 양호한 컨트롤 세트`로 사용하도록 받아들여졌습니다.
1077 0x0435 지난 시동 이후로 서비스를 시작하려는 시도가 없었습니다.
1078 0x0436 그 이름이 서비스 이름이나 서비스 디스플레이 이름으로 이미 사용 중입니다.
1100 0x044C 테이프의 실제 끝에 도달했습니다.


1101 0x044D 테이프 액세스가 파일 표시에 도달했습니다.
1102 0x044E 테이프의 앞부분이나 분할 영역에 도달했습니다.
1103 0x044F 테이프 액세스가 파일 세트의 끝에 도달했습니다.
1104 0x0450 테이프에 더 이상의 데이터가 없습니다.
1105 0x0451 테이프를 분할할 수 없습니다.
1106 0x0452 다중 볼륨 분할 영역을 가진 새 테이프에 액세스할 때 현재 블록 크기가 올바르지 않습니다.
1107 0x0453 테이프를 로드할 때 테이프 분할 영역 정보를 찾을 수 없습니다.
1108 0x0454 매체를 꺼내는 메카니즘을 잠글 수 없습니다.
1109 0x0455 매체의 적재를 해제할 수 없습니다.
1110 0x0456 드라이브의 매체가 변경되었을지도 모릅니다.
1111 0x0457 I/O 버스가 재설정되었습니다.
1112 0x0458 드라이브에 매체가 없습니다.
1113 0x0459 대상 멀티바이트 코드페이지에 Unicode 문자의 매핑이 없습니다.
1114 0x045A 동적 연결 라이브러리 (DLL) 초기화 루틴이 실패했습니다.
1115 0x045B 시스템 종료 중입니다.
1116 0x045C 시스템 종료 중이 아니므로 시스템 종료를 취소할 수 없습니다.
1117 0x045D I/O 장치 오류때문에 요청을 처리할 수 없습니다.
1118 0x045E 초기화가 완료된 직렬 장치가 없습니다. 직렬 드라이버의 로드가 해제됩니다.
1119 0x045F 다른 장치들과 인터럽트 요청(IRQ)을 공유 중이던 장치를 열 수 없습니다.
그 IRQ를 사용하는 다른 장치가 이미 하나 이상 열려 있습니다.
1120 0x0460 직렬 포트에 씀으로써 직렬 I/O 연산이 완료되었습니다.
(IOCTL_SERIAL_XOFF_COUNTER이 0에 도달했습니다.)
1121 0x0461 시간 초과 기간이 만료되어 직렬 I/O 연산이 완료되었습니다.
(IOCTL_SERIAL_XOFF_COUNTER이 0에 도달하지 않았습니다.)
1122 0x0462 플로피 디스크에서 ID 주소 표시를 찾을 수 없습니다.
1123 0x0463 플로피 섹터 ID 필드와 플로피 디스크 컨트롤러 트랙 주소가 일치하지 않습니다.
1124 0x0464 플로피 디스크 드라이버가 플로피 디스크 컨트롤러를 인식하지 못하는 오류가 보고되었습니다.
1125 0x0465 플로피 디스크 컨트롤러가 레지스터에 일관성이 없는 결과값을 돌려주었습니다.
1126 0x0466 하드디스크를 액세스하는 동안 위치 재조정 작업이 여러 번의 재시도에도 불구하고 실패하였습니다.
1127 0x0467 하드디스크를 액세스하는 동안 디스크 연산이 여러 번의 재시도에도 불구하고 실패하였습니다.
1128 0x0468 하드디스크를 액세스하는 동안 디스크 컨트롤러 재설정이 필요하였으나 실패하였습니다.
1129 0x0469 테이프의 실제 끝에 도달했습니다.
1130 0x046A 이 명령을 처리하는데 사용할 서버 저장 공간이 부족합니다.
1131 0x046B 잠재적 교착 조건이 검색되었습니다.
1132 0x046C 기본 주소나 지정된 파일 오프셋의 맞춤이 올바르지 않습니다.
1140 0x0474 다른 응용 프로그램이나 드라이버에서 시스템 전원 상태를 변경하지 못하게 했습니니다.
1141 0x0475 시스템 BIOS로 시스템 전원 상태를 변경하려고 했으나 실패했습니다.
1150 0x047E 지정한 프로그램에 새 버전의 Windows가 필요합니다.
1151 0x047F 지정한 프로그램이 Windows 또는 MS-DOS용 프로그램이 아닙니다.
1152 0x0480 지정한 프로그램의 인스턴스를 하나 이상 실행할 수 없습니다.
1153 0x0481 지정한 프로그램이 구 버전의 Windows에서 작성되었습니다.
1154 0x0482 이 응용 프로그램을 실행하는데 필요한 라이브러리 파일 중의 하나가 손상되었습니다.
1155 0x0483 지정한 파일로 이 작업을 할 수 있도록 연결된 프로그램이 없습니다.
1156 0x0484 응용 프로그램에 명령을 보내는 동안 오류가 발생했습니다.
1157 0x0485 이 응용 프로그램을 실행하는데 필요한 라이브러리 파일 중의 하나를 찾을 수 없습니다.
1200 0x04B0 지정한 장치 이름이 올바르지 않습니다.


1201 0x04B1 장치가 현재 연결되어 있지 않지만 연결을 기억하고 있습니다.
1202 0x04B2 이전에 기억된 적이 있는 장치를 기억하려고 했습니다.
1203 0x04B3 네트워크 공급자에서 네트워크 경로를 알 수 없습니다.
1204 0x04B4 지정한 네트워크 공급자 이름이 올바르지 않습니다.
1205 0x04B5 네트워크 연결 초기화 파일을 열 수 없습니다.
1206 0x04B6 네트워크 연결 초기화 파일이 손상되었습니다.
1207 0x04B7 컨테이너가 아닌 것을 이뉴머레이트할 수 없습니다.
1208 0x04B8 확장 오류가 발생했습니다.
1209 0x04B9 지정한 그룹 이름의 형식이 올바르지 않습니다.
1210 0x04BA 지정한 컴퓨터 이름의 형식이 올바르지 않습니다.
1211 0x04BB 지정한 사건 이름의 형식이 올바르지 않습니다.
1212 0x04BC 지정한 도메인 이름의 형식이 올바르지 않습니다.
1213 0x04BD 지정한 서비스 이름의 형식이 올바르지 않습니다.
1214 0x04BE 지정한 네트워크 이름의 형식이 올바르지 않습니다.
1215 0x04BF 지정한 공유 이름의 형식이 올바르지 않습니다.
1216 0x04C0 지정한 암호의 형식이 올바르지 않습니다.
1217 0x04C1 지정한 메시지 이름의 형식이 올바르지 않습니다.
1218 0x04C2 지정한 메시지 대상의 형식이 올바르지 않습니다.
1219 0x04C3 신용장이 기존의 신용장들과 충돌을 일으킵니다.
1220 0x04C4 네트워크 서버에 세션을 설정하려고 했으나 이미 그 서버에 너무 많은 세션이 설정되어 있습니다.
1221 0x04C5 워크그룹 또는 도메인 이름이 이미 네트워크상의 다른 컴퓨터에서 사용중입니다.
1222 0x04C6 네트워크가 없거나 시작되지 않았습니다.
1223 0x04C7 사용자가 작업을 취소했습니다.
1224 0x04C8 사용자 매핑 섹션이 열려 있는 파일에서 요청한 작업을 수행할 수 없습니다.
1225 0x04C9 원격 시스템에서 네트워크 연결을 거부했습니다.
1226 0x04CA 네트워크 연결이 닫혔습니다.
1227 0x04CB 네트워크 전송 끝점이 이미 그것과 연결된 주소를 갖고 있습니다.
1228 0x04CC 주소가 아직 네트워크 끝점과 연결되지 않았습니다.
1229 0x04CD 존재하지 않는 네트워크 연결에서 작업이 시도되었습니다.
1230 0x04CE 활성 네트워크 연결에서 잘못된 작업이 시도되었습니다.
1231 0x04CF 원격 네트워크에 전송할 수 없습니다.
1232 0x04D0 원격 시스템에 전송할 수 없습니다.
1233 0x04D1 원격 시스템에서 전송 프로토콜을 지원하지 않습니다.
1234 0x04D2 원격 시스템의 대상 네트워크 끝점에 실행중인 서비스가 없습니다.
1235 0x04D3 요청이 취소되었습니다.
1236 0x04D4 네트워크 연결이 지역 시스템에서 취소되었습니다.
1237 0x04D5 작업을 완료할 수 없습니다. 재시도하십시오.
1238 0x04D6 이 계정에 동시에 연결할 수 있는 개수의 한계를 초과했으므로 서버에 더 연결할 수 없습니다
1239 0x04D7 허용되지 않는 시간에 이 계정에 로그인 하려고 했습니다.
1240 0x04D8 이 국에서 그 계정에 로그인할 수 없습니다.
1241 0x04D9 네트워크 주소를 요청한 작업에 사용할 수 없습니다.
1242 0x04DA 서비스가 이미 등록되었습니다.
1243 0x04DB 지정한 서비스가 존재하지 않습니다.
1244 0x04DC 사용자에게 권한이 없으므로 요청한 작업을 수행할 수 없습니다.
1245 0x04DD 사용자가 네트워크에 로그온하지 않았으므로 요청한 작업을 할 수 없습니다. 지정한 서비스가 존재하지 않습니다.
1246 0x04DE 처리중인 작업을 계속하기 위해 호출기를 필요로 하는 것을 되돌립니다.
1247 0x04DF 초기화가 이미 완료되었을 때 초기화 작업을 수행하려고 했습니다.
1248 0x04E0 지역 장치가 더 이상 없습니다.
1300 0x0514 참조되는 모든 특권이 호출자에게 지정되어 있지는 않습니다.


1301 0x0515 계정 이름과 시스템 보안 ID간의 일부 매핑이 완료되지 않았습니다.
1302 0x0516 이 계정에 특정하게 설정되어 있는 시스템 할당량 제한이 없습니다.
1303 0x0517 사용 가능한 암호화 키가 없습니다. 잘 알려진 암호화 키가 리턴되었습니다.
1304 0x0518 NT 암호가 너무 복잡하여 LAN Manager 암호로 변환할 수 없습니다.
리턴된 LAN Manager 암호가 NULL 문자열입니다.
1305 0x0519 수정 수준을 알 수 없습니다.
1306 0x051A 두 수정 수준이 호환되지 않습니다.
1307 0x051B 이 시스템 보안 ID가 이 개체의 소유자로서 지정되지 않을지도 모릅니다.
1308 0x051C 이 시스템 보안 ID가 개체의 주 그룹으로서 지정되지 않을지도 모릅니다.
1309 0x051D 현재 클라이언트를 의인화하고 있지 않은 스레드를 이용하여 의인화 토큰 방식으로 작업하려고 했습니다.
1310 0x051E 그룹을 사용할 수 없을지도 모릅니다.
1311 0x051F 로그온 요청을 처리하는 데 사용할 수 있는 로그온 서버가 현재 없습니다.
1312 0x0520 지정한 로그온 세션이 존재하지 않습니다. 이미 종료되었을지도 모릅니다.
1313 0x0521 지정한 특권이 존재하지 않습니다.
1314 0x0522 요청한 특권을 클라이언트가 갖고 있지 않습니다.
1315 0x0523 제공한 이름이 올바르게 형성된 계정 이름이 아닙니다.
1316 0x0524 지정한 사용자가 이미 존재합니다.
1317 0x0525 지정한 사용자가 존재하지 않습니다.
1318 0x0526 지정한 그룹이 이미 존재합니다.
1319 0x0527 지정한 그룹이 존재하지 않습니다.
1320 0x0528 지정한 사용자 계정이 이미 지정한 그룹의 구성원이거나, 지정한 그룹에 구성원이 있어서
삭제할 수 없습니다.
1321 0x0529 지정한 사용자 계정이 지정한 그룹 계정의 구성원이 아닙니다.
1322 0x052A 마지막 남은 관리 계정을 사용 불능 상태로 하거나 삭제할 수 없습니다.
1323 0x052B 암호를 업데이트할 수 없습니다. 현재 암호로 제공된 값이 올바르지 않습니다.
1324 0x052C 암호를 업데이트할 수 없습니다. 새 암호로 제공된 값이 암호에 사용할 수 없는 문자를 포함합니다.
1325 0x052D 암호 업데이트 규칙을 위반했으므로 암호를 업데이트할 수 없습니다.
1326 0x052E 로그온할 수 없음: 사용자 이름을 알 수 없거나 암호가 틀립니다.
1327 0x052F 로그온할 수 없음: 사용자 계정 한계.
1328 0x0530 로그온할 수 없음: 계정 로그온 시간 한계 위반
1329 0x0531 로그온할 수 없음: 이 컴퓨터에 로그온할 수 없는 사용자입니다.
1330 0x0532 로그온할 수 없음: 지정한 계정 암호 사용 기간이 만료되었습니다.
1331 0x0533 로그온할 수 없음: 계정이 현재 사용할 수 없는 상태입니다.
1332 0x0534 계정 이름과 시스템 보안 ID간에 매핑이 되지 않았습니다.
1333 0x0535 한번에 너무 많은 지역 사용자 식별자(LUID)를 요청했습니다.
1334 0x0536 사용 가능한 지역 사용자 식별자(LUID)가 더 이상 없습니다.
1335 0x0537 시스템 보안 ID의 부인증 부분이 이 특별 사용에 적합하지 않습니다.
1336 0x0538 액세스 컨트롤 목록(ACL) 구조가 올바르지 않습니다.
1337 0x0539 시스템 보안 ID 구조가 올바르지 않습니다.
1338 0x053A 시스템 보안 기술자 구조가 올바르지 않습니다.
1340 0x053C 상속된 액세스 컨트롤 목록(ACL)이나 액세스 컨트롤 항목(ACE)를 작성할 수 없습니다.
1341 0x053D 서버가 현재 사용불능 상태입니다.
1342 0x053E 서버가 현재 사용가능 상태입니다.
1343 0x053F 제공된 값이 식별자 인증에 부적합합니다.
1344 0x0540 시스템 보안 정보의 업데이트에 필요한 메모리가 더 이상 없습니다.
1345 0x0541 지정한 속성이 올바르지 않거나 전체적으로 그룹의 속성과 호환되지 않습니다.
1346 0x0542 의인화 수준이 올바르지 않거나 제공한 내용이 올바르지 않습니다.
1347 0x0543 익명 수준의 시스템 보안 토큰을 열 수 없습니다.
1348 0x0544 요청한 확인 정보 클래스가 올바르지 않습니다.
1349 0x0545 토큰의 종류가 사용 목적에 부적합합니다.
1350 0x0546 연결된 시스템 보안이 없는 개체에서 시스템 보안 작업을 수행할 수 없습니다.


1351 0x0547 Windows NT 서버에 연락할 수 없거나 도메인 내의 개체가 필요한 정보를 검색당하지 않도록
보호되고 있습니다.
1352 0x0548 시스템 보안 계정 관리자(SAM)나 지역 시스템 보안 권한(LSA) 서버가 시스템 보안 작업을 수행하기에
부적절한 상태에 있습니다.
1353 0x0549 시스템 보안 작업을 수행하기에 도메인의 상태가 부적절합니다.
1354 0x054A 이 작업은 도메인의 주 도메인 컨트롤러에서만 할 수 있습니다.
1355 0x054B 지정한 도메인이 존재하지 않습니다.
1356 0x054C 지정한 도메인이 이미 존재합니다.
1357 0x054D 서버 당 도메인 수의 한계를 초과하려고 했습니다.
1358 0x054E 심각한 매체 오류나 디스크상의 데이터 구조 손상때문에 요청한 작업을 완료할 수 없습니다.
1359 0x054F 시스템 보안 계정 데이터베이스에 내부적인 불일치가 있습니다.
1360 0x0550 일반 액세스 종류가 이미 비일반 종류에 매핑되어야 하는 액세스 마스크에 포함되어 있습니다.
1361 0x0551 시스템 보안 기술자의 형식이 올바르지 않습니다(절대적 또는 자기 상대적).
1362 0x0552 요청한 작업을 로그온 과정에서만 수행할 수 있습니다.
호출 과정이 로그온 과정으로 등록되지 않았습니다.
1363 0x0553 이미 사용중인 ID로 새 로그온 세션을 시작할 수 없습니다.
1364 0x0554 지정한 인증 패키지를 알 수 없습니다.
1365 0x0555 로그온 세션이 요청한 작업과 일치하는 상태에 있지 않습니다.
1366 0x0556 로그온 세션 ID가 이미 사용 중입니다.
1367 0x0557 로그온 요청에 잘못된 로그온 유형 값이 있습니다.
1368 0x0558 파이프에서 데이터를 읽기 전까지 이름 파이프를 통해 의인화가 불가능합니다.
1369 0x0559 레지스트리 하위 트리의 트랜잭션 상태가 요청한 작업과 호환되지 않습니다.
1370 0x055A 내부 시스템 보안 데이터베이스가 손상되었습니다.
1371 0x055B 제공된 계정에서 이 작업을 수행할 수 없습니다.
1372 0x055C 제공된 특별 그룹에서 이 작업을 수행할 수 없습니다.
1373 0x055D 제공된 특별 사용자에서 이 작업을 수행할 수 없습니다.
1374 0x055E 그룹이 현재 사용자의 주 그룹이므로 그룹에서 사용자를 제거할 수 없습니다.
1375 0x055F 기본 토큰에서 이미 이 토큰을 사용 중입니다.
1376 0x0560 지정한 지역 그룹이 존재하지 않습니다.
1377 0x0561 지정한 계정 이름이 지역 그룹의 구성원이 아닙니다.
1378 0x0562 지정한 계정 이름이 이미 지역 그룹의 구성원입니다.
1379 0x0563 지정한 지역 그룹이 이미 존재합니다.
1380 0x0564 로그온할 수 없음: 사용자가 이 컴퓨터에서 요청한 로그온 유형을 받지 못했습니다.
1381 0x0565 단일 시스템에 저장할 수 있는 기밀의 최대 개수를 초과했습니다.
1382 0x0566 기밀의 길이가 허용되는 최대 길이를 초과했습니다.
1383 0x0567 지역 시스템 보안 권한 데이터베이스에 내부 불일치가 있습니다.
1384 0x0568 로그온하려는 동안 사용자의 시스템 보안 환경에 너무 많은 시스템 보안 ID가 쌓였습니다.
1385 0x0569 로그온할 수 없음: 사용자가 이 컴퓨터에서 요청한 로그온 종류를 받지 못했습니다.
1386 0x056A 사용자 암호를 변경하는데 교차 암호화된 암호가 필요합니다.
1387 0x056B 구성원이 존재하지 않으므로 새 구성원을 지역 그룹에 없습니다.
1388 0x056C 구성원이 잘못된 계정 종류를 가지므로 지역 그룹에 추가할 수 없습니다.
1389 0x056D 너무 많은 시스템 보안 ID가 지정되었습니다.
1390 0x056E 이 사용자 암호를 변경하려면 교차 암호화된 암호가 필요합니다.
1391 0x056F ACL에 상속 가능한 구성요소가 없습니다.
1392 0x0570 파일이나 디렉터리가 손상되었으므로 읽을 수 없습니다.
1393 0x0571 디스크 구조가 손상되었으므로 읽을 수 없습니다.
1394 0x0572 지정한 로그온 세션에 사용자 세션 키가 없습니다.
1395 0x0573 액세스 중인 서비스는 특정 수의 연결에만 사용할 수 있습니다.
서비스에서 허용하는 연결 수에 이미 도달했으므로 더 이상 연결할 수 없습니다.
1400 0x0578 잘못된 창 핸들입니다.


1401 0x0579 잘못된 메뉴 핸들입니다.
1402 0x057A 잘못된 커서 핸들입니다.
1403 0x057B 잘못된 바로 가는 키 표 핸들입니다.
1404 0x057C 잘못된 후크 핸들입니다.
1405 0x057D 다중 창 위치 구조에 부적합한 핸들입니다.
1406 0x057E 최상위 수준의 하위 창을 작성할 수 없습니다.
1407 0x057F 창 클래스를 찾을 수 없습니다.
1408 0x0580 다른 스레드에 속한 잘못된 창입니다.
1409 0x0581 바로 가는 키가 이미 등록되었습니다.
1410 0x0582 이미 존재하는 클래스입니다.
1411 0x0583 존재하지 않는 클래스입니다.
1412 0x0584 클래스에 여전이 열려 있는 창이 있습니다.
1413 0x0585 잘못된 색인입니다.
1414 0x0586 잘못된 아이콘 핸들입니다.
1415 0x0587 개인 DIALOG 창 단어를 이용 중입니다.
1416 0x0588 목록 식별자를 찾을 수 없습니다.
1417 0x0589 대표 문자를 찾을 수 없습니다.
1418 0x058A 스레드에 열려 있는 클립보드가 없습니다.
1419 0x058B 바로 가는 키가 등록되어 있지 않습니다.
1420 0x058C 올바른 대화 창이 아닙니다.
1421 0x058D 컨트롤 ID를 찾을 수 없습니다.
1422 0x058E 편집 컨트롤 기능이 없으므로 명령 실행 상자에 부적절한 메시지입니다.
1423 0x058F 창이 명령 실행 상자가 아닙니다.
1424 0x0590 높이가 256보다 작아야 합니다.
1425 0x0591 잘못된 장치 환경 (DC) 핸들입니다.
1426 0x0592 잘못된 후크 프로시저 종류입니다.
1427 0x0593 잘못된 후크 프로시져입니다.
1428 0x0594 모듈 핸들 없이 지역 후크가 아닌 후크를 설정할 수 없습니다.
1429 0x0595 이 후크 프로시저는 전역으로만 설정가능합니다.
1430 0x0596 저널 후크 프로시저가 이미 설치되었습니다.
1431 0x0597 후크 프로시저가 설치되어 있지 않습니다.
1432 0x0598 단일 선택 목록에 부적합한 메시지입니다.
1433 0x0599 LB_SETCOUNT가 비지연 목록에 전송되었습니다.
1434 0x059A 이 목록에서는 탭 정지를 지원하지 않습니다.
1435 0x059B 다른 스레드에서 작성한 개체를 삭제할 수 없습니다.
1436 0x059C 하위 창에서 메뉴를 가질 수 없습니다.
1437 0x059D 창에 시스템 메뉴를 가질 수 없습니다.
1438 0x059E 잘못된 메시지 상자 유형입니다.
1439 0x059F 잘못된 시스템 범위의 (SPI_*) 매개 변수입니다.
1440 0x05A0 화면이 이미 잠겨졌습니다.
1441 0x05A1 다중 창 위치 구조에서 창의 모든 핸들은 같은 상위 핸들을 가져야 합니다.
1442 0x05A2 하위 창이 아닙니다.
1443 0x05A3 잘못된 GW_* 명령입니다.
1444 0x05A4 잘못된 스레드 식별자입니다.
1445 0x05A5 다중 문서 인터페이스(MDI) 창이 아닌 창으로부터 메시지를 처리할 수 없습니다.
1446 0x05A6 돌출 메뉴가 이미 실행중입니다.
1447 0x05A7 창에 스크롤 막대가 없습니다.
1448 0x05A8 스크롤 막대 범위는 0x7FFF보다 클 수 없습니다.
1449 0x05A9 지정한 방식으로 창을 표시하거나 제거할 수 없습니다.
1450 0x05AA 시스템 리소스가 부족하여 요청한 서비스를 완료할 수 없습니다.
1451 0x05AB 시스템 리소스가 부족하여 요청한 서비스를 완료할 수 없습니다.
1452 0x05AC 시스템 리소스가 부족하여 요청한 서비스를 완료할 수 없습니다.
1453 0x05AD 할당된 리소스가 부족하여 요청한 서비스를 완료할 수 없습니다.
1454 0x05AE 할당된 리소스가 부족하여 요청한 서비스를 완료할 수 없습니다.
1455 0x05AF 페이징 파일이 너무 작아서 이 작업을 완료할 수 없습니다.
1456 0x05B0 메뉴 항목을 찾을 수 없습니다.
1500 0x05DC 이벤트 로그 파일이 손상되었습니다.


1501 0x05DD 열 수 있는 이벤트 로그 파일이 없으므로 사건 기록 서비스를 실행할 수 없습니다.
1502 0x05DE 이벤트 로그 파일이 가득 찼습니다.
1503 0x05DF 이벤트 로그 파일이 읽는 동안 변경되었습니다.
1700 0x06A4 문자열 바인딩이 올바르지 않습니다.


1701 0x06A5 바인딩 핸들의 종류가 올바르지 않습니다.
1702 0x06A6 바인딩 핸들이 올바르지 않습니다.
1703 0x06A7 RPC 프로토콜 시퀀스가 지원되지 않습니다.
1704 0x06A8 RPC 프로토콜 시퀀스가 올바르지 않습니다.
1705 0x06A9 문자열의 범용 유일 식별자(UUID)가 올바르지 않습니다.
1706 0x06AA 끝점 형식이 올바르지 않습니다.
1707 0x06AB 네트워크 주소가 올바르지 않습니다.
1708 0x06AC 끝점을 찾을 수 없습니다.
1709 0x06AD 시간 초과 값이 올바르지 않습니다.
1710 0x06AE 개체의 범용 유일 식별자(UUID)를 찾을 수 없습니다.
1711 0x06AF 개체의 범용 유일 식별자(UUID)가 이미 등록되었습니다.
1712 0x06B0 유형의 범용 유일 식별자(UUID)가 이미 등록되었습니다.
1713 0x06B1 RPC 서버가 이미 듣고 있습니다.
1714 0x06B2 등록된 프로토콜 시퀀스가 없습니다.
1715 0x06B3 RPC 서버가 듣고 있지 않습니다.
1716 0x06B4 관리자 유형을 알 수 없습니다.
1717 0x06B5 인터페이스를 알 수 없습니다.
1718 0x06B6 바인딩이 없습니다.
1719 0x06B7 프로토콜 시퀀스가 없습니다.
1720 0x06B8 끝점을 작성할 수 없습니다.
1721 0x06B9 이 작업을 완료하는 데 사용할 리소스가 부족합니다.
1722 0x06BA RPC 서버를 사용할 수 없습니다.
1723 0x06BB RPC 서버가 너무 바빠서 이 작업을 완료할 수 없습니다.
1724 0x06BC 네트워크 옵션이 올바르지 않습니다.
1725 0x06BD 이 스레드에 활성 상태의 원격 프로시저 호출이 없습니다.
1726 0x06BE 원격 프로시저 호출을 할 수 없습니다.
1727 0x06BF 원격 프로시저 호출을 할 수 없으므로 실행되지 않았습니다.
1728 0x06C0 원격 프로시저 호출(RPC) 프로토콜 오류가 발생했습니다.
1730 0x06C2 RPC 서버에서 지원하지 않는 전송 구문입니다.
1732 0x06C4 지원되지 않는 범용 유일 식별자(UUID) 종류입니다.
1733 0x06C5 태그가 올바르지 않습니다.
1734 0x06C6 배열 한계 값이 올바르지 않습니다.
1735 0x06C7 바인딩에 항목 이름이 없습니다.
1736 0x06C8 이름 구문이 올바르지 않습니다.
1737 0x06C9 지원되지 않는 이름 구문입니다.
1739 0x06CB 범용 유일 식별자(UUID)를 작성하는 데 사용할 수 있는 네트워크 주소가 없습니다.
1740 0x06CC 끝점이 중복되었습니다.
1741 0x06CD 알 수 없는 인증 유형입니다.
1742 0x06CE 호출의 최대 횟수가 너무 작습니다.
1743 0x06CF 문자열이 너무 깁니다.
1744 0x06D0 RPC 프로토콜 시퀀스를 찾을 수 없습니다.
1745 0x06D1 프로시저 개수가 범위를 벗어났습니다.
1746 0x06D2 바인딩에 인증 정보가 없습니다.
1747 0x06D3 알 수 없는 인증 서비스입니다.
1748 0x06D4 인증 수준을 알 수 없습니다.
1749 0x06D5 시스템 보안 환경이 올바르지 않습니다.
1750 0x06D6 알 수 없는 인증 서비스입니다.


1751 0x06D7 항목이 올바르지 않습니다.
1752 0x06D8 서버의 끝점에서 작업을 수행할 수 없습니다.
1753 0x06D9 끝점 매퍼에서 사용 가능한 더 이상의 끝점이 없습니다.
1754 0x06DA 저장된 인터페이스가 없습니다.
1755 0x06DB 항목 이름이 불완전합니다.
1756 0x06DC 버전 옵션이 올바르지 않습니다.
1757 0x06DD 더 이상의 구성원이 없습니다.
1758 0x06DE 저장을 해제할 대상이 없습니다.
1759 0x06DF 인터페이스를 찾을 수 없습니다.
1760 0x06E0 항목이 이미 존재합니다.
1761 0x06E1 항목을 찾을 수 없습니다.
1762 0x06E2 이름 서비스를 사용할 수 없습니다.
1763 0x06E3 네트워크 주소 패밀리가 올바르지 않습니다.
1764 0x06E4 요청한 작업이 지원되지 않습니다.
1765 0x06E5 의인화하는데 시스템 보안 환경을 사용할 수 없습니다.
1766 0x06E6 원격 프로시저 호출(RPC)에서 내부 오류가 발생했습니다.
1767 0x06E7 RPC 서버에서 정수를 0으로 나누려고 했습니다.
1768 0x06E8 RPC 서버에서 주소 지정 오류가 발생했습니다.
1769 0x06E9 RPC 서버에서 0으로 나누는 부동 소수점 연산이 있었습니다.
1770 0x06EA RPC 서버에서 부동 소수점 언더플로가 발생했습니다.
1771 0x06EB RPC 서버에서 부동 소수점 오버플로가 발생했습니다.
1772 0x06EC 자동 핸들의 바인딩에 사용할 수 있는 RPC 서버 목록을 다 써버렸습니다.
1773 0x06ED 문자 번역표 파일을 열 수 없습니다.
1774 0x06EE 문자 번역표가 수록되어 있는 파일의 바이트 수가 512보다 적습니다.
1775 0x06EF 원격 프로시저 호출동안 클라이언트로부터 호스트로 널 컨텍스트 핸들이 전달되었습니다.
1777 0x06F1 원격 프로시저 호출동안 컨텍스트 핸들이 변경되었습니다.
1778 0x06F2 원격 프로시저 호출로 전달된 바인딩 핸들이 일치하지 않습니다.
1779 0x06F3 스터브에서 원격 프로시저 호출 핸들을 가져올 수 없습니다.
1772 0x06F4 널 참조 포인터가 스터브에 전달되었습니다.
1781 0x06F5 정리 값이 범위를 벗어났습니다.
1782 0x06F6 바이트 수가 너무 작습니다.
1783 0x06F7 스터브에 불량 데이터가 수신되었습니다.
1784 0x06F8 공급된 사용자 버퍼가 요청한 작업에 부적합합니다.
1785 0x06F9 디스크 매체를 인식할 수 없습니다. 포맷되지 않은 것같습니다.
1786 0x06FA 워크스테이션에 신뢰성 있는 기밀이 없습니다.
1787 0x06FB Windows NT Server의 SAM 데이터베이스에 이 워크스테이션의 신뢰성 있는 관계를 위한 컴퓨터
계정이 없습니다.
1788 0x06FC 주 도메인과 신뢰성 있는 도메인 간에 신뢰성 있는 관계를 설정할 수 없습니다.
1789 0x06FD 이 워크스테이션과 주 도메인 간에 신뢰성 있는 관계를 가질 수 없습니다.
1790 0x06FE 네트워크 로그인 할 수 없습니다.
1791 0x06FF 이 스레드에 대한 원격 프로시저 호출이 이미 진행주입니다.
1792 0x0700 로그온을 시도했지만 네트워크 로그온 서비스가 시작하지 않았습니다.
1793 0x0701 사용자의 계정이 만료되었습니다.
1794 0x0702 네트워크 재지정이 사용 중이므로 해제할 수 없습니다.
1795 0x0703 지정한 프린터 드라이버가 이미 설치되었습니다.
1796 0x0704 지정한 포트는 알 수 없는 포트입니다.
1797 0x0705 알려지지 않은 프린터 드라이버입니다.
1798 0x0706 인쇄 프로세서가 알 수 없는 것입니다.
1799 0x0707 지정한 분리자 파일이 잘못되었습니다.
1800 0x0708 지정한 우선순위가 잘못되었습니다.


1801 0x0709 프린터 이름이 잘못되었습니다.
1802 0x070A 프린터가 이미 존재합니다.
1803 0x070B 프린터 명령이 잘못되었습니다.
1804 0x070C 지정한 데이터 형식이 잘못되었습니다.
1805 0x070D 지정한 환경이 잘못되었습니다.
1806 0x070E 바인딩이 더 이상 없습니다.
1807 0x070F 사용된 계정이 도메인 상호 신탁 계정입니다. 전역 사용자 계정이나 지역 사용자 계정을 사용해서
서버에 액세스하십시오.
1808 0x0710 사용한 계정이 컴퓨터 계정입니다. 전역 사용자 계정이나 지역 사용자 계정을 사용하여 서버에
액세스하십시오.
1809 0x0711 사용한 계정이 서버 신탁 계정입니다. 전역 사용자 계정이나 지역 사용자 계정을 사용하여 서버에
액세스하십시오.
1810 0x0712 지정한 도메인의 이름이나 시스템 보안 ID (SID)가 해당 도메인의 신탁 정보와 일치하지 않습니다.
1811 0x0713 서버가 사용 중이며 언로드할 수 없습니다.
1812 0x0714 지정한 이미지 파일에 리소스 영역이 없습니다.
1813 0x0715 지정한 리소스 형식을 이미지 파일에서 찾을 수 없습니다.
1814 0x0716 지정한 리소스 이름을 이미지 파일에서 찾을 수 없습니다.
1815 0x0717 지정한 리소스 언어 ID를 이미지 파일에서 찾을 수 없습니다.
1816 0x0718 할당된 리소스가 부족하여 이 명령을 처리할 수 없습니다.
1817 0x0719 등록된 인터페이스가 없습니다.
1818 0x071A 호출을 처리하는 과정에서 서버가 수정되었습니다.
1819 0x071B 바인딩 핸들에 필요한 정보 중 일부가 없습니다.
1820 0x071C 통신 오류.
1821 0x071D 요청한 인증 단계가 지원되지 않습니다.
1822 0x071E 주요 이름이 등록되지 않았습니다.
1823 0x071F 지정한 오류는 올바른 Windows RPC 오류 코드가 아닙니다.
1824 0x0720 이 컴퓨터에서만 유효한 UUID가 할당되었습니다.
1825 0x0721 보안 패키지 특정 오류가 발생했습니다.
1826 0x0722 스레드가 취소되지 않았습니다.
1827 0x0723 인코딩/디코딩 핸들에서 잘못된 작동이 발생했습니다.
1828 0x0724 일련의 순차적인 패키지 중 호환되지 않는 버전입니다.
1829 0x0725 RPC 중 호환되지 않는 버전입니다.
1898 0x076A 그룹 멤버를 찾을 수 없습니다.
1899 0x076B 엔드포인트 매퍼 데이터베이스를 작성할 수 없습니다.
1900 0x076C 개체 일반 고유 식별자(UUID)에 아무런 값이 없습니다.


1901 0x076D 지정한 시간이 잘못되었습니다.
1902 0x076E 지정한 폼 이름이 잘못되었습니다.
1903 0x076F 지정한 폼 크기가 잘못되었습니다.
1904 0x0770 지정한 프린터 핸들이 이미 대기중입니다.
1905 0x0771 지정한 프린터가 삭제되었습니다.
1906 0x0772 프린터의 상태가 올바르지 않습니다.
1907 0x0773 최초로 로그온하기 전에 사용자 암호를 바꾸어야 합니다.
1908 0x0774 이 도메인에 대한 도메인 컨트롤러를 찾을 수 없습니다.
1909 0x0775 참조 계정이 현재 잠겨 있어서 로그온할 수 없습니다.

2000 0x07D0 픽셀 포맷이 올바르지 않습니다.

2001 0x07D1 지정한 드라이버가 올바르지 않습니다.
2002 0x07D2 창 모양 또는 클래스 속성이 이 작업에 올바르지 않습니다.
2003 0x07D3 요청한 메타파일 작업은 지원하지 않습니다.
2004 0x07D4 요청한 변환작업은 지원하지 않습니다.
2005 0x07D5 요청한 자르기 작업은 지원하지 않습니다.
2202 0x089A 지정한 사용자 이름이 올바르지 않습니다.
2250 0x08CA 네트워크 연결이 없습니다.
2401 0x0961 네트워크 연결에 얼려 있는 파일이 있거나 처리되지 않은 요청이 있습니다.
2402 0x0962 사용중인 연결이 아직 있습니다.
2404 0x0964 장치가 활성 프로세서에서 사용 중이며 연결 해제할 수 없습니다.

3000 0x0BB8 지정한 인쇄 모니터를 알 수 없습니다.
3001 0x0BB9 지정한 프린터 드라이버가 현재 사용중입니다.
3002 0x0BBA 스풀 파일을 찾을 수 없습니다.
3003 0x0BBB StartDocPrinter 호출이 사용되지 않았습니다.
3004 0x0BBC AddJob 호출이 사용되지 않았습니다.
3005 0x0BBD 지정한 인쇄 프로세서가 이미 설치되었습니다.
3006 0x0BBE 지정한 인쇄 모니터가 이미 설치되었습니다.
4000 0x0FA0 명령을 처리하는 동안에 WINS에서 오류가 발생했습니다.
4001 0x0FA1 지역 WINS를 삭제할 수 없습니다.
4002 0x0FA2 파일로부터 들여오기 작업이 실패했습니다.
4003 0x0FA3 백업을 실패했습니다. 이전에 전체 백업을 하셨습니까?
4004 0x0FA4 백업을 실패했습니다. 데이터베이스를 백업한 디렉터리를 확인해 보십시오.
4005 0x0FA5 WINS 데이터베이스에 이 이름이 없습니다.
4006 0x0FA6 구성되지 않은 파트너를 갖는 이중화는 사용할 수 없습니다.
6118 0x17E6 이 작업 그룹의 서버 목록을 현재 사용할 수 없습니다.

2007/05/21 22:29 2007/05/21 22:29
이 글에는 트랙백을 보낼 수 없습니다
New Object Model

PHP 5 에는 새로운 객체 모델이 존재한다. PHP의 객체 조작사항은 더 나은 성능과 보다 많은 특징을 위해 완벽하게 재작성되었다. PHP이전 버전에서 객체는 원시적인 형태로 조작되었다. ( 실례를 들자면 정수나 문자열 같은 것들을 말이다. ). 이 객체 안의 메소드가 가진 결점은 의미론적으로, 어떤 변수가 할당되었거나 파라미터의 값을 메소드로 전달할 때 모든 객체들이 복제된다는 것이었다. 이제 새로운 접근방식에서는 객체가 핸들에 의해 참조되고 값으로 참조되지 않는다. ( 객체식별자와 같은 핸들로 생각할 수 있을 것이다 ).

   많은 PHP 프로그래머들은 심지어는 과거의 객체모델이 멋대로 복제되는 것을 눈치채지 못했고 그랬기때문에 PHP어플리케이션의 대부분은 아주 좁은 틀을 벗어나 동작했을 것이다. 


Private and Protected Members

PHP 5 는 클래스 프로퍼티의 가시성( 可視性 : visibility )을 정의할 수 있게 하는 private 와 protected 멤버 변수를 도입했다.


Example B-4. Private and Protected Members accesibility

Protected 멤버 변수는 확장된 클래스에서 중복 선언되면 접근할 수 있는데 비해, private 멤버 변수는 단지 멤버 변수를 소유한 클래스에서만 접근이 가능하다.

<?php
class MyClass
{
  
private $Hello = "Hello, World!\n"
;
  
protected $Bar = "Hello, Foo!\n"
;
  
protected $Foo = "Hello, Bar!\n"
;

   function
printHello
() {
       print
"MyClass::printHello() " . $this->Hello
;
       print
"MyClass::printHello() " . $this->Bar
;
       print
"MyClass::printHello() " . $this->Foo
;
   }
}

class
MyClass2 extends MyClass
{
  
protected $Foo
;
            
   function
printHello
() {
      
MyClass::printHello();                         
/* Should print */
      
print "MyClass2::printHello() " . $this->Hello;
/* Shouldn't print out anything */
      
print "MyClass2::printHello() " . $this->Bar
/* Shouldn't print (not declared)*/
      
print "MyClass2::printHello() " . $this->Foo
/* Should print */
  
}
}

$obj = new MyClass
();
print
$obj->Hello
/* Shouldn't print out anything */
print $obj->Bar;   
/* Shouldn't print out anything */
print $obj->Foo;   
/* Shouldn't print out anything */
$obj->printHello();
/* Should print */

$obj = new MyClass2
();
print
$obj->Hello
/* Shouldn't print out anything */
print $obj->Bar;   
/* Shouldn't print out anything */
print $obj->Foo;   
/* Shouldn't print out anything */
$obj->printHello
();
?>
 
 

PHP 5에서는 또한 private 와 protected 메소드가 도입되었다.


Example B-5. Protected methods example

<?php
class Foo
{
  
private function aPrivateMethod
() {
       echo
"Foo::aPrivateMethod() called.\n"
;
   }

  
protected function aProtectedMethod
() {
       echo
"Foo::aProtectedMethod() called.\n"
;
      
$this->aPrivateMethod
();
   }
}

class
Bar extends Foo
{
  
public function aPublicMethod
() {
       echo
"Bar::aPublicMethod() called.\n"
;
      
$this->aProtectedMethod
();
   }
}

$o = new Bar
;
$o->aPublicMethod
();
?>

과거의 코드에서는 사용자 정의된 클래스나 함수가 "public", "protected" 또는 "private"를 명기하지 않고 실행했을 것이다.


Abstract Classes and Methods

PHP 5는 또한 추상클래스라는 것과 추상메소드라는 것이 도입되었다. 추상메소드는 단지 메소드의 특징적인 사항을 선언하고 수행사항을 제공하지는 않는다. 다음 예시에서 클래스는 abstract로 선언하는 것을 필요로 하는 추상 메소드를 포함한다.


Example B-6. Abstract class example

<?php
abstract
class AbstractClass
{
  
abstract public function test
();
}

class
ImplementedClass extends AbstractClass
{
  
public function test
() {
       echo
"ImplementedClass::test() called.\n"
;
   }
}

$o = new ImplementedClass
;
$o->test
();
?>

요약클래스는 명시화될 수 없다. 과거의 코드에서는 사용자 정의된 클래스나 함수에 "abstract"를 명기하지 않고 실행했을 것이다.


Interfaces

PHP 5 에서 interface라는 새로운 객체지향 개념이 도입되었다. 클래스는 인터페이스의 임의의 목록을 수단으로 삼을 것이다.


Example B-7. Interface example

<?php
interface Throwable
{
  
public function getMessage
();
}

class
MyException implements Throwable
{
  
public function getMessage
() {
      
// ...
  
}
}
?>

과거에는 사용자정의된 클래스나 함수에 "interface"나 "implements"를 붙이지 않고 실행했을 것이다.


Class Type Hints

 느슨한 유형이 남아있는 PHP5는 클래스 타입 힌트를 사용하여 파라미터로부터 메소드로 값을 전달하는 개체의 요청된 클래스를 선언하는 능력을 도입하였다.


Example B-8. Class type hinting example

<?php
interface Foo
{
   function
a(Foo $foo
);
}

interface Bar
{
   function
b(Bar $bar
);
}

class
FooBar implements Foo, Bar
{
   function
a(Foo $foo
) {
      
// ...
  
}

   function
b(Bar $bar
) {
      
// ...
  
}
}

$a = new FooBar
;
$b = new FooBar
;

$a->a($b
);
$a->b($b
);
?>

이러한 클래스 타입 힌트는 보통 유형화된 언어에서와 같이 컴파일 할 때에는 체크하지 않는다. 그러나 실행 도중에 체크한다. 이것은 다음을 의미한다:

<?php
function foo(ClassName $object
) {
  
// ...
}
?>


는 다음과 같다 :

<?php
function foo($object
) {
   if (!(
$object instanceof ClassName
)) {
       die(
"Argument 1 must be an instance of ClassName"
);
   }
}
?>
 

PHP 5 는 최종 멤버와 메소드를 선언하기 위해 "final" 키워드를 도입하였다. final을 붙인 메소드와 멤버는 하위클레스에 의해 오버라이드 되지 않는다.


Example B-9. final method

<?php
class Foo
{
  
final function bar
() {
      
// ...
  
}
}
?>

이것은 더군다나 최종적인 클래스를 만드는데 가능하다. 이것은 클래스가 특별화 되는 것을 막는다. (다른 클래스에 의해 '절대' 상속될 수 없다.). final 클래스는 자신들의 메소드를 final 로 선언할 필요가 없다.


Example B-10. final class

<?php
final
class Foo
{
  
// class definition
}

// the next line is impossible
// class Bork extends Foo {}
?>

프로퍼티는 final 속성을 가질 수 없다..

과거에는 사용자정의된 클래스나 함수에 "final"을 붙이지 않고 실행했을 것이다.


Objects Cloning

   PHP 4 에서는 객체가 복제되었을 때 복제된 생성자를 실행하기 위한 결정을 사용자가 할 수 없는 그런 방법을 제공했었다. 복제하는 동안에는 PHP 4 에서는 모든 객체의 프로퍼티에 대한 이상적 복제물을 만들기 위해 비트복사를 하는 단역을 수행하였다.  

   전체적으로 복제된 프로퍼티와 함께 복제된 객체를 만드는 것은 항상 원하는 행동이 아니다. 생성자 복제를 위해 필요한 것에 대한 좋은 예는 만약 GTK 윈도우를 묘사하는 객체를 소유하고 있고 객체는 이 객체를 복제할때의 GTK 윈도우의 리소스로 유지될 때, 아마 같은 프로퍼티를 가진 새로운 윈도우를 생성하길 원할것이고 새 윈도우의 리소스를 새 객체가 포함하길 원할 것이다. 다른 예로는 만약 객체가 다른 객체를 사용하는 참조자를 붙들어매고 있고, 개발자는 부모객체를 복제할때, 이 다른 객체의 새로운 인스턴스를 생성하길 원할 것이고 그래서 복제된 개체는 그 자신의 분산된 복제물을 가지고 있을 것이다.

복제되는 객체는 clone 키워드를 사용하여 생성된다. ( 가능하다면 객체의 __clone() 메소드를 호출한다 ). 객체의 __clone() 메소드는 직접적으로 호출될 수는 없다.

개발자들이 객체의 새로운 복제물을 생성하기 위해 요청할때, PHP 5 는 __clone() 메소드가 정의 되었는지 아닌지의 여부를 체크할 것이다. 만약 정의되지 않았다면 모든 객체의 프로퍼티를 복제할 기본적인 __clone() 을 호출할 것이다. __clone() 메소드가 정의되었다면 생성된 객체 안에 필요한 프로퍼티를 정할 의무가 있을 것이다. 편의를 위해 엔진은 소스객체로부터 모든 프로퍼티를 임포팅하는 함수를 공급해서 그것들이 소스 오브젝트의 값에 의한 복제물 과 함께 시작할 수 있도록 할 것이고 변경할 필요가 있는 프로퍼티를 단지 오버라이드 할 것이다.


Example B-11. Objects cloning

<?php
class MyCloneable
{
   static
$id = 0
;

   function
MyCloneable
() {
      
$this->id = self::$id
++;
   }

   function
__clone
() {
      
$this->address = "New York"
;
      
$this->id = self::$id
++;
   }
}

$obj = new MyCloneable
();

$obj->name = "Hello"
;
$obj->address = "Tel-Aviv"
;

print
$obj->id . "\n"
;

$obj_cloned = clone $obj
;

print
$obj_cloned->id . "\n"
;
print
$obj_cloned->name . "\n"
;
print
$obj_cloned->address . "\n"
;
?>
 

PHP 5 는 개발자로 하여름 클래스를 위해 생성자 메소드를 선언할 수 있게 한다. 생성자 메소드를 가진 클래스들은 새롭게 생성된 객체에서 이 메소드를 초기화 하는데 적당하게 호출해서 객체가 사용되기 전에 필요로 하지 않게 할 것이다.

PHP 4 에서 생성자 메소드는 클래스 자신의 이름과 같은 이름을 가진 클래스 메소드였다. 파생된 클래스로부터 부모 생성자를 호출하는 것이 매우 일반적일 때, PHP 4는 거대한 클래스 계층 주변으로 클래스를 옮기는 성가신 부분을 만들어내는 방법을 택해 작업했다. 만약 클래스가 다른 부모클래스 아래로 귀속하기 위해 옮겨졌을 때, 부모클래스 생성자의 잘 바뀌는 이름과 부모클래스 생성자를 호출하는 파생된 클래스의 코드는 수정될 것이다.

PHP 5 는 __construct()라는 이름으로 불리우는 생성자 메소드를 선언하는 표준방법을 도입하였다. 


Example B-12. using new unified constructors

<?php
class BaseClass
{
   function
__construct
() {
       print
"In BaseClass constructor\n"
;
   }
}

class
SubClass extends BaseClass
{
   function
__construct
() {
      
parent::__construct
();
       print
"In SubClass constructor\n"
;
   }
}

$obj = new BaseClass
();
$obj = new SubClass
();
?>

이전 버전과의 호환성을 위해 만약 PHP 5 가 주어진 클래스를 위한 __construct() 함수를 찾지 못한다면 클래스의 이름으로 이루어진 이전 방식의 생성자 함수를 찾을 것이다. 효과적으로 이것은 클래스가 __construct() 라는 이름을 가진 메소드를 가진 경우 이것이 다른 의미론을 위해 쓰일것이라는 의미를 가지고 있는데 호환성 문제를 가진 경우에 한해서만 해당한다.

Destructors

객체를 위해 파괴자를 정의하는 능력을 소유하는 것은 매우 유용할 수 있다. 파괴자는 디버깅을 위해, 데이터베이스의 연결을 끊을 때,기타 다른 여러가지 일을 마무리할 때 로그 메세지를 남길 수 있다. PHP 4에선느 객체 파괴자를 위한 기술이 존재하지 않았지만, PHP가 셧다운을 요청하는 도중에 실행될 등록함수를 위해 이미 지원했다. 

PHP 5 는 자바와 같은 다른 객체지향언어에서와 유사한 개념을 가진 파괴자를 도입하였다. : 객체의 파괴자를 파괴한 객체가 최종적으로 참조되었을때,  어떤 파라미터 값도 받지 않는 __destruct() 라고 이름 붙여진 클래스 메소드는 메모리에서 객체가 해제되기 전에 호출된다.

Example B-13. Destructor

<?php
class MyDestructableClass
{
   function
__construct
() {
       print
"In constructor\n"
;
      
$this->name = "MyDestructableClass"
;
   }

   function
__destruct
() {
       print
"Destroying " . $this->name . "\n"
;
   }
}

$obj = new MyDestructableClass
();
?>

생성자와 같이 부모클래스의 파괴자는 엔진에 의해 명시적으로 호출되지는 않을 것이다. 부모클래스의 파괴자를 실행하기 위해 파생된 클래스에서는 명확하게 파괴자 본문에 parent::__destruct() 를 적어 호출하여야 한다.

Constants

PHP 5 는 완전한 클래스 상수를 도입하였다:

Example B-14. Class constant example

<?php
class Foo
{
   const
constant = "constant"
;
}

echo
"Foo::constant = " . Foo::constant . "\n"
;
?>

과거에는 사용자정의된 클래스나 함수에 "const"를 붙이지 않고 실행했을 것이다.


Exceptions

PHP 4 는 예외사항 조정에 대한 요소가 없었다. PHP 5 에서는 다른 프로그래밍 언어와 유사한 예외모델을 도입하였다. "catch all"을 위해 지원하지만, "finally"조항을 위한 것이 아님을 주의하여라.

Exception 은 catch 블록에서 다시 제시될 수 있다. 또한 다양한 catch 블록에서 소유하고 있는것도 가능하다. 이러한 경우에서 잡힌 exception 은 꼭대기부터 바닥까지의 각각의 catch 블록의 클래스타입과 비교되고, 'instanceof'를 지닌 첫 블록의 매치는 실행될 것이다. catch 블록이 끝날때 실행사항은 마지막 catch 블록까지 지속된다. 만약 어떤 catch 블록도 'instanceof' 매치를 지니고 있지 않은경우, 그 다음 try/catch block이 다음 try/catch 블록이 존재하지 않고 사용가능하지 않을때까지 검색될 것이다. 이러한 경우 exception 은 exception 절을 발견하지 못하고 프로그램은 예외사항을 보여주면서 중단한다.


Example B-15. Exception creation example

<?php
try
{
  
throw new Exception('Hello'
);
}
catch (Exception $exception
) {
   echo
$exception
;
}
?>

과거에는 사용자정의된 클래스나 함수에 "catch","throw" 또는 "try"를 붙이지 않고 실행했을 것이다.

Dereferencing objects returned from functions

PHP 4 에서는 함수에 의해 반환되는 객체들의 값을 부분참조하기란 불가능했고, 더 이상의 메소드를 만드는 것은 이러한 객체를 호출하였다. PHP 5에서는 다음과 같은 것들이 가능하다 :


Example B-16. Dereferencing example

<?php
class Circle
{
   function
draw
() {
       print
"Circle\n"
;
   }
}
    
class
Square
{
   function
draw
() {
       print
"Square\n"
;
   }
}

function
ShapeFactoryMethod($shape
) {
   switch (
$shape
) {
       case
"Circle"
:
           return new
Circle
();
       case
"Square"
:
           return new
Square
();
   }
}

ShapeFactoryMethod("Circle")->draw
();
ShapeFactoryMethod("Square")->draw
();
?>

Static member variables initialization

정적 클래스의 정적 멤버 변수가 이제 초기화될 수 있다.


Example B-17. Static variable initialization example

<?php
class foo
{
   static
$my_static = 5
;
  
public $my_prop = 'bla'
;
}

print
foo::$my_static
;
$obj = new foo
;
print
$obj->my_prop
;
?>
 

PHP 5 는 메소드를 정적으로 선언하기 위해 'static' 키워드를 도입하였다. 그래서 객체의 외부 전후에서 호출할 수 있다.


Example B-18. Static Methods example

<?php
class Foo
{
  
public static function aStaticMethod
() {
      
// ...
  
}
}

Foo::aStaticMethod
();
?>

가상변수 $this 는 static으로 선언된 메소드 안에서는 사용할 수 없다.


instanceof

PHP 5 는 객체가 클래스의 인스턴스건 아니건간에 그것을 조사하고 클래스를 확장하고 인터페이스조건을 제시하게 하기 위한 instanceof  키워드를 도입하였다.


Example B-19. instanceof example

<?php
class baseClass
{ }

$a = new baseClass
;

if (
$a instanceof baseClass
) {
   echo
"Hello World"
;
}
?>
 

Static은 개발자로 하여금 참조의 방식으로 변수의 값을 상수로 전달하도록 하는 컴파일 도중에 다루어진다. 이러한 변화는 또한 그들의 성능을 엄청나게 향상시켰지만 상수로의 우회적 참조가 더이상의 일을 할 수 없음을 의미한다.


Parameters passed by reference

참조에 의해 함수로 전달된 파라미터값은 아마 이제부터 기본값을 가질 수 있을 것이다.


Example B-20.

<?php
function my_function(&$var = null
) {
   if (
$var === null
) {
       die(
"$var needs to have a value"
);
   }
}
?>
 

__autoload() 요격실행 함수는 선언되지 않은 클래스가 명시화되었을 때, 자동적으로 호출될 것이다. 해당 클래스의 이름은 __autoload() 요격실행 함수로 인자처럼 전달될 것이다.


Example B-21. __autoload() example

<?php
function __autoload($className
) {
   include_once
$className . ".php"
;
}

$object = new ClassName
;
?>
 

메소트 호출과 프로퍼티 접근은 __call(), __get() 그리고 __set() 메소드에 의해 오버로드 될 수 있다.


Example B-22. __get() and __set()

<?php
class Setter
{
  
public $n
;
  
public $x = array("a" => 1, "b" => 2, "c" => 3
);

   function
__get($nm
) {
       print
"Getting [$nm]\n"
;

       if (isset(
$this->x[$nm
])) {
          
$r = $this->x[$nm
];
           print
"Returning: $r\n"
;
           return
$r
;
       } else {
           print
"Nothing!\n"
;
       }
   }

   function
__set($nm, $val
) {
       print
"Setting [$nm] to $val\n"
;

       if (isset(
$this->x[$nm
])) {
          
$this->x[$nm] = $val
;
           print
"OK!\n"
;
       } else {
           print
"Not OK!\n"
;
       }
   }
}


$foo = new Setter
();
$foo->n = 1
;
$foo->a = 100
;
$foo->a
++;
$foo->z
++;
var_dump($foo
);
?>

Example B-23. __get() example

<?php
class Caller
{
  
private $x = array(1, 2, 3
);

   function
__call($m, $a
) {
       print
"Method $m called:\n"
;
      
var_dump($a
);
       return
$this->x
;
   }
}

$foo = new Caller
();
$a = $foo->test(1, "2", 3.4, true
);
var_dump($a
);
?>
 

객체는 아마 foreach와 같은 방식을 사용하였을때의 오버로딩하는 방법으로 반복될 수 있을 것이다. 기본적인 행동은 모든 프로퍼티를 반복한다.


Example B-24. Object iteration example

<?php
class Foo
{
  
public $x = 1
;
  
public $y = 2
;
}

$obj = new Foo
;

foreach (
$obj as $prp_name => $prop_value
) {
  
// using the property
}
?>

인스턴스의 각각의 클래스는 Traversable이라는 빈 인터페이스를 실행할 foreach로 반복될 수 있다. 따가서 적합하게 실행되는 몇몇 객체들은 foreach와 합께 사용될 수 있다.

IteratorAggregate 와 Iterator 인터페이스는 PHP 코드에서 반복사용되는 방법을 상술할 수 있도록 한다. 그것들에 있어 첫번째는 Iterator 인터페이스를 이행하거나 반복가능한 내부 클래스에서 명시회되는 배열이나 객체를 반드시 리턴해야 하는 getIterator() 라는 메소드를 포함한다.


Example B-25. Iterator creation example

<?php
class ObjectIterator implements Iterator
{

  
private $obj
;
  
private $num
;

   function
__construct($obj
) {
      
$this->obj = $obj
;
   }
   function
rewind
() {
      
$this->num = 0
;
   }
   function
valid
() {
       return
$this->num < $this->obj->max
;
   }
   function
key
() {
       return
$this->num
;
   }
   function
current
() {
       switch(
$this->num
) {
           case
0: return "1st"
;
           case
1: return "2nd"
;
           case
2: return "3rd"
;
           default: return
$this->num."th"
;
       }
   }
   function
next
() {
      
$this->num
++;
   }
}

class
Object implements IteratorAggregate
{

  
public $max = 3
;

   function
getIterator
() {
       return new
ObjectIterator($this
);
   }
}

$obj = new Object
;

// this foreach ...
foreach($obj as $key => $val
) {
   echo
"$key = $val\n"
;
}

// matches the following 7 lines with the for directive.
$it = $obj->getIterator
();
for(
$it->rewind(); $it->valid(); $it->next
()) {
  
$key = $it->current
();
      
$val = $it->key
();
         echo
"$key = $val\n"
;
}
unset(
$it
);
?>
 

새로운 __METHOD__ 가상 상수는 현재 클래스와 메소드 내부에서 메소드가 사용될 때의 메소드를 보여주고, 클래스 밖에서 사용될때의 함수를 보여준다.


Example B-26. __METHOD__ use example

<?php
class Foo
{
   function
show
() {
       echo
__METHOD__
;
   }
}

class
Bar extends Foo
{
}

Foo::show();
// outputs Foo::show
Bar::show();
// outputs Foo::show either since __METHOD__ is
             // compile-time evaluated token

function test
() {
   echo
__METHOD__
;
}

test();     
// outputs test
?>
 

새로운 __toString() 기술 메소드는 객체를 문자열 변환객체로 오버로드 하도록 한다.


Example B-27. __toString() example

<?php
class Foo
{
   function
__toString
() {
       return
"What ever"
;
   }
}

$obj = new Foo
;

echo
$obj;
// call __toString()
?>
 

PHP 5 는 재설계한 클래스와 인터페이스, 함수와 메소드를 확장요소들과 같은 기능들을 추가한 완벽한 Reflection API 와 함께 출시되었다. 

Reflection API 는 또한 함수와 클래스 그리고 메소드를 위한 도큐먼트 해설을 얻는 방법을 제공한다.

거의 모든 객체지향코드의 외관모습은 개별적으로 상세히 기록된 Reflection API를 사용함에 의해 반영될 수 있다.


Example B-28. Reflection API use example

<?php
class Foo
{
  
public $prop
;
   function
Func($name
) {
       echo
"Hello $name"
;
   }
}

reflectionClass::export('Foo'
);
reflectionObject::export(new Foo
);
reflectionMethod::export('Foo', 'func'
);
reflectionProperty::export('Foo', 'prop'
);
reflectionExtension::export('standard'
);
?>

========================================================================================

2007/05/18 19:25 2007/05/18 19:25
이 글에는 트랙백을 보낼 수 없습니다

x64 프라이머
 

64비트 윈도우를 프로그래밍 하기 위해서 알아야 할 모든 것들


 

이 글은 아래의 내용을 다루고 있습니다:

    64비트 윈도우의 기초적인 내용

    x64의 간략한 내부 구조

    Visual C++ 2005로 x64용 소프트웨어 개발

    x64용 소프트웨어를 위한 몇 가지 디버깅 기술들

이 글은 아래의 기술들을 사용합니다:
Windows, Win64, Visual Studio 2005

목차


새 로운 64비트 윈도우에서 일했던 경험의 좋았던 점 중의 하나는, 새로운 기술이 어떻게 동작하는지 눈으로 확인할 수 있다는 것이었습니다. 저 자신은 특히 어떤 운영체제 밑바닥에 대해서 조금이라도 알기 전까지는, 그 운영체제에 대해서 그렇게 편안함을 느끼지 못하는 편입니다. 그래서, 64비트 Windows XP와 Windows Server™ 2003이 나타났을 때, 저는 아주 열심히 그 운영체제에 대해서 연구하였습니다.
 

Win64 와 x64 CPU의 좋은 점은, 그 전의 CPU와 조금 다른 구조를 가지고 있지만, 그 차이를 배우는데 그렇게 많은 시간이 요구되지 않는 다는 점입니다. 저희 같은 개발자들에게는, x64로의 이동이 단지 컴파일만 다시 하면 끝나는 그런 작업이었으면 좋겠지만, 그렇게 생각하고 작업을 하신다면, 앞으로 디버거에서 너무 많은 시간을 보내셔야 할 것 같습니다.
 

이 글에서, 저는 Win64와 x64의 내부구조에 대한 저의 지식을 종합해서 유능한 Win32® 프로그래머가 x64로 이동하기 위해서 꼭 필요한 지식들 제공하겠습니다. 저는 여러분이 이미 Win32의 개념과 기본 x86개념, 그리고 왜 여러분의 코드가 Win64에서 동작 해야 하는지에 대해서 이미 알고 계신다고 가정하겠습니다. 그렇게 해야지 제가 좀 더 핵심적인 것들에 집중할 수 있거든요. 여기서의 이 요약이 x86 내부구조와 Win32에 대한 여러분의 지식과 비교해서 상대적으로 중요한 차이점에 대한 고찰이라고 생각하시기 바랍니다.
 

x64 시스템에서 한 가지 좋은 점은, 아이템니윰(Itanium)기반 시스템과는 틀리게 여러 분이 심각한 효율 저하에 대한 고민을 하지 않고, Win32 혹은 Win64를 동일한 기계에서 이용하실 수 있다는 점입니다. 그리고 인텔과 AMD의 x64 구현에 조금 불명확한 몇 가지의 차이가 존재함에도 불구하고, x64용 윈도우는 둘 중 어느 곳에서나 동작합니다. 여러분이 AMD x64와 Intel x64 시스템을 위해서 각각 다른 버젼의 윈도우가 필요하지 않습니다.
 

저는 여기서 크게 세 가지 영역으로 이 글을 나누어서 진행하도록 하겠습니다: 운영체제의 구현의 몇 가지 자세한 내용들, x64 CPU 내부 구조에 대한 개괄적인 설명, 그리고 Visual C++로 x64용 프로그램 개발.


x64 운영체제

 

저 는 어떤 윈도우 내부구조를 설명하더라도, 처음에는 메모리와 주소 공간에서 부터 시작합니다. 비록 64비트 프로세서가 이론적으로는 16 엑사 바이트(exabytes) 의 메모리에 접근할 수 있다고 하더라고, Win64는 현재 16 테라바이트(terabytes), 44비트 만을 지원하고 있습니다. 왜 64비트 전부를 사용해서, 16 엑사바이트 전부를 쓸 수 없었을 까요? 거기에는 몇 가지 이유가 존재합니다.
 

맨 처음 이유로는, 현재 x64 CPU들이 물리적인 메모리 공간에 접근할 때, 오직 40비트(1테라바이트)만을 허용합니다. 그 제한이 없어진다고 하더라고, 지금 현재의 하드웨어가 아닌, 향후에 나올 수 있는 CPU들의 내부구조들이 오직 52비트 (4페타바이트)만큼만 확장될 수 있습니다.그 정도만 해도, 그 많은 메모리를 매핑하기 위한 페이지 테이블의 사이즈는 어마 어마 할 것입니다.
 

Win32 에서와 같이, 접근할 수 있는 주소 공간은, 사용자와 커널의 영역으로 나누어 집니다. 커널 모드의 코드가 8 테라바이트 상위에서 모든 프로세스에 의해 영역되고, 각각의 프로세서는 8 테라바이트 이하에 자신의 고유 영역을 가집니다. 64비트 윈도우의 각 버젼들은 그림 1그림 2 에서 보여지는 것 처럼, 서로 다른 물리적인 메모리 한계를 가지고 있습니다.

Win32 에서와 같이, x64의 페이지 사이즈는 4KB 입니다. 처음의 64KB 공간은 절대로 매핑 되지 않기 때문에, 여러분이 볼 수 있는 맵핑 주소중 가장 낮은 번지는 0x10000입니다. Win32에서는 다르게, 시스템 DLL들은 사용자 모드 주소 공간의 제일 위 부분과 근접해 있는, 기본 로드 어드레스라는 것이 없습니다. 그 대신, 시스템 DLL들은 4GB 위의 공간에 적재 됩니다. 통상적으로, 그 주소는 0x7FF00000000 (8 테라바이트) 근처입니다.
 

새 로운 x64 프로세서의 좋은 기능 중에 하나는, 윈도우가 하드웨어적으로 데이터 실행 보호(Data Execution Protection-DEP)를 할 수 있게 지원해 준다는 것입니다. x86 플랫폼에서는, 많은 버그와 바이러스가 CPU가 데이터를 올바른 코드 바이트로 인식하고 실행했기 때문에 존재할 수 있었습니다. 실수이든 혹은 고의적인 버퍼 오버런(buffer overrun)이 원래는 데이터 저장을 목적으로 했던 메모리 블락을 CPU에서 명령어로 인식하고 실행해 버리는 결과가 발생하곤 했었습니다. 이 DEP의 도움으로, 운영체제는, 의도한 코드 영역의 경계를 명료하게 설정할 수 있고, 이 의도된 경계를 벗어나는 코드 실행에 대해서는 CPU가 일종의 덫을 놓을 수 있게 되었습니다. 이 기능은 윈도우를 악의적인 공격에 덜 취약하게 만드는데 큰 도움을 줄 수 있습니다.
 

에 러들을 잡기 위해서 고안된 장치들 중에 하나가, x64 링커가 실행파일에 대한 기본 적재 주소를 32비트 (4GB) 이상으로 설정한 것입니다. 이것은 코드가 Win64로 포팅된 후, 기존에 있던 코드에서 위에서 이야기한 보안에 문제를 일으키는 부분을 찾기 쉽게 만들어 줍니다. 특히, 만약 포인터가 32비트 값(예를 들어, DWORD)값으로 저장되어 있으면, 그러면, 그 값은 Win64 빌드를 동작시킬 때, 값이 일부분이 짤림 으로서, 포인터를 무효화 시키고, 접근위반(access violation)을 일으킵니다. 이러한 잔기술은 지저분한 포인터 버그를 찾기 쉽게 만들어 줍니다.
 

포 인터와 DWORD에 대한 주제들도 Win64의 타입을 이야기하는데 빠질 수 없습니다. Win64에서 포인터의 크기가 얼마나 될까요? LONG형의 길이는요? 그리고, 핸들과 HWND의 크기는 얼마나 될까요? 다행스럽게도, 마이크로 소프트가 Win16에서 Win32로의 좀 지저분한 변환을 하면서, 새로운 자료형의 모델에 대해서는 64비트 까지의 확장이 쉽게 될 수 있도록 만들었습니다. 일반적으로 이야기 해서, 몇 가지 예외를 제외하고, 새로운 64비트 세계에서도, 포인터와 size_t를 제외한 모든 나머지 자료형 들은 Win32에서와 동일한 길이를 가지고 있습니다. 즉, 64비트 포인터는 8바이트인 반면에, int,long, DWORD는 아직도 4바이트입니다. 후반부에 Win64 개발에 대해서 이야기 하면서, 이 자료형에 대해서 좀 더 이야기 하도록 하겠습니다.[편집자 주 - 5/2/2006: 핸들은 포인터 값으로 선언되었습니다. 그래서, Win64에서는 4바이트가 아닌 8바이트 값입니다.]
 

Win64 의 포맷은 PE32+라고 불립니다. 거의 모든 관점에서 이 포맷은 Win32PE 파일과 거의 구조적으로 동일합니다. ImageBase 같은 몇 몇의 포맷은 더 크기가 커졌고, 한 필드는 없어졌고, 그리고 다른 하나의 필드는 다른 CPU 타입을 반영할 수 있도록 변경되었습니다. 그림 3 은 바뀐 필드들을 보여 줍니다.
 

PE 헤더를 말고는, 그렇게 많이 바뀐 부분이 없습니다. IMAGE_LOAD_CONFIG, IMAGE_THUNK_DATA같은 몇 몇 구조체들은 단순히 필드들을 64비트로의 확장을 했을 뿐입니다. PDATA 섹션의 추가는 Win32와 Win64 구현의 주된 차이점중의 하나를 두드러지게 한다는 점에서 아주 흥미롭습니다: 그 차이는 바로 예외 처리(exception handling) 입니다.
 

x86 세계에서는, 예외 처리는 스택에 기반했었습니다. Win32 함수가 try/catch 혹은 try/finally 코드를 가지고 있을 때, 컴파일러는 스택에 작은 데이타 블락을 만들어 놓는 코드를 생성했었습니다. 추가로, 각각의 데이타 블락은 이전 try 데이타 구조체를 가리켰었습니다. 그래서, 최근에 추가된 구조체가 리스트의 헤드가 되는 링크드 리스트를 생성했었습니다. 함수가 불려지고, 종료될 때, 링크드 리스트의 헤더는 계속 갱신이 되었었습니다. 그러다, 예외가 발생하는 경우, 운영체제가 스택에 있는 링크드 리스트의 블락을 살펴서, 적절한 핸들러를 찾는 방식으로 이루어져 있었습니다. 저의 1997년 MSJ article 에서 좀 더 자세한 내용을 찾아 보실 수 있습니다.
 

Win32 예외처리와는 반대로, Win64 (x64와 아이템니움(Itanium) 버젼 둘 다 해당됩니다.)에서는 테이블 기반의 예외 처리를 사영합니다. 더 이상 스택 위에 있는 try 데이타 블락의 링크드 리스트는 없습니다. 대신, 각각의 Win64 실행파일은 런타임 함수 테이블을 가지고 있습니다. 각각의 함수 테이블은 함수의 시작 주소와 끝 주소를 가지고 있을 뿐만 아니라, 함수의 스택 프레임 레이아웃과 예외 처리 코드에 관련된 데이타들의 위치 역시 가지고 있습니다. 이 구조체들의 핵심을 보기 위해서 WINNT.H안에 들어 있는 x64 SDK안에 WINNT.H 안에 있는 IMAGE_RUNTIME_FUNCTION_ENTRY 구조를 살펴 보시기 바랍니다.
 

예 외가 발생했을 때, 운영체제는 쓰레드 스택을 하나씩 탐색합니다. 스택을 탐색하면서 각각의 프레임을 탐색하고, 저장된 인스트럭션 포인터를 찾아서, 운영체제가 어떤 실행 모듈안에 인스트럭션 포인터가 있는지를 결정합니다. 그리고 나서, 운영체제는 런타임 함수 테이블을 그 모듈에서 찾아서, 적절한 런타임 함수를 찾아서, 데이타로 부터 적절한 예외 처리 결정을 내려 줍니다.
 

만 약, 여러분이 로켓 과학자이고, PE32+ 모듈 없이 직접적으로 메모리에서 코드를 생성했으면 어떻게 될까요?  RtlAddFunctionTable API를 이용하여, 운영체제에게 여러분이 동적으로 생성한 코드에 대해서 알려 줄 수 있습니다.
 

테 이블 기반의 예외 핸들링의 단점은 (x86 스택 기반 모델에 비해 상대적으로) 함수 테이블을 찾아 보는 것이, 링크드 리스트에서 값을 찾는 것 보다 훨씬 시간이 많이 걸린다는 점입니다. 장점은, 함수를 실행시킬 때 마다 매 번 try 데이타 블락을 생성시키는 오버헤드가 없다는 점입니다.
 

꼭 기억하세요! 이 글이 아무리 재미있고, 흥미로워도, 이 글은 x64 예외 핸들링에 대한 자세한 설명이라기 보다, 간단한 소개에 불과합니다. x64의 예외 핸들러에 대한 좀 더 깊은 지식을 알고 싶으시다면, Kevin Frei의 블로그을  꼭 한 번 읽어 보시기 바랍니다.
 

x64 에 호환되는 윈도우에 새로운 API가 그렇게 많지는 않습니다; 거의 모든 새로운 Win64 API들은 아이템니움(Itanium) 프로세서를 위한 윈도우 출시 때 이미 추가되었던 것들 입니다. 간단하게, 그 API들 중 가장 중요한 두 개의 API는 IsWow64Process와 GetNativeSystemInfo 입니다. 이 함수들은 Win32 어플리케이션이 자기 자신들이 Win64에서 돌고 있는지의 여부를 알려 줍니다. 그래서, 만약 64비트 환경에서 동작하고 있다면, 시스템의 진짜 사양(capability)를 올바르게 결정할 수 있게 해 줍니다. 반면에, 32비트 프로세스는 GetSystemInfo 함수를 호출하고, 오직 32비트 시스템인 것 처럼, 시스템의 사양(capability)를 볼 수 있습니다. 예를 들어, GetSystemInfo는 32비트 프로세스 주소 영역만을 보고 합니다. 그림 4 는 x86에서는 사용할 수 없고, 오직 x64에서 쓸 수 있는 API들을 보여 주고 있습니다.
 

전 부 다 64비트로 동작하는 윈도우 시스템이 아주 멋지게 들리겠지만, 현실적으로 여러분은 잠시 동안 Win32 코드를 필요로 하게 될 것 같습니다. 그러한 작업을 위해서, x64 버젼의 윈도우는 Win32와 Win64 프로세스를 동시에 동일한 시스템에서 동작시킬 수 있는 WOW64 서브시스템이 포함되어 있습니다. 그러나, 여러분의 32비트 DLL을 64비트 프로세스로 올리거나 혹은 반대의 일들은 지원되지 않습니다. (저를 믿으세요, 아주 좋은 일입니다.) 그리고 마침내 여러분은 구닥다리 16비트 코드에게 잘 가라고 인사를 할 수 있게 되었습니다!
 

x64 버젼의 윈도우에서는, 프로세서는 오직 Win64 DLL들만 로딩할 수 있는, Explorer.exe 같은 64비트 실행 파일에서 부터 시작될 수 있습니다. 반면에, 32비트 실행 파일에서 시작한 프로세스는 오직 Win32 DLL 들만 로딩할 수 있습니다. Win32 프로세스가 커널 모드의 함수를 호출할 때-예를 들어서 파일을 읽는다든지-WOW64는 그 함수를 조용히 가로채서, 올바른 x64 코드의 주소를 주어서 호출하게 합니다.

물 론, 서로 다른 종족(32비트와 64비트) 프로세서들 끼리 통신할 일도 생길 수 있습니다. 운좋게도, Win32에서 여러분이 사랑하고 좋아했던 모든 프로세스간 통신 방법은 Win64에서도 동작합니다. 쉐어드 메모리(shared memory), 네임드 파이프(named pipe), 그리고, 기타 이름이 있는 동기화 객체들을 포함해서 말입니다.
 

여 러분이 혹시 "그럼 시스템 디렉터리도 Win32와 Win64가 동일한가?"라고 생각하실 지도 모르겠습니다. 동일한 디렉터리가 32 비트와 64 비트 KERNEL32 나 USER32 등과 같은 동일한 이름의 시스템 DLL들을 동시에 가질 수 없습니다, 그렇지요? WOW64는 요술같이 파일 시스템의 리다이렉션(redirection)을 통해서 이 문제를 해결합니다. Win32 프로세스에서의 파일에 대한 쓰기 혹은 읽기 등이 발생하면, SysWow64라는 디렉토리에 있는 커널의 함수를 호출하는 것이 아닌, System32에 있는 커널의 함수를 호출하게 합니다. WOW64가 안보이게 SysWow64 디레토리로 요청한 것을 조용히 바꾸어 주는 것입니다. 그래서, Win64 시스템이 효과적으로 두 개의 시스템 디렉터리, 하나는 x64 용 바이너리들과 또 하나는 Win32용의 바이너리를 가지는 것 입니다.
 

약 간 혼란스러울 수 있지만, 이러한 내부적인 처리는 상당히 부드러운 것 처럼 보입니다. 제가 System32 디렉터리의 Kernel32.dll에서 Dir을 실행했을 때, SysWow64 디렉터리에서 했던 것과 정확히 똑같은 결과를 볼 수 있었습니다. 파일 시스템의 리다이렉션이 이런 방식으로 동작하는 것을 정확히 이해하기 까지, 제 자신은 머리가 좀 많이 아팠었습니다. 여러분이 x64 어플리케이션에서 32비트 Windows\System32 폴더를 알기를 정말로 원하신다면, GetSystemWow64Directory 라는 API가 여러분께 정확한 경로를 전달해 줄 것 입니다. 그래도, 전체 내용을 알기 위해서 MSDN 문서를 꼭 읽어 보시기 바랍니다.
 

파 일 시스템의 리다이렉션이외에도, WOW64가 해주는 또 다른 마법 중의 하나가 레지스트리 리다이렉션입니다. 제가 아까 Win64 프로세스에서는 Win32 DLL들을 불러오지 않는다고 했던 말을 생각해 보시고, 그리고 COM 과 in-process 서버 DLL을 불러올 때, 레지스트리를 이용하는 것을 생각해 보시기 바랍니다. 만약, Win32 DLL에 구현되어 있는 COM 오브젝트를 64비트 어플리케이션이 CoCreateInstance를 이용해서, 생성하려고 하면 어떻게 될까요?  DLL이 올라올 수 없습니다, 맞지요? WOW64는 Win32 어플리케이션으로 부터의 접근을 \Software\Classes 레지스트리 노드로 리다이렉션 해 줍니다. 결과적으로 Win32 어플리케이션에서 보는 레지스트리 구조는 x64 어플리케이션에서 보는 것과 서로 다르게 됩니다. 그리고, 여러분이 기대하시는 대로, 운영체제는 32비트 어플리케이션이 RegOpenKey와 그 계열 함수군을 이용하여, 실제로는 64비트인 레지스트리에 접근하려고 할 때,내부적으로 새로운 플래그 값을 주어서, 그 값들에 접근할 수 있게 합니다.
 

약 간만 더 깊숙이 들어가서, 쓰레드 로컬 데이타 영역도 살펴 보아야 합니다. x86 버젼의 윈도우에서는, FS 레지스터가 각 쓰레드의 메모리 영역과 가장 마지막 에러(GetLastError로 확인할 수 있는 에러 값), 그리고 쓰레드의 지역 저장 영역(TLS:Thread Local Storage, TlsGetValue로 값을 얻을 수 있는) 에 사용되었습니다. x64 버젼의 윈도우에서는, FS 레지스터는, GS 레지스터로 교체되었습니다. 그 외에는 거의 동일한 방식으로 x32와 x64의 운영체제가 동작합니다.
 

비 록, 이 글이 x64의 사용자 입장에 초점을 두고 있기는 하지만, 커널 모드의 내부 구조에서 한 가지 추가된 중요한 점이 있습니다. PatchGuard라고 불리는 새로운 기술이 x64 윈도우에 추가되었습니다. 이 기술은 보안과 견고함을 위한 목적으로 추가되었습니다. 작게는 syscall 테이블이나 인터럽트 디스패치 테이블(interrupt dispatch table-IDT)를 변경하는 사용자 프로그램이나 드라이버들은 보안상의 문제와 잠재적인 안정성의 문제를 일으켜 왔었습니다. x64의 내부에서는, 그러한 방식으로 커널의 메모리를 지원되지 않는 방식으로 바꾸는 방식이 허용되지 않습니다. 이러한 것을 강화시키는 기술이 PatchGuard 입니다. 이 기술은 중요한 커널 메모리의 위치가 바뀌는 것은 커널 모드의 쓰레드에서 항상 감시합니다. 그리고 메모리가 바뀌면, 시스템은 버그체크를 통하여 멈춰 버립니다.
 

모 든 것을 고려해 보아도, 만약 여러분이 Win32의 내부 구조에 어느 정도 알고 있고, 어떻게 코드를 쓸 줄 알고, 동작하는지를 알고 있으면, Win64로의 이동에 있어서 크게 놀라지 않으실 겁니다. 거의 대부분은 좀 더 넓은 환경으로의 이동이라고 간주하셔 됩니다.


x64의 간략한 내부 구조

 

자 이제, CPU의 구조 자체에 대해서 조금 살펴 보기로 하겠습니다. 왜냐하면, 기본적인 CPU의 명령어(instructions)에 대해서 알고 있는 것이, 개발(특히 디버깅!)을 훨씬 쉽게 만들기 때문입니다. 처음에 여러분이 알아 차릴 수 있는 것은, 컴파일러가 생성한 x64 코드가 여러분이 알고 있고, 사랑하는 x86 코드와 거의 흡사하다는 점입니다. IA64 코딩의 경우는 그렇게 유사하지 않았었습니다.
 

그 리고, 두번째로 여러분이 알아차릴 수 있는 것은, 레지스터 이름이 여러분이 사용하던 것들과 조금씩 다르고, 레지스터 자체도 조금 많다는 점 입니다. 일반적인 용도의 x64 레지스턷들의 이름은 R로 시작합니다. 예를 들어서, RAX, RBX, 이런 것들이 있습니다. 이것들은 E이름을 가지고 있는 32비트 x86 레지스터들의 확장입니다. 아주 오래 전에, 16비트 AX 레지스터가 32비트 EAX가 되고, 16비트 BX 레지스터가 32비트 EBX가 되었던 것 처럼 말입니다. 32비트로 전이가 될 때 생겨난 E 레지스터들은 64비트로 이동하면서는 R 레지스터들이 된 것이죠. 그래서, RAX는 EAX의 계승자이고, RBX는 EBX의 계승, RSI는 ESI, 그런 식으로 확장되었습니다.
 

추가로, 8개의 일반적인 용도의 레지스터 (R8-R15)가 추가되었습니다. 64비트에서 주로 쓰이는 일반적인 용도의 레지스터들은 그림 5 와 같습니다.

물론 32비트 EIP 레지스터도 RIP 레지스터가 되었습니다. 그리고 32비트 명령어도 여전히 계속 동작하고 32비트 레지스터는 물론이고16비트 레지스터도 (EAX,AX,AL,AH등과 같은) 여전히 유효합니다.
 

그 래픽 작업을 하거나 혹은 과학적인 연산이 필요한 고수들이 사용할 수 있도록, x64 CPU는 여전히 XMM0에서 XMM15로 명명된 16개의 128비트 SSE2 레지스터를 가지고 있습니다. 그 외 여기서 이야기하지 않는 다른 x64 레지스터들에 대한 모든 정보들은 WINNT.H 안에서 _CONTEXT로 적적하게 #ifdef된 구조체에서 찾아 보실 수 있습니다.
 

아 뭏튼, x64 CPU는 언제라도 구형의 32비트 모드 혹은 64비트 모드 둘 다 에서 동작할 수 있습니다. 32비트 모드에서, CPU는 다른 x86 CPU처럼 명령어를 해석하고, 이에 기반하여 동작합니다. 64비트 모드에서는, CPU는 새로운 레지스터와 명령어를 지원하기 위해서 어떤 특정 명령어 인코딩에 대해서 약간의 사소한 조정을 하였습니다.
 

만 약 여러분이 CPU 오피코드 인코딩 다이아그램(opcode encoding diagram)에 익숙하시다면, 아마도, 새로운 명령어 인코딩을 위한 공간은 빨리 없어진다는 것과 새로운 명령을 위해 여덟 개의 새로운 레지스터를 쥐어짜는 것은 쉬운 일은 아니라는 것을 기억하실 겁니다. 새로운 명령어를 추가하는 방법 중의 하나는, 거의 쓰이지 않는 명령어를 삭제하는 것 입니다. 그래서, x64에서는 기존의 CPU에서 사용되던 몇 개의 명령어가 삭제되었고, 지금까지, 제가 오직 그리워 하는 명령어는 스택의 일반용도 레지스터의 값을 모두 저장했다가, 다시 복원해 주는, 64비트 PUSHAD와 POPAD 입니다. 또 다른 방법 명령어 인코딩 공간을 확보하는 방법은, 64비트에서 더 이상 쓰이지 않는 세그먼트관련 레지스터들을 전부 제거해 버리는 것 입니다. 그래서, CS, DS, ES, SS, FS,그리고 GS 레지스터가 더 이상을 쓰지 않습니다. 그렇게 많은 사람이 이 레지스터들을 그리워할 것 같지는 않군요.
 

64비트 주소가 사용됨에 따라, 여러분이 코드 사이즈에 대해서 궁금해 할지도 모르겠습니다. 예를 들어서, 아래의 경우는 흔한 32비트 명령어 입니다:
 

CALL DWORD PTR [XXXXXXXX]
 

위 에서, X가 된 부분이 바로 32비트 주소입니다. 64비트 모드에서는, 위에서 X가 된 부분이 64비트 주소가 됩니다. 그래서, 5바이트 명령어가 9바이트로 되겠지요? 운 좋게도, 그렇지는 않습니다. 명령어는 여전히 똑같은 사이즈를 유지합니다. 64비트 모드에서는, 명령어의 32비트 오퍼랜드 부분은 현재 명령어에 상대적인 데이터의 오프셋으로 취급됩니다. 예를 하나 들어 보겠습니다. 32비트 모드에서는, 아래의 명령어가 00020000h번지에 있는 32비트 포인터 값을 부릅니다. 
 

00401000: CALL DWORD PTR [00020000h]
 

64 비트에서는, 똑같은 명령어가 0042100h(4010000h + 20000h)에 있는 64비트 포인터 값을 호출합니다. 만약 여러분이 여러분이 스스로 코드를 생성하고 있다면, 조금만 생각해 보아도 이러한 상대 주소 모드가 의미를 가지고 있다는 것을 알 수 있습니다. 여러분은 명령어에 8 바이트 포인트 값을 제공할 수 없습니다. 그 대신에, 여러분은 실제의 64비트 대상 주소가 존재하는 곳의 32비트 상대 경로를 제공해줘야 합니다. 그래서, 64비트 대상 포인터는 반드시 이 포인터를 이용하는 명령어와 2GB 내외로(앞뒤로 각각 2GB씩) 존재해야 한다는 말하지 않아도 알 수 있는 가정이 생깁니다. 거의 모든 사람들에게는 이것이 문제가 될 이유가 별로 없겠지만, 동적으로 코드를 생성한다든지 혹은 기존 코드를 메모리에서 변경하는 경우라면, 문제가 생길 수도 있습니다 .

x64 레지스터의 가장 큰 장점 중의 하나는, 컴파일러가 스택 보다 모든 파라미터들을 레지스터에 전달하는 코드를 마침내 생성할 수 있다는 점 입니다. 스택에 파라미터를 구겨 넣는 것은 메모리 억세스를 필요로 합니다. 그리고, 우리는 CPU 캐쉬에 없는 메모리 억세스는 램에서 그 내용을 가져올 때 까지, 몇 사이클 동안 CPU를 잠시 서있게 한다는 사실도 알고 있습니다.
 

함 수 호출 방식(Calling Convention) 의 경우, x64 내부구조를 이용하여 _stdcall, _cdecl, _fastcall, _thiscall 같은 기존에 존재하는 Win32 함수 호출 방식을 모조리 정리할 수 있는 기회를 가졌습니다. Win64의 경우는, 딱 하나의 함수 호출 방식이 존재합니다. _cdecl 같은 방식은 그냥 컴파일러에서 무시됩니다.  이러한 함수 호출 방식의 단일화는 무엇보다도 디버깅을 원활하게 하는데 큰 혜택입니다.

x64의 함수 호출 방식은 fastcall 방법과 유사합니다. x64 호출 방식에서는, 처음 네 개의 정수 인자가 이 목적을 위해 디자인된 레지스터에 전달됩니다:
 

RCX: 1 번째 정수 인자 RDX: 2 번째 정수 인자 R8: 3번째 정수 인자 R9: 4번째 정수 인자


네 개 이상의 정수 인자는 스택을 통해서 전달됩니다. 그리고 this 포인터는 정수 인자로 간주되어 항상 RCX 레지스터에서 발견될 수 있습니다.
부동 소수점 인자들에 대해서는, 처음 네 개의 인자들은 XMM0에서 부터 XMM3 레지스터를 통해서 전달되고, 나머지 부동 인자들은 쓰레드 스택을 통해서 전달 됩니다.
 

함 수 호출 방식에 대해서 조그만 더 깊숙이 들어가면, 인자들이 레지스터를 통해서 전달될 수 있음에도 불구하고, 컴파일러는 RSP 레지스터를 감소시키면서, 여전히 스택에 공간을 예약해 놓습니다. 최소한, 각각의 함수는 반드시 32바이트 (네 개의 64비트 값)을 예약해 놓아야 합니다. 이 공간은 레지스터들이 함수에 전달되어, 잘 알려진 스택 위치에 쉽게 복사되도록 합니다. 물론, 불리는 함수 측에서 함수의 인자를 채우지는 않습니다. 하지만, 필요한 경우에 이러한 스택 공간의 예약은, 함수 인자들이 레지스터에서 쉽게 스택으로의 복사를 가능하게 합니다. 물론, 네 개 이상의 인자가 전달된다면, 적절한 스택의 추가 공간이 반드시 예약되어야 합니다.
 

예 를 한 번 들어 보겠습니다. 어떤 함수가 자식 함수에게 두 개의 정수 인자를 전달하는 경우가 있다고 가정을 한 번 해보겠습니다. 컴파일러는 두 개의 인자를 각각 RCX와 RDX에 각각 전달할 뿐만 아니라, RSP 스택 포인터 레지스터에서 32바이트를 빼놓습니다. 불리는 함수 입장에서는, 파라미터의 값을 RCX와 RDX 레지스터를 통해서 접근 가능합니다. 만약, 불리는 함수 코드가 레지스터가 다른 이유에서 필요할 경우, 이 값들은 예약된 32바이트 스택 영역에 복사됩니다. 그림 6은 6개의 정수 인자가 전달된 뒤의 레지스터와 스택을 보여 주고 있습니다.

Figure 6 Passing Integers
Figure 6 Passing Integers
 

x64 시스템에서의 파라미터 스택 정리는 약간 재밌는 모습을 보여 주고 있습니다. 기술적으로는, 불리는 함수(callee)가 아닌, 부르는 함수(caller)가 스택의 정리를 책임지고 있습니다. 그러나, 여러분은 프로롤그와 에필로그 코드를 제외하고 다른 부분에서 RSP를 조정하는 모습을 거의 보기가 힘들 것 입니다. PUSH와 POP 명령어로 스택에서 인자를 더하거나 빼주는 x86 컴파일러와 다르게, x64 코드 생성기는 (파라미터의 입장에서 보면) 얼마든지 큰 대상 함수에서도 쓸 수 있을 만큼 충분한 스택을 예약해 놓았습니다. 그래서, 자식 함수를 호출 시에, 파라미터를 설정하기 위해 똑같은 스택 영역을 계속 반복해서 씁니다.


짧게 말해서, RSP 레지스터는 거의 변하지 않습니다. 이 점은 ESP 레지스터 값이 파라미터가 스택에 추가되거나 정리되면서, 계속 변하는 x86 코드와 상당히 틀립니다.

예 를 하나 들어 보겠습니다. 세 개의 다른 함수를 호출하는 x64 함수가 있다고 생각해 보시기 바랍니다. 처음 함수는 네 개의 인자(0x20 바이트=32바이트)를 받습니다. 두번째 인자는 열 두개의 인자(0x60바이트=96바이트)를 받습니다. 세번째 함수는 여덟 개의 인자(0x40=64바이트)를 받습니다. 프롤로그에서는, 생성된 코드는, 스택에 단지 96바이트만 예약해서 대상 함수가 인자들을 찾을 수 있도록, 96 바이트 안의 적절한 위치에 인자들을 복사해 놓습니다.
 

x86 함수 호출 방식에 대한 좀 더 자세한 내부 구조는 Raymond Chen's blog에 서 찾을 수 있습니다. 이 이상은 더 자세히 설명하지는 않겠습니다만, 몇 가지만 중요한 점을 더 말씀 드리자면. 첫 번째, 함수의 인자들 중, 처음 네 개의 인자들 중에서, 64비트 보다 적은 정수 인자들은 부호 확장(sign extended)이 일어나고, 적절한 레지스터를 통하여 전달할 수 있습니다. 두 번째로, 64비트 얼라인을 지키기 위해서, 절대로 8바이트의 정수배가 아닌 함수 인자가 스택에 존재해서는 안됩니다. 구조체를 포함해서, 1, 2, 4, 혹은 8 바이트가 아닌 인자들은 래퍼런스를 통해서 전달됩니다. 그리고 마지막으로, 8,16,32, 64비트의 구조체와 고용체는 동일한 크기의 정수 인 것 처럼, 전달됩니다.

함 수 결과 값은 RAX 레지스터에 저장됩니다. 부동 소수점 형식의 값은 예외적으로 XMM0으로 돌려 받습니다. 함수 호출을 통하여, 제가 말씀드리는 레지스터들은 예약되어 있어야 합니다: RBX, RBP, RDI, RSI, R12, R13, R14, 그리고 R15. 그리고 지금 말씀드리는 레지스터들은 휘발성이고, 값이 없어질 수 있습니다:RAX, RCX, RDX, R8, R9, R10, 그리고 R11.
 

위 에서 제가 예외 처리 메커니즘의 일환으로, 운영체제가 스택 프레임을 검사한다고 말씀 드렸습니다. 여러분이 한 번이라도, 스택을 검사하는 코드를 써보신 적이 있다면, 거의 임시적인 Win32 프레임의 레이아웃이 프로세스를 다루기 힘들게 한다는 것을 아실 겁니다. 이러한 상황이 x64에서는 더 좋아졌습니다. 만약 함수가 스택 공간을 할당하고, 다른 함수를 호출하고, 어떤 레지스터를 예약하거나, 예외 처리를 이용하다면, 그 함수는 반드시 표준화된 프롤로그와 에필로그를 생성하기 위하여 잘 정의된 명령어 집합(well-defined set of instructions)을 써야 합니다.
 

표 준화된 함수의 스택 프레임을 사용하도록 강제하는 것은, 운영체제가 스택을 언제든지 탐색할 수 있는 것을 보장하는 한 방법입니다. 이러한 일관성에, 표준화된 프롤로그를 이용하여 컴파일러와 링커는 관련된 테이블에 데이터를 생성해야 합니다. 궁금하신 분들을 위해서, 좀 더 자세히 설명하면, 테이블의 모든 함수 정보들은 winnt.h에 정의되어 있는 IMAGE_FUNCTION_ENTRY64의 배열 테이블에 저장되어야 합니다. 어떻게 그 테이블을 찾는지 궁금하시다구요? 그 테이블은 PE헤더의 데이터 디렉터리 영역 안에 IMAGE_DIRECTORY_ENTRY_EXCEPTION의 엔트리가 지정하고 있습니다.

상당히 짧은 분량이지만, 내부구조의 많은 부분을 다루었습니다. 그러나, x86에 대한 큰  개념과 32비트 어셈블리 언어에 대한 지식이 있으신 분들은, 상당히 짧은 시간 안에 x64 명령어를 이해하실 수 있으실 겁니다.


Visual C++로 x64용 어플리케이션 개발

 

Visual Studio® 2005 이전에도 마이크로 소프트에서 나온 C++ 컴파일러로 x64용 코드를 생성하는 게 가능했지만, IDE와 완벽히 통합은 되지 않았었습니다. 이 글에서는, 저는 여러분이 Visual Studio® 2005를 가지고 있고, 여러분이 x64용 도구 (기본 설치 옵션이 아닙니다.)을 선택했다고 가정하고 이 글을 진행하겠습니다. 그리고, 여러분이 기존에 Win32 사용자 모드에서 C++를 이용한 프로젝트 경험이 있다고 가정하겠습니다.
 

x64 를 위한 첫 번째 단계는, 64비트 빌드 환경을 구축하는 것 입니다. 이미 다 아시겠지만, 여러분 프로젝트에는 기본으로 두 개의 환경이 있습니다. Debug와 Retail이 그것입니다. 여러분이 여기에서 더 해야 하는 것은, 두 개의 환경을 더 생성하는 것 뿐입니다. x64를 위한 Debug와 Retail 를 추가하는 것 입니다.
 

기 존의 프로젝트 혹은 솔루션을 한 번 열어 보시기 바랍니다. 빌드 메뉴에서, Configuration Manager를 선택해 보십시오. Configuration Manager 다이얼로그 박스에서, Active Solution Plaftfrom 콤보 박스를 눌러서, New를 선택하시기 바랍니다(그림 7). 그러면, 여러분은 New Solution Plaftform이라고 명명된 다른 다이얼로그를 보실 수 있을 것 입니다.
 

Figure 7 Creating a New Build Configuration
Figure 7 Creating a New Build Configuration
 

x64 를 새 플랫폼(그림 8)로 선택하고, 다른 설정은 그냥 놔두시기 바랍니다; 그리고 OK를 클릭하세요. 그게 전부랍니다! 여러분은 이제 네 가지의 가능한 빌드 환경을 구축하셨습니다: Win32 Debug, Win32 Retail, x64 Debug, x64 Retail. Configuration Manager 를 이용해서, 쉽게 다른 환경으로의 변경도 가능합니다.
 

자, 이제 여러분의 코드가 어떻게 64비트에 적용 가능한지 한 번 살펴 보기로 하겠습니다. x64 Debug 설정을 기본으로 놓으시고, 프로젝트를 빌드해 보시기 바랍니다. 프로젝트 자체가 아주 가볍지 않은 이상, Win32 환경에서 볼 수 없었던 컴파일 에러들을 보실 수 있을 겁니다. 여러분이 포팅이 불가능할 정도의 C++코드를 쓰지 않는 이상, 상대적으로 쉬운 에러 몇 개만 발생해서, 여러분은 쉽게 Win32와 x64에 대응하는 코드를 가지게 될 것 입니다. 특별히 조건부 컴파일 같은 것을 이용하지 않아도 말이죠.
 

Figure 8 Selecting the Build Platform
Figure 8 Selecting the Build Platform

Win64 호환되는 코드 만들기

 

아 마도, Win32 코드에서 x64로 컨번팅할 때 가장 문제가 되는 부분은, 타입 정의를 변경하는 일이 될 것 같습니다. 제가 혹시 앞에서 Win64의 자료형에 대해서 이야기 했던 것에 대해서 기억하시나요? C++ 컴파일러의 원래 자료형 (int, long 기타 등등) 을 쓰는 것 보다, 윈도우에서 정의한 typedef로 정의된 자료형을 쓰는 편이 깨끗한 Win32 x64 코드를 생성하는데 쉽습니다. 그리고 Win32의 자료형을 쓰실 때, 일관성있게 쓰셔야 할 필요가 있습니다. 예를 들어서, 윈도우가 HWND을 넘겨줄 때, 단지 편하고 쉽다고 해서, FARPROC 형식의 변수에 이 값을 저장하지 마십시요.
 

많 은 코드를 업그레이드 하면서, 아마도 제가 흔히 그리고 쉽게 보는 에러는, 포인터 값이 32비트 데이타 타입인 int 혹은 long, 그리고 DWORD에 저장되어 있는 것 이었습니다. Win32와 Win64에서의 포인터는 사이즈는 틀리지만, 정수형은 동일한 크기를 유지합니다. 그러나, 컴파일러에게 포인터 값을 정수형에 저장하는 것은 금지하는 것 역시 가능하지 않습니다.
 

이 런 상황을 해결하기 위해서, 윈도우 헤더에는 _PTR 타입이 선언되어 있습니다. 예를 들면, DWORD_PTR, INT_PTR, 그리고 LONG_PTR 같은 타입들이 대상 플랫폼에 따라서, 안전하게 포인터 변수를 이용하게 해줍니다. 예를 들어서, DWORD_PTR타입은 Win32에서 컴파일 되었을 때는, 32비트지만, Win64에서는 64비트입니다. 저의 경우는, 그동안의 연습으로, 어떤 자료형을 선언할 때, "내가 여기서 DWORD를 선언해야 할까? 아니면 DWORD_PTR를 선언해야 할까?"라고 물어보는 것이 버릇이 되어 버렸답니다.
 

기 대하시는 대로, 정수형에서 얼마나 많은 바이트를 원하는지 계산하는데, 약간의 문제가 있을 수 있습니다. DWORD_PTR를 정의하는 헤더파일(basetsd.h)에서 역시 INT32, INT64, INT16, UINT32, DWORD64 같은 여러 형태의 정수를 선언해 놓고 있기 때문입니다.
 

자 료형의 크기에 관련된 또 다른 문제는 printf와 sprintf의 포맷팅(formatting)입니다. 저는 확실히 과거에 %X 혹은 %08X등을 포인터 값을 나타내는데 사용했다는 점에서 약간의 죄책감(?)을 느끼고 있고, 그 코드는 x64 시스템에서 문제를 일으키고 있습니다. 옳은 방식은 대상 플랫폼의 포인터의 크기를 자동으로 계산해 주는 %p를 사용하는 것 입니다. 추가로, printf와 sprintf는 사이즈에 의존하지 않는 타입인 'I' 프리픽스(prefix)를 가지고 있습니다. 예를 들어서, 여러분은 %Iu를 UINT_PTR 변수를 출력하기 위해서 사용할 수 있습니다. 이와 동일한 방식으로, 여러분이 특정 변수가 언제 64비트 부호 있는 값이 될 것이라는 알고 있다면, %I64d를 쓰실 수 있습니다.
 

자 료형의 불일치에서 일어난 에러들을 정리하는 것만으로는 Win64가 준비되었다고 말할 수 없습니다. 여러분은 아직은 아마도 x86에서만 돌아갈 수 있는 소스코드를 가지고 계실겁니다. Win64로의 포팅을 위해서, 특정 코드에서는 여러분이 Win32와 x64를 위한 두 가지 버젼의 함수를 쓰실 수도 있습니다. 이 때야말로, 전처리자(preprocessor)가 아주 유용하게 쓰입니다:
 

_M_IX86 _M_AMD64 _WIN64
 

전절한 전처리자의 사용이야 말로, 여러 플랫폼에서 동작하는 소프트웨어를 만들기 위해서는 필수적입니다. _M_IX86과 _M_AMD64는 특정 프로세서를 위해서 컴파일할 때를 위해서 정의되어 있습니다.

전처리자 매크로를 사용할 때, 뭘 원하는지 한 번 열심히 생각해 보시기 바랍니다. 예를 들어서, 이 코드가 정말로 x64 프로세서만을 위한 것이라면, 아래와 같이 매크로를 쓰시기 바랍니다:
 

#ifdef _M_AMD64


반면에, 동일한 코드가 x64와 아이템니움(Itanium)에서 동작하기를 원한다면, 여러분은 아래와 같이 쓰시는게 좋을 것 같습니다:


#ifdef _WIN64


이러한 매크로를 쓸 때 제가 유용하게 쓰는 버릇이 하나 있습니다. 바로 제가 무엇을 잊어 버렸을 때를 대비해서, 명시적으로 모든 경우에 대해서 #else 케이스문을 써주는 것입니다. 아래에 코드를 한 번 살펴봐 주시기 바랍니다:
 

#ifdef _M_AMD64
    // My x64 code here
#else
    // My x86 code here
#endif
 

또 다른 세번째 CPU가 나타나면? 저의 x86 코드가 저의 의도와는 다르게 컴파일 됩니다. 위의 코드를 더 좋은 방법으로 구성하는 방법은 아래와 같습니다:


#ifdef _M_AMD64
    // My x64 code here
#elif defined (_M_IX86)
    // My x86 code here
#else
    #error !!! Need to write code for this architecture
#endif


그 리고, 마지막으로 저의 경우는 Win32코드 중에서 x64로 쉽게 포팅되지 않는 부분 중의 하나는 인라인 어셈블러 부분이었습니다. Visual C++가 x64 를 위한 인라인 어셈블러를 지원하지 않거든요. 대신에 64비트 MASM이 제공되고, MSDN에 문서화가 되어 있습니다. ML64.exe와 다른 x64툴들은 (CL.EXE와 LINK.EXE를 포함해서) 커멘드라인에서도 쓸 수 있습니다. 단지 VCVARS64.BAT를 실행시키세요. 그러면, 여러분의 패스에 이 파일들의 경로를 포함해 줄 것 입니다.


디버깅

 

여 러분은 마침내, 여러분의 코드를 Win32와 x64 양쪽에서 깨끗하게 컴파일 할 수 있게 되었습니다. 이 대장정의 마지막은 "어떻게 디버깅을 하느냐?" 입니다. 여러분이 x64 에서 x64 버젼을 빌드 했는지의 여부에 상관없이, 여러분은 x64 모드에서 디버깅 하기 위해서는, Visual Studio의 리모트 디버깅 기능이 필요합니다. 운좋게도, 여러분이 64비트 컴퓨터에서 Visual Studio를 동작시키면, IDE가 내부적으로 이 단계를 여러분을 위해서 해줍니다. 어떤 이유로, 리모트 디버깅을 할 수 없을 때, 여러분의 또 다른 옵션은 x64용 WinDbg를 사용하시는 방법도 있습니다. 그러나, 그렇게 되면, Visual Studio 디버그의 많은 훌륭한 기능들을 포기하셔야 합니다.
 

여러분이 리모트 디버깅을 한 번도 해보신 적이 없다고 하더라도, 걱정하실 필요 없습니다. 한 번만 설치 되면, 리모트 디버깅도 로컬처럼 쉼없이 잘 동작합니다.
 

첫 번째 단계는 64비트 MSVSMON을 대상 컴퓨터에 설치하는 것 입니다. 이것은 일반적으로 VisualStudio와 같이 오는 RdbgSetup을 동작시키면, 알아서 해 줍니다. 일단 MSVSMON이 설치되면, Tools 메뉴에서 적절한 보안 설정(혹은 lack)등을 설정할 수 있습니다.  

다음에는, Visual Studio 안에서, 여러분이 x64코드를 위해서 리모트 디버깅을 위한 설정을 하셔야 합니다. 이것은 프로젝트의 프로퍼티(그림 9)에서 할 수 있습니다.
 

Figure 9 Debugging Properties
Figure 9 Debugging Properties
 

여 러분의 64비트 구성을 선택해서, Configuration 프로퍼티 아래에 있는 Debugging을 선택하시기 바랍니다. 위쪽에 Debugger to launch라고 되어 있는 부분이 있습니다. 일반적으로 이 부분이 Local Windows Debugger로 설정되어 있습니다. 이 설정을 Remote Windows Debugger로 변경하시기 바랍니다. 그 아래에서, 여러분이 디버깅을 시작할 때의 리모트 명령(예를 들어서, 프로그램 이름)과 리모트 시스템의 이름과 연결 속성을 선택해 줄 수 있습니다.
 

모 든 것이 다 제대로 설정되었다면, 여러분의 x64 어플리케이션을 여러분이 Win32 프로그램에서 하듯이 디버깅할 수 있습니다. 여러분은 MSVSMON이 매 번 성공적으로 디버그와 연결이 될 때, "connected"라는 메세지를 보내 주기 때문에, 연결이 되었는지의 여부를 판별할 수 있습니다. 여기서 부터는, 여러분이 알고 있고, 사랑하는 Visual Studio 디버거와 거의 모든 것이 동일합니다. 64비트 레지스터를 보기 위해서, 그리고 비슷하지만 조금 다른 x64 어셈블리 코드를 보기 위해서 레지스터 창과 디스어셈블러 창을 띄우는 것을 잊지 마세요.
 

주 의: 64비트 미니덤프는 32비트 덤프처럼 Visual Studio에서 직접 불러 올 수 없습니다. 대신, 여러분은 리모트 디버깅을 이용해야 합니다. 네이티브(native)와 매니지드(managed) 64비트 코드의 상호호환은 현재로서 Visual Studio 2005에서 지원되지 않습니다.


매니지드 코드는 어떻게 하나요?

 

마 이크로 소프트 .NET 프레임 웍에서 코딩 하는 것의 장점 중의 하나는, 깔려있는 운영제제의 많은 부분이 일반적인 목적으로 코드로 추상화 되어 사라져 버렸다는 점입니다. 추가로 IL 명령어 포맷은 CPU에 의존적이지 않습니다. 그래서, 이론적으로는, Win32에서 제작된 .NET에 기반을 둔 바이너리 파일은 x64시스템에서 수정하지 않고 동작해야 합니다. 그러나, 현실은 아주 복잡합니다.
 

x64 버젼의 .NET 프레임웍 2.0을 제 x64시스템에 설치한 뒤에, 저는 제가 Win32에서 실행했던 똑같은.NET 실행파일을 동작시킬 수 있었습니다. 정말 좋지요? 물론, 모든 .NET에 기반을 둔 프로그램이 Win32와 x64에서 컴파일 하지 않고, 동일하게 동작 한다는 보장은 없습니다만, 다만 합리적인 범위의 시간 안에 그냥 동작합니다.
 

만 약 여러분의 코드가 명시적으로 native code를 호출한다면 (예를 들어서, C# 혹은 Visual Basic 에서의 P/Invoke) 64비트 CLR에서 실행 시에 문제에 부딫힐 확률이 커집니다. 그러나, 다행스럽게도 컴파일러 스위치에 여러분의 코드가 어떤 플랫폼에서 동작할지를 명시하는 부분이 있습니다. 예를 들어서, 여러분은 64비트 CLR이 존재함에도 불구하고, 여러분의 코드가 WOW64에서 동작하기를 원한다고 명시할 수도 있습니다.


최종 정리

 

모 든 것을 고려해 보아도, x64 버젼의 윈도우로 이동하는 것은, 비교적 저에게는 어렵지 않은 과정이었습니다. 일단, 여러분이 운영체제 구조와 툴의 상대적으로 미미한 차이점을 한 번 훑어 보기만 하면, 하나의 코드를 바탕으로 해서 두 개의 플랫폼에서 동작하는 것이 어렵지 않습니다. Visual Studio 2005가 이 노력의 과정을 훨씬 쉽게 만들어 주고 있고, x64에 특화된 디바이스 드라이버나 툴, 예를 들면 Sysinternals.com의 Process Explorer은 매일 새롭게 나타나고 있습니다, 그러니 이 쪽으로 뛰어 들지 않을 이유가 없습니다!


2007/05/09 13:50 2007/05/09 13:50
이 글에는 트랙백을 보낼 수 없습니다
웅쓰:웅자의 상상플러스
웅자의 상상플러스
전체 (379)
게임 (5)
영화 (2)
기타 (23)
맛집 (5)
영어 (2)
대수학 (3)
형태소 (5)
Hacking (9)
Linux (112)
HTML (48)
Application_developing (48)
Web_developing (102)
Window (11)
«   2007/05   »
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
  1. 2016/01 (1)
  2. 2015/12 (3)
  3. 2015/10 (3)
  4. 2015/03 (2)
  5. 2015/01 (4)