API

MAKEINTRESOURCE 매크로 분석

디버그정 2008. 9. 8. 11:10

리소스 로딩시 다음과 같은 매크로를 사용한다.
#define MAKEINTRESOURCE( i ) ( LPSTR )( ( DWORD )( ( WORD )( i ) ) )

무의식적으로 사용하는 매크로이다.
대부분 다만 문자열 치환정도로 이해하고 책에도 별다르게 설명하지 않는다.
api나 mfc 프로그래밍시 초반에는 워낙 배울게 많다 보니 리소스는 저런 매크로를
사용하는구나 하고 그려려니 하고 넘어간다.

내부를 들여다 보자.
어떻게 이런 처리가 가능할까?

함수호출은 명시적으로 문자열을 취하지만,
파일에 리소스를 저장시에는 실제적으로 두 가지 방식을 사용하고 있기 때문이다.
문자열로 저장하는 방법과 정수값으로 저장하는 방식이 그것이다.
실제로 EnumResourceNames - EnumResNameProc을 사용하여
과거에 파일로부터 모든 아이콘들을 추출해 본 적이 있는데
실제로 두가지 방식으로 저장됨을 볼 수 있었다.

그렇다면 어떻게 이 리소스 네임이 문자열로 주어졌는지 정수아이디로 주어졌는지 구분할까?
그 해답은 치환식에 있다.

맨처음에 정수를 받아서 하는 일이 2바이트형 워드로 자르는 일이다....
그러므로 정수 아이디를 부여하는 경우 2의 16승 즉 65536을 넘지 않도록 주의한다.
그 이상 부여하면 절삭된 숫자가 들어가므로 재수없으면 겹칠 수도 있을 것이다.
이를테면 1과 65537은 절삭과정을 거치면 1이라는 같은 수가 될 것이다.....

그런데 왜 2바이트로 잘라버릴까?

프로그램 실행시 메모리상에서 코드 영역 주소나 전역 변수, 데이터 영역 주소, 스택 지역변수 주소들은 기본적으로 00401000, 00522ACD,  0013A35E 처럼 반드시 워드형의 범위를 초과한다.(4바이트에서 하이워드 값이 반드시 존재) 그래서 프로그램에서 정상적인 메모리상의 주소가 될 수 없는 워드형 값은 주소가 아닌 걸로 인식해서 별개로 처리할 수 있다.

참고) 32비트에서 메모리 주소

Null 포인터 할당 파티션 : 0x00000000 ~ 0x0000FFFF 이 프로세스 주소 공간 파티션은 프로그래머가 NULL포인터를 할당하는 경우를 대비하기 위한 영역으로  읽기/쓰기/실행 X  접근시도시 access violation이 일어난다.
유저 모드 파티션 : 0x00010000 ~ 0x7FFEFFFF 이 영역이 프로그래머가 실제적으로 사용하는 영역이다.
64KB 접근 금지 파티션 : 0x7FFF0000 ~ 0x7FFFFFF
커널 모드 파티션 : 0x80000000 ~ 0xFFFFFFFF

여기서 두번째 유저모드 부분이 프로그램에서 일반적으로 접근이 허락되는 영역이고 상위워드가 1인 0x00010000부터 시작함을 알 수 있다.


아래는 착각이었다.
이것이 바로 문자열과 정수를 구분하는 경계가 되기 때문이다.
문자열은 기본적으로 어떻게 시스템이 구분할까?
대부분이 알듯이 널터미네이트 방식으로 끝에 0을 붙여주는 방식을 주로 사용하고, 리소스 네임의 경우도
위의 경우를 사용한다.
시스템은 이런식으로 문자열을 인식할 수 있다.
그런데 만약에 리소스 네임의 정수아이디가 헥스) 0e 03 09 00 이라는 값을 취하는 경우
(리틀엔디안 방식을 취하는 경우 앞의 메모리가 낮은자리수이다.)
시스템은 이것을 널터미네이트로 인식해서 문자열로 인식할 수 있다.
그러므로 이것을 막기위해 워드 - 더블워드 형변환을 통해서 뒤의 메모리 두자리(2바이트)를
아예 00 00 이런식으로 만들어 버린 것이다.
참고로 물론 유니코드로 컴파일시 실제 파일에 리소스 이름은 멀티바이트 방식으로 들어 간다.


그러므로 실제적으로 리소스 네임으로 리소스를 추출하는 경우
다음과 같은 방식으로 구분해서 추출해야 한다.
if(HIWORD(lpszName) == 0){ // 정수 아이디로 부여된 경우
....
}
else{ // 문자열로 부여된 경우
....
}