Module32FirstA를 찾아서…

@codemaru · June 26, 2009 · 4 min read

Windows API는 많은 부분에서 일관성을 제공한다. 일관성이 있어야 기억하기 쉽고, 새로운 것을 배우더라도 과거 방식을 사용하면 된다는 것을 개발자들이 알 수 있기 때문이다. 어쨌든 잘 추상화가된 API 세트를 제공한다고 할 수 있다.

이러한 것 중에 하나가 유니코드 함수에 대한 지원이다. 윈도우에 포함된 거의 대부분의 함수는 유니코드 버전은 끝에 W가 붙어있고, 안시 버전은 A가 붙어있다. 글자가 붙어 있지 않은 기본 함수는 프로젝트 셋팅에 따라서 달라진다. 즉, 아래와 같이 기본적으로 API를 설계한다는 이야기다.

void SomeGoodFunctionA(LPCSTR parameter);
void SomeGoodFunctionW(LPCWSTR parameter);

#ifdef _UNICODE
#define SomeGoodFunction SomeGoodFunctionW
#else
#define SomeGoodFunction SomeGoodFunctionA
#endif

그런데 그 많은 함수들 — API는 정말 많다 — 중에 저런 원칙을 어긴게 하나도 없을 순 없다. 그 중 대표적인 놈 하나가 툴헬프 라이브러리다. 전통적으로 NT 시스템은 psapi를 사용하고, 9x 시스템은 툴헬프를 사용한다. 이 녀석들의 역할은 프로세스 열거, 모듈 열거 따위를 하는 일이다. 여튼 툴헬프 라이브러리는 조금 엉뚱한 방식으로 포팅이 되거나 진화가 되었다.

9x 시스템의 툴헬프에는 Module32FirstW 함수가 없다. 좋다. 여기까진 그럴수도 있다. NT에는 Module32FirstW 함수가 있다. 그렇다면 NT에는 Module32FirstA 함수가 있을까? 없다. 9x에도 없다. Module32First란 이름의 함수는 안시를 입력받도록 설계되어 있고, Module32FirstW만 추가된 형태로 NT에 있는 것이다. 즉 다음과 같이 되었다는 이야기다.

BOOL WINAPI Module32First(HANDLE, MODULEENTRY32);
BOOL WINAPI Module32FirstW(HANDLE, MODULEENTRY32W);

이해할 수 있는 부분이다. 원래 A함수가 없었고, W 함수만 NT에 추가되었다고 생각할 수 있다. 그런데 문제는 이것을 포함하고 있는 tlhelp32.h다. 이 파일은 Module32First 함수가 일반적인 함수와같이 설계가 되었다고 생각한다. tlhelp32.h에서 가장 문제가 있는 줄은 다름아닌 다음과 같은 라인이다.

#ifdef UNICODE
#define Module32First Module32FirstW
#define Module32Next Module32NextW
#define MODULEENTRY32 MODULEENTRY32W
#define PMODULEENTRY32 PMODULEENTRY32W
#define LPMODULEENTRY32 LPMODULEENTRY32W
#endif  // !UNICODE

즉, 유니코드 환경에서는 Module32First를 Module32FirstW에다 맵핑을 시켜 버리는 것이다. 그렇다면 위 헤더를 추가하는 순간부터 우리는 영영 유니코드 환경에서는 Module32FirstA에 해당하는 Module32First 함수를 호출할 수 없게 된다. 즉, 툴헬프 함수는 환경에 따라서 한 가지 버전밖에 호출을 못하는 것이다. 일반적인 상황에서는 문제가 없지만 한 컴파일 단위에서 A/W 함수를 모두 호출해야 하는 상황에서는 쪼다가 된다. 그런 상황이라면 아래와 같이 tlhelp32.h를 포함한 다음 정의를 전부 해제해 주면 된다.

#include <tlhelp32.h>

#undef Module32First
#undef Module32Next
#undef MODULEENTRY32
#undef PMODULEENTRY32
#undef LPMODULEENTRY32

A/W에 대한 약간의 편집증이 있는 개발자라면 아래와 같은 방법을 추가할 수도 있다.

#define Module32FirstA Module32First
#define Module32NextA Module32Next
#define MODULEENTRY32A MODULEENTRY32
#define PMODULEENTRY32A PMODULEENTRY32
#define LPMODULEENTRY32A LPMODULEENTRY32
@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중