EnumChildWindows 사용 코드 버그 패치

2006-07-25 신영진

7월호 기사에 EnumChildWindows를 사용한 소스에 치명적인 결함이 있습니다. 너무너무 치명적이어서 정말 죄송할 따름입니다. ㅠ.ㅜ~ 수정하지 않고 사용할 경우 제대로된 결과를 보장받지 못하며, 성능상에 심각한 문제가 발생합니다. 꼭 고쳐서 사용하도록 합시다.

EnumChildWindows

EnumChildWindows에 대한 함수 소개는 7월호 원고를 참고 하세요. 아래 화면을 봅시다. Spy++로 Shell_TrayWnd를 확장한 화면입니다. 자식이 총 10개가 존재합니다. EnumChildWindows로 Shell_TrayWnd를 열거하면 어떤 윈도우들이 열거 될까요?

아래와 같이 10개의 모든 자식 윈도우가 열거 됩니다. 아마 이쯤 되면 7월호 원고에 무슨 문제가 있는지 모두들 아시리라 생각됩니다. 혹시나 아직 이해가 되지 않는 분들은 아래 내용을 마저 보도록 합시다.

Recursive Call

위 코드는 마소 7월호에 소개된 모든 윈도우를 열거하는 코드 입니다. 재귀 호출을 사용되어 제작되어 있습니다. 이 코드가 동작하려면 앞서 살펴 보았던 윈도우 구조에서 EnumChildWindows가 직접적인 자식인 Button, TrayNotifyWnd, ReBarWindow32만 열거해야 합니다. 하지만 그렇지 않죠.

그렇다면 위 코드를 통해서 Shell_TrayWnd를 열거해 보도록 합시다. 어떻게 될까요?

위의 결과와 같이 중복된 값들이 나오게 됩니다. 계층 구조가 깊을수록, 자식의 자식이 많을 수록 중복된 윈도우가 많이 나오게 됩니다. 결국 위 출력 결과는 완전 틀린 것이죠.

바로잡아 봅시다

문제를 해결할 수 있는 방법으론 무엇이 있을까요? 그렇습니다. 가장 손쉬운 방법인 GetWindow를 사용하는 방법이 있습니다. 코드를 다음과 같이 고쳐봅시다.

전체 윈도우를 열거하기 위해서는 아래와 같이 호출하면 됩니다.

아래는 위 코드를 통해서 Shell_TrayWnd를 열거한 화면 입니다.

구차한 변명입니다.

EnumChildWindows의 MSDN 설명을 보면 "If this parameter is NULL, this function is equivalent to EnumWindows." 문구가 있습니다. NULL을 넣으면 EnumWindows와 동일하게 동작한다는 것이죠. 여기서 NULL을 제가 GetDesktopWindow의 리턴 값으로 착각을 했던 것 같습니다. 그리고 추가적으로 98/ME의 경우에는 NULL을 넣으면 동작하지 않는다고 합니다. 왜 굳이 NT 코어에서는 NULL을 넣었을 때 EnumWindows처럼 동작하도록 만들었나 이해가 되지 않는 부분입니다.

어떻게 알았을까요?

9월호에 제공될 Spy++과 유사한 프로그램 샘플을 제작하다 알게 되었습니다. 트리를 구성하는데 자그마치 아주 오랜 시간이 걸리더군요~ 왜 그럴까? 하던 고민 끝에 Spy++을 디버깅 해 보았습니다. EnumWindows, EnumChildWindows에 브포를 걸어도 잡히지 않길래 살펴봤더니 GetWindow를 통해서 루프를 돌고 있더군요. 그래서 GetWindow버전과 EnumChildWindow 버전을 프로파일링 해보았습니다. 함수명, 호출횟수, 시간이라고 보면 됩니다.

GetWindow 사용 버전
CheckTime(class CTreeCtrl &)        2       0.001218    0.001218    689.497084  52.968649
FillTreeProc(struct HWND__ *,long)  7138    8.581973    4.867138    689.494853  52.966418

EnumChildWindows 사용 버전
FillTreeProc(struct HWND__ *,long)  336655  309.217339  243.460755  26262.506450    2393.042883
CheckTime(class CTreeCtrl &)        2       0.000949    0.000949    26262.508388    2393.044822

EnumChildWindows 버전의 호출 횟수를 한번 보죠. 무려 336655번 입니다. 말도 안 되는 호출 횟수죠. 그래서 지난달 강좌에 사용된 코드를 디버깅 해보았습니다. 그랬더니 아니나 다를까 EnumChildWindows에 대한 잘못된 이해에서 나온 아주 잘못된 코드였던 것 입니다. ㅎㅎ~ 지난 달에도 테스트를 한다고 했었는데 제가 테스트 시에는 여러 창을 닫고 해서 제대로 테스트가 되지 않았던 것 같습니다.