1. 3차원 그래픽 시연 프로그램의 개편

지난 2003년부터 2005년 사이, 본인의 대학 재학 시절에 만들어진 뒤 10년이 넘게 버전업이 없었던 '3차원 그래픽 시연 프로그램'이 정말 오랜만에 업데이트 되었다. 무엇이 바뀌었냐 하면.. '시선 고정' 모드란 게 추가됐다.

이 프로그램은 지금까지 3D FPS 게임처럼 앞뒤 좌우 상하로 움직이는 것에만 최적화돼 있었는데, 시선 고정 모드에서는 카메라가 어디 있든지 시선이 언제나 기준점을 향해 고정되게 된다.
시선이 고정되니, 이때는 통상적인 좌우 화살표나 page up/down을 이용한 시점 변경이 동작하지 않는다. 끄덕끄덕(pitch)/설레설레(yaw) 말고 시선에 영향을 주지 않는 갸우뚱(roll)만 동작한다.

그리고 좌우 게걸음인 ZX와 상승/하강인 QA를 누르면 그냥 움직이는 게 아니라 기준점과 같은 거리를 유지하면서.. 기준점 주변을 상하 좌우로 돌게 된다.
요것이 본인이 원하던 바였다. 사실, 3차원 그래픽 편집 프로그램에서는 FPS 게임 같은 움직임보다는 이렇게 시선이 고정된 '빙글빙글' 앵글 이동을 더 흔히 볼 수 있었을 것이다. 이 동작을 내 프로그램도 뒤늦게 지원하게 됐다.

F를 누르면 시선 고정 모드를 켜거나 끌 수 있다. 아래의 상태 표시줄에 기준점의 좌표가 같이 나타난다.
그리고 D를 누르면 지금 카메라가 있는 위치를 기준점으로 지정한다. 초창기에는 (0, 0, 0) 원점이 기준점이다.
기준점을 파란색 동그라미 같은 별도의 부호로 표시해 주면 사용자에게 도움이 될 수 있겠지만.. 일단은 그런 걸 생략했다.

예제 데이터들 중에서는 구(sphere)가 제일 볼 만할 것이다. 이 구는 그렇잖아도 원점이 중심으로 만들어져 있다. 구에서 적당히 떨어진 뒤 시선 고정 모드를 켜고 QA/ZX를 누르면 우리가 인공위성이라도 된 것처럼 구 주변을 빙글빙글 돌게 된다.
그에 비해 토러스(torus)는 원점을 기준으로 만들어져 있지 않은 듯하니(구체적인 값은 기억이..) 적당히 다른 점을 기준으로 설정해야 튜브 안을 이탈하지 않고 빙글빙글 돌 수 있다.

사용자 삽입 이미지

지난 2016년에는 삼각형 오심 그리는 프로그램을 오랜만에 업데이트 했는데.. 이번에는 3차원 그래픽 프로그램을 손보게 되니 감회가 새롭다. '옛날 자료실'에 있는 프로그램들도 이런 식으로 최소한의 유지 보수는 여전히 하는 중이다.

2. 날개셋 개발 관련 미스터리

본인은 하루는 키보드 보안 ActiveX를 사용하는 어느 사이트에서 날개셋 한글 입력기 외부 모듈이 뻗는 걸 발견했다. 한글만 연달아 입력하는 것은 문제가 없는데, 그렇게 조합을 만들었다가 숫자나 마침표 같은 기호를 찍어서 조합을 중단하면.. 에러가 나고 브라우저 창이 다시 열리곤 했다.

마소 IME 같은 타 프로그램에서는 괜찮고 내 프로그램에서만 100% 재연 가능한 문제가 뻔히 발견되었는데.. 그렇다면 이 문제는 독 안에 든 쥐나 마찬가지이고 곧바로 원인을 추적해서 해결되어야 할 것이다. 그런데 믿어지지 않지만 도저히 그러지를 못했다. 디버깅에 필요한 모든 절차와 방법론을 IE와 보안 유틸리티가 원천봉쇄하고 있었기 때문이다.

exception handler를 지정해서 뻗었을 때 덤프 파일을 만들려고 해도, 윗선에서 예외 이벤트를 가로채기라도 하는지 덤프가 만들어지지 않았다. (덤프는 프로그램이 뻗은 지점이 소스 코드상으로 어디이고 그 당시 함수들의 호출 계층이 어떠한지에 대한 정보를 담고 있음. 원인 추적에 매우 중요!)

입력란이 떠 있는 IE 프로세스에다가 디버거를 붙이면.. 보안 유틸이 이를 감지하고 디버거를 끄라고 요구하면서 동작을 거부했다.
뻗었을 때 디버거를 붙여도 문제의 프로세스는 상황을 확인할 틈도 없이 혼자 싹 종료되어 버렸다.

결국은 무식하게 키 입력이 감지됐을 때.. 등 의심되는 모든 곳에다가 화면/파일로 로그를 찍어서 테스트를 해 봤다.
그런데 이거 뭐 내가 짠 코드는 모조리 정상 통과한 뒤에 이상한 데 엄한 데서 에러가 발생하는 것이었다.

이건 정황상 키보드 보안 유틸과 3rd-party IME와의 충돌이긴 하지만 내가 아는 방법으로는 도저히 문제의 원인이나 해결책을 파악할 수 없어서 이번 9.61 버전에서도 부득이하게 해결되지 못했다. 언젠가 여유가 있으면 그 보안 유틸의 개발사와도 협조를 구해서 합동 수사 공조라도 해야 하지 않을까 싶다.

3. 스레드

어느 플랫폼에서든 프로그램을 짜다 보면, 백그라운드 스레드에서 뭔가를 열심히 수행한 뒤에 결과값을 표시하는 마무리 작업은 반드시 main UI 스레드에서 실행해야 할 때가 생긴다. 이에 대해서 본인은 예전에 글을 쓴 적이 있다.

요즘 프로그래밍 언어들은 언어 차원에서 별도의 블록을 분리해서 이 블록 안의 코드는 별도의 스레드에서 비동기적으로 실행되다던가, main UI 스레드에서 실행시키는 식으로 간편하게 제약을 가할 수 있다. 요런 걸 macOS의 Objective C에서도 보고 Java, C# 등에서도 봤던 것 같다.
그런 게 지원되지 않는 언어나 플랫폼에서는 해당 기능을 직접 구현하게 되는데.. Windows라면 메시지를 보내는 것과 일맥상통한다. main UI 스레드라면 그 정의상 message loop을 돌리고 있을 것이기 때문이다.

그런데 Windows용 IME는 자기가 만들지 않은 남의 프로그램 창, 남의 스레드, 남의 message loop을 기반으로 돌아가기 때문에 거기에다가 자신만의 메시지와 자신만의 메시지 핸들러를 슬쩍 얹기가 좀 난감하다.
그나마 옛날에 프로토콜이 IME 방식이던 시절에는 IME가 제각기 자신만의 보이지 않는 윈도우가 있었기 때문에 내부적으로 custom 메시지를 얼마든지 처리할 수 있었다. 하지만 TSF로 바뀐 뒤에는 그런 윈도우가 존재하지 않는다.

IME는 키보드 포커스를 받는 남의 윈도우에다가 WM_USER+* 이상한 메시지를 함부로 보내서는 안 되고, 타이머 ID도 함부로 선점하지 않아야 한다. 그러면 윈도우 핸들 없이 콜백 함수를 바로 호출하는 타이머밖에 선택의 여지가 없는데.. 그렇게 하면 SetTimer를 호출하는 자신과는 다른 스레드 문맥에서 처리되는 타이머를 생성할 수가 없다.

이게 생각보다 굉장히 난감한 문제이다. 결국은 타 스레드에서 main UI 스레드 문맥으로 특정 코드를 실행하기 위해 본인은 TSF 모듈도 임시로 나만의 message-only 윈도우를 main UI 스레드에서 생성하고, 이 윈도우가 특정 메시지를 받았을 때 그 코드가 실행되도록 프로그램을 작성했다. 메시지라는 게 알고 보면 스레드 간 교통 정리에 큰 기여를 하고 있는 셈이다.

사실 스레드, 특히 콘솔 프로그램이 아니라 main UI가 있는 프로그램에서 스레드를 쓰는 건 십중팔구 사용자 입장에서 프로그램의 반응성을 올려 주기 위해서 하는 게 대부분이다. 일시불로 프로그램이 잠시 멈추고 뜸을 들이는 게 싫으니 이자를 감수하고라도 찔끔찔끔 할부를 선택하는 것이다.

스레드 그 자체는 메모리를 더 잡아먹고 컨텍스트 스위칭 오버헤드 때문에 전반적으로 CPU가 할 일을 더 늘리고 성능을 떨어뜨린다. 하지만 스레드를 만들어서 컴퓨터가 더 많이 고생할수록 사용자의 입장에서는 더 편리한 기능이 많이 구현되는 것이 사실이다.

4. 날개셋 외부 모듈과 입력 패드의 동작 차이

날개셋 한글 입력기의 구현체 프로그램 중에서 편집기는 오로지 자기 혼자서만 돌아가는 프로그램이니 다른 고민의 여지가 없는데.. 외부 모듈과 입력 패드는 본격적으로 타 프로그램에다 문자를 보내기 때문에 다양한 환경에서 최대한 동일한 동작을 보장해야 한다. 그게 불가능한 경우 불가능한 한계에 대해서 사용자에게 미리 고지를 해야 한다.

Windows용 IME의 입장에서 좀 별종인 환경은 전통적으로 (1) 로그인(잠금) 화면, 그리고 (2) 명령 프롬프트가 있다. 그리고 Windows 8/10부터는 (3) Metro UI도 추가됐다.
입력 패드는 원래 명령 프롬프트에서는 전혀 동작하지 못하다가 Windows 7에서부터는 동작 가능해졌다.

외부 모듈은 Metro UI에서는 조합 중인 한글을 보내는 일반적인 동작은 가능하지만 tab, enter 같은 글쇠 입력을 보내지는 못한다(날개셋문자 또는 각종 입력 도구의 버튼을 통해서).
그 외에 Metor UI에서는 프로그램이 외부의 데이터 파일을 참조하지 못해서 입력 설정이 데스크톱 환경과 동기화되어 있지 않다거나, 문자표 같은 입력 도구들도 제대로 동작하지 않는 한계가 있다. 입력 도구를 X 버튼을 눌러서 닫을 수도 없다. (우클릭 메뉴를 이용해야..)

한편, 입력 패드는 외부 모듈과 달리 자신이 독립된 프로세스이기 때문에 파일을 못 여는 한계는 없다. 단지, 반대로 Metro UI로 조합 문자 같은 입력 동작을 보낼 수 없을 뿐이지..;;
그런데 굉장히 의외로 글쇠 입력을 보낼 수는 있다. 가령, 화면 키보드에서 ㄱ, ㅏ 같은 한글 조합 글쇠는 못 보내지만 tab, enter 버튼을 누른 것은 전해진다는 뜻이다. 외부 모듈이 할 수 없는 일을 입력 패드가 예외적으로 할 수 있으니 흥미로운 차이점이다.

외부 모듈에서 글쇠 입력을 못 보냈거나 입력 패드에서 자체 입력을 못 보냈을 때, 못 보냈다는 에러 메시지라도 출력할 수 있으면 좋겠지만 일단은 방법이 없어 보인다.

외부 모듈과 입력 패드에는 이것 말고도 흥미로운 차이점이 더 있다.
외부 모듈은 Windows 메시지를 직접 받지 못한다는 특성 때문에 alt가 섞인 단축글쇠를 전혀 인식할 수 없다. 원래 alt는 운영체제 차원에서의 단축키나 메뉴 구동용으로 쓰이지 IME 같은 데서 가로챌 여지가 없기도 하고 말이다.

그 반면, 비록 프로토콜을 제대로 지원하는 소수의 프로그램 한정이긴 하지만 그래도 편집기와 다를 바 없는 온전한 A급 동작을 보장 가능한 것도 외부 모듈이다. 입력 패드로는 완성된 한글을 낱자 단위로 지우거나 달라붙는 자유자재 동작까지 지원하지는 못한다. 서로 일장일단이 있는 셈이다.

Posted by 사무엘

2018/12/09 08:35 2018/12/09 08:35
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1563

1. Windows

과거에 Windows 95에는 워드패드, 그림판, 메모장, 계산기, 지뢰 찾기 같은 친근한(?) 프로그램 말고, 디스크 검사나 조각 모음, 남은 리소스 표시기 같은 프로그램도 같이 제공되었다. 이런 건 도스 시절부터 유틸리티 내지 '툴'이라는 카테고리로 분류되어 온 프로그램들이다.

Windows 98에는 간략히 보기(QuickView)라는 유틸리티도 있어서 탐색기의 우클릭 메뉴를 통해 실행할 수 있었다. 주요 문서· 이미지 파일들을 본격적인 편집 프로그램을 실행하지 않고 내용만 재빨리 들여다볼 수 있었으며, 제공되는 COM 인터페이스를 확장하면 제3자가 임의의 파일 포맷에 대해서 간략히 보기 기능을 추가로 제공해 줄 수도 있었다.

또한 이 QuickView는 자체적으로 exe/dll의 내부 구조를 분석해서 보여주는 기능도 있었다. 32비트 바이너리에 대해서는 PE 헤더에 기록된 내용과, import/export 심벌의 내용을 보여줬으며, 16비트 바이너리에 대해서는 각종 resident/non-resident 문자열 테이블의 내용을 보여줬기 때문에 파일 내부를 들여다보는 용도로 꽤 괜찮았다.

다만, Visual C++ 6부터는 빌드하는 바이너리에서 import 및 export 심벌들을 고유한 섹션 없이 그냥 rdata 섹션에다가 집어넣기 시작했는데, 이건 QuickView가 제대로 감지해서 보여주지 못했다. 그리고 QuickView는 Windows 2000/ME 같은 후대 버전에서는 더 존재하지 않고 없어졌다.
그 대신 Windows 95가 아닌 98에서 첫 도입되어 지금까지 전해져 오는 유틸리티는 (1) 시스템 정보(msinfo32), 그리고 (2) DirectX 진단 도구인 dxdiag이다.

시스템 정보는 도스 시절부터 PC-Tools나 Norton 같은 3rd-party 유틸리티들이 단골로 제공하던 기능이었는데, Windows용으로는 딱히 마땅한 게 없었다. 단순히 운영체제의 버전이나 남은 메모리 양 말고 컴퓨터 프로세서의 명칭이나 정확한 속도 벤치마킹 같은 건 쉽게 얻을 수 있는 정보가 아니었다.

사실, 시스템 정보는 Windows 팀이 아니라 Office 팀에서 먼저 개발해서 독자적으로 제공하고 있었다. Windows 95보다도 더 이른 1994년에 출시된 Word 6.0의 About 대화상자를 보면 System Info라는 버튼이 있다. 그러던 것이 Windows 98부터는 운영체제 기능으로 옮겨진 것이다.

그래서 그런지 System Info의 초기 버전에는 현재 실행 중인 Office 프로그램이 있는 경우, 그 제품이 현재 열어 놓은 문서 같은 시시콜콜한 정보도 표시해 주는 기능이 있었다. 그러나 그 기능은 Windows XP인가 Vista 즈음부터 삭제됐다.
지금은 시스템 정보에서 본인이 종종 유용하게 열람하는 것은 CPU 관련 정보, 그리고 현재 실행 중인 모든 exe/dll들을 조회하는 기능 정도이다.

한편, dxdiag도 처음 도입됐을 때와 달리 인터페이스가 갈수록 단순해지고, 그냥 '드라이버 상태 이상 무'만 출력하는.. 있으나마나 한 유틸이 돼 간다.
처음 도입됐을 때는 간단한 예제 그래픽과 애니메이션, 음악을 출력하면서 상태를 점검하는 기능이 있었다. 그랬는데 DirectDraw는 그냥 운영체제의 기능으로 흡수되고, DirectMusic나 DirectPlay 같은 건 짤리고 DirectX는 오로지 Direct3D에만 올인을 하면서 진단 도구도 지금처럼 바뀌게 됐다. 그래픽 쪽도 데모 기능은 없어졌다.

2. Visual Studio

Visual Studio (더 정확히는 Visual C++)는 개발툴이다 보니, 프로그래머의 입장에서 도움이 되는 자그마한 유틸리티들이 이것저것 같이 제공되곤 했다.

가장 대표적인 물건은 (1) Spy++이다. 응용 프로그램이 생성하는 모든 윈도우들과 거기 내부에서 발생하는 메시지들을 들여다볼 수 있으니 역공학 분석 용도로도 아주 좋다. 이 프로그램은 Visual C++ 초창기 버전부터 지금까지 거의 모든 버전에서 개근하고 있다. 아이콘에 그려져 있는 스파이(?) 아저씨도 처음에는 초록색 복장이다가 분홍색, 보라색을 거쳐 검정색까지 여러 번 변모해 왔다.

다만, 최신 운영체제에서 새로 추가된 메시지를 지원하는 것 외에 딱히 추가적인 개발이 되고 있지는 않으며, 도움말도 2000년대 초에 chm 기반으로 바뀌고 나서 딱히 변화가 없는 것 같다. VC++ 2005 즈음 버전에서, 트리 구조가 리스트 박스를 쓰던 것이 진짜 트리 컨트롤 기반으로 바뀐 것이 그나마 큰 변화였다.

그리고 (2) Dependency Walker도 아주 훌륭한 도구이다. 어떤 EXE/DLL이 외부로 제공하는(export) 심벌, 그리고 자신이 import하는 심벌들을 모두 분석해서 모듈별 dependency tree를 딱 구축해 준다. 아까 소개했던 QuickView와 달리, 최신 버전 바이너리들도 잘 지원한다.
심지어 Profiling이라고, 프로그램이 실행 중에 동적으로 불러들이는 dll과 심벌까지 찾아 주는 기능도 있다.

얘는 Visual C++ 6에서 1.x대의 구버전이 같이 제공되었지만 2000년대 이후부터는 같이 제공되지 않고 있다. 개발자의 홈페이지에서 최신 버전을 따로 받아야 한다. 다만, 이 프로그램은 2000년대 중반, Windows Vista 타이밍 이후로 개발과 지원이 사실상 중단된 듯하다.

(3) GUID 생성기는 프로그램 짜면서 내 오브젝트의 GUID를 생성할 일이 있을 때 사용하면 되는 물건이고..
(4) Error lookup도 매번 winerror.h를 뒤지는 수고를 덜어 주니 좋다. 소켓 API처럼 특정 모듈을 불러들인 뒤에야 내역을 알 수 있는 에러 코드값도 의미를 알 수 있다.

옛날에, 2003 정도 시절까지는 뭐랄까 COM 기술과 관계가 있는 도구가 더 있었다.
(5) ActiveX Test Container는 운영체제에 등록돼 있는 ActiveX 컨트롤들을 마치 Visual Basic처럼 클라이언트 영역 여기저기에 설치해 놓고는 이벤트 로그를 확인하고, 속성값을 변경하고 메소드들을 invoke할 수 있는 툴이었다.

ActiveX 컨트롤은 기술적으로 따지자면 무슨 비주얼 베이직이나 델파이의 컴포넌트를 개발툴 밖에서 어디서나 쉽게 사용할 수 있게 해 놓은 열린 규격에 가까웠다. 취지 자체는 나쁘지 않은데 후대에서 적극적으로 쓰이지를 않았으며, 기껏 웹에서나 비표준으로 잔뜩 쓰이다가 지금은 마소에서도 사실상 버린 자식 신세가 됐으니 참 안습하다.

본인도 실무에서 ActiveX 컨트롤을 삽입한 건 IE 웹브라우저 컨트롤과 플래시 컨트롤 딱 둘밖에 없었다. 원래는 Word/Excel 문서조차도 ActiveX 형태로 내 프로그램에다 삽입할 수 있다. 하지만 지금은 ActiveX 없이 웹 브라우저 화면에서 그런 문서 편집 화면을 띄우는 세상이 됐다. 거 참...

(6) 그리고 OLE/COM Object Viewer는 HKEY_CLASSES_ROOT 레지스트리를 싹 뒤져서 시스템에 등록돼 있는 COM 객체들을 몽땅 출력하고, 이들 프로그램 바이너리에 내장된 type library를 추출해서 해당 객체들의 구체적인 스펙을 보여줬다. 이건 문서화되지 않고 COM 형태로만 몰래 제공되는 운영체제의 숨은 기능들을 찾는 데 굉장히 큰 도움이 됐다. 이놈의 GUID는 무엇이고 어떻게 불러 오고 어떻게 호출하면 되는지를 말이다.

(5)와 (6)은 언제부턴가 짤려서 Visual Studio 201x에서부터는 찾아볼 수 없게 됐다. 맥은 레지스트리도 없고 COM, OLE 같은 것도 존재하지 않겠지만, 그래도 Spy++나 Dependency Walker의 맥 버전에 해당하는 유틸은 없는지 궁금해진다. Windows 프로그래밍 공부에 굉장히 큰 도움이 되었던 프로그램인걸 말이다.

(7) regsvr32는 개발툴이 아니라 운영체제에서 제공하는 유틸이긴 하지만, Visual Studio의 외부 도구 메뉴에다가 등록해 놓을 만한 물건이다. 내가 코딩해서 빌드한 바이너리에 대해 곧장 DllRegisterServer를 호출해서 등록을 할 수 있게 말이다.

얘는 운영체제의 system 디렉터리에 32비트용과 64비트용이 제각각 존재하는데, 아무 프로그램에다가 32/64비트 아무 바이너리를 넘겨 줘도 잘 동작한다. 64비트 운영체제를 개발하면서 regsvr32에 그 정도 유도리는 같이 구현돼 있다.
COM 객체의 등록이나 제거를 위해서는 HEKY_CLASSES_ROOT 레지스트리를 건드려야 하니, 관리자 권한 실행은 언제나 필수이다.

끝으로, 독립된 프로그램은 아니지만.. Visual Studio의 200x대 버전부터는 (8) 전용 명령 프롬프트를 띄우는 기능이 외부 도구 메뉴에 등록돼 있다. 즉, 어느 디렉터리에서나 CL 같은 컴파일러와 링커를 띄울 수 있도록 LIB, INCLUDE, VCINSTALLDIR 같은 환경 변수들이 맞춰진 명령 프롬프트이다.

Visual Studio가 버전이 올라갈수록 IDE와 플랫폼 SDK가 분리되고, IDE와 컴파일러 툴킷 계층이 분리되고, 한 컴퓨터에서 여러 버전이 설치되는 것에도 유연하게 대응 가능하게 변모해 온 것은 매우 바람직한 현상으로 보인다.

Posted by 사무엘

2018/11/28 08:34 2018/11/28 08:34
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1559

컴퓨터 프로그램은 실행되는 과정에서 단순히 주메모리만 읽고 쓰는 게 아니라, 디스크처럼 기록이 영구적으로 남는 보조 기억 장치에다가도 정보를 기록한다.
여기에 기록되고 보관되는 것은 단순히 사용자가 직접 만들어 내서 Save라는 명령을 내려서 저장하는 문서 데이터만이 전부가 아니다. 사용자가 지정한 프로그램 자체의 각종 옵션· 설정값도 저장되어서 나중에 이 프로그램을 다시 시작할 때 그대로 보존되곤 한다. 세심한 프로그램이라면 직전에 프로그램 창을 띄워 놨던 크기와 위치 같은 시시콜콜한 정보도 몽땅 다 기억해 놓는다.

이런 '설정 정보'를 저장하기 위해 프로그램들은 예로부터 자신만의 cfg 내지 config.dat 같은 파일을 만들어 두곤 했다. 본인이 개발한 날개셋 한글 입력기도 imeconf.dat라는 파일을 운영체제의 사용자 계정별로 고정된 디렉터리에다 만들어 놓는다. (물론 날개셋 프로그램의 경우.. 입력 설정이라는 게 단순한 프로그램 setting 수준을 넘어 그 자체가 이미 사용자가 새로 창조하는 '문서 데이터'라는 성격도 지닌다만...)

Windows에서는 이런 설정 파일을 저장하고 불러오는 절차를 정형화하기 위해서 ini(초기화 정보)라는 텍스트 파일 포맷을 도입했으며, 이걸 읽고 쓰는 [Get/Write][Private]Profile[Int/String]이라는 API 함수를 제공해 왔다. [섹션] 구분과 함께 "이름=값" 이런 게 주욱 들어가 있는 그 파일 말이다.

이들 함수는 파일을 읽고 씀에도 불구하고 뭔가 열어서 핸들값을 얻었다가 나중에 닫는 절차가 없는 게 특징이었다. 구현하는 사람 입장에서는 그리 좋은 설계 형태가 아닐 텐데..
Private이 안 붙은 API를 사용하면 자기 ini 말고 심지어 운영체제의 설정 파일인 win.ini에다가도 정보를 기록할 수 있었다.

ini 파일은 당장 사용하기는 간편하지만 한계와 문제점이 많았다. 가장 먼저 XML 같은 다단계 계층 구조를 지원하지 않았다(과거 Windows 3.x의 프로그램 관리자의 그룹이 계층 구조가 아니었던 것처럼).
그리고 아까 언급했듯이 핸들/상태 정보가 없는 API의 설계 형태에다가 텍스트 포맷이라는 점까지 겹쳐서 데이터가 방대해질 때의 처리 성능도 썩 좋지 않았다.

응용 프로그램이 있는 디렉터리, 또는 Windows 디렉터리에 온갖 자잘한 ini 파일들이 쌓이면서 지저분해지는 점 역시 문제였다. 그리고 저장을 할 거면 사용자 설정 데이터들 전용 디렉터리라도 마련해 놔야지, 그게 프로그램 실행 파일들이 있는 곳에 버젓이 저장되는 건 오늘날의 보안 내지 권한 관점에서 봤을 때 좋지 못한 설계 형태였다.

그래서 성능, 보안, 효율 등등을 모두 잡기 위해서 마소에서는 운영체제와 응용 프로그램들의 설정 저장은 파일 형태로 노출시킬 게 아니라 이를 전담하는 별도의 거대한 중앙집권 데이터베이스를 만들 생각을 하게 됐다. 그래서 이를 레지스트리라는 이름으로 일찍부터 도입했다.
마치 디렉터리 경로처럼 역슬래시로 구분된 계층 구조의 key를 만들고, 그 아래에 파일처럼 여러 개의 value들을 집어넣을 수 있게 했다.

물론 레지스트리는 구조적으로 장점도 있고 단점도 있다. 컴덕이나 프로그래머들 사이에서 호불호가 갈리며, 레지스트리를 아주 싫어하는 사람도 있다. macOS나 리눅스는 레지스트리 그딴 거 없이도 잘 돌아가고 더 안정적이기까지 하다고 주장한다.
하지만 레지스트리가 없다고 해서 그런 OS에 레지스트리가 하는 일 자체가 존재하지 않는 건 아니다. 그 OS에서도 운영체제나 응용 프로그램들이 자기 설정을 저장하는 공간이 있어야 할 텐데 도대체 어디에 저장되는 걸까?

프로그램을 제거한 뒤에도 그 프로그램이 써 놓은 레지스트리 데이터가 같이 지워지지 않아서 레지스트리가 갈수록 지저분해지고 통제불능이 된다는 식의 비판이 있다. 그런데 그건 ini/cfg 파일을 쌩으로 다룰 때도 부주의하면 어차피 똑같이 발생할 수 있는 문제이다. 한쪽에서 존재하는 문제는 대부분 다른쪽에서도 형태만 바뀐 채로 고스란히 존재할 수밖에 없다. 본인은 이런 논리를 근거로 레지스트리 무용론 급의 회의적인 시각에는 동의하지 않는다.

프로그램마다 설정 데이터라는 게 수십~수천 바이트, 정말 커 봤자 수만 바이트 남짓할 아주 작은 분량에 지나지 않는다. 그런 자잘한 데이터를 한데 관리해 주는 DB가 운영체제 차원에서 제공되면 편하면 편하지 상황이 더 나쁘지는 않을 것이다.

레지스트리는 딱 32비트 Windows 95/NT와 함께 등장했다. 이때부터 마소에서는 소프트웨어 개발자들로 하여금 구닥다리 INI 파일 함수를 쓰지 말고 ADVAPI32.DLL에 있는 Reg** 레지스트리 조작 함수를 사용할 것을 적극 권해 왔다.
다만, 이들 함수는 구닥다리 함수보다 받아들이는 인자가 많고 사용하기가 번거롭고 귀찮긴 하다. 마치 fopen과 CreateFile의 차이만큼이나 말이다.

그래서 별도의 클래스를 만들어서 쓰면 편하다. HKEY를 멤버로 가지면서 소멸자에서는 RegCloseKey를 해 주고, 각종 타입별로 인자를 다양하게 오버로딩한 Get/Set 함수를 만들고 말이다. 레지스트리 관련 API는 MFC에서도 의외로 클래스화를 전혀 하지 않았으니 이거 연구는 프로그래머들 개인 재량이다. MFC는 16비트 시절부터 있던 CWinApp 클래스의 [Get/Write]Profile* 함수를 상황에 따라 ini 대신 레지스트리 기록으로 대신하도록 동작을 확장했을 뿐이다.

사실, 16비트 Windows 시절에는 지금처럼 HKEY_* 어쩌구 하는 그런 형태의 레지스트리는 없었지만, 그 전신 비스무리한 건 있었다. Windows 3.1에서 그 이름도 유명한 OLE라는 기능 내지 개념이 추가됐기 때문이다.
응용 프로그램들의 설정 같은 건 몰라도 확장자별 연결 프로그램이라든가 OLE 서버 같은 정보들은 운영체제 차원에서 관리하는 별도의 바이너리 데이터 파일에 등재되었다(Windows\reg.dat). 그리고 여기에 정보를 사용하는 Reg[Open/Create/Close]Key와 Reg[Query/Set]Value 같은 함수가 있었다.

16비트 시절부터 이런 초보적인 함수가 있었는데 이를 확장하여 오늘날의 레지스트리가 된 것이다. 그렇기 때문에 오늘날 사용되는 레지스트리 함수들은 대부분 뒤에 Ex가 추가돼 있다. 옛날에는 루트 키로 HKEY_CLASSES_ROOT 하나만이 존재했었다.
이 점을 생각하면 옛날에는 지금 같은 레지스트리가 있지도 않았는데 지금 왜 RegCreateKeyEx, RegQueryValueEx 등등 Ex 함수를 써야 하는지 이유를 알 수 있다.

지금까지 Windows의 레지스트리 개념과 관련 API를 살펴봤으니, 다음으로는 역대 버전별 레지스트리 편집기에 대해서 살펴보도록 하겠다.
레지스트리 편집기는 과거 도스 시절로 치면 디스크 내부 구조를 저수준에서 수정할 수 있는 노턴 유틸리티의 DiskEdit와도 비슷한 아주 저수준 유틸리티이다. 아예 없어서는 안 되지만 일상적으로 쓸 일은 없으며(없어야 하며), 초보자에 의한 섣부른 조작은 절대 권장되지 않는 위험한 프로그램이다. 이걸 조작함으로써 운영체제를 맛이 가게 만들고 컴퓨터 부팅조차 안 되게 만들 수도 있기 때문이다. 그러니 보조 프로그램 그룹에 정식으로 등재될 일도 없다.

우리가 흔히 알고 있는 레지스트리 편집기는 Windows 95 시절부터 지금까지 큰 변경 없이 이어져 오고 있는 탐색기(왼쪽에 트리, 오른쪽에 리스트 컨트롤) 비슷한 형태의 프로그램이다.

사용자 삽입 이미지

(1) Windows 9x에서는.. 레지스트리는 내부적으로 Windows\System.dat와 Windows\User.dat라는 두 파일에 저장되었다. 그리고 HKEY_DYN_DATA라는 predefined root key가 있어서 여기를 들여다보면 일부 시시각각 변하는 시스템 정보 같은 걸 얻을 수 있었다고 한다.

그리고 Windows 9x용 레지스트리 편집기는.. 단순히 This program cannot be run in DOS mode가 아니라 유효한 도스용 코드가 stub으로 같이 들어간, 다시 말해 DOS/Windows 겸용 프로그램이었다.
레지스트리에 이상이 생겨서 Windows 부팅이 안 되더라도 레지스트리의 백업과 복구 정도는 도스에서 할 수 있게 하기 위해서이다. regedit.exe를 순수 도스에서 실행하면 놀랍게도 이런 옵션 안내를 볼 수 있다.

사용자 삽입 이미지

마소에서 프로그램을 이런 식으로 특수하게 빌드한 예는 극히 드물다. 이는 레지스트리 편집기가 그만치 특수한 프로그램임을 의미한다.

(2) 그럼 Windows NT로 넘어가기 전에, 더 옛날 Windows 3.x를 잠시 살펴보도록 하겠다.
이때도 레지스트리의 전신이 있었고 비스무리한 API도 있었듯이, regedit.exe라는 프로그램 자체는 있었다.
하지만 얘 역시 그룹에 정식으로 등록되어 있지는 않았으며, 이때 registry란 보다시피 그냥 확장자 별 연결 프로그램과 관련 DDE 명령.. 이런 것이 전부였다. 아까 언급했던 Windows\reg.dat의 내용을 편집한다.

사용자 삽입 이미지

저 때는 Properties도 '등록정보'라고 번역하고 Registry도 '등록 정보'라고 번역했다니.. 참 므흣하다. 띄어쓰기 하나 차이밖에 없다.
Windows 95부터는 확장자 연결 설정은 그냥 탐색기의 옵션에서 별도의 탭으로 들어가 있다. 그리고 98을 넘어서 2000/XP즈음부터 Property의 한국 마소 공식 번역은 '속성'으로 완전히 바뀌어서 지금에 이르고 있다.
그리고 이 시절의 regedit.exe에는 95의 것과 같은 유의미한 도스 stub이 들어가 있지도 않았다.

(3) 이제 오늘날 사용되고 있는 NT 계열 차례이다.
일단, 레지스트리가 저장되는 파일은 Windows\system32\config에 있는 default, software, system, components 같은 확장자 없는 파일들이다. 크기가 다들 수십 MB씩 한다.
그리고 각 사용자별 정보는 사용자 계정의 루트에 있는 ntuser.dat 파일이다.
9x와 NT 계열이 파일 포맷이 동일한지는 모르겠다. 지금의 NT 계열은 도스 부팅 기능이 없고, 운영체제가 가동 중일 때는 이들 파일들이 언제나 열리고 잠겨 있어서 일반 프로그램이 레지스트리를 파일 차원에서 들여다볼 수가 없다.

Windows NT 계열은 HKEY_PERFORMANCE_DATA라는 전용 root key가 있다. HKEY_DYN_DATA처럼 실제 레지스트리 데이터는 아니지만 레지스트리 API를 통해 시스템 정보를 얻어 오는 용도인데.. 이것도 비슷한 기능이 9x와 NT 계열의 구현이 서로 파편화된 경우가 아닌가 싶다. 로드된 프로세스/DLL 정보를 얻는 API만 해도 과거에는 두 계열이 서로 달랐으니 말이다.

그리고 Windows NT는 초창기 3.1 시절부터 지금과 같은 형태의 레지스트리를 갖고 있었기 때문에 자체적인 레지스트리 편집기도 보유하고 있었다. 바로 regedt32.exe이다.

사용자 삽입 이미지

얘는 만들어진 시기가 시기이다 보니, 탐색기가 아니라 구닥다리 파일 관리자 스타일로 만들어져 있다. 왼쪽의 계층 트리와 오른쪽의 값 목록은 모두 재래식 리스트 컨트롤 기반이다. 또한, 루트별로 제각각 다른 레지스트리 창들이 MDI 형태로 구성되어 있다.
오늘날 쓰이는 공용 컨트롤 기반 regedit.exe는 바로 regedt32.exe를 베껴서 새로 만들어진 거나 마찬가지이다.

Windows 2000, 그리고 확인은 안 해 봤지만 NT4에는 regedit와 regedt32가 같이 들어있었다. 기능이 대등하긴 하지만 regedit는 오리지널 NT용 유틸리티에 있던 '레지스트리 하이브'를 통째로 불러들이는 기능이 없었다.
그러다가 regedit에 이 기능이 들어가서 regedt32를 완전히 대체하게 된 것은 Windows XP부터이다. NT의 regedt32를 보면 메뉴가 뭔가 많이 있는 것 같은데, 실제로 열어 보면 별 거 아니다.

Windows NT의 레지스트리 편집기는 9x의 것과 달리 값의 이름과 데이터뿐만 아니라 REG_SZ, REG_DWORD 같은 타입도 표시해 줬다. 9x에서는 어려운 전문 용어라고 일부러 뺐던 것 같다.
그리고 이런 새 GUI 기반의 레지스트리 편집기는 오랫동안 REG_MULTI_SZ라고 0으로 구분된 복수 문자열을 편집하는 기능이 없어서 그냥 바이너리 에디터 창이 떴었다. 그러다가 regedt32와 기능이 통합된 Windows XP에서부터 한 줄에 하나씩 복수 문자열을 편집하는 기능이 도입되었다.

이렇듯, regedit와 관련하여 Windows 3.1, NT 3, 9x 등.. 복잡한 사연이 많은 걸 알 수 있다.
이 프로그램에는 레지스트리의 일부 구간을 별도의 파일로 저장하거나 도로 불러오는 기능이 응당 들어있는데, reg 파일은 아이러니하게도 key 이름이 []로 둘러싸이고 값들이 a=b 이런 식으로 쓰인 게 마치 과거의 ini와 형태가 비슷하다.

한번 만들어진 뒤에 딱히 기능이 바뀔 일이 별로 없는 프로그램이다만.. Windows 2000부터는 reg 파일의 인코딩이 UTF-16으로 바뀌었다. 그리고 Windows 10 어느 업데이트부터는 현재의 레지스트리 주소(key 이름)이 아래의 상태 표시줄 대신에 위의 주소 표시줄에 표시되고, 사용자가 거기에 인터넷 주소 치듯이 수동으로 입력을 해서 원하는 key로 바로 찾아갈 수도 있게 UI가 편리해졌다. 나름 굉장히 바람직한 개편이다.

※ 부록: 레지스트리 편집기에 준하는 위상의 자매품

1. sysedit

과거 Windows 3.x 시절에는 sysedit라는 이름으로 config.sys, autoexec.bat, win.ini, system.ini라는 4대 시스템 설정 파일만 한데 모아서 편집할 수 있는 MDI 텍스트 에디터가 있었다. 이게 Windows 9x는 말할 것도 없고 NT 계열 제품에도 32비트 한정으로 포함돼 있었다.
Windows에 내장돼 있는 극소수의 16비트 프로그램이기 때문에 날개셋 한글 입력기를 테스트 할 때도 개인적으로 유용하게 사용했다.

사용자 삽입 이미지

물론 Windows 9x는 config.sys와 autoexec.bat를 거의 퇴출시켰고, NT 계열은 뒤이어 두 ini 파일까지 완전히 퇴출시킨 거나 마찬가지다.

2. msconfig

Windows 98부터 잘 알려지지 않았지만 꽤 유용한 유틸리티가 추가됐다.
얘는 9x용은 sysedit가 하는 일을 모두 포함하면서(해당 파일에 내용을 추가하거나 기존 내용을 간편하게 제거), 레지스트리에 등록돼 있는 시작 프로그램들의 실행 여부를 제어하고, 각종 시스템 관리 유틸리티도 실행하는 기능을 제공했다.

사용자 삽입 이미지

이 프로그램은 레지스트리 편집기와 마찬가지로 오늘날까지도 남아 있기 때문에 명령 프롬프트에서 실행 가능하다.

Posted by 사무엘

2018/11/22 08:33 2018/11/22 08:33
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1557

Windows에서 돌아가는 GUI 프로그램은 커다란 자기 창을 띄우며, 그러면 작업 표시줄(task bar)에서도 그 창이 있다는 걸 표시해 준다.
그런데 작업 표시줄은 어떤 프로세스가 생성하는 여러 창들을 어떤 기준으로 선별하여 표시하는 걸까? 화면에만 표시되고 작업 표시줄에는 나타나지 않는 일명 '스텔스 윈도우'는 어떻게 만드는 걸까?

심지어 이 작업 표시줄에 등재된 창 목록은 Alt+tab을 눌렀을 때 나타나는 task 목록과도 완전히 일대일 대응하지는 않는 것 같다. 그 관계는 어떻게 될까?

일단, 작업 표시줄은 (1) child가 아니고(overlapped 또는 popup) owner(parent)가 NULL인 모든 윈도우를 자기 목록에 등재해서 표시해 준다. 대화상자건, 응용 프로그램이 클래스를 따로 등록한 윈도우건 모두 상관없다. 이건 대부분의 상황에서 충분히 합리적인 조치이다.

옛날에 대략 Windows XP 정도까지 쓰던 기억을 떠올려 보면, 디스플레이/키보드/마우스 같은 제어판 애플릿들은 분명 rundll32라는 독립된 프로세스로부터 구동된 대화상자임에도 불구하고 작업 표시줄에는 제목이 뜨지 않았다. 그 이유는 제어판 애플릿은 제어판 셸로부터 주어진 보이지 않는 부모 윈도우를 owner로 삼고 생성되기 때문이다.

그러므로 보이지 않는 윈도우를 생성하여 owner로 잡으면 대화상자뿐만 아니라 크기 조절 가능한 멀쩡한 overlapped 윈도우라도 작업 표시줄에 표시되지 않는 '스텔스' 형태로 만들 수 있다. 일반적으로 그런 건 만들 필요가 없고 그게 UI 디자인 상으로 권장되는 짓도 아니니 안 할 뿐이다.
그리고 저런 모델은 응용 프로그램의 입장에서는 자기 창이 하나가 아니라 두 개가 존재하는 셈이므로 창을 관리하는 게 약간 더 귀찮아진다.

한편, 위의 (1) 다음으로 몇 가지 단서가 있다. (2) WS_EX_TOOLWINDOW는 owner가 NULL이더라도 이 창이 무조건 작업 표시줄에 등록되지 않게 하고 심지어 Alt+tab 작업 목록에도 나타나지 않게 한다.
얘는 덤으로 제목 표시줄도 더 얇게 찍히게 한다(WS_CAPTION이 명시된 경우). 그래픽 에디터의 도구 팔레트처럼 작고 키보드 포커스도 안 받고, 화면에 언제나 표시되어 있는 그런 창을 찍으라고 있는 스타일인 것이다.

이 스타일 한 방이면 스텔스 윈도우를 아주 쉽게 만들 수 있다. 단지 제목 표시줄이 꼭 필요하고 그걸 다른 평범한 윈도우처럼 두껍게 만들고 싶을 때에만 owner 편법을 쓰면 될 듯하다.

그리고 다음으로.. WS_EX_TOOLWINDOW와 상극인 WS_EX_APPWINDOW 스타일이 있다. (3) 얘는 자기가 owner가 지정되었다 하더라도 반드시 작업 표시줄에 표시되게 한다.
Windows Vista인가 7부터는 디자인이 바뀌었는지 제어판 애플릿이 작업 표시줄에 나타나는 걸 볼 수 있다. 얘들은 여전히 owner 윈도우가 따로 있음에도 불구하고 저 스타일이 지정되었기 때문에 작업 표시줄에도 보인다. 중간에 뭔가 디자인 정책이 바뀐 듯하다.

사용자 삽입 이미지사용자 삽입 이미지

내가 표시하는 대화상자나 창이 작업 표시줄을 건드리지 않고 조용히 떴다가 없어질지, 아니면 독립된 응용 프로그램처럼 뜰지 결정하는 것은 개발자의 재량이다. 다만, 작업 표시줄에다가도 나타나게 할 거면 창에다가 적절한 아이콘도 넣어 주는 게 좋을 것이다.

하지만 어지간해서는 owner가 NULL인 것만으로도 작업 표시줄에 창이 자동으로 등록되니 이 스타일이 굳이 따로 쓰일 일은 내 경험상 별로 없다.
WS_OVERLAPPEDWINDOW나 WS_POPUPWINDOW는 여러 기존 스타일들의 조합이지만 WS_EX_*WINDOW는 그렇지 않고 자신만의 고유한 값이다.

그리고 마지막으로 하나 살펴볼 게 있다.

MS Office Excel의 경우, 워크시트 문서창이 응용 프로그램 창의 내부에 소속된 child이다. 단독의 popup 윈도우 같은 게 아니다.
그럼에도 불구하고 한 프로그램에서 여러 파일을 열면 그 문서창들이 작업 표시줄에 제각각 나타난다. 이게 먼 옛날 Office 2000쯤부터 그렇게 되기 시작했는데.. 어떻게 이런 일이 가능한지 신기하지 않으신가?

이건 윈도우 스타일 조작만으로 가능한 동작이 아니고 별도의 API를 사용해서 구현한다.
셸 API들, 특히 작업 표시줄 근처에 있는 트레이(notification) 아이콘을 조작하는 API는 다 SH_*로 시작하는 고전적인 C 함수인 반면, 작업 표시줄을 조작하는 API는 ITaskbarList라는 COM 인터페이스 형태이다.

ITaskbarList를 얻어 온 뒤 HrInit를 호출해서 초기화하고, MDI 문서 창이 생성되면 자기 자신에 대해 AddTab + ActivateTab을 호출한다(ActiveTab도 반드시 해 줘야 됐음). 그리고 문서 창이 닫힐 때는 DeleteTab를 하면 된다. 이렇게만 하면 당장 날개셋 편집기조차도 얼추 Excel처럼 문서 창을 작업 표시줄에서 곧장 접근 가능하게 수정할 수 있다.

사용자 삽입 이미지

다만, 일이 마냥 간단하지만은 않다. 문서 창뿐만 아니라 기존 날개셋 편집기 자체의 창이 등록된 것도 같이 관리해야 하기 때문이다.
문서 창이 없거나 하나밖에 없으면 그냥 프로그램 자체의 창 하나만 유지하고 있고, 문서 창이 2개 이상이 되면 그때부터 프로그램 창은 날리고 문서 창들이 작업 표시줄에 나타나게 해야 한다. 불가능한 일은 아니지만 자동화가 돼 있지 않아 귀찮다. 마치 클립보드 viewer chain을 관리하는 것처럼 말이다.

요컨대, owner 윈도우, WS_EX_TOOL/APPWINDOW 스타일, 그리고 ITaskBarList 인터페이스만 기억하고 있으면 창과 작업 표시줄의 연계는 다 마스터했다고 볼 수 있겠다.
참고로 ITaskBarList는 초창기에는 이렇게 탭을 수동으로 등록하고 삭제하는 원시적인 기능만 있었다. 아마 IE 4나 5 시기쯤, 웹 브라우저와 운영체제 셸이 얽혀서 마개조되고 DLL hell 현상이 악명을 떨치던 20여년 전에 첫 등장했다.

그러다가 얘가 온갖 기능이 추가되어 ITaskBarList3으로 발전한 게 2000년대 말의 Windows 7 타이밍이다. 작업 표시줄에다가 응용 프로그램의 고유한 썸네일을 표시하고, 백그라운드 작업 진행률을 나타내고, 재생기의 경우 간단한 재생/멈춤 같은 버튼까지 갖다박는 UI 요소가 그때 추가됐기 때문이다. 그런 기능들을 바로 저 API를 통해 사용할 수 있다.

Posted by 사무엘

2018/10/17 08:34 2018/10/17 08:34
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1544

1. COM

1970년대 중반, MS-DOS의 전신격인 CP/M 때부터 있었던 완전 초창기 실행 파일 포맷이다. 고안자는 개리 킬달.
엄밀히 말해, 얘는 파일 포맷이라 할 것도 없는 쌩 메모리 이미지 덤프였다. 그 어떤 고유한 헤더나 메타데이터도 없이 그냥 곧장 기계어 코드와 데이터가 쭉 이어질 뿐이었다. 코드와 데이터는 모두 64KB 단일 세그먼트에 묶여 있었고, 메모리 주소의 첫 256바이트는 시스템 용도로 예약되어 있어서 프로그램이 사용할 수 없었다.

확장자가 com인 실행 파일은 그냥 명령 프롬프트에서 돌아가는 간단한 유틸밖에 없을 것 같지만, 그래도 겨우 몇만 바이트 남짓한 com 형태로 그래픽 모드에서 실행되는 게임도 많이 나왔었다. 그래픽이라고 해 봤자 320*200 4색 CGA 수준이긴 했지만.. Alley Cat처럼 말이다.
1980년대에 들어서 컴퓨터의 대세가 8비트에서 16비트로 넘어가고 성능과 메모리 용량이 향상되자, 이 형식은 큰 프로그램을 만들기에는 너무 비좁아졌다. 그래서 확장할 필요가 생겼다.

2. MZ EXE

1983년, MS-DOS 2.0에서 최초로 도입됐다. 그 전의 1.0은 파일 시스템에 서브디렉터리라는 게 지원되지 않았으며 실행 파일도 아직 COM밖에 없었다.
EXE는 단일이 아닌 다중 세그먼트(특히 코드 영역과 데이터 영역의 분리)를 도입하여 64KB 공간 한계를 얼추 극복했다. 메모리 모델이니, far near 포인터니 뭐니 하면서 일이 굉장히 복잡해지긴 했지만 말이다.
또한, 멀티태스킹 환경에 대비해서 재배치 정보도 도입했다. 이제 좀 운영체제에서 파일을 있는 그대로 메모리에 올리는 게 아니라 최소한의 가공과 상대 주소 보정을 하는 loader가 필요해졌다.

오늘날 모든 EXE들은 앞부분에 MZ라는 문자로 시작하는 간단한 헤더를 갖추고 있다. MZ는 EXE 파일 포맷의 설계자인 당시 마소의 프로그래머 Mark Zbikowski의 이니셜이다! zip 압축 파일의 식별자인 PK (개발자 필립 카츠)만큼이나 세계에서 제일 유명한 파일 포맷 식별자 이니셜일 것이다. MZ 저분은 미국 토박이라고 하지만, 이름으로 보아하니 러시아 계열 이민자의 후손인 듯하다.

비록 도스는 역사 속으로 사라졌지만, Windows용 실행 파일들은 지금까지도 과거 호환을 위해서 앞부분에 최소한의 MZ EXE 헤더 껍데기는 넣어 놓는 게 관행이 돼 있다.
한편, 32비트 이후부터는 프로그램들이 옛날처럼 다시 단일 세그먼트 기반 flat 모델로 돌아갔다. 단지, 그 세그먼트의 이론적 최대 크기가 꼴랑 64KB이던 것이 4GB로 왕창 커졌을 뿐이다.

3. NE (new)

1985년에 발표된 Windows 1.0과 함께 등장한 포맷이다. 도스와는 다른 방식의 API 호출, exe와 dll의 구분, 표준화된 리소스와 버전 정보 데이터, 함수의 import와 export 내역처럼 도스용 exe에는 없던 추가적인 정보가 많이 필요해진 관계로 새로운 실행 파일 포맷을 또 제정한 것으로 보인다. 단, 맨 앞부분은 그냥 도스 EXE처럼 시작하고, 새로운 방식은 다른 오프셋에서부터 시작된다. 이는 NE 다음의 PE도 마찬가지이다.

사실, NE는 Windows뿐만 아니라 마소에서 1986년경에 잠시 만들다 말았던 일명 '멀티태스킹 MS-DOS 4.0'(일반적인 그 MS-DOS 4.0 말고)용 실행 파일 포맷으로도 쓰였다고 알려져 있다. 하지만 도스는 텍스트 기반 환경이지만 Windows는 GUI 환경이고, 16비트 Windows에는 딱히 콘솔(명령 프롬프트)이라는 서브시스템이 존재하지 않았다. 그러니 바이너리 수준에서의 파일 포맷만 일치할 뿐, 양 플랫폼의 실행 파일을 딴 데서 원활하게 실행할 수는 없었을 것이다.

Windows 1.x부터 3.x까지 16비트 시절에 실행 파일 포맷은 크게 바뀌지 않았다. 단, 2에서 3으로 넘어가던 시절에는 Windows에 386 확장 모드라는 게 도입되었기 때문에, 이 프로그램은 종전의 리얼(real) 모드뿐만 아니라 보호(protected) 모드에서도 잘 실행된다는 보증 플래그가 추가되었다. 평범한 Windows API만 쓴 프로그램이 여기에 큰 영향을 받지는 않았겠지만, 그래도 혹시나 해서 말이다.

1980년대 왕창 옛날에 만들어졌던 일부 Windows용 프로그램들은 95뿐만 아니라 3.x에서 실행해도 "구버전용임. 여기서도 일단 실행은 되지만 케바케이기 때문에 온전하고 정상적인 동작을 기대할 수 없음. 최신 버전을 구해서 쓰셈.." 이런 주의 메시지가 뜨는 게 있었는데, 바로 이 플래그가 없이 옛날 방식대로 빌드된 프로그램이어서 그렇다.
특히 대화상자가 캡션(제목 표시줄)이 없이 표시되는 프로그램을 보면 옛날 냄새가 풀풀 난다. 캡션은 popup 윈도우가 아닌 overlapped 윈도우의 전유물이던 시절이 있었기 때문이다.

NE 방식의 실행 파일에는 각종 코드와 데이터, 리소스들이 resident, non-resident, discardable 이런 식으로 속성 구분이 있었다. 컴퓨터에 메모리는 왕창 부족한데, CPU와 운영체제 차원에서의 가상 메모리 지원이 없고, 그 열악한 환경에서 멀티태스킹을 구현하려다 보니, 돌아가는 방식이 가난함과 처절함 그 자체였다.
읽어들인 데이터는 언제든지 주소가 재배치되거나(단편화를 막고 연속된 많은 영역의 메모리를 확보할 수 있게), 삭제되어서 디스크로 되돌아갈 수 있었다. 반드시 메모리에 언제나 불러들여 놓는 데이터는 성능 차원에서 정말 중요한 것에만 한해서 지정해야 했다.

4. PE (portable)

1993년에 등장한 Windows NT 3.1에서 첫 도입된 또 다른 새로운 실행 파일이다. AT&T에서 오래 전에 개발한 COFF라는 오브젝트/라이브러리 파일 포맷의 연장선상에 있다. 아마 Windows NT 개발진의 출신 배경이 그쪽 계열 연구소여서 이런 포맷에 친숙하지 않았나 싶다. 덕분에 마소에서 개발하는 개발툴들은 16비트 시절에는 obj/lib의 포맷으로 인텔에서 개발한 OMF 방식을 썼지만 32비트부터 COFF로 갈아탔다. 그리고 exe/dll을 로딩하는 방식도 쿨하게 memory mapped file 방식으로 바꿨다.

PE는 현대적인 32비트 가상 메모리 환경에 맞춰졌기 때문에, 16비트 NE처럼 수동 메모리 관리를 염두에 둔 지저분한 속성이 존재하지 않는다. 세그먼트 대신에 코드, 데이터, 리소스 등의 용도별 섹션이 있고, 이들 섹션은 간단한 문자열 태그로 구분하기 때문에 섹션의 추후 확장이 가능하다. 그리고 헤더에 CPU 식별자도 넣어서 굳이 x86뿐만 아니라 다른 CPU 아키텍처의 실행 파일도 이 방식으로 기술 가능하게 했다.

훗날 64비트 CPU가 등장하면서, 아니 정확히는 1990년대 말에 IA64의 출시를 염두에 두고 PE의 기본 틀은 동일한데 메모리 주소나 몇몇 size 필드만 4바이트에서 8바이트로 확장된 일명 PE+ 규격이 나왔다. 그래도 기존 32비트에서도 얘를 알아보고 최소한 "에러 메시지+실행 거부" 정도의 대처는 할 수 있다.
리소스는 64비트도 32비트와 바이너리 차원에서 포맷이 완전히 동일하다. 이게 무슨 기계어 코드도 아닌데 필드 크기가 굳이 64비트 크기로 확장됐다거나 한 건 없다. 문자열이 유니코드 기반으로 바뀌었으니 16비트 방식과는 호환되지 않지만 32와 64비트끼리는 호환된다.

오늘날은 재래식 네이티브 코드뿐만 아니라 닷넷 기반(가상 머신), 그리고 UWP용(일명 metro) 앱 같은 것도 나왔지만, 이들도 실행 파일들의 기본 골격은 PE로 동일하다. 그 안에서 읽어들이는 시스템 DLL과 구동 방식이 서로 차이가 날 뿐이다.

Posted by 사무엘

2018/06/17 08:36 2018/06/17 08:36
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1501

시스템 복원

Windows에는 시스템 자신의 소프트웨어적인 유지 보수와 관련하여 (1) 업데이트와 (2) 시스템 복원이라는 두 기능을 제공한다. 전자는 프로그램을 최신 상태로 유지하는 기능으로, 개념적으로 미래로 나아가는 것에 대응한다. 후자는 그와 반대로 과거로 돌아가는 기능이다.

옛날에는 프로그램의 업데이트/패치라는 게 오프라인 상으로 동작하는 기능에 버그가 발견되어 고쳐졌거나, 아니면 작게나마 새로운 기능이 추가됐을 때 이를 반영하기 위해 시행되었다. 그러나 2000년대부터는 굳이 그런 게 아니라 '보안 취약점'을 수정하는 업데이트의 비중이 커졌다.

보안 취약점은 세상의 컴퓨터들이 인터넷에 한데 연결돼 있지 않거나 아주 제한된 시간 동안 잠시만 연결된다면 별로 심각한 문제가 아닐 것이다. 하지만 그렇지 않고 컴퓨터가 상시 네트워크에 연결되어 있으며 아무나 아무 컴퓨터로 패킷을 보낼 수 있고, 그 패킷이 해석하는 방식에 따라서 특정 지시를 수행하고 코드를 실행할 수 있기 때문에(편의라는 명목 하에) 보안 문제가 불거지는 것이다. 엑셀· 워드 문서가 그냥 데이터뿐만 아니라 매크로가 추가됨으로써 보안 위험이 커졌듯이 말이다.

이러면 최악의 경우 나도 모르는 사이에 악성 코드가 원격 조작으로 실행될 수 있으며, 내 컴퓨터에 있는 데이터와 내가 키보드로 입력하는 문자가 나의 동의 없이, 나도 모르는 사이에 내 컴퓨터 밖으로 새어 나갈 수 있다. 내 데이터가 날아가고 내 개인 정보가 유출될 수 있다. 내 컴퓨터가 주변의 컴퓨터로 악성 코드를 퍼뜨리는 좀비가 될지 모른다. 한 마디로 요약하면, 컴퓨터 시대에서 상상할 수 있는 모든 끔찍한 재앙이 벌어질 수 있다.

보안 업데이트는 프로그램의 그런 허점들을 막아 준다. 정적 분석 기술로 컴퓨터 프로그램이 취급하는 모든 데이터의 처리 양상을 원천적으로 분석해서 보안 취약점을 자동으로 찾아낼 수는 없다. 그러니 그때 그때 취약점이 발견되면 해당 소프트웨어의 제조사에서 패치와 업데이트를 내는 식으로 "사후 약방문" 식 대응이 어쩔 수 없이 통용된다.

소프트웨어는 굳이 새로운 기능을 추가하기 위해서가 아니라 지금의 현 상태를 안전하게 유지하기 위해서라도 계속해서 유지 보수를 해야 하고 업데이트를 해야 하는 반제품이 되었다. 첫 버전 출시를 한 것은 전반부 종료일 뿐이고, 그 다음부터가 후반부의 시작이다.

업데이트라는 게 평범한 기능 개선과 추가에 지나지 않는다면.. "난 그런 기능 없어도 지금 프로그램 쓰는 데 아무 불편 없어요" 이런 사용자는 굳이 업데이트를 받을 필요가 없다. 하지만 보안 업데이트는 마치 예방접종과 비슷한 구석이 있어서 내가 안 받으면 남에게 피해를 줄 수도 있다. 그렇기 때문에 모든 사용자들이 반드시 받을 필요가 있다. 일단은 말이다.

업데이트에 대한 얘기가 좀 길어졌는데, 다음으로 시스템 복원은 위급한 상황에서 굉장히 유용한 기능이다. 개발자의 입장에서 이런 기능은 구현하고 테스트· 디버깅 하는 게 굉장히 엄청나게 어려웠을 것이다.

본인의 경우 꽤 오래 전(거의 2009~2010년경.. Vista 시절), 언제부터인가 집 컴퓨터가 인쇄가 안 되기 시작했다.
프린터가 USB 포트 상으로 인식은 분명히 되고, 인쇄 명령을 내리면 프린터가 이를 받아서 예열 작업까지는 한다(레이저임).

그런데 그 후로 프린터는 아무 반응이 없이 인쇄가 전혀 진행되지 않으며, 도리어 인쇄를 내린 응용 프로그램만 응답 불능 상태에 빠진 채 멎어 버리는 것이었다.
멎은 프로그램은 CPU를 사용하지는 않으며, 다른 프로그램들은 정상 동작했다. 하지만 그 멎은 프로그램은 작업 관리자로 아무리 죽여도 사라지지 않았다.

하드웨어 문제라면 이거 프린터를 수리 받아야 하는데, 무슨 충격을 받은 것도 아니고 멀쩡한 프린터가 갑자기 고장 날 리가 없으니 무척 난감한 상황이었다.
만약 소프트웨어 문제라면 프린터 드라이버를 다시 설치하거나 최악의 경우 운영체제를 새로 설치해야 할 것이다. 최근에 부모님께서 내가 없는 동안 이 컴퓨터로 이것저것 ActiveX도 깔고 인쇄를 하긴 하셨는데 도대체 어쩌다가 프린터가 이렇게 됐는지 몰라 답답하기 그지없었다.

하지만 다행히 문제는 비교적 간단하게 해결할 수 있었다. 혹시나, 설마 해서 ‘시스템 복원’을 해 봤는데 이게 날 살렸다.
부모님께서 컴퓨터를 건드리기 전인, 약 1주일 전으로 복원을 시켰다. 그 사이에 컴퓨터에 생긴 변화는 운영체제 업데이트 몇 개가 자동으로 설치된 것 정도가 떠 있었다.

시스템 복원을 하고 나자 프린터는 거짓말처럼 인쇄가 되기 시작했다. 아까는 무엇 때문에 안 됐는지 모르겠지만, 어쨌든 시스템 복원 기능을 이용해서 만족스러운 결과를 잘 얻었다.
Vista보다 더 옛날, XP 시절에도 본인은 시스템 복원으로 여러 하드웨어/소프트웨어 문제를 딱 해결한 적이 있었다. 이런 기능이 없었으면 영락없이 운영체제를 재설치해야 했을 터이다. 물론 이제는 운영체제를 재설치할 일 자체가 거의 없어지다시피했지만 말이다.

이런 Windows 업데이트와 시스템 복원은 하는 일이 완전히 다르지만 그래도 서로 한데 맞물려서 돌아간다. 업데이트를 설치하는 것부터가 시스템 복원 지점을 만든 뒤 진행되기 때문이다.
이런 일이 있어서는 안 되겠지만, 업데이트를 설치한 뒤에 운영체제에 오히려 부작용이 발생할 수가 있다. 상태가 예전보다 나빠졌다면 시스템 복원을 실행해서 원상복구를 시킬 수 있다.

시스템 복원은 Windows 2000도 아니고 ME에서 첫 도입된 정말 얼마 안 되는 기능 중의 하나이다. 이 기능으로 인해 Windows는 평시에 차지하는 하드디스크 용량이 본격적으로 크게 늘어나기 시작했다.

본인은 가끔 갖고 놀 목적으로 Windows ME 가상 머신을 갖고 있다. 여차여차 하다 보니 하드의 파일 시스템을 FAT32가 아닌 FAT로 잡아 버려서, 주 파티션의 용량이 겨우 2GB가 됐다. 거기에다가 MS Office와 날개셋 정도만 설치하면 하드 용량을 딱 절반인 1GB 남짓 차지했는데..

그렇게 날개셋 한글 입력기를 테스트 하고 빌드 스냅샷을 설치하고 지우기를 반복했던 가상 머신은 나도 모르는 사이에 여유 공간이 갈수록 줄어들더니, 겨우 50~60MB 남짓밖에 안 남는 사태가 벌어졌다.
뜨악 하다가 정신을 차리고 시스템 복원 기능을 완전히 끄고 기존 스냅샷들을 모두 삭제한 뒤 재부팅을 하자.. 사라졌던 1GB 남짓한 공간이 다시 거짓말처럼 나타났다. 용량 쳐묵쳐묵의 범인은 시스템 복원 기능이었다.

얘는 마치 휴지통처럼 최대 몇백 MB까지만 공간을 사용하라고 옵션을 지정하는 게 분명히 있음에도 불구하고, 그걸 훨씬 더 초과하여 디스크를 잡아먹고 있었다. ME의 복원 기능만 그런 문제가 있었는지는 모르겠다.

오늘날의 Windows 10은 브랜드 이름과 주 버전은 이제 더 안 고치고, 찔끔찔끔 업데이트만으로 보안 패치와 서비스 팩, 버전업을 모두 겸하게끔 배포 방식을 바꿨다. 이제 날짜와 빌드 번호가 사실상 버전 번호가 된 셈이다.
그런데 시도 때도 없이 너무 자주 업데이트가 발생해서 CPU 잡아먹고, 시간이 흐를수록 하드디스크 용량 소모가 너무 심하며, 컴퓨터를 원하는 때에 제대로 끄지도 못하게 만드는 등 민폐가 너무 심하다.

게다가 업데이트 설치 후에 부팅이 안 되고 컴이 먹통이 되는 현상을 집과 회사에서 두 번씩이나 겪은 뒤부터 본인은 학을 떼 버렸다. 인터넷 연결망이 종량제 기반이니 니 멋대로 업데이트 받아서 설치하지도 말고 알리지도 말라고 레지스트리를 조작해서 넣었다.
Windows 10만 그런 게 아니라, 구닥다리 7을 굴리는 작업실 컴도.. 하는 일 없이 CPU 잡아먹으면서 열받고 팬을 돌아가게 만드는 주범이 update 서비스인 걸 보고는 이거 nProtect만 욕할 처지가 아니라는 걸 알게 됐다. 서비스 다 내리고 업데이트 따위 꺼 버렸다.

아무리 보안과 안전이 중요하다지만 나는 최소한의 보안 관념이 있고 내 컴퓨터 통제를 스스로 할 줄 알며, 대부분의 보안 결함은 여느 교통사고나 범죄 사건과 마찬가지로 정말 극단적이고 예외적인 막장 상황에서나 발생하는 것들이다. 위험이 너무 과장되고 부풀려진 면모가 있다. 그리고 이 정도의 횡포는 가성비를 따졌을 때 강제 업데이트를 justify하지 못한다는 결론을 내렸다.

컴퓨터 자원은 무한한 게 아니다. 아무쪼록 시스템의 안정성을 관리하는 기능들이 지금보다 자원을 좀 아껴 쓰고 민폐 안 끼치며 동작했으면 좋겠다는 생각이 들었다. 이상.

Posted by 사무엘

2018/04/28 08:30 2018/04/28 08:30
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1483

요즘은 좀 덜해진 감이 있지만 한때 마소에서는 자사의 운영체제인 Windows에 단순히 기술과 기능뿐만 아니라 감성을 담으려고 애쓰곤 했다. 애플 진영만 감성 마케팅을 한 게 아니라는 얘기이다.
이쪽으로 굉장히 신경을 많이 썼던 때는 크게 세 시즌으로 나뉜다. (1) 95, (2) XP, 그리고 그 다음 (3) Vista 정도 되겠다.

Windows 95는 그야말로 마소의 Windows 개발 역사상 가장 큰 격변을 이룬 작품이었다.
그러니 3.1 시절의 너무 식상했던 tada.wav를 대신하여, 참신하고 세련되고 오픈되고 모던한 이미지를 표현할 수 있게, Windows 95 부팅이 마치 미래로 가는 창문을 열어젖히기라도 한 인상을 줄 수 있는.. 그런 시작음을 외주를 줘서 개발하게 되었다.

그 유명한 "또르릉~ 띵~ 띵.." 95의 시작음을 작곡한 사람은 Brian Eno이다. 파일 이름부터가 참 거창하게 The Microsoft Sound였다.
다만, 정작 그 작곡자는 DOS고 Windows고 전혀 사용하지 않는 골수 Mac 유저였다는 것이 훗날 당사자의 회고 인터뷰를 통해 알려지기도 했다.;;

Windows 95는 누구나 듣는 시작음뿐만 아니라, 소수의 매니아만이 열어 보는 이스터 에그 화면에다가도 고유한 음악을 집어넣었다. 여기에 들어간 음악이 바로 그 유명한 Clouds이다. 이거 작곡자는 Brian Orr이니 또 다른 Brian이다. 단, 이건 길이와 용량 관계상 wav가 아니라 mid 포맷이다.

저 작곡자가 회고하기를, 작곡을 의뢰받을 때 컨셉으로 받은 키워드가 clouds, floating, peaceful이었다고 한다. Windows 95는 부팅 스플래시 화면부터가 파란 창공과 구름이었으니까..;; 그래서 그 컨셉에 맞게 멜로디를 써 넣은 결과물이 저 음악이라고 한다.
여느 음악 예제들과 마찬가지로 Windows\media 디렉터리에 있었다고는 하지만 본인은 얘는 Windows 95가 실제로 사용되던 시절에 PC에서 찾아서 들어 보지 못했다.

저거 이후로 마소의 제품에서 이스터 에그 재생 중에 그럴싸한 음악이 나온 경우는 Visual Basic 5~6의 이스터 에그가 유일했던 듯하다. 공교롭게도 저 이스터 에그도 파란 창공을 배경으로 정육면체 상자 4개가 뱅글뱅글 돌아가고 그 배경 위로 개발자 명단이 스크롤 되어 올라간다.

물론 이스터 에그라는 것도 2002년 이후로는 마소의 제품에서는 싹 자취를 감춰서 볼 수 없게 됐지만 말이다.
우리나라 지하철에다 비유하자면, 처음에 인테리어가 좀 독특하게 꾸며졌던 역들이 모두 안전을 이유로 스크린도어로 뒤덮이고 불연재 재질로 교체되어 미관이 예전보다 안 좋게 바뀐 것과 비슷해 보인다. 뭐 아무튼..

Windows NT 4라든가 98~ME 사이에서는 전자 악기 기반의 시작음들이 많이 쓰였으며 특히 chimes, chord, ding 같은 메시지 비프음도 다시 만들어졌다. 이것들은 다른 아티스트들의 작품이다.

그러다가 Windows XP는 프로그램의 시청각 요소가 완전히 쇄신했다. 이제 PC의 속도와 메모리가 충분해진 덕분에 9x 계열 커널이 수명을 다할 때가 됐고, 그리고 64비트와 멀티코어 CPU도 등장하다 보니 하드웨어가 큰 변화를 겪을 시기였다. 이 시기에 맞춰 마소에서는 OOBE (out-of-box experience)라는 말까지 만들어 내면서 새 운영체제로 '사용자에게 새롭고 특별한 경험을 선사하자'에 목숨을 걸었다. 굳이 Windows 2002 대신 XP라는 브랜드명까지 만들면서 말이다.

일단 피아노 소리 위주인 시작음, 비프음들은 다 Bill Brown이라는 작곡가가 작곡하고 오케스트라를 동원해서 연주했다. 그리고 (1) Tour를 실행했을 때 나오는 고퀄의 배경 음악들도 이 사람의 작품이다. 시퍼런 Luna 테마와 풀밭 사진뿐만 아니라 음악도 Windows XP를 뭔가 종합 예술 작품 같은 인상을 심어 주는 요인이다.

사실, Windows XP는 애초에 설치를 하다가 작업이 마무리되고 비디오/사운드 카드가 자동으로 잡히고 나면 간단한 애니메이션과 함께 (2) 몽환적인 분위기의 intro 음악이 나온다. 길이도 무려 5분 24초나 된다. 이걸 듣고서 강렬한 인상을 받은 사람이 많았을 것이다.

그런데 이 유명한 음악의 작곡자는 의외로 Bill Brown이 아니며, 정확하게 알려져 있지 않다. 인터넷 상으로는 Brian Eno가 작곡했다는 말이 많지만 저 사람은 공식적으로는 Windows 95 로고송 이후로 딱히 마소와 다시 작곡과 관련된 계약을 맺은 내역이 없다.

Susan Ciani라는 미국의 여성 작곡자를 지목하는 곳도 있으나, 이 역시 정확한 출처나 근거가 부족하다. 이 곡은 Windows XP의 정체성 그 자체로서 Tour 음악과 쌍벽을 이룬다고 해도 과언이 아닌데, 작곡자가 공식적으로 미상인 것이 무척 미심쩍게 여겨진다.

그 뒤, Windows Vista부터 도입된 "따단 따단" 그 4개 음표짜리 전자음 멜로디는 Robert Fripp이라는 사람이 작곡한 것으로 알려져 있다. 이때 이후로 마소에서는 운영체제의 음악에 큰 신경을 쓰지 않고 있다.
옛날에는 사용자가 선택한 테마에 따라 GUI의 색상과 글꼴, 각종 사운드가 싹 달라지게 하는 게 유행이었고 애초에 Windows와 Office, Visual Studio 제품들도 버전이 바뀔 때마다 프로그램 외형과 색상도 같이 바뀌던 시절이 있었거늘, 그마저도 2010년대 이후로는 약발이 다한 모양이다.

시작음처럼 운영체제나 프로그램의 구동과 함께 연주되는 음악 말고.. Windows\media 디렉터리에 예제로 제공되는 음악들도 버전별로 바뀌어 왔다.
Windows 3.1 시절에는 canyon과 passport라는 이름의 mid 파일이 있었다. 95와 그 이후까지 존속했는지는 기억이 확실치 않다.

98/2000쯤에는 '엘리제를 위하여'를 포함해 뜬금없이 클래식 음악의 미디 파일이 갑자기 쭈욱 추가되었던 걸로 본인은 기억하는데, 후대 버전에서는 몽땅 다시 사라졌다.
그 대신 ME와 XP에서는 그 당시의 최신 외국 가요 음반에서 발취한 샘플 wma 한두 곡이 잠시 들어갔다. 미디로는 town, flourish, onestop이 들어가서 오늘날 Windows 10에까지 전해져 내려오고 있다.

특히 onestop의 경우 마치 음악계의 the quick brown fox jumps over the lazy dog처럼.. 미디에 정의되어 있는 모든 악기들을 일부러 모두 동원하는 형태로 만들어졌으며, 구간별로 분위기가 오락가락 하면서 굉장히 괴랄한 흐름과 중독성을 자랑한다.
뭔가 RPG 게임의 BGM 같기도 하고.. "이 음악 들으니 문득 집 앞 편의점까지 희망찬 모험을 떠나고 싶어졌어!" 뭐 이런 말이 나올 정도라고 한다..;;

Posted by 사무엘

2018/03/31 08:34 2018/03/31 08:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1473

Windows API에서 BitBlt는 DC간에 비트맵 블록을 찍어 주는 아주 중요한 함수이다.
장치 독립 비트맵인 DIB라는 게 컬러 비트맵의 원활한 처리를 위해서 Windows 3.0에서 처음 도입되었지, DDB와 관련된 CreateBitmap, BitBlt 같은 함수, 그리고 컬러 brush 자체는 Windows의 초창기부터 있었다.
단순히 memcpy나 memmove 같은 함수와는 달리, BitBlt는 1차원이 아니라 2차원 평면을 표현하는 메모리 영역을 취급하는 관계로 동작 방식이 더 복잡하다.

BitBlt의 처리 대상인 두 DC는 내부 픽셀 포맷 같은 게 당연히 서로 호환이 돼야 한다.
원시적인 모노크롬 비트맵의 경우, 위치와 크기에 해당하는 좌표의 x축이 바이트 경계(8의 배수)로 딱 떨어지지 않을 때의 복잡한 보정이 필요하다.
그리고 원본과 타겟 DC가 동일한 경우, memmove 같은 overlap 처리도 x축과 y축 모두 고려하여 memmove보다 더 복잡한 상황 가짓수를 처리해야 한다.

BitBlt는 비트맵을 그냥 찍는 게 아니라 원본(S), 타겟(D)에 대해서 비트 단위 연산을 시킨 결과를 집어넣도록 아주 범용적으로 설계돼 있다. 일명 raster operation이다.
게다가 래스터 연산의 피연산자가 저 둘만 있는 게 아니라 타겟 DC에 지정되어 있는 브러시 패턴(P)까지.. 무려 세 개나 존재한다. 그래서 BitBlt는 PatBlt라든가 InvertRect 함수가 하는 일을 다 할 수 있을 정도로 범용적이다.

원본 S를 있는 그대로 복사해 넣는 건 SRCCOPY이고, 그냥 타겟 비트맵을 반전만 시키는 건 ~D이다.
그리고 마스크 비트맵(흰 배경에 검은 실루엣) M과 그림 비트맵(검은 배경에 실제 그림) S에 대해서 D&M|S (각각 and, or 연산)를 해 주면 보다시피 직사각형 모양이 아닌 스프라이트를 찍을 수도 있다. 래스터 연산을 갖고 할 수 있는 일이 이렇게 다양하다.

BitBlt가 사용하는 래스터 연산은 3비트짜리 정보(S, D, P)에 대해서 임의의 1비트(0 또는 1) 값을 되돌리는 함수라고 볼 수 있다. 이 함수가 받을 수 있는 인자의 종류는 8가지(2^3)이고.. 서로 다른 래스터 연산 함수는 2^(2^3)인 총 256가지가 존재할 수 있다.
그리고 SRCCOPY, SRCPAINT 같은 것들은 그렇게 존재 가능한 래스터 연산 함수를 나타내는 값이다. 각 변수별로 S는 11110000, D는 11001100, P는 10101010 이런 식으로 정해 놓으면 00000000부터 11111111까지가 S|D, D&~P 등 각 변수들을 조작한 모든 가짓수를 나타내게 된다.

그런데 컴퓨터에서 범용성과 성능은 대체로 동전의 양면과도 같아서 하나를 살리다 보면 다른 하나를 희생해야 하는 관계이다.
the old new thing 블로그의 설명에 따르면.. 과거 16비트 시절에 BitBlt는 사용자의 요청을 파악해서 좌표 보정 같은 전처리 준비 작업을 한 뒤, 실제로 for문을 돌면서 점을 찍는 부분은 내부 템플릿으로부터 기계어 코드를 실시간으로 생성해서 돌렸다고 한다. 이게 무슨 말인가 하면.. 단순히

void Loop(int l, int t, int r, int b);
Loop(r.left, r.top, r.right, r.bottom);

수준이 아니라

template<int LEFT, int TOP, int RIGHT, int BOTTOM>
void Loop()
{
    for(int j=TOP; j<BOTTOM; j++)
        for(int i=LEFT; i<RIGHT; i++)
            어쩌구저쩌구;
}

Loop<r.left, r.top, r.right, r.bottom>();

이런 걸 추구했다는 뜻이다.
비트맵을 찍을 때 범위 체크는 매 픽셀마다 그야말로 엄청나게 자주 행해지는 일이다. 그러므로 그 한계값을 컴퓨터의 입장에서 변수가 아닌 상수로 바꿔 버리면 레지스터도 아끼고 성능 향상에 도움이 될 수 있다.

16비트 Windows에는 32비트 OS 같은 가상 메모리 관리자라는 게 없으며, Java/.NET 같은 가상 머신과 garbage collector도 없었다. 그 대신 (1) 메모리의 단편화를 방지하기 위해 moveable한 메모리 블록들의 주소를 수동으로 한데 옮기고 (2) discardable한 메모리 블록을 해제하는 동작이 있었다.

가상 머신이 없으니 just-in-time 컴파일이라는 개념도 있을 리 없다. 하지만 BitBlt의 저런 동작은 Java 내지 JavaScript의 JIT 같아 보이기도 한다. 물론 진짜 JIT 기술보다는 코드 생성 패턴이 훨씬 더 정향화돼 있고 단순하지만 말이다. (뭐, BitBlt의 세부 알고리즘 자체가 단순하다는 뜻은 아님)
그리고 그 시절엔 DEP도 없었다. 메모리에 데이터가 담겼건 실행 가능한 코드가 담겼건, 아무런 차별이 없었다.

게다가.. 저 때는 그래픽 출력과 관련된 하드웨어 지원조차도 없었다. 1990년대 일반 VGA 화면에서는 화면이 갱신될 때 마우스 포인터의 잔상이 남지 않게 하는 처리조차도 소프트웨어적으로 해야 했다. IBM 호환 PC는 전통적으로 게임기용 CPU에 비해서 멀티미디어 친화적이지 않은 컴퓨터로 정평이 나 있었으며, 그나마 좀 미려한 그래픽 애니메이션을 보려면 한 프로그램이 하드웨어 자원을 독점하는 도스밖에 답이 없었다. 그러니 BitBlt 같은 함수는 CPU 클럭을 하나라도 줄이려면 정말 저런 눈물겨운 최적화라도 해야 했던 것이다.

이런 여러 이유로 인해 16비트 Windows 시절에는 지금의 32/64비트보다 어셈블리어라든가 실시간 코드 생성 테크닉이 확실히 더 즐겨 쓰였던 것 같다.
외부에서 호출 가능한 콜백 함수를 지정하기 위해 껍데기 썽킹 함수를 생성해 주는 MakeProcInstance (해제하는 건 FreeProcInstance)부터가 그 예이며..

또 그때는 API 훅킹도 대놓고 훅킹 대상 함수 메모리 주소에다가 내 함수로 건너뛰는 인스트럭션을 덮어쓰는 식으로 행해졌다. 지금이야 이식성 빵점에 가상 메모리와 프로세스 별 메모리 보호, 멀티스레드 등 여러 이유 때문에 위험성이 크고 사용이 강력히 비추되는 테크닉으로 봉인됐지만 말이다.

Windows 95는 비록 32비트 명령어와 32비트 메모리 주소 공간을 사용하지만 GDI 계층은 여전히 16비트 코드를 쓰고 있으니 내부적으로 과거의 테크닉이 그대로 쓰였다. 그 당시의 PC 환경에서는 최고의 성능을 발휘했겠지만, 그리기 코드 자체의 32비트화, 좌표계의 32비트 확장이라든가 멀티스레드 대비 같은 건 전혀 불가능한 레거시로 전락할 수 밖에 없다.

그에 반해 Windows NT의 BitBlt는.. 이식성이 전혀 없는 기계어 코드 실시간 생성 같은 테크닉이 쓰였을 리가 만무하며, 어느 플랫폼을 대상으로나 동일하게 적용 가능한 C 코드로만 구현되었을 것이다. 겉으로 하는 동작은 비슷해 보여도 내부 구현은 완전히 달랐으며, 같은 사양의 PC에서 속도가 더 느린 것은 어쩔 수 없었다. 그 대신 NT의 코드는 플랫폼과 시대를 뛰어넘어 살아 남을 수 있었다.

뭐, 1990년대에는 OS/2도 얼리어답터들이 관심을 갖던 레알 32비트 운영체제이긴 했는데.. 얘는 Windows NT와 달리 32비트 계층도 코드가 전반적으로 그리 portable하지 않았다고 한다. 그러니 타 CPU로 포팅은 고사하고 훗날 같은 CPU에서 64비트에 대처하는 것도 유연하게 되기 어려웠으리라 여겨진다.

그에 반해, OS가 아니라 게임이긴 하다만 Doom은 Windows NT와 비슷하게(약간만 타이밍이 더 늦은) 1993년 말에 첫 출시됐는데.. 세계를 놀라게 한 3차원 그래픽을 실현했음에도 불구하고 어셈블리어를 거의 사용하지 않고 순수하게 C 코딩만 한 거라는 제작사의 증언에 업계가 더욱 충격에 빠졌다. 사운드처럼 상업용 라이브러리를 사용한 부분의 내부 구현을 제외한 전체 소스 코드가 수 년 뒤에 공개되면서 이 말이 사실이었음이 입증되었다.

도스에서 Doom을 가능케 한 것은 쑤제 어셈블리어 튜닝이 아니라 Watcom 같은 최적화 잘 해 주는 32비트 전용 C 컴파일러였다.
엔진 코드가 C로 나름 이식성 있게 깔끔하게 작성된 덕분에 Doom은 소스가 공개되자마자 오픈소스 진영의 덕후들에 의해 온갖 플랫폼으로 이식되면서 변종 엔진과 게임 MOD들이 파생돼 나올 수 있었다. 물론 소스 공개 이전에도 상업용으로 갖가지 플랫폼에 출시되기도 했고 말이다.

오늘날이야 컴퓨터 아키텍처라는 게 2, 30년 전 같은 춘추전국시대가 아니며, 가상 머신이라든가 웹 같은 환경도 발달해 있다. 그러니 "C언어는 이식성이 뛰어나다" 이런 식의 진술이 뭐 거짓말은 아니지만 약간 어폐가 있다. 하지만 BitBlt API부터 시작해서 이식성 있는 코드와 그렇지 않은 코드가 궁극적으로 어떤 상태가 되었는지를 생각해 보니 이 또한 의미 있는 일인 것 같다.

다시 Windows API 얘기로 돌아와서 글을 맺자면..

  • BitBlt는 비트맵 출력 API 중에서는 그나마 가장 기본적인 형태이다. StretchBlt는 비트맵을 크기를 변형(확대· 축소)해서 찍을 수 있다. 이들의 DIB 버전에 대응하는 것은 각각 SetDIBitsToDevice와 StretchDIBits이다.
  • TransparentBlt와 AlphaBlend는 아까 같은 AND/OR 래스터 연산 대신 color key 내지 알파 채널을 적용해서 투명색이 적용된 비트맵을 찍어 주는 함수이다. Windows 98/2000에서 새로 추가됐다. 본인은 사용해 본 적이 없다.
  • PatBlt는 원본 DC의 지정이 없이 브러시 패턴과 타겟 DC와의 래스터 연산만이 가능한 마이너 버전이다.
  • PlgBlt와 MaskBlt는 마스크 비트맵까지 한꺼번에 받아서 스프라이트 처리가 가능한 버전이다. 거기에다 PlgBlt는 일차변환을 적용해서 직사각형이 아닌 임의의 평행사변형 모양으로 비트맵을 찍을 수도 있는데.. Windows 9x에서는 지원되지 않고 NT에서만 존재해서 그런지 본인 역시 이런 함수가 있다는 걸 아주 최근에야 알게 됐다.

실무에서는 이렇게 비트맵을 한꺼번에 찍어 주는 함수를 쓰지, SetPixel이라든가 무식한 FloodFill 같은 기능은 그래픽 출력에서 쓸 일이 거의 없는 것 같다.
BitBlt과 유사 계열의 비트맵 출력 GDI 함수들은 비트 연산을 다루는 시대 배경에서 만들어진 만큼, 요즘 PNG 이미지처럼 비트맵 내부에 들어있는 알파 채널을 제대로 취급하지 못한다. 그리고 비트맵을 확대해서 출력할 때의 안티앨리어싱도 부드럽게 처리를 못 한다. 레거시 코드에다가 그런 기능까지 플래그로 넣기에는 너무 복잡하고 지저분해져서 그렇지 싶다. 그도 그럴 것이 GDI는 하드웨어 통합적으로 얼마나 추상적으로 설계되었던가?

현대의 화면 래스터 그래픽에서 필요로 하는 최신 기능들은 한때 GDI+가 따로 담당하다가 요즘은 그것도 너무 느리다고 도태됐고 Direct2D 같은 다른 패러다임으로 옮겨 갔다.

Posted by 사무엘

2018/03/10 08:37 2018/03/10 08:37
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1466

1. 도스 시절 명령어

도스에서 파일(과 디렉터리)을 다른 곳에 복사하는 명령은 copy이다. 얘는 명령 셸인 command.com이 자체 지원하는 내장 명령이다.
그런데 도스에는 copy의 일종의 강화 버전이라 할 수 있는 xcopy라는 것도 있으며, 이건 별도의 프로그램을 통해 실행되는 외부 명령이다.

xcopy는 일반 copy와 달리 (1) 서로 다른 드라이브 사이에 서브디렉터리까지 재귀적으로 통째로 복사하는 걸 지원했으며, (2) 복사에 사용하는 메모리 버퍼 크기가 더 크고, 여러 개의 파일을 한꺼번에 읽은 뒤(대략 수백 KB 정도 크기까지) 타겟에다 쓰는 걸 지원했다. xcopy의 전치사 X는 일반적으로 cross라고 발음되었다.

지금 생각해 보면 이 둘은 차별화 요소가 전혀 될 수 없으며 굳이 분리할 필요가 없다. 그냥 copy가 원래 (2)처럼 동작하면 되고, (1)은 /s 같은 옵션을 추가해서 지원하면 될 일이다.
하지만 옛날에 xcopy가 외부 명령으로 존재했던 이유는 겨우 그 정도 고급 복사 옵션/기능마저도 command.com에다 상시 집어넣고 있기에는 메모리가 부족하고 아까웠기 때문이다. 옛날에는 베이직 인터프리터가 Ready 출력할 메모리조차 아까워서 프롬프트를 Ok로 바꾼 시절이 있었다는 것 기억하시는가? (단 3바이트를 아끼려고!) 검색을 해 보니 xcopy는 1987년, MS-DOS 3.2에서 첫 도입됐다고 한다.

하긴, 옛날에는 다단계 디렉터리를 재귀적으로 탐색하면서 뭔가를 하는 일이 쉽지 않아서 외부 유틸리티를 많이 이용해야 했다. 파일 찾기는 말할 것도 없고, 지우는 것도 옛날에는 deltree라는 명령이 따로 있었지 싶다. 그건 지금은 del에 /s옵션으로 통합됐지만 말이다.
유닉스 계열 셸은 내장과 외장 명령어 구분이 어찌 되나 모르겠다. cp, mv, rm은 외부 프로그램인 것 같던데 설마 pwd, cd 이런 것들도 다 외부 프로그램이려나?

그리고 옛날에는 플로피 디스크(일명 디스켓)란 게 쓰여서 파일 시스템 차원에서 완전히 똑같은 디스크 복제를 해 주는 diskcopy라는 외부 명령이 있었다. 얘는 굳이 외부 명령으로 만들 거면 디스크 이미지 파일을 만들거나 이로부터 복제 디스크를 만드는 기능도 같이 있었으면 좋았을 거라는 아쉬움이 남는다.

1.2~1.44MB짜리 디스크를 단일 드라이브에서 복사하려면 source와 target 디스켓을 여러 번 갈아 끼워야 했는데.. Norton Utilities 같은 다른 유틸리티들은 EMS 및 XMS 메모리를 활용하여 한 번만 갈아 끼우고 바로 복사가 된다는 걸 장점으로 내세우곤 했다. 프로그램을 하나 설치하려 해도 "제품의 이제 1~N번 디스크를 넣고 아무 키나 누르세요" 이러던 참 아련한 옛날 추억이다.

2. 16비트 Windows의 실행 모드

예전에 언급한 바와 같이, 1990년대 초중반에 Windows라는 운영체제가 32비트 플랫폼으로 처음 갈아타던 시절에는 Win32 API라는 것의 구현체가 Windows NT, Windows 95, Windows 3.1+Win32s라는 세 계통으로 나뉘었다. NT가 가장 이상적인 레퍼런스 구현체이고, Win32s는 제일 허접하다.

그리고 그 중간의 95는 비록 NT처럼 스레드도 지원하고 도스로부터 많이 독립했다고는 하지만, 도스로부터 완전히 독립했다기보다는 도스를 내부적으로 흡수· 합병한 것에 가깝다. Windows NT의 마이너 축소판이라기보다는 Win32s가 각종 한계 없이 제대로 구현된 형태라고 보는 게 더 타당하다.

그런데 95 계통의 전신이라 할 수 있는 Windows 3.0이 나왔을 때에는 동일 제품· 단일 바이너리 하에서 실행 모드가 세 갈래로 나뉘었다. 바로 8086 리얼 모드, 286 표준 모드, 386 확장 모드이다.
Windows NT 4가 가장 많은 아키텍처를 지원하는 32비트 운영체제였다면, Windows 3.0 (3.1 말고)은 실행 모드가 가장 다양했던 16비트 도스용 운영 환경(?)이었다.

원래 Windows 1과 2에서는 리얼 모드밖에 존재하지 않았으며, Windows는 진짜 도스 위의 덧실행 껍데기 그 이상도 이하도 아니었다. 리얼 모드에서는 메모리가 기본 640K밖에 없었으며, 그 번거로운 16비트 Windows 3.x보다도 프로그래밍 환경이 더 열악했다.

그러다 Windows 2.0의 후속 버전으로 2.1은 286 표준 모드를 도입한 Windows/286과, 386 확장 모드의 전신인 Windows/386 이렇게 두 갈래로 나왔다. 사실, 16비트 80286 프로세서에도 보호 모드가 있긴 했지만 프로그래밍에 애로사항이 많았는지 그걸 사용한 프로그램은 매우 소수였으며 여전히 거의 봉인돼 있었다. 그냥 EMS/XMS 같은 규격으로 메모리를 수백 KB 남짓 더 사용할 수 있다는 것에 의미를 둬야 했다. 386 확장 모드도 첫 버전답게 문제가 많았으며, 2.11이 더 나와야 했다.

그러다가 Windows 3.0은 리얼, 286 표준, 386 확장을 모두 통합하여 출시됐다. 이때는 DPMI 규격도 갓 제정되었기 때문에 2.x 시절보다 보호 모드 지원도 더 개선되었다.
이때는 3.0에다가 멀티미디어 API가 최초로 추가된 확장팩이 나왔으며, 3.1은 네트워크 API가 추가된 Windows for Workgroup 3.11, 그리고 중국어 입출력 지원이 추가된 3.2 이런 식으로 기능 확장팩이 많던 시절이었다. 지금처럼 인터넷을 통한 업데이트가 가능한 시절도 아니었으니 말이다.

Windows 3.1에서는 리얼 모드가 삭제되고 win /2 또는 /3을 통해 286 표준 모드만 지원하지만, 이것은 성능이 굉장히 많이 열화된 모드였다. 그리고 Windows 95부터는 표준 모드도 빠져서 기술적으로 언제나 386 확장 모드로만 동작하는 형태가 됐다.

이렇듯, Windows 3.x는 비록 순수한 32비트 운영체제는 아니지만 보호 모드라든가 도스용 프로그램을 내부에서 구동할 때처럼 일부 기능에서 80386 이상 CPU가 제공하는 가상화 기능을 사용하기 때문에 결과적으로 32비트 CPU가 필요했다. 386 확장 모드가 지원하는 기능이 그런 것이었다.
이는 과거에 존재하던 PIF 편집기가 확장 모드에서는 표준 모드에 비해서 지원하는 옵션이 얼마나 더 다양해지는지를 보면 얼추 짐작 가능하다. 286 표준 모드에서는 요게 전부이던 옵션이..

사용자 삽입 이미지

386 확장 모드에서는 XMS뿐만 아니라 EMS 규격, 그리고 멀티태스킹 우선권 등 다양한 옵션들이 별도의 대화상자와 함께 추가되는 걸 알 수 있다.

사용자 삽입 이미지사용자 삽입 이미지

Windows 95 역시 순수 32비트 프로그램이 아니면 도스용 프로그램에 대해서만 제대로 된 가상 머신과 선점형 멀티태스킹을 지원했다. 16비트 Windows 프로그램을 강제 종료하면 운영체제와 얽혀 있는 스레드 동기화 오브젝트 같은 것이 얼어붙으면서 시스템의 안정성에 심각한 문제가 발생하곤 했다.

LimitEmsPages 이런 함수는 32비트가 아니라 이미 Windows 3.x 시절부터 deprecated돼 있었는데, 아마 리얼 모드 시절의 잔재이지 싶다.
Windows 1~2.x용 실행 파일은 후대 Windows와 실행 파일 포맷은 동일하지만(NE) 내부의 플래그로 구분되어 있어서 3.x에서 실행하면 "제대로 실행되지 않을 수 있음" 경고가 뜨곤 했다. 요컨대 Windows의 역사를 살펴보면, 16비트에서 32비트로 넘어갈 때만치 큰 변화는 아니지만, 같은 16비트 안에서도 1~2.x와 3.x 사이에 보호 모드가 도입되기 전과 후에는 기술적으로 나름 변화와 단절이 있었다고 볼 수 있다.

3. 외주로 제작되었던 보조 프로그램과 게임

Windows에 내장돼 있는 기본 프로그램들 중에는 마소에서 직접 만들지 않은 외주 프로그램도 있다. Windows 95 시절에 잠깐 있었던 하이퍼터미널이라는 터미널 접속 프로그램이 대표적인 예로, 스플래시 화면이라든가 각종 UI 외형이 대놓고 기존 마소 프로그램과는 어울리지 않아서 마소 자체 개발이 아니라는 걸 알 수 있었다.

또한 게임 중에서도 Windows XP에까지 제공되었던 3D 핀볼(시네마트로닉스 개발, 맥시스 유통)은 외부 프로그램이었으며, Vista/7 시절에 그래픽이 쇄신했던 지뢰찾기 등의 기본 게임들도 외주였다(Oberon games).

그런데 더 옛날에 Windows 3.1의 내장 프로그램들을 보면, 굉장히 단순하게 생겼고 이 정도면 자체 제작했을 법도 한 프로그램이 About 대화상자를 보면 외주인 경우가 은근히 더 있었다. 게다가 아래의 목록에서 보다시피 제작자/제작사가 프로그램마다 완전 제각각이었다.

  • 계산기: Kraig Brockschmidt
  • 터미널: Future Soft Engineering 사
  • 레코더: Softbridge 사
  • 지뢰찾기: Robert Donner & Curt Johnson
  • 카드놀이: Wes Cherry

그러니 Vista/7 이전에 제공되던 기본 게임들도 알고 보니 외주였던 셈이다. 특히 지뢰찾기는 Windows 1.0부터 3.0까지 존재하던 Reversi(일명 오델로)를 대체할 목적으로 3.1에서 처음 선보였던 게임이기도 하다.
레코더의 경우 Windows의 역사상 거의 전무후무하게 존재하던 키보드· 마우스 매크로 유틸리티인데 95와 그 이후로는 결코 재등장하지 않았으니 희소성이 크다.

물론 이것들 말고 프로그램 관리자, 파일 관리자, 제어판이라든가 간판 앱인 문서 작성기(오늘날의 워드패드)와 페인트(오늘날의 그림판)은 외주가 아니라 내부 자체 제작이다.

4. 색깔의 미묘한 차이

말이 나왔으니 옛날 추억 회상을 더 해 보자면,
컴퓨터의 주메모리가 아니라 비디오 메모리가 딱 1MB이던 시절에는 Windows 3.1의 비디오 모드를 (1) 꼴랑 640*480 저해상도인 대신에 트루컬러, (2) 800*600에서 적당하게 하이 컬러, 아니면 (3) 1024*768에서 256색.. 셋 중 하나로 골라 쓰는 재미(?)가 있었다.

그리고 Windows 3.1은 원래는 우리에게 익숙한 짙은 파란색으로 윈도우의 굵은 틀을 표현했지만, 하이 컬러 이상부터는 어인 일인지 은은한 하늘색으로 색깔을 바꿔 표시했다. 왜 무슨 근거로 색깔을 바꿨는지는 모르겠지만 개인적으로 아주 신기하게 느껴졌었다.

사용자 삽입 이미지사용자 삽입 이미지

그 이전 3.0은 슈퍼 VGA나 트루컬러로 진입할 일이 있긴 있었는지, 그래픽 드라이버가 개발되거나 3.1 것과 호환되긴 했는지에 대해 본인은 아는 바가 없으며, 이에 대해서 회의적이다.

Posted by 사무엘

2018/03/04 08:30 2018/03/04 08:30
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1464

컴퓨터 프로그램의 GUI 구성요소들 중에는 여러 아이템들을 한데 나열하는 리스트 박스(list box)라는 게 있고, 고정된 한 문장에 대해서 예/아니요, 참/거짓 여부를 지정하는 체크 박스(check box)라는 게 있다.

체크 박스는 프로그램이 고정 붙박이 형태로 제공하는 기능이나 옵션 하나에 대한 설정을 할 수 있다. 그리고 리스트 박스는 보통은 가변적인 개수의 항목들 중에 하나를 선택할 때 쓰인다.
그런데 가끔은 이 두 물건의 기능을 한데 합치고 싶은 상황이 생긴다. 리스트 박스의 각 아이템들에 대해서 1비트짜리 정보를 배당해서 선택 여부를 지정하는 것 말이다.

뭐, Windows의 리스트박스 컨트롤은 모든 아이템들에 대해서 1비트도 아니고 그냥 machine word 크기 하나로 custom 정보 data를 지정하는 기능이 있다. 또한 필요하다면 하나가 아닌 복수 개 multi-selection 모드로 동작하게 할 수 있고, 각 아이템에 대한 custom drawing도 가능하다.

하지만 딱 부러지게 아이템들 앞에 자동으로 운영체제의 check box 그림을 그려 주고 체크 박스의 리스트를 구현하는 기능 자체는 없다. 필요하면 사용자가 그걸 직접 구현해서 쓰게 여건만 만들어 놨을 뿐이다.
그래서 MFC의 경우 기존 리스트박스를 서브클래스 해서 CCheckListBox라는 걸 제공한다. owner drawing만 구현하는 것으로는 충분치 않고, space를 누른 키보드 입력과 check 버튼 주위를 누른 마우스 클릭도 감지하게 메시지 몇 개를 서브클래스 했다.

자고로 화면에 뭔가 길다란 리스트를 만들고 아이템들을 복수 선택할 수 있게 해 놓은 프로그램의 원조는 PC-Tools나 MDIR, Norton Commander, 심지어 Windows 3.x의 파일 관리자 같은 파일 관리 유틸리티이지 싶다. 복수 개의 파일을 복사하거나 삭제하는 기능을 제공해야 하니 리스트의 복수 선택 기능이 무조건 필수이기 때문이다.

그런데, selection이라는 것과 highlight 선택막대의 관계를 어떻게 보느냐에 따라서 프로그램의 동작이 달라지곤 했다. 도스용 프로그램들은 selection과 선택막대가 서로 따로 논다고 본 반면, Windows는 selection이 곧 선택막대의 연장선이라고 봤다.

그래서 Windows의 리스트박스는 화살표 키를 누르는 순간 기존 selection들이 다 사라지면서 선택막대가 움직이곤 했다. Shift+화살표로 연속된 영역을 한꺼번에 선택하는 것 말고 불연속적인 영역을 취사선택하려면 Shift+F8부터 눌러서 선택막대가 아닌 포커스 테두리가 깜빡거리는 상태로 들어간 뒤, 포커스 테두리만 움직이면서 Space로 아이템들을 선택하면 됐다.

굉장히 특이한 동작인데 Windows에서는 이게 기본이다. 기본적으로 포커스 테두리만 움직이게 하는 모드는 extended 플래그(LBS_EXTENDEDSEL)로 따로 있었다.
그에 비해 평소에는 선택막대와 selection이 다같이 움직이고 Ctrl+화살표로 포커스 테두리를 움직여서 Space로 선택하는 비교적 '직관적인 방법'은 훗날 리스트뷰 컨트롤이 도입하게 된다. 아이템을 복수 선택하는 방식은 이 두 컨트롤이 서로 호환되지 않는다.

또한, 각 아이템들에 대해 체크 플래그를 지원하는 건 아이템을 그냥 복수 선택할 수 있게 하는 것과는 UI의 관점이 다르다. 비록 내부적으로 본질적으로는 아이템별로 1비트짜리 boolean 정보를 지정한다는 점에서는 차이가 없겠지만 용도가 같지 않다는 것이다.

복수 선택은 대체로 아이템들이 진짜 가변적이고 사용자에 의해 아이템을 추가하거나 삭제까지 할 수 있는 상황에서 쓰이겠지만 체크 리스트는 그렇지 않은 경우가 대부분이다. 단순히 응용 프로그램이 제공하는 기능과 옵션이 많기 때문에 리스트 형태로 만들었을 뿐이다. 체크 리스트는 복수 선택과 달리, 선택 막대 selection과는 완전히 별개로 관리되기도 해야 할 것이고 말이다.

다음은 MFC의 CCheckListBox를 사용했던 먼 옛날 날개셋 한글 입력기 1.x의 옵션 대화상자이다.
Windows XP부터는 테마도 등장했기 때문에 지금 상황에 따라 체크 박스를 그리는 방법 역시 더 복잡해졌다.

사용자 삽입 이미지

다음은 리스트 박스가 아니라 무려 트리 컨트롤(공용 컨트롤)을 사용했던 날개셋 2.x의 옵션 대화상자의 모습이다.
Internet Explorer가 4인가 5에서부터 인터넷 고급 옵션들을 이렇게 트리 컨트롤로 구현해서 오늘날 최후의 11 버전에까지 이어져 오고 있다. 지원하는 옵션이 너무 많기 때문이다. 본인 역시 이 스타일을 따라해 보았다.

사용자 삽입 이미지

공용 컨트롤들은 owner-draw 안 쓰고도 자체적으로 아이템별 비트맵을 지정할 수 있으며, 더구나 트리 컨트롤은 아이템들을 카테고리별로 분류도 할 수 있으니 더욱 좋다.
날개셋 3과 그 이후부터는 이들 옵션이 상당수가 오토마타와 글쇠의 수식, 별도의 카테고리 옵션 등으로 떨어져나간 관계로, 저렇게 트리 컨트롤까지 써야 할 정도로 긴 옵션 리스트를 만들 일이 없어졌다.

사실, 트리 컨트롤은 IE 4 타이밍에서 TVS_CHECKBOXES라는 스타일이 추가되기도 했다. 기존 이미지 스타일을 활용하는 게 아니라 그건 놔두고 옆에 체크 박스를 별도로 추가해 주는 형태이다.

트리 컨트롤에서 체크 박스는 설치 프로그램에서 어떤 소프트웨어 제품의 구성요소들을 계층 구조로 나열한 뒤 설치· 제거할 부분을 선택받는 부분에서 유용하게 쓰일 듯하다. 이런 데서는 자식 노드가 하나라도 선택되면 부모 노드들은 중간 상태로 바뀌고, 부모 노드를 선택하거나 해제하면 자식들도 한꺼번에 선택이나 해제되는 동작이 필요할 것이다.

하지만 트리 컨트롤의 체크박스 기능은 깔끔하게 구현되지 않아서 잡음이 많다. 스타일을 윈도우를 생성한 뒤에 SetWindowLongPtr로 런타임 때, 그리고 아이템을 하나라도 추가하기 전에 적절한 타이밍에만 지정할 수 있다.
레이먼드 챈 아저씨는 저건 차라리 스타일이 아니라 메시지 형태로 구현하는 게 더 나았을 정도라면서 API 설계 구조를 비판한 바 있다. (☞ 링크) 실제로 콤보 박스의 extended UI 여부는 스타일이 적절해 보임에도 불구하고 덩그러니 CB_SETEXTENDEDUI라는 메시지를 통해 지정하게 돼 있다.

한편, 트리 컨트롤은 처음 도입됐을 때부터 지금까지 체크 박스와는 달리, '복수 선택'은 지원하지 않고 있는 것으로 유명하다. 리스트뷰 컨트롤처럼 아이콘을 Shift 및 Ctrl을 이용하여 복수 선택할 수 있지 않다는 뜻이다.
Windows 운영체제는 탐색기에서 볼 수 있듯이, UI 디자인 철학이 "트리로는 분야를 하나 선택만 하고", "리스트에다가 그 분야에 속하는 아이템들을 출력한 뒤 복수 선택해서 지지고 볶는다" 형태이긴 했다.

계층 구조를 나타낼 수 있는 복잡한 UI 컨트롤에서 복수 선택까지 가능하면 프로그램의 기능이 매우 복잡해지며, 리스트도 아니고 트리 컨트롤이 굳이 복수 선택까지 가능해야 할 일은 매우 드문 것도 사실이다.
하지만 그럼에도 불구하고 당장 Visual Studio IDE부터가 클래스· 리소스· 솔루션 뷰의 트리 목록이 진작부터 복수 선택을 지원한다. 걔들은 4.0 시절부터 공용 컨트롤 없이 진작부터 자체 구현 트리 컨트롤을 써 왔기 때문이다.

끝으로, 체크와 다중 선택을 짬뽕한 듯한 기괴한 UI가 Windows의 역사상 단 한 번, 8의 리스트뷰 컨트롤에서 잠시 등장한 적이 있었다.
아이템의 좌측 상단 같은 특정 부위를 마우스로 가리키고 있으면 체크 박스가 나타나고, 그걸 클릭하면 아이템을 복수 선택할 수 있었던 것이다.
보통 아이템을 클릭하면 기존 selection들은 다 없어지고 그것'만' 선택되곤 하는데, 체크 박스를 선택하면 기존 selection들을 놔두고 그걸 추가로 선택할 수 있었다.

사용자 삽입 이미지사용자 삽입 이미지

그 당시엔 아마 터치 장치를 염두에 두고.. Ctrl/Shift+클릭이나 드래그 없이 클릭만으로도 아이템들을 복수 선택할 수 있게 고심 끝에 저런 기능을 넣었던 듯하다.
하지만 반응이 좋지 않았는지, 이런 기능은 내 기억이 맞다면 Windows 8.1에서 곧장 없어졌고 다시 등장하지 않았다. 하긴, Windows 8은 저 정도면 약과이지, 아예 시작 버튼을 없애 버렸을 정도로 엄청 과격한 모험을 한 물건이기도 했으니까.

이렇듯, 리스트 박스, 리스트뷰 컨트롤, 트리 컨트롤을 두고 아이템의 복수 선택 및 체크 선택과 관련하여 할 말이 무척 많은 걸 알 수 있다. 복수 선택은 단수 선택만치 일상적으로 자주 쓰이는 기능은 아닐 뿐더러 어떤 방식으로 구현할지 동작의 customization의 폭도 넓은 편이다. 그래서 운영체제의 GUI가 곧장 직통으로 지원하지 않고 구현을 사용자에게 맡기는 편이었던 것 같다.

Posted by 사무엘

2017/12/20 08:36 2017/12/20 08:36
, ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/1439

« Previous : 1 : 2 : 3 : 4 : 5 : 6 : ... 18 : Next »

블로그 이미지

철도를 명절 때에나 떠오르는 4대 교통수단 중 하나로만 아는 것은, 예수님을 사대성인· 성인군자 중 하나로만 아는 것과 같다.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2019/12   »
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        

Site Stats

Total hits:
1295767
Today:
160
Yesterday:
461