큼직한 그래픽 화면에서 마우스로 조작하는 컴퓨터-사용자 인터페이스를 일명 GUI라고 부른다.
매킨토시가 원조라고 하는 이런 환경에서는 대화상자가 라벨, 입력란, 리스트박스, 버튼 같은 몇몇 기초 UI 요소들로 구성되며, 이 구성요소들을 윈도우 프로그래밍에서는 ‘컨트롤’이라고 부른다. GUI에서 일종의 부품과도 같다.
이런 GUI와는 달리, 그냥 전통적인 선택 막대만으로 각종 기능을 선택하고 옵션을 설정하는 단순한 인터페이스도 있는데, 과거의 도스용 아래아한글이 대표적인 예였다.
단순한 인터페이스는 말 그대로 너무 단조로워 보이기는 하지만, 다른 건 몰라도 화면 차지 면적이 작다는 장점 하나는 독보적이기 때문에 요즘은 스마트폰의 UI에서 제 위치를 찾은 것 같다.
이 글의 주제는 윈도우 GUI 컨트롤이므로 다시 GUI로 화제를 바꾸기로 한다.
아까 말했던 입력란(edit box), 라벨, 리스트박스, 버튼(체크, 라디오 등도 포함) 같은 건 기본 중의 기본 필수 요소이며, 까놓고 말해 윈도우 1.0 시절부터 존재했던 녀석이다. 해당 컨트롤의 기능은 운영체제와 완전히 일심동체가 되어 깊숙이 박혀 있다.
비단 운영체제의 GUI뿐만이 아니라 어느 GUI 툴킷을 보더라도 저런 컨트롤이 빠진 물건은 없다.
그런데 세월이 흐르다 보니 좀 더 새끈하고 산뜻한 컨트롤이 필요해졌다.
그래서 1990년대 후반, 윈도우 NT 3.51, 그리고 윈도우 95에서부터 운영체제 차원에서 새로운 컨트롤들이 여럿 도입되었다.
이름하여 공용 컨트롤(common control). 윈도우 1.0 때부터 있었던 innate한 선배들 system control과 구분하기 위해 붙은 이름이다.
무엇이 추가되었냐 하면,
트리 컨트롤: 계층 구조, 목차 따위를 표시할 수 있다.
리스트 컨트롤: 수많은 개체를 단순히 리스트 형태뿐만이 아니라 아이콘 모양으로도 표시할 수 있고, 개체의 특성을 여러 칼럼으로 분할해서 표현할 수 있어서 유용함.
도구모음줄(toolbar)과 상태표시줄(status bar)
탭 컨트롤
진행 상황 게이지 컨트롤(progress bar)
나름 쓸모 있는 것들이 많다. 특히 윈도우 95의 탐색기는 죄다 이 새로운 컨트롤을 잔뜩 우려먹어서 만든 것이다.
리스트 컨트롤과 기존 리스트박스는 하는 역할이 일면 비슷하다 할 수 있으나, 아이템을 추가하거나 정보를 얻어 오는 프로그래밍 인터페이스는 서로 완전히 다르다. 새로운 녀석이 기능이 워낙 많다 보니 훨씬 더 복잡하다. 게다가 둘은 사용법도 차이가 있다. 가령, 아이템을 복수 선택할 때 기존 리스트박스는 Shift+F8과 Space를 사용하지만, 리스트 컨트롤은 Ctrl+화살표와 Ctrl+Space를 사용한다.
이들 공용 컨트롤들은 어차피 MS가 오피스 같은 선구자적(?) 제품에서 자체 구현해 놓고 쓰던 컨트롤들을 운영체제 차원에서 정형화해 놓은 게 많았다.
예를 들어 도구모음줄과 상태표시줄은 어느 응용 프로그램들이나 자체적으로 구비해 놓던 GUI 요소였는데 그걸 다루기 쉬운 정식 컨트롤로 만들어 놨다. MFC도 16비트 시절에는 CToolBarCtrl이 도구 아이콘을 그리고 관리하는 자체 구현이었지만, 32비트부터는 공용 컨트롤에다 요청만 하는 형태로 바뀌었다.
윈도우 3.x의 매체 재생기는 자체 구현한 slider로 재생 위치를 표시했지만, 윈도우 95의 매체 재생기(지금 있는 Media Player의 전신)는 공용 컨트롤에 있는 slider를 썼다.
윈도우 3.x 시절의 설치 프로그램들은 자체 구현한 게이지로 설치 진행 상황을 표시했지만, 윈도우 95의 설치 프로그램은 공용 컨트롤에 있는 게이지를 쓴다. 컨트롤들의 재사용성이 향상된 셈.
비주얼 C++ 4.x의 예제 프로그램 중에는 이런 공용 컨트롤들을 다루는 모습만 시연해 놓은 놈도 있을 정도였다. 아래 그림을 참고하라.
공용 컨트롤들은 시기적으로 나중에 추가된 만큼, 기존 시스템 컨트롤만치 운영체제와 뗄레야 뗄 수 없는 일심동체 형태는 아니었다. 시스템 컨트롤들의 코드가 운영체제의 3대 요소 중 하나인 user(32)에 통합되어 있다면, 공용 컨트롤은 comctl32라는 고유한 라이브러리에 따로 들어있었다. 그리고 이들 컨트롤을 쓰려면 응용 프로그램이 comctl32.dll을 로딩하고 InitCommonControls 같은 함수도 호출해 줘야 했다.
실제로, ListBox, ComboBox, Edit 같은 시스템 컨트롤들은 언제라도 GetClassInfo 함수로 컨트롤의 정체성 정보를 확인할 수 있는 반면 SysListView32, msctls_statusbar32 같은 공용 컨트롤은 comctl32.dll을 별도로 읽어들이고 나야 정보를 얻을 수 있다. 운영체제와 완전한 일심동체가 아니라는 말이 이런 의미인 것이다.
다만, 굳이 InitCommonControls 초기화를 할 필요는 없이 그 DLL을 LoadLibrary만 해 줘도 되는 듯하다.
또한, 이들 컨트롤을 운영체제의 대화상자에서 쓴다면, 어차피 DialogBox 같은 함수가 comctl32.dll의 로딩과 공용 컨트롤의 초기화 정도는 알아서 해 주는 것 같다. 따라서 현실적으로는 응용 프로그램 개발자가 일일이 InitCommonControls를 호출은 안 해도 된다.
공용 컨트롤 라이브러리는 저렇게 새로운 컨트롤만 부품 차원에서 제공하는 게 아니라, 이들 컨트롤을 이용한 자체 UI 기능도 함수 형태로 제공한다. 탭 컨트롤을 이용한 Property Sheet와, ‘이전, 다음’ 형태인 Wizard가 대표적인 예이다. 이것도 윈도우 3.x 시절부터 자체 구현이 하도 유행으로 뜨다 보니까 운영체제 차원에서 자동화 기능을 넣어 준 셈이다.
윈도우 95 이후로 공용 컨트롤 라이브러리는 윈도우 XP에서 또 큰 변화를 겪었다. 이는 물론 테마라는 기능이 추가되었기 때문이다.
컨트롤을 그리는 방식이 예전과는 근본적으로 완전히 다르게 바뀌었기 때문에, 예전 라이브러리를 고칠 수는 없어서 그건 호환성 차원에서 그대로 두고 새 라이브러리를 덧씌우는 방식이 채택되었다. 바로 윈도우 XP에서 추가된 DLL side-by-side assembly 매니페스트 방식으로 말이다.
윈도우 시스템 디렉터리에 있는 comctl32.dll은 이제 구형이다. 윈도우 비스타나 7에서도 이놈의 제품 버전은 6.x이지만 파일 버전은 5.8x에서 멈춰 있음을 알 수 있다. 그 반면, 새로운 comctl32.dll은 Windows\winsxs 같은 완전히 다른 곳에 숨어 있다.
새로운 comctl32는 원래 user32에 있던 기존 컨트롤들을 테마가 적용된 자기 걸로 다 대체해 버린다. 그래서 Spy++ 같은 프로그램으로 확인해 보면, Edit· Button 같은 시스템 컨트롤들도 클래스 스타일에 CS_GLOBALCLASS 같은 플래그가 있다.
또한, URL 링크 같은 새로운 공용 컨트롤이 XP에서 추가되었는데, 이런 것들은 응용 프로그램이 6.0 버전 이상의 새로운 공용 컨트롤 라이브러리를 사용하겠다는 표식을 명시적으로 해야만 사용 가능하다. 나중에 새롭게 추가된 기능은 호환성을 지키기 위해 옛날 프로그램들에게는 바로 노출되지 않으며, 접근 내지 사용하가 이런 식으로 더욱 까다로워진다는 걸 알 수 있다.
이거 지정을 위해 예전에는 개발자가 매니페스트 xml 문서를 직접 써 줘야 했지만 비주얼 C++ 2005부터는 #pragma comment(linker, ...) 한 줄로 손쉽게 할 수 있다. 사실, 2005부터는 MFC와 C 라이브러리 DLL 지정도 이런 방식으로 바뀌었고, 2008부터는 윈도우 비스타에서 추가된 응용 프로그램 권한 등급 지정도 이렇게 할 수가 있다는 것도 잘 알려진 사실이다.
윈도우 비스타에서부터 추가된 UI 기능인 Task dialog 역시 comctl32.dll에 기능이 구현되어 있으며, 공용 컨트롤 6.0 이상이 지정된 응용 프로그램에서만 사용 가능하다. 쉘(shell32)이나 공용 대화상자(comdlg32) 계층에 구현되어 있지 않나 생각했는데 그렇지는 않다.
닷넷 프레임워크에서는 저런 운영체제의 지저분한 버전 별 디테일을 전혀 신경 쓸 필요가 없다는 게 좋은 점 중 하나이겠다.
Posted by 사무엘