예전에 했던 말도 있지만.. 암튼 지난 30여 년에 달하는 컴퓨터의 역사를 곱씹어 보는 건 재미있다~!

1. 인텔 CPU

(1) 인텔 8086은 유구한 x86 시대의 서막을 연 기념비적인 16비트 CPU이다(1978). 나중에 출시된 8088은(1979) 거기에서 외부 데이터 버스만 8비트로 낮춰서 성능을 약간 디버프 했지만 가격도 낮췄다.
80186의 존재감은 비행기에서 보잉 717의 존재감과 아주 비슷하게 듣보잡이다. -_-;; 애초에 PC보다는 임베디드용으로 만들어진 8086의 변종이었다.

(2) 80286의 제일 큰 존재 의의는 보호 모드의 첫 지원이지만.. 이게 많이 부족하고 불완전해서 역시 듣보잡으로 묻혔다. CPU로서 80286은 그냥 클럭 속도 더 빨라진 8086이나 다름없고, 현실적으론 컴퓨터 완성품으로서 AT (286 기반)가 XT (8088 기반)보다 나아진 점이 훨씬 더 많이 와 닿곤 했다. 2HD 고밀도 디스켓, 배터리 기반 시계, 키보드 속도 조절 등..
그에 비해 101키 키보드, VGA 컬러 그래픽이나 하드디스크는 XT에도 일단 장착 가능은 했던 구성요소이다.

(3) 80386은 드디어 32비트 CPU이다. 32비트 정도는 돼야 어지간히 큰 정수라든가 부동소수점을 원활히 표현할 수 있고, 메모리 주소 공간도 넉넉히 확보해서 보호 모드 가상 메모리 같은 것도 구현할 수 있다.
오리지널 DX는 외부 데이터 버스와 메모리 주소 버스도 모두 32비트인 반면, 염가 다운그레이드 에디션으로 나중에 출시된 SX는 이게 각각 16비트, 24비트였다. 과거 8086과 8088의 관계와 거의 동일하다.

(4) 80486도 DX와 SX 구분이 있었는데, 이때는 단순히 부동소수점 코프로세서가 기본 내장된 게 DX이고, 안 그런 게 SX였다. 거기에다 486은 DX조차도 클럭 속도를 더 끌어올린 DX2, DX4 이런 구분이 있었다.
이때 'VESA 로컬 버스' 규격 갖고 많이 떠들곤 했다. 천상 486 전용 규격으로 쓰이다 말았지만..
그리고 캐시 메모리라는 게 들어가기도 하고.. 486이 386에 비해 많이 발전하긴 했었다.
1990년대 중반, 486? 펜티엄쯤부터 컴퓨터 본체의 모양이 모니터 아래에 가로로 놓는 게 아니라 모니터 옆에 세로로 놓는 형태로 슬슬 바뀌어 정착했다.

(5) 펜티엄은.. 외부 데이터 버스가 CPU의 레지스터보다도 더 큰 64비트로 확장됐다. 물론 그렇다고 펜티엄이 아키텍처 차원에서 64비트 CPU인 건 아니었다.
인텔 셀러론의 초창기 버전은 펜티엄 2에서 L2 캐시 메모리가 없는 보급 염가판이었다. 그런데 이게 아예 전혀 없으니까 성능이 너무 떨어져서 나중에는 캐시가 약간이나마 장착되기도 했다.

이렇듯, 컴퓨터의 성능에는 클럭만 영향을 끼치는 게 아님을 알 수 있다.
그리고 한때는 옵션으로 주어졌던 요소들이 나중엔 다 기본으로 포함돼 들어가고 다른 새로운 기능이 옵션으로 도입된다.

2. Windows 운영체제

  • Windows 3.0은 MDI 창, VGA와 본격적인 컬러 지원을 위한 장치 독립 비트맵(DIB), WinHelp(!!!) 같은 획기적인 기능을 도입했고, 3.1에서는 OLE, 트루타입 글꼴, 공용 대화상자를 도입함으로써 현대의 Windows 근간을 닦았다.
  • 거기에다 Windows 3.0은 386 확장 모드라는 걸 도입해서 80386 이상 CPU에서 지원되는 보호 모드 멀티태스킹 기능을 일부 사용하기도 했다. 하지만 앱이 전반적으로 돌아가는 건 다 16비트 기반이다.
  • Windows NT는 저렇게 도스 진흙탕인 기존 Windows와는 달리, 미래를 바라보며 개발됐다. DEC Alpha라는 64비트 CPU용 에디션이 있기도 했으나.. 이때는 컴퓨터의 메모리도 4GB보다 훨씬 모자랐고 Windows 역시 그냥 32비트 모드로 동작했다고 한다. 포인터 8바이트니 INTPTR이니 그런 거 없었다는 뜻..

그렇기 때문에 Windows의 역사상 최초의 진정한 64비트 프로그래밍을 개막한 아키텍처는 IA64였다.
그 전 20세기의 NT4 시절에는 DEC Alpha뿐만 아니라 PowerPC네 MIPS네 여러 자잘한 아키텍처를 지원하다가 말았고, 2000년대부터는 x64와 ARM64가 살아남았으니 2000년대 초가 중대한 전환점이었다.

허나, 그 전환점의 중심에 서 있던 IA64는 좋은 타이밍을 날리고 장렬히 자폭했다... =_=;; 사실은 IA64가 채택했던 VLIW라는 설계 방식부터가 성능 대비 단점과 위험 부담도 너무 큰 방식었다. 마치 자동차 엔진에서 통상적인 왕복 엔진이 아니라 로터리 엔진처럼 말이다.
이런 사연으로 인해 Windows 2000은 NT 계열의 개발 역사상 전무후무하게 오로지 x86 전용으로만 출시되는 이변이 벌어졌었다. 무슨 9x처럼 말이다.

  • Windows 98은 마우스 휠과 멀티모니터를 최초로 공식 지원하기 시작했다.
  • Windows 2000/ME에서는 일부 마우스의 옆구리에 달려 있는 추가 버튼을 L, R 말고 X-button이라는 이름으로 최초로 지원하기 시작했다. 아마 전통적인 상하 스크롤 말고 좌우 스크롤 휠도?
  • Windows 7은 SSD와 멀티터치 디스플레이를 최초로 공식 지원하기 시작했다.

아울러,

  • USB 메모리를 별도의 드라이버 설치 없이 자체적으로 인식하기 시작한 건 2000/ME부터다.
  • XP/Vista 어느 때쯤부터 이제 와이파이도 별도의 프로그램/드라이버 설치 없이 자체적으로 잡기 시작했다.
    무슨 프로그램 띄워서 모뎀 전화를 걸어서 인터넷 접속하고, 그 뒤부터 연결 시간이 올라가던 게 1990년대 말쯤 일이었는데.. 참 격세지감이다.

3. 하드웨어의 발전 양상

성능 증가

  • 1990년대 동안은 클럭 속도가 뻥튀기 하듯 폭증했다.
  • 1990년대 후반부터는 메모리 양이 폭증했다. Windows 95~98 사이 말이다.
  • 2000년대 이후부터는 무선 인터넷 네트웍 속도가 폭증해 왔다.

64비트화

  • 워크스테이션/슈퍼컴 쪽은 모르겠고, 개인과 가정 레벨에서는 1990년대 말에 게임기부터 가장 먼저 64비트 CPU를 도입했다. 내 기억으로 닌텐도64..;;
  • PC는 2000년대 초에 IA64가 대차게 망하는 바람에 한 타이밍을 완전히 놓쳤고, 2000년대 중반쯤 램 용량이 실제로 4GB를 넘긴 뒤에야 64비트가 대중화됐다. Windows 2000/XP가 아니라 Vista/7 타이밍이다.
  • 스마트폰 업계는 2010년대 중반쯤에 슬슬 64비트로 전환이 시작돼서 2010년대 말엔 32비트 앱에 대한 지원을 끊네 마네 하는 상태가 된 것 같다.

오늘날 경전철이라고 해서 협궤를 쓰는 게 아니듯, 주머니에 넣어 다니는 작은 모바일 컴퓨터라고 해서 16/32비트 따위를 쓰지는 않는다. 커다란 화면에다 현란한 천연색 3D 그래픽과 고화질 동영상을 찍으려면 64비트 고성능 CPU는 필수이다. 물론 고성능 CPU는 전기도 많이 먹으니 고성능 배터리도 필수..

4. 그래픽

(1) 그래픽 가속이라고 하니까 게임용 3차원 그래픽 렌더링이라든가 동영상 코덱 같은 것만 떠올리기 쉬운데.. 사실은 2D 기반의 통상적인 GUI 구현을 위해서도 작은 수준의 하드웨어 가속이 오래 전부터 쓰여 왔다.
마우스 포인터라든가(깜빡이지 않는 것, 마우스 포인터 자취 표시, 포인터 주변의 그림자).. 화면 스크롤도 다 가속의 결과물이다. CPU 연산 기반으로 도트를 옮기는 수작업이 아니다.

(2) Windows의 그래픽 API (GDI)는 너무 범용적이고 장치 독립적으로 만들어졌다 보니, 당장 화면에 그려지는 픽셀 도트 값을 알아 내거나 색깔 바꾸기, 메모리 내용을 그대로 비트맵으로 간주해서 뿌리기 같은 간단한 작업조차도 오버헤드가 크고 일이 쉽지 않았다.
비디오 메모리에다 숫자 하나만 쓰면 끝날 일을 뭐 펜을 만들고 브러시를 만들고 DC에다 select시키고.. 운영체제 차원에서 직통 접근을 허용하지 않았던 것이다.

그러던 것이 Windows 3.0에서 DIB가 도입됐고, Windows 95 내지 NT 3.5에서 CreateDIBSection 계열 함수가 추가됨으로써.. 메모리 내용을 비트맵으로 그대로 뿌리는 일은 그럭저럭 가능해졌다. 옛날 WinG가 제공했던 기능도 다 이런 것들이었다. ‘비트맵 고속 전송’
다른 3D 가속 같은 거 전혀 없이 이거 하나만으로 Windows에서 Doom을 포팅하고 돌릴 수 있게 됐다.;;

Doom은 3D 전용 가속 기능이 없이 CPU와 초보적인 그래픽 가속만으로 만들어진 마지막 3D FPS였던 셈이다.
이거 마치 인어공주가 CG 없이 100% 셀 애니로만 만들어진 마지막 디즈니 애니인 것과 비슷한 느낌이다.

(3) 하긴, 옛날에는 그래픽 파일의 압축이라는 것도 원시적인 run-length 방식이 고작이었다. 더 빡세게 압축된 GIF나 PNG 파일 하나 열려면 386급 이상 컴퓨터가 필요했고, 디코딩도 훨씬 더 오래 걸렸었다. 하물며 JPG는 뭐 말할 것도 없고..
동영상조차도 1990년대 초중반에 Video for Windows 이러면서 나돌던 AVI는 쌩 run-length 압축인 게 많았다. 화질이나 압축률은 완전 허접 수준이었다.

WinAMP로 486/펜티엄 급 Windows 95 PC에서 128kbps짜리 mp3을 하나 재생하면 CPU 사용률이 10~20%까지 치솟았는데.. 이 역시 아련한 추억이다.
지금 우리가 전화기로도 당연하게 감상하는 디지털 멀티미디어 데이터들이 불과 2~30년 전에는 이렇게 가볍게 다뤄지던 물건이 전혀 아니었다. 그나마 가볍게 다루려면 기술 수준이 더 낮은 아날로그 매체만으로 만족해야 했다.;;

5. 나머지

(1) 64비트와 멀티코어는 서로 다른 별개의 분야이지만 거의 같은 시기에 태동해서 동시에 도입됐다(Core 2 Duo). plug & play와 USB하고 비슷한 관계인 것 같다. Win95/98 시절엔 USB 없이 직렬 포트에다가 프린터나 스캐너를 연결하고는 "새 하드웨어 발견.." 이러기도 했었다는 것 기억 나시는가? =_=;;
아울러, 모니터가 와이드 화면이 대세가 된 것도 2000년대 중반쯤으로 64비트니 멀티코어니 하던 때와 시기가 아주 비슷하다.;;

(2) 2000년대 초중반, 사운드 카드가 '인텔 사운드맥스'인지 뭔지 아무튼 메인보드에 내장돼 들어갔다. 그래픽 카드도 어지간히 까다로운 게임을 하는 게 아니라 기본 기능은 그냥 메인보드 내장으로 퉁쳐졌다.

(3) 요즘 노트북이나 스마트폰은 너무 얇아져서 뭔가를 꽂는 단자조차 너무 간소화되는 것 같다. 이건 개인적으로 좀 불편하게 느낀다.;;
가령, 구형 맥북은 큼직한 USB-A를 바로 꽂을 수 있지만 요즘 맥북은 그렇지 않고 C형만 꽂을 수 있다. 그리고 구형 갤럭시는 컴터용 이어폰을 바로 꽂을 수 있는 반면, 요즘 갤럭시는 그렇지 못하다.

(4) 범용적인 컴퓨터 말고 다른 기계들의 사정은 어떨까?
가정용 게임기, 업소용 오락기.. 이 둘도 차이가 있을 것 같고 내비게이션, 노래방 기계, 그리고 VR 게임기에 쓰이는 컴퓨터도 평범한 가정용 CPU 기반은 아닐 것 같은데.. 심지어 x86 계열이 아닐지도..??
요즘은 폰에 밀려서 디지털 카메라라는 물건이 많이 도태했지만, 그래도 부팅이 엄청 빨리 되는 것과 zoom이(= 렌즈빨) 더 뛰어난 건 디카만의 독자적인 장점이다. 그런 기기를 프로그래밍 하는 건 아무래도 임베디드 영역이지 싶다.

Posted by 사무엘

2024/03/02 08:35 2024/03/02 08:35
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/2270

1. 컴포넌트화의 필요성

전산학 중에서 소프트웨어공학이라는 것은 방대한 소프트웨어를 인간이 여전히 유지보수 가능하게 복잡도를 제어하며 설계하기, 기능과 파트별로 역할을 잘 분담시켜서 각 파트만 재사용하거나 딴 걸로 교체를 쉽게 가능하게 하기, 소프트웨어의 분량· 작업량· 품질을 정확하게 측정하고 효율적인 개발 절차를 정립하기처럼..
응용수학이나 전자공학보다는 산업공학과 가까운 측면이 있다. 단지, 얘는 유형의 제품이 아니라 무형의 코드 형태이기 때문에 여느 공산품과는 성격이 약간 다르게 취급될 뿐이다.

20세기 후반에 인공지능 연구 업계에 "AI 겨울"이 있었고 게임 업계에 "아타리 쇼크"라는 재앙이 있었던 것처럼, 프로그래밍 업계에도 "소프트웨어의 위기"라는 게 이미 1970년대에부터 있었다.

  • 코딩을 너무 중구난방으로 하고 나니, 일정 규모 이상의 프로젝트에서는 도대체 유지보수가 되질 않고 그냥 처음부터 다시 새로 만드는 게 더 나을 지경이 된다.
  • 이놈의 빌어먹을 스파게티 코드는 하는 일도 별로 없는데 쓸데없이 너무 복잡해서.. 처음에 작성했던 사람 말고는 알아먹을 수가 없고 maintainable하지가 않다.
  • 소프트웨어의 개발 속도가 오히려 하드웨어의 발전 속도를 따라가지 못한다.
  • 작업 기간을 줄이기 위해서 사람을 더 뽑았는데.. 웬걸, 신입들을 가르치느라 시간이 더 소요된다;;;
이런 문제들이 체계적인 소프트웨어공학이라는 이론의 도입 필요성을 촉진시킨 것이다.

그래서일까..??
"무식한 goto문 사용을 자제하자"라는 구조화 프로그래밍 이후로 객체지향 프로그래밍이란 게 프로그래밍 언어와 코딩 패러다임을 완전히 정복했다. 요즘 주류 언어들 중에 '클래스', 그리고 '상속'이라는 게 없는 언어는 찾을 수 없을 것이다.;; 이게 캡슐화, 은닉, 재사용성 등 소프트웨어공학적으로 여러 바람직한 이념을 코드에다 자연스럽게 반영해 주기 때문이다.

실험적으로 시도됐던 초창기의 순수 객체지향 언어들은 유연하지만 느린 런타임 바인딩 기반의 메시지로 객체 메소드를 호출한다거나.. 심지어 정수 하나 같은 built-in type에다가도 몽땅 타입 정보 같은 걸 덧붙이며 객체지향을 구현하느라 성능 삽질이 많은 경우도 있었다.
그러나 C++은 객체지향 이념에다가 C의 저수준, 그리고 빌드타임 바인딩(경직되지만 빠른..)을 지향하는 현실 절충형 디자인 덕분에 상업적으로 굉장히 성공한 객체지향 언어로 등극했다.

2. 마소의 실험

자 그래서..
1990년대에 마소에서는 고유 브랜드인 Windows가 대히트를 치고 소프트웨어 OEM (IBM 납품..)으로 그럭저럭 먹고 살던 처지를 완전히 벗어나니.. 당장 먹고 사는 고민보다 더 본질적이고 고차원적인, 소프트웨어공학적인 고민을 시작했던 것 같다.
얘들 역시 소프트웨어를 재사용 가능한 컴포넌트 형태로 만드는 것에 관심을 많이 기울였다. 그래서 재사용을 위해 바이너리 수준의 공통 규약, 프로토콜을 만들어서 자기들의 운영체제 차원에서 밀어붙이고 홍보하기 시작했다.

이때가 마침 C에 이어 C++ 컴파일러를 개발하고 MFC라는 라이브러리도 만들고, 코딩 스타일에 본격적으로 '객체지향'이란 게 가미되기도 했던 때이다. 하지만 마소에서 추구했던 것은 단순히 언어나 개발툴 차원에서 함수나 클래스의 모음집인 라이브러리 SDK 만들고 DLL 만드는 것 이상의 수준이었다.

제일 먼저.. (1) Windows라는 이름답게, 특정 기능을 수행하는 윈도 컨트롤을 컴포넌트화한다. 리치 에디트 컨트롤을 비롯해 각종 공용 컨트롤, 웹브라우저 컨트롤 같은 것 말이다. 이 사고방식이 극대화되어 "컴포넌트를 내 폼에다가 끌어다 놓고, 프로퍼티를 설정하고 이벤트 핸들러를 구현해서 응용 프로그램을 곧바로 만든다" RAD라는 개념이 완성되었으며.. Visual Basic이라는 정말 똘끼 충만한 개발툴이 만들어지게 됐다.

도스 시절의 GWBASIC이나 QuickBasic에서 참신한 점은 그 특유의 대화식 환경이었는데, Visual Basic은 또 다른 새로운 돌풍을 일으켰다. 경쟁사인 볼랜드에서는 이런 개발 스타일을 파스칼과 C++에다가도 도입하게 됐다.

(2) 그리고 마소에서는 서로 다른 응용 프로그램에서 만든 결과물을 문서에 자유롭게 삽입할 수 있게 했다. 이름하여 OLE라는 기술이다.
가령, Windows의 워드패드는 아래아한글이나 MS Office Word에 비하면 아주 허접한 프로그램일 뿐이다. 하지만 문서 안에 그림판에서 만든 비트맵 이미지를 집어넣고, 엑셀에서 만든 차트를 집어넣을 수 있다.

별도의 수학 수식 편집기에서 만든 수식, 악보 편집기에서 만든 악보, 그리고 WordArt/글맵시 같은 프로그램으로 만든 각종 글자 꾸임 배너까지..
단순히 무식한 그림 형태로 집어넣는 게 아니라는 것이 핵심이다. 이것들은 벡터 이미지로 취급되기 때문에 크기를 키워도 화질이 깔끔하게 유지된다.

그리고 그런 출력 이미지 자체뿐만 아니라, 각 프로그램에서 취급하는 내부 원본 데이터, 즉 소스가 그대로 보존된다. 그렇기 때문에 만들었던 객체를 손쉽게 수정도 할 수 있다.
그 객체를 더블 클릭하면 프로그램 내부에서 그림판이나 악보 편집기, 수식 편집기 등이 잠시 실행돼서 객체를 수정하는 상태가 된다..;;

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

서로 다른 프로그램이 이런 식으로 서로 분업 협업한다니.. 신기하지 않은가? 도스 시절에는 상상도 못 한 일일 것이다.

3. 프로그래밍의 관점

그러니 Windows에서 워드처럼 뭔가 인쇄 가능한 출력물을 만드는 업무 프로그램이라면 OLE 지원은 그냥 닥치고 무조건 필수였다. 다른 OLE 프로그램의 결과물을 삽입하든(클라), 아니면 다른 프로그램에다 자기 결과물을 제공하든(서버), 혹은 둘 다 말이다. macOS나 리눅스에는 비슷한 역할을 하는 규격이나 기술이 있는지 궁금하다.

Windows 프로그래밍을 다루는 책은 고급 topic에서 OLE를 다루는 것이 관행이었다. 다만, Windows API만으로 OLE 지원을 저수준 구현하는 건 굉장히 노가다가 심하고 귀찮았다. 그래서 MFC 같은 라이브러리 내지 아예 VB 같은 상위 런타임이 이 일을 상당수 간소화해 줬었다. MFC 앱 신규 프로젝트 세팅 마법사의 경우, OLE 지원 기능의 추가 여부를 선택하는 옵션이 응당 제공되었다.

Windows라는 플랫폼의 프로그래밍에 입문하려면 창(윈도)의 스타일과 특성, 메시지 메커니즘을 알아야 할 것이고 그래픽 API라든가 셸의 구조에 대해서도 알아야 할 것이다.
그런데 그런 것뿐만 아니라 이 바닥도 완전히 독립된 별개의 프로그래밍 분야이며, 기초부터 고급까지 한데 연결된 Windows 프로그래밍의 정수라고 생각된다.

이건 제일 간단하게는 확장자 연결이나 클립보드, drag & drop 구현과도 연결고리가 있다. 이 분야 API를 제공하다 보니 COM이라는 IUnknown이 어떻고 type library가 어떻고 하는 규격이 제정되었다.
사실, Windows에 레지스트리라는 것도 맨 처음엔 확장자 연결이나 OLE 클라/서버 정보만 저장하기 위해서 만들어졌다가.. 나중에 ini를 대체하는 응용 프로그램 설정 저장 DB로 용도가 확장된 것이다.

COM 형태로 제공되는 운영체제 기능을 사용하려면 CoCreateInstance를 호출해야 하고, 이런 프로그램은 처음에 CoInitialize라는 함수를 호출해 줘야 한다. 즉, 운영체제를 상대로도 별도의 초기화가 필요하다는 것이다.
그런데 OLE 기능을 사용하려면 OleInitialize라는 함수를 사용하게 돼 있는데, 얘가 하는 일은 CoInitialize의 상위 호환이다. OLE가 COM의 형태로 구현돼 있기 때문에 그렇다. 둘의 관계가 이러하다.

굳이 OLE 관련 기능뿐만 아니라 가까이에는 DirectX, 그리고 날개셋 한글 입력기와도 관계가 있는 TSF 문자 입력 인터페이스도 다 COM 기반이다. 하지만 문자 입력은 굳이 COM이나 OLE 따위 기능을 사용하지 않는 프로그램에서도 관련 기능을 접근할 수 있어야 하기 때문에 COM 초기화 없이 관련 인터페이스들을 바로 생성해 주는 함수를 별도로 제공하는 편이다.

4. ActiveX

과거에 인터넷 환경에서 마소 IE 브라우저의 지저분한 독점과 비표준 ActiveX는 정말 악명 높았다. 그런데 ActiveX라는 건 도대체 무슨 물건인 걸까??

마소에서는 앞서 컴포넌트화했던 그 윈도 컨트롤들을 데스크톱 앱뿐만 아니라 인터넷 웹에서도 그대로 돌려서 그 당시 1990년대 중후반에 각광받고 있던 Java applet에 대항하려 했다. Visual Basic 폼 내지, 특정 프로그램의 내부에서 플래시나 IE 컨트롤 생성하듯이 꺼내 쓸 법한 물건을 웹에서 HTML object 태그를 지정해서 그대로 띄운다는 것이다.

그때는 컴퓨터의 성능이 지금처럼 좋지 못했고 지금 같은 방대한 웹 표준이 존재하지도 않았었다.
그러니 웹브라우저에서 동영상도 보고 초고속으로 돌아가는 게임도 하고, 특히 무엇보다도 금융 거래를 위한 각종 암호화 기능을 돌리기 위해서는 닥치고 웹에서 그냥 생짜 x86 native 앱을 돌리는 게 제일 편했다.

이런 컴포넌트의 이름이 OLE Control이었는데, 이걸 웹에다 특화된 형태로 신비주의 마케팅 명칭을 붙인 게 ActiveX 컨트롤이다. 아마 마소 역사를 통틀어 길이 남을 엽기적인 작명이 아닐까? 하긴, DirectX도 비슷한 시기의 작명이니까 말이다. ㅡ,.ㅡ;;

하지만 웹에서 가상 머신이 아니라 특정 플랫폼의 네이티브 코드를 직접 구동하는 건 너무 무식하고 이식성도 떨어지고 표준 친화적이지 못하니 ActiveX는 마소에서도 버림받고 늦어도 2010년대부터는 완전히 퇴출 단계에 들어섰다.
지금은 Java applet도 완전히 멸망했고, 이들의 대체제는 정말 눈부시게 성능이 향상된 JavaScript 가상 머신이라고 보면 될 것이다.

5. OLE와 관련된 과거 유행: embed된 형태로 실행

저렇게 마소에서 COM/OLE니 ActiveX를 막 밀고 양성하던 1990년대 말~2000년대 초에는 어떤 프로그램이 다른 프로그램의 내부에 embed된 형태로 실행된 모습을 지금보다 훨씬 더 자주 볼 수 있었던 것 같다. 그와 관련해서 ActiveDocument (!!)라는 기술도 있긴 했다.

당장, MS Office 97에 있었던 Binder라는 유틸은 여러 Word, Excel 따위의 문서를 한데 묶어서 자기 안에서 해당 프로그램을 띄워서 내용을 편집하는 유틸이었다. 대단한 기술이 동원됐을 것 같지만 그래도 쓸모는 별로 없었는지 후대에는 짤리고 없어졌다.

Visual C++ 6의 IDE는 “새 파일” 대화상자를 보면 통상적인 텍스트 파일이나 프로젝트/Workspace뿐만 아니라 맨 끝에 Other documents라는 탭도 있어서 MS Office 문서를 자기 IDE 안에서 열어서 편집할 수 있었다. 당연히 MS Office가 설치돼 있는 경우에만 한해서.. 아까 그 Binder처럼 말이다.
그런데 이 역시 현실에서는.. 그냥 Word/Excel을 따로 띄우고 말지 굳이 워드/엑셀 문서를 왜 Visual C++ IDE에서 편집하겠는가? 그 기능은 후대엔 없어졌다.

ActiveX 기술의 본가인 IE 브라우저야 더 말할 것도 없다.
저런 MS Office 문서를 다운로드 해서 열면 해당 앱이 따로 열리는 게 아니라, 문서 보기/편집창이 웹페이지 화면에 떴었다. 별도의 프로세스로 말이다. 그 기술이 최초로 도입된 건 IE4가 아니라 1996년의 IE3부터였다.
파워포인트 슬라이드는 그 웹페이지 화면에서 곧장 슬라이드 쇼가 시작됐다. 이건 괜찮은 기능인 것 같다.

옛날에 pdf를 보기 위해서 Acrobat Reader를 쓰던 시절엔, 이 앱도 OLE 기술을 이용해서 IE 내부에 embed된 상태로 뜨는 걸 지원했었다. 지금이야 브라우저가 자체적으로 PDF를 표시해 주는 시대이지만 말이다.;;

요즘 컴터 다루면서 OLE 개체 삽입 기능을 쓸 일이 과연 얼마나 될까?
요즘은 개체 삽입으로 프로그램을 실행했을 때, 예전처럼 프로그램이 embed 형태로 실행되는 게 아니라 그냥 별도의 창으로 따로 뜨는 편인 것 같다. 앞서 소개했던 XP 시절의 모습과는 좀 다르다.
따로 실행됐을 때는 원래 문서에 표시된 컨텐츠와 자기가 다루는 컨텐츠가 따로 노니, 전자는 검은 빗금을 쳐셔 구분해야 한다는 UI 가이드라인도 있긴 하다.

사용자 삽입 이미지

이런 것들이 다 20여 년 전의 아련한 추억이고 한물 간 유행이다.
그나저나 인터넷으로 ppt 슬라이드를 받으면 바로 열리지 않아서 불편하다. 속성을 꺼내서 ‘신뢰할 수 없는 파일 차단’을 해제해야 볼 수 있다. 이것도 chm 도움말 파일을 바로 열리지 않는 것처럼 보안 강화를 위해서 취해진 조치인지 모르겠다.

Posted by 사무엘

2023/03/16 08:35 2023/03/16 08:35
, , , , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/2137

Windows는 태생적으로 ‘유니코드 = 2바이트 단위 인코딩’이라는 걸 전제에 깔고 만들어졌다.
거기에다 유니코드라는 게 없던 쌍팔년도 도스 시절과의 호환성을 너무 중요시해서 그런지, 2바이트가 아닌 1바이트 단위 인코딩 쪽은 일명 ANSI라 불리는 국가별 지역구 문자 코드에 오랫동안 얽매여 있었다. (cp949 따위)

그래서 이쪽 진영은 ‘유니코드의 1바이트 단위 인코딩’에 속하는 UTF-8의 지원이 맥이나 리눅스 같은 타 운영체제에 비해 굉장히 미흡한 편이었다.
가령, 파일의 경우 앞에 BOM을 꼭 넣어야만 ANSI가 아닌 UTF-8이라고 인식했는데.. 그러면 이건 말짱 도루묵이어서 지원하지 않는 것과 별 차이 없었다.

이러니 한 git 저장소에다가 넣고 여러 플랫폼에서 공통으로 사용하는 소스 파일의 경우, 영문이 아닌 한글로 주석은 무서워서 넣지도 못할 지경이었다.
Windows만 ANSI cp949를 선호하니 이건 타 운영체제의 IDE에서는 인코딩을 번거롭게 수동 지정하지 않는 한, 제대로 인식을 못 했다. 거기서 다시 저장을 하면 한글 내용은 당연히 다 날아갔다.

Windows에서도 UTF-8로 인식시키려면 파일 앞에다 BOM을 집어넣어야 하는데, 이러면 Windows 말고 타 컴파일러에서는 이게 배탈을 일으켰다.
정말 거지 같은 상황이었다. Windows는 1993년 NT 첫 버전부터 나름 유니코드를 염두에 두고 설계된 물건임에도 불구하고, 이런 분야에서는 전혀 유니코드에 친화적이라는 티가 느껴지지 않았다.

무려 2010년대 중후반이 돼서야 Visual C++ 2017인가 2019쯤에서야 드디어 BOM이 있건 없건 소스 파일의 인코딩을 다 UTF-8로 인식시키는 옵션이 추가됐다. 아마 202x 버전쯤에서는 이게 디폴트 옵션이 돼야 할 것이다.
그리고 언제부턴가 메모장이 편집하는 파일의 기본 저장 인코딩이 ANSI 대신 UTF-8로 바뀌었다.

응용 프로그램뿐만 아니라 Windows 자체도 10의 후대 패치를 통해 일단 명령 프롬프트의 인코딩에 UTF-8 지정이 가능해졌다. CHCP 65001 말이다.
단, 이런 명령 말고 프로그램 상으로 UTF-8 기반의 명령 프롬프트 환경을 어떻게 생성하는지는 잘 모르겠다. 검색해 보면 있겠지.. 배치 파일과 명령 argument를 몽땅 다 유니코드로 줄 수 있어야 진정한 유니코드화일 텐데 말이다.

다음으로 2019년쯤엔가 굉장히 큰 변화가 생겼는데..
유니코드를 지원하지 않는 구닥다리요 과거 Windows 9x의 잔재로나 여겨지던 각종 ...A 함수 말이다.
A 함수도 ANSI가 아닌 UTF-8 인코딩으로 문자열을 취급함으로써 유니코드를 지원하게 하는 통로가 뚫렸다.
그래.. 내가 원하던 게 이거였다. 진작에 좀 지원해 줄 것이지..!!

물론 Windows가 내부적으로는 문자열을 몽땅 UTF-16 방식으로 처리하고 있고, 2000년대부터는 ..A 함수 같은 건 만들지도 않는다. 그러니 ..A 함수의 유니코드화가 막 획기적으로 대단한 일은 아닐 것이다.
그러나 이렇게 해 주면 1바이트 단위로 문자열을 취급하는 각종 오픈소스 라이브러리에 대해서 골치 아프게 문자열을 변환하고 W 함수를 호출하는 thunk를 만들지 않아도 유니코드 파일명에 접근할 수 있어서 기존 코드의 포팅이 굉장히 수월해진다.

이 ANSI 코드 페이지라는 개념은 원래 시스템 global한 설정이며, 변경한 뒤에는 재부팅이 필요할 정도로 보수적인 속성이었다.
그런데 이걸 응용 프로그램마다 샌드박스를 씌워서 다른 값으로 가상화할 수 있고 심지어 UTF-8로 지정 가능해진 것은 고해상도 DPI 설정과 양상이 굉장히 비슷하다. 이것도 시스템 global이다가 응용 프로그램 단위로, 심지어 모니터 단위로 세부 지정과 변경이 가능해졌기 때문이다.

응용 프로그램의 매니페스트 정보를 통해 지정한다는 점마저도 동일하다. application → windowsSettings에 있다~!

<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>

20여 년 전에는 마소에서 unicows라고, 응용 프로그램이 Windows 9x에서 ...W 함수를 호출하면 문자열들을 변환해서 A 함수로 재호출해 주는 호환 layer를 개발· 배포한 적이 있었다. 한 프로그램이 2000/XP에서는 유니코드를 지원하고, 9x에서는 유니코드를 지원하지 않아도 기본적인 실행만은 되라고 말이다.
이제는 A 함수로도 UTF8 인코딩을 통해 유니코드에 접근하는 통로가 생겼다니, 참 오래 살고 볼 일이다.

또한, 이렇게 세월이 흐르면 Windows에서도 2바이트 완성형 CP949는 2바이트 조합형만큼이나 점점 보기 힘들어지고 역사 속으로 사라지지 싶다. 마치 플래시나 IE6, 보안이 안 좋은 http가 퇴출되듯이 말이다.
Windows가 일찍부터 유니코드를 지원했다고는 하지만 실질적으로 재래식 1바이트 인코딩의 퇴출을 가능하게 한 것은 UTF-8의 도입이라고 봐야 할 것이다.

한편, 웹이야 살아 있는 프로그램이 아니라 문서이니.. EUC-KR이니 CP949이 더 오래 남아 있을 것이다. 그러고 보니 내 홈페이지부터가 블로그 말고 HTML 페이지는 다 구닥다리 ANSI 인코딩을 쓰고 있구나. =_=

※ 여담: 2바이트 인코딩의 문자 집합 크기

우리나라의 KS X 1001 완성형 2바이트 한글 코드는 ISO/IEC 2022라는 옛날 규격에 맞춰서 94*94 = 8836 크기의 격자 안에 완성형 한글 2350자와 상용 한자 4888자, 그리고 나머지 1000여 자에 달하는 특수문자를 배당해 놓았다.

그 뒤 CP949, 일명 마소 확장완성형은 현대 한글 11172자에서 2350자를 제외한 나머지 한글 8822자를 KS X 1001이 사용하지 않는 2바이트 문자 조합에다가 억지로 집어넣었다.
KS X 1001이 lead byte와 tail byte 공히 0xA1부터 0xFE까지만을 사용하는 반면, CP949는 영역이 더 넓다. 특히 tail byte로는 알파벳 A~Z, a~z까지 사용한다.

그런데 이 ISO/IEC 2022 격자 크기 8836과, 비완성형 한글 수 8822는 값이 놀라울정도로 비슷하다. 우연인지, 의도된 결과인지 모르겠다.;;
한글 글자 수 11172와, 16*16픽셀 8*4*4벌 도깨비 한글 폰트의 크기 11520도 꽤 비슷하게 느껴진다. 이건 진짜로 의미가 서로 전혀 무관하기는 하다만 말이다.

Posted by 사무엘

2022/08/13 08:35 2022/08/13 08:35
, , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/2054

1. 존재를 부정 당하고 있는 IE 브라우저

지난 2010년대 초는 IE6을 작정하고 퇴출시키려던 시기였다. 그 뒤, 2020년대 초는 IE를 통째로 퇴출시키려는 시기가 됐다. 그 전에 플래시와 ActiveX부터 먼저 퇴출됐고 말이다.
그럼 지금은 키보드 보안이나 특수한 암호화 같은 게 필요하면 DLL 형태인 ActiveX 대신, EXE 실행 파일로 다 바뀐 건지? 그럼 요즘은 크롬에서도 인터넷 뱅킹이 가능한 건지?? 잘 모르겠다. 내 개인적으로는 여전히 뭔가 잘 안 돼서 PC에서 금융 거래를 할 때는 아직 "Edge + IE 모드"에 의존한다.

Media Player는 더 업데이트를 하지 않는 레거시로 남았을지언정 일부러 없애지는 않는 물건이다. 그러나 IE는 하루가 다르게 바뀌는 웹 프로그래밍 플랫폼이며 보안에 민감한 놈이다. 그래서 마소에서도 매우 적극적으로 존재 자체를 부정하고 없애려는 중이다.

그래서 작년 하반기쯤이었나? Windows 10의 어느 버전인가 업데이트를 설치한 뒤부터는 이제 Internet Explorer가 대놓고 실행되지 않게 됐다. 그냥 꺼져 버리고 Edge가 대신 뜬다.
과거 Windows Me가 여전히 도스를 완전히 벗어나지 못한 9x 커널 기반이었음에도 불구하고, 겉으로만 도스로 부팅하거나 도스로 돌아가는 기능을 없애 버린 것과 비슷한 조치 같다.

그러나 IE라는 껍데기 말고, IE가 사용하는 그 트라이던트 웹 렌더링 엔진과 윈도우 컨트롤은 사실상 Windows 운영체제의 일부나 마찬가지이다. 얘에 의존해서 돌아가는 기존 프로그램도 있기 때문에 그걸 통째로 없앨 수는 없다.
마치 Visual Basic 6처럼 말이다. 얘는 마소에서 21세기 이래로 줄기차게 없애고 .NET으로 대체하려 애썼지만 끝내 그러지 못했다. 게다가 Office 제품군의 매크로 언어인 VBA가 여전히 재래식 VB 기반이니.. 이것 때문에라도 완전히 없애는 건 불가능하다.

IE 엔진이 여전히 쓰이는 대표적인 분야는 도움말이지 싶다. 특히 HTML 도움말(*.chm) 말이다. 얘는 이미 10년 이상 전부터 추가적인 개발과 지원이 끊겼으며, 마소로부터 사실상 완전히 버려진 자식 신세이다.
그런데 마소에서 지난 2010년대부터는 '로컬 오프라인 환경에서 열람하는 도움말/문서'라는 개념 자체를 완전히 송두리째 폐기한 것 같다. IE나 HTML 도움말만 없앤 게 아니라는 뜻이다.

그러니 이제 메모장을 띄웠을 때 F1을 누르면.. '메모장 도움말'을 Bing에서 검색한 결과가 뜬다.;;; 이렇게 무심할 데가..
정말 믿어지지 않지만, 현재의 Windows 10/11은 로컬 도움말이란 게 사실상 존재하지 않는 것 같다. 오죽했으면 이젠 Windows\help 디렉터리가 거의 텅 비어 있다. 거기 그나마 들어가 있는 건 뭐 nvidia 제어판이나 개발 관련 듣보잡 문서이지, Windows 자체에 대한 일반 사용자용 도움말이 아니다.

개발툴도 예외가 아니어서 Visual Studio 2010/2012를 즈음해서는 10여 년의 역사를 자랑하던 msdn Document Explorer가 없어졌고.. 이제 내장 도움말 컨텐츠 다운로드 같은 것도 없어졌다.
이런 게 완전히 없어지기 전에는 chm이 아니어도 자기들만 쓰는 html + IE 엔진 기반의 개발 문서 조회 시스템이 있었는데.. 요즘은 그런 것 자체가 없다는 것이다. 걍 단순 정보 조회는 구글로, 문제 해결 정보는 Stack overflow를 뒤지라는 뜻인 듯..

안 그랬으면 HTML 도움말을 IE 대신 크로뮴 엔진 기반으로 다시 만드는 지원이라도 할 텐데 그런 것조차 없다.
근데 현실적으로는 다른 대안이 있나? Windows용 앱 내부에서 뭐 최신 반응형 웹 따위 바라지도 않고, 간단히 html 렌더링해서 표시하고 싶은데 IE ActiveX 컨트롤보다 더 간편한 방법이 있을까? 그건 의문이 든다.
아무리 웹이 기존 앱의 영역을 많이 잠식하긴 했어도, 그래도 앱 내부에서 웹페이지를 표시할 일도 있을 텐데 말이다.

그리고 또 하나 생각할 점은 웹페이지의 인쇄이다.
내 경험상.. 당장 이 블로그 웹페이지를 같은 pdf 드라이버로 인쇄해 보면 IE는 그럭저럭 최적화된 형태의 pdf가 만들어진다. 그 반면, 크롬은 훨씬 더 bloat된 이상한 형태로 pdf가 생성되며, 인쇄하는 데 시간도 더 오래 걸린다.
무작정 IE를 없애 버리기에는 제대로 된 대안· 대체제가 여전히 존재하지 않는 것 같다.

2. 이상하게 바뀐 메모장

올해 초쯤에 Windows 11의 업데이트를 설치하고 나자.. 프로그램 창의 ‘최대화’ 버튼의 동작이 살짝 달라졌다. 마우스로 얘를 가리키면 고전적인 전체 최대화 외에, 좌우 1:1:1 등 어느 레이아웃으로 최대화할지 고르는 타일 메뉴가 나타나기 시작했다.
Windows 7에서 창을 마우스로 화면 좌우상하 구석으로 끌었을 때 그렇게 배치하는 기능이 처음 추가되긴 했는데, 그게 13년쯤 뒤에 저렇게 강화됐다. 요건 뭐.. 나쁘지 않아 보이네.

그런데 그것 말고도 마소가 꽤 큰 사고를 쳤더라. 세상에.. 메모장이 메트로 UI 형태로 다시 만들어졌다.
본문이 운영체제 기본 에디트 컨트롤이 아니라.. 워드패드와 동일한 리치 에디트 기반으로 바뀌었다.
개인적으로 이거 완전 비호감이더라. 메모장은 단순한 앱의 상징 마지노 선으로 봉인 좀 시켜 놓지, 왜 저런 짓을 했나 모르겠다. 사람들이 메모장이 기능이 많아서 애용하는 줄 아는가??

그런 주제에 UTF-16랑 KS X 1001 인코딩의 파일이 열리지 않고, 우클릭 컨텍스트 메뉴에 알파벳 단축키가 먹히지 않는다. 예전에 되던 것이 안 되니 새 버전을 사용하기가 매우 꺼려지게 된다.
저런 쓸데없는 걸 건드리지 말고, sihost.exe가 CPU 다 쳐먹으면서 폭주하는 버그나 좀 고칠 것이지.. 그 문제는 여전한 걸 보고는 더 좌절했다.

난 지금까지도 솔직히 Win10이랑 11의 차이를 모르겠다. 업데이트 해야 할 필요를 느끼지 못한다.
10에서는 작업 표시줄을 우클릭해서 바로 작업 관리자에 들어갈 수 있었는데 11에서는 못 하고..
정말 필요한 기능이 아니라 그냥 괜히 바꾸기 위해서 바꾼 게 많다. 이럴 거면 걍 10의 업데이트나 낼 것이지 왜 11이라고 차별화를 한 걸까?

난 멀쩡히 잘 돌아가는 기능들에다가 쓸데없이 자꾸 메트로 UI를 도입하는 것도 근본적으로 마음에 안 든다. 현실에서 멀쩡한 길거리 인도 보도블럭을 쓸데없이 교체하는 것과 비슷한 느낌이 든다.
아 그래, 25년쯤 전에 마소에서 Windows UI 셸을 전부 IE4 Active desktop으로 도배하려는 것과 비슷하게 느껴지기도 한다.

Posted by 사무엘

2022/06/11 08:36 2022/06/11 08:36
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/2030

1. Dependency Walker

Dependency Walker라고.. Windows용 실행 파일에서 export 심벌과 import 심벌들을 재귀적으로 분석해서 모듈 간의 전체 의존 관계를 그래프 형태로 출력해 주는 굉장히 유용한 유틸리티가 있다. macOS나 리눅스 같은 타 OS에도 모듈 간 의존이라는 개념이 응당 있을 텐데, 타 OS용 실행 파일을 분석하는 프로그램도 좀 있었으면 좋겠다.

얘는 15년쯤 전, Windows Vista의 출시와 비슷한 시기에 마지막 버전이 나온 뒤부터는 원저자에 의한 개발과 유지 보수가 사실상 중단됐다. 뭐, 지금도 그럭저럭 쓸 만하긴 하지만 한 가지 문제가 있다.
Windows 7인지 8인지 10쯤부터는 모듈을 열어 보면 내부적으로 무한 루프에 빠져서 분석하는 데 시간이 굉장히 오래 걸리고 불필요한 정보가 너무 많이 걸려 나오는 경우가 있다.

사용자 삽입 이미지

자세한 속사정은 모르겠지만, 요즘 마소에서는 운영체제 API DLL들을 분야별로 최대한 잘게 쪼개고 있다. Windows 7에서는 kernel32.dll 하나가 제일 먼저 시범타로 쪼개졌었다. 가령 api-ms-win-core-heap, processthreads, memory, file 따위로 말이다.
그랬는데 요즘은 다른 dll들도 마찬가지이다. 레지스트리 API는 전통적으로 advapi32에 있었는데 그건 api-ms-win-core-registry로 가고, gdi32조차 ext-ms-win-gdi-draw, font, paint, path 등등으로 리모델링 됐다.

응용 프로그램들이야 과거와의 호환성을 위해 여전히 kernel32, gdi32 따위로 링크 되겠지만, 이 운영체제에 내장된 기본 프로그램들은 저런 잘게 쪼개진 dll을 직통으로 사용하는 형태로 빌드 된다.
쪼개진 dll들은 시스템 디렉터리에 있지도 않고, winsxs 아래로 도무지 정체를 알 수 없는 길고 복잡한 디렉터리 한구석에 처박히는데.. 딱히 매니페스트가 있지도 않아 보이구만 어떤 원리로 직통 연결되는지 나로서는 모르겠다.

내가 보아하니, Dependecy Walker가 어떤 PC에서는 이런 쪼개진 stub DLL을 모종의 이유로 인해 제대로 분석하지 못하는 것 같다. 거기서 loop cut을 못 하고 위의 스샷에서 표시된 바와 같이 무한 순환 오동작을 일으킨다.
차라리 그 파일을 찾지 못해서 넘어가는 것이면 다행인데, 이것도 100% 올바른 동작이 아닌 건 마찬가지이다.
이런 게 고쳐졌으면 하지만, 저 프로그램은 현재 버전업이 중단된 상태이다. 그렇기 때문에 모 오픈소스 진영에서는 Dependency Walker의 클론을 직접 만들고 있기도 하다.

참고로, 과거의 Windows 9x에서는 kernel32.dll이 원초적인 dll이었다. 즉, 심벌을 export만 하지, 자신은 실행 과정에서 다른 dll을 import 하는 것이 없었다.
그러나 오늘날의 Windows는 ntdll.dll이 원초적인 dll이다.

2. 32비트 프로그램에서 실행 중인 64비트 프로그램의 경로 얻기

GetModuleFileNameEx는 현재 컴퓨터에서 실행 중인 다른 프로세스, 혹은 거기 안에 같이 load된 dll의 전체 파일 경로를 얻어 오는 함수이다.
그런데 얘는 전통적으로 32비트 프로그램에서 64비트 프로그램을 대상으로 정보를 요청하는 건 잘 되지 않는 것으로 유명했다.

그냥 단칼에 실행이 실패하는 것도 아니고, 경로를 되돌리기는 하는데 온전한 형태가 아니라 뒷부분이 짤린 일부만을 되돌렸다. 그리고 에러 코드도 ERROR_PARTIAL_COPY라고 당당히 되돌렸다.
32비트 프로그램이 64비트 프로세스의 주소 공간에 접근하는 게 기술적으로 쉬운 일은 아니겠지만 그건 걔네들 사정일 뿐이다. 사용자 내지 프로그래머의 입장에서는 겨우 이런 간단한 정보 하나 온전히 얻으려고 IPC용 64비트 exe를 따로 만들어야 하나.. 멀쩡한 함수가 무용지물이니 우회 경로를 뚫느라 굉장한 불편을 겪을 수밖에 없었다.

그랬는데 오늘 우연히 이 함수를 호출해 보니 Windows 10의 20xx이후의 업데이트 버전에서는 이 문제가 해결된 것 같다. 32비트 프로그램에서 다른 32비트나 64비트 프로그램의 전체 경로를 얻는 것.. 반대로 64비트 프로그램에서 다른 32비트나 64비트 프로그램의 전체 경로를 얻는 것 모두 아무 문제 없다.
Windows 10 구버전이나 Windows 7, 8 같은 거 64비트 에디션이 있으면 같은 프로그램을 구동해서 결과를 확인하고 비교할 수 있겠다만.. 대조군을 구하지 못해서 그것까지 실험은 못 해 봤다.

옛날에는 도대체 무슨 한계 때문에 이 함수가 제대로 동작하지 않았는지, 그리고 지금은 무엇이 해결되었는지 이 함수와 관련된 사연을 좀 알고 싶다.
이 함수는 원래 psapi.dll에 있던 시스템 정보 조회용 부가 액세서리에 가까운 물건이었으나..
앞서 언급한 바와 같이 Windows 7 즈음부터 시작된 API 재배치 정리 작업 과정에서 kernel32의 세부 카테고리로 본진이 이동한 듯하다. 사실, GetModuleFileName이 있던 곳과 같은 곳에 있는 게 논리적으로 훨씬 더 타당하기도 하다.

이런 커널 API 말고 GDI 쪽에서도.. 옛날에 AlphaBlend처럼 Windows 98에서 처음 추가된 그러데이션 그리기 함수들은 msimg32.dll이라는 별도의 DLL에 들어가 있다가 Windows XP인지 Vista인지 그때쯤부터 gdi32로 자리를 옮긴 적이 있었다.
새로 추가된 함수가 이런 식으로 재분류되는 게 완전히 새로운 관행은 아니었던 셈이다.

3. 파일 대화상자의 동작과 current directory

Windows에서 제공하는 파일 열기/저장 공용 대화상자는 사용자가 선택한 파일이 있는 곳으로 프로그램의 current directory도 같이 바꿔 버린다.
그래서 어떤 프로그램에서 USB 메모리 안에 있는 파일을 열기 대화상자로 골라서 열고 나면, 그 파일을 닫은 뒤에도 계속해서 USB 메모리가 사용 중이라면서 안전하게 제거가 되지 않는 불상사가 벌어진다. 파일을 열었던 프로그램을 통째로 종료하거나, 열기 대화상자를 꺼내서 다른 드라이브에 있는 파일을 열면 문제를 해결할 수 있다.

사실, 아주 극단적으로 특이하게 동작하는 물건이 아닌 이상, GUI 프로그램은 자기가 작업하는 파일의 절대 경로를 갖고 있다. 상대 경로를 통해 다른 파일을 참조한다 하더라도 기준이 되는 절대 경로가 따로 있지, 프로그램의 current directory 정보에 의존할 일은 없다. 게다가 current directory는 스레드가 아니라 프로세스마다 하나씩만 보관되는 정보이기 때문에 thread-safe 하지도 않다.

그러니 파일 대화상자가 굳이 저렇게 동작할 필요가 전혀 없어 보이는데도 current directory를 변경하는 이유는.. (1) 레거시 프로그램과의 호환도 있고.. (2) 그리고 다음에 파일 대화상자를 또 열 때 사용자가 마지막으로 선택했던 파일이 있는 곳을 current directory라는 수단을 통해 기억하고 공유하기 위해서이지 싶다.

도스 같은 명령 프롬프트 환경에서는 사용자의 타이핑 수고를 덜기 위해서 current directory라는 개념이 반드시 필요했으며, 그때는 아예 각 드라이브별로 current directory를 다 기억하고 있었을 정도였다. 지금 Windows 환경은 그 정도까지는 아니다.
그리고 어떤 프로그램은 불러들이는 문서 파일이 있는 디렉터리와 current directory가 동일하다는 게 보장돼야만 제대로 동작하는가 보다.

하지만 파일 대화상자도 OFN_NOCHANGEDIR라는 플래그가 있어서 사용자가 어느 파일을 선택하건 current directory를 건드리지 않게 하는 옵션 자체는 있다.
그리고 내부 동작도 바뀌어서 굳이 current directory에 의존하지 않고 자체적으로 사용자가 마지막으로 파일을 선택했던 위치를 기억해서 보여준다.

그러니 오늘날 새로 개발되는 프로그램들은 파일 대화상자를 꺼낼 때 가능한 한 OFN_NOCHANGEDIR를 사용하는 게 좋을 것 같다.
또한, 이런 조치와는 별개로 current directory 때문에 USB 메모리가 안전하게 제거되지 않는 문제를 운영체제 차원에서도 좀 최소화해야 하지 않나 싶다.

이건 모니터를 2~3대 연결해서 컴퓨터를 잘 쓰다가 일부 모니터의 선을 뽑아 버린 것과 비슷한 상황이다. 이 경우, 운영체제에서는 없어진 모니터의 영역에 있던 프로그램 창들을 재주껏 다른 모니터로 잘 옮겨 줘야 한다. 그런 것처럼 USB 메모리가 뽑혔다면, 거기를 current directory로 참조하던 프로그램은 다른 디렉터리를 참조하도록 상태가 적절히 바뀌어야 할 것이다.

4. 그래픽 뷰어

끝으로, 이건 프로그래밍과 큰 관계 없이 특정 앱에만 해당되는 사항인데..
요즘 Windows 10에 기본 내장돼 있는 그래픽 뷰어 말이다. 오랫동안 사용해 본 내 경험에 따르면, 얘는 좀 불안정한 것 같다. 창을 여러 개 띄워 놓다 보면(5개 이상 여러 파일)..

  • 종종 뻗으면서 지금까지 띄웠던 창들이 한꺼번에 싹 없어진다.
  • 혼자 CPU를 잔뜩 소모하면서 노트북 PC를 열받게 하기도  한다.
  • 사진 파일을 더블 클릭했는데 프로그램이 실행만 되고 창이 뜨지 않고 먹통이 되기도 한다. "파일 시스템 오류 (-805305975)" 이러면서 아예 실행이 안 된다.

2004/2009대 버전으로 개인용 컴과 회사 컴에서 모두 동일한 현상이 발생하니, 이건 내 환경만이 문제가 아닌 것으로 보인다.
한 가지 확실한 건 얘는 화면 표시에 그래픽 카드의 하드웨어 가속 기능을 적극 활용한다는 것이다. 뻗는 것도 여느 프로그램들 같은 메모리 버그 때문에 뻗는 게 아니며, 그래픽 카드 드라이버와의 충돌 내지 그쪽의 오류 때문에 뻗는다.

내 기억이 맞다면 Windows XP~7 사이의 기본 그래픽 뷰어는 256색 GIF에 대해서는 트루컬러 JPG와 달리 부드러운 확대/축소를 지원하지 않는다거나, GIF/APNG 애니메이션을 지원하지 않는다거나 하는 한계가 있었다.

지금 뷰어는 그런 한계가 다 없어지고 한 프로그램에서 사진과 동영상을 모두 취급할 수 있어서 기능 면에서는 제일 좋아졌다. 하지만 반대로 구버전에 비해서 안정성은 명백하게 떨어져 있는 게 아쉽다. 특히 앱이 실행되지 않기 시작하면 운영체제를 재시작/재로그인 하는 것 말고는 다른 해결책이 없다.

5. CPU 점유 문제

요즘 누구든지 컴퓨터나 폰을 다루면서 겪는 굉장히 성가신 문제 중 하나는.. 어떤 불필요한 프로세스/서비스가 나도 모르는 사이에 CPU 자원을 독식해서 기기가 갑자기 혼자 열받고 팬 돌아가고 배터리가 눈에 띄게 빨리 닳기 시작하는 것이다. 그러고 보니 스마트폰은 팬이 없구나~!

개인적으로는 이럴 때 CPU 도둑을 감지해서 “이놈이 지금 폭주 중인데 죽일까요?” 안내를 해 주는 유틸/툴이 있어야 한다고 생각한다.
도스 시절로 치면 memmaker, 윈도 XP 시절에 잠깐 있었던 바탕화면 정리 마법사, 어지간한 악성코드 진단 유틸 같은 명목으로 말이다.

물론 어지간한 컴잘알이라면 이럴 때 작업 관리자를 띄워서 CPU 사용량이 높은 놈을 강제 종료시킬 것이다. 하지만 범인이 평범한 프로그램이 아니라 서비스 같은 부류라면 뭐가 문제인지를 진단하기 어렵다.

본인의 과거 경험을 떠올려 보면 Windows Update와 관련된 서비스가 폭주한 적도 있었고, 크롬 브라우저가 쓸데없이 폭주한 적도 있었고.. 요 근래에는 WMI provider host인지 뭔지 하는 놈도 폭주해서 강제 종료시킨 적이 있었다.
자고로 업데이트는 CPU를 최하 우선순위로 받으면서 민폐를 절대 끼치지 않고 몰래 몰래 돌아가야 할 것이다. 사용자가 명시적으로 시키지 않은 작업이 저 따위로 돌아가는 건 어떤 경우든 디자인 결함이고 버그이지 않을까?

6. 프로그램 창의 떨림 현상

끝으로, 이건 프로그램이 뻗는 급의 치명적인 문제나 불편한 현상은 아니지만..
요즘 컴퓨터를 쓰면서 프로그램 창을 전환하다 보면, 아주 가끔씩 프로그램들이 non-client 영역(제목 표시줄, 메뉴 따위)이 수십 번 다시 그려지는지.. 수 초 동안 부르르 깜빡거리고 떨리는 경우가 있다.

이 역시 2004/2009급 Windows 10이 깔린 개인용과 회사용 컴퓨터에서 모두 목격하는 현상이다. 201x년대에는 딱히 본 적이 없었던 새로운 현상이다.
정확하게 어떤 조건 하에서 왜 발생하는지는 모르겠다.
내가 모르는 사이에 업데이트가 깔리면서 운영체제의 아랫단은 알게 모르게 많이 바뀌는데, 버그나 부작용도 슬그머니 들어갔다가 또 잠수함 패치되기도 하는 것 같다.

Posted by 사무엘

2022/01/23 08:34 2022/01/23 08:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1978

Windows API에서 LoadLibrary는 말 그대로 실행 파일(exe/dll)을 현재 프로세스의 주소 공간에다 불러들여서 거기 있는 코드를 실행하거나 리소스를 추출하게 해 주는 함수이다.
그리고 얘의 심화 버전은 LoadLibraryEx이다. Ex 버전은 옵션을 추가로 받아서 절대 경로 없이 파일명만 주어졌을 때 디렉터리를 탐색하는 순서를 지정할 수 있고, 파일이 이미 load되어 있을 때 레퍼런스 카운트 변경 여부 같은 것도 수동 지정할 수 있다.

하지만 그런 옵션들은 현업에서 잘 쓰이지 않는다. 저 함수에서 실질적으로 자주 쓰이는 옵션은.. DLL에서 리소스를 추출할 준비만 하고, 코드를 실행할 준비--기준 주소 재배치, DllMain 함수 실행--는 생략해서 로딩 속도를 좀 더 향상시키는 LOAD_LIBRARY_AS_DATAFILE이다. 특히 x86, x64, ARM 같은 아키텍처를 불문하고 동일 DLL에 있는 리소스 데이터를 추출하려면 이 '간소화' 플래그를 반드시 지정해야 한다(다국어 UI 리소스 같은..).

그런데 문제는.. 이 DATAFILE 간소화 로딩이란 게, 과거에는 "리소스 추출에만 특화"이라는 자기 본연의 기능에도 모종의 이유로 인해 뭔가 2% 부족한 구석이 있었다는 것이다.

Windows 9x 시절에는 이 제약이 제일 심했다. 간소화 로딩된 DLL 핸들에 대해서는 (1) 리소스를 제일 저수준에서 탐색하는 EnumResourceLanguages/Names/Times 및 Enum/Find/LoadResource 계열 함수만 사용할 수 있었다. 이들보다 상위 계층에서 동작하는 Load*계열 함수들은(string, menu, bitmap, image 따위) 지원되지 않았다. 그러니 간소화 로딩의 활용성이 부족했으며, 여전히 기존 full(?) 방식 로딩을 해야 하는 경우도 있었다.

허나, 한편으로는 저 제약이 그렇게까지 본질적이고 치명적인 문제가 아니었다.
Windows 프로그램에서 리소스 전용 DLL을 사용하는 주 목적은 다국어 UI 제공.. 아니면 대화상자· 메뉴 같은 표준 리소스가 아니라 자기 자신만 사용하는 custom 데이터의 저장이기 때문이다.

그리고 표준 리소스들도 특정 언어에 속하는 놈을 지정하려면 "DLL 핸들 + 리소스 ID"만으로는 어차피 충분치 않다. FindResourceEx와 LoadResource의 결과값인 메모리 포인터를 줘야 하며, 함수도 LoadMenuIndirect, DialogBoxIndirect처럼 뒤에 indirect라는 단어가 붙은 '저수준 버전'을 써야 한다.

그렇기 때문에 리소스 추출용 간소화 방식으로 load한 DLL은 저수준 함수로만 다룰 수 있더라도 그럭저럭 사용할 만했다. 그런데 여기에는 다른 이상한, 자잘한 문제도 있었다.

DialogBoxIndirect 함수는 대화상자 리소스를 "모듈(인스턴스) 핸들 + 리소스 ID"가 아니라 대화상자 템플릿 포인터 하나로만 곧장 지정함에도 불구하고, 모듈 핸들을 여전히 인자로 받는다. 내부적으로 CreateWindowEx 함수를 호출할 때 모듈 핸들이 필요하기 때문이다(대화상자 자신, 그리고 내부의 child 컨트롤들 생성).

그런데 (2) 이때 리소스 추출 간소화 방식으로 load한 DLL의 핸들을 주면.. 구형 운영체제에서는 여러 문제들이 발생했다.
일단, 자기 자신이 내부적으로 사용하는 커스텀 컨트롤--표준 컨트롤이 아니고, CS_GLOBALCLASS 등록된 커스텀 컨트롤도 아닌 놈--이 만들어지지 않는다. 이건 CreateWindowEx 함수의 특성상 자연스러운 귀결이지만, 그 이상으로..

내 기억이 맞다면 대화상자의 배경색이 일반적인 회색이 아니라 흰색으로 바뀌고 좀 만지다 보면 프로그램이 뻗었다. Windows 9x뿐만 아니라 나름 NT 계열인 2000에서도 말이다.
그 이유는 딱히 알 수 없었다. 그저 경험적으로 이런 DLL 핸들을 집어넣어서는 안 된다고 날개셋 한글 입력기 소스의 주석에도 엄청 옛날에 적혀 있었다.

물론 이 역시 본질적이고 치명적인 문제는 아니다.
윈도우의 생성과 관련해서 전달하는 인스턴스/모듈 핸들은 그 윈도우의 클래스를 등록한 주체를 식별하는 용도이다. 애초부터 리소스가 전혀 아니라 코드와 관계가 있다. 그러니 여기는 애초에 리소스 추출 간소화 방식으로 load된 DLL이 들어갈 자리가 아니다. 그런 DLL을 집어넣은 것은 사실상 프로그래머의 실수에 지나지 않는다.

하지만 이쯤 되니 의문이 생긴다. 프로그래머가 아무리 실수할 수 있기로서니, 그걸 넘겨주면 단순히 custom 컨트롤이 생성되지 않는 것 이상으로 왜 다른 이상한 부작용까지 발생한 것일까? 차라리 깔끔하게 에러와 실패 처리를 하는 것도 아니고 말이다.
DLL을 일반적인 방식으로 load하는 것과 datafile(리소스 특화 간소화) 방식으로 load하는 것은 내부적으로 무슨 차이가 있는 걸까?

오늘날의 32비트 및 64비트 Windows 환경에서는 DLL을 로딩한 결과 핸들(HMODULE / HINSTANCE)은 그 파일 내용을 가리키는 데이터 포인터와 거의 동급이라고 여겨진다. 파일을 memory-mapped file 형태로 통째로, 혹은 약간의 보정만 거쳐서 읽어들인 첫 지점이다. 쉽게 말해 그 핸들이 가리키는 메모리에는 EXE 파일 시그니처인 MZ부터 쭉 나온다는 것이다.

그리고 실행 파일은 메모리 주소가 언제나 64KB의 배수 단위로만 배당된다는 것도 이 바닥에서 프로그래밍 좀 한 사람들은 아실 것이다. 그 말인즉슨, 일반적으로 HMODULE 내지 HINSTANCE의 값은 64KB의 배수이며, 하위 word가 언제나 0이 된다는 뜻이다.
하지만 특수한 상황에서는 이런 조건을 만족하지 않는 핸들도 있을 수 있다.

(1) 먼저, 과거의 Windows 9x 환경에서는 16비트 프로그램에서 호출한 LoadLibrary의 리턴값이 대표적인 예이다. 얘들은 핸들의 크기 자체가 16비트밖에 안 되니 리턴값과 내부 의미 역시 32비트 프로그램과는 완전히 다른 형태여야 한다.
물론 이미 32비트 형태로 빌드된 프로그램이야 이런 거 신경 쓸 필요가 전혀 없으며, 16비트와 32비트 프로그램을 모두 한데 관리하는 운영체제의 관점에서나 구분이 필요하다.

(2) 그리고 LoadLibraryEx + datafile 방식으로 불러들인 dll 핸들도 형태가 약간 달라진다. 운영체제의 버전에 따라 차이가 있지만 일단 해당 DLL의 preferred base는 완전히 무시되며, 굳이 64KB라는 큼직한 단위로 주소가 배당되지 않는다.
결정적으로는 최하위 비트에 1이 추가돼서(= 홀수!!) 얘는 datafile 방식으로 생성되었다는 것을 나타낸다. 메모리 주소로서의 DLL 핸들은 하위 16비트에 어차피 유의미한 정보가 담겨 있지 않으니.. 그 잉여 공간에다 이런 정보를 보관한다는 뜻이다.

요컨대 HMODULE / HINSTANCE는 16비트 프로그램 또는 datafile 방식에 한해서는 64KB의 배수 단위인 깔끔한 포인터가 아니게 된다. 그런데 과거에는 운영체제 내부에서 이런 변칙적인 핸들을 취급하는 방식이 서로 충돌했던가 보다.

kernel32는 이 DLL이 datafile 방식으로 load되었다는 것을 식별하기 위해서 핸들값에다가 1을 추가했다. 하지만 user32의 대화상자 표시 함수는 datafile 방식에 대해서는 전혀 관심이 없으며, 이 핸들값이 하위 16비트가 비영인 것을 보고는 이건 16비트 모듈이라고 인식해 버렸다. 그리고 16비트 프로그램과의 하위 호환을 위한 보정 처리를 수행했다.

그 보정 처리 중에는 대화상자 내부의 각 에디트 컨트롤들에 대해 고유한 데이터 세그먼트를 생성하는 것도 있었다.
아시다시피 에디트 컨트롤, 특히 multiline으로 동작하는 놈은 혼자서 수백, 수만 바이트에 달하는 텍스트를 저장할 수 있다. 모든 컨트롤들이 한 64KB 데이터 세그먼트를 공유할 게 아니라 각각이 고유한 세그먼트를 갖는 게 낫다. 이것을 대화상자 표시 함수가 내부적으로 해 줬다.

(그럼 이건 특별히 메모리가 많이 필요한 에디트 컨트롤에 대해서 고유한 스타일을 줘서 그 컨트롤이 알아서 처리하면 되지, 이런 걸 왜, 어떻게 상위 윈도우인 대화상자에서 처리하는지는 잘 모르겠다. 그리고 그런 식이면 에디트 컨트롤뿐만 아니라 리스트나 콤보박스도 수천 개의 아이템을 추가하느라 메모리가 많이 필요할 때가 있을 텐데 걔네들은 어떻게 처리되는지도.. 개인적으로는 잘 모르겠다. ㄲㄲ)

어쨌든.. 대화상자를 생성할 때 datafile DLL의 핸들이 지정되면 저런 복잡한 이유로 인해 16비트 보정이 수행되는데.. 실제로 대화상자를 돌리는 이 프로그램은 16비트 프로그램이 아니다. 그래서 보정 처리가 제대로 되지 않고 프로그램이 죽는 등 갖가지 오동작과 이상 현상이 발생한다고 한다. 그래서 그랬던 것이군~!! (☞ 더 자세한 설명)

대화상자에도 스타일이 있다. 하지만 이건 윈도우 스타일의 형태로 지정해 주는 게 아니고 DialogBox 계열 함수에다가 인자로 전하는 것도 아니며, 그냥 대화상자 리소스 템플릿에 박혀 들어가는 값일 뿐이다. 그러니 다른 스타일 플래그들에 비해 인지도가 매우 낮으며 프로그램 코드에서 볼 일이 없다시피하다.

이 대화상자가 다른 대화상자의 child로 들어갈 수 있음을 나타내는 DS_CONTROL, 용도가 좀 모호하긴 하지만 [?] 모양의 도움말 버튼을 우측 상단에다 표시하는 DS_CONTEXTHELP 같은 건.. 오늘날까지도 유효하다. 하지만 16비트 시절의 잔재이고 오늘날은 아무 의미 없는 플래그도 있다.

대표적으로 DS_3DLOOK은.. Windows 95/NT4부터는 대화상자들이 처음부터 기본적으로 버튼과 동일한 은색/회색이고 각종 테두리도 양각 음각 입체(?) 효과가 적용되어 나오므로 존재의 의미가 없어졌다.
그리고 DS_LOCALEDIT라는 놈이 있는데.. 얘는 자기 내부의 모든 에디트 컨트롤들이 고유한 데이터 세그먼트가 아니라 기본 제공되는 단일 64K 세그먼트를 공유하게 해서 메모리를 아끼는 플래그이다. 에디트 컨트롤에 많아야 수십~수백 자밖에 들어가지 않는다는 게 보장되면 사용해 볼 만한 옵션이었다. 32비트 이후부터는 아무런 의미가 없어졌지만..

그리고 이렇게 DS_LOCALEDIT 옵션이 적용된 대화상자는 아까처럼 Windows 9x에서 datafile DLL 핸들을 지정해 주더라도 16비트 보정 처리가 행해지지 않기 때문에 오동작· 오류도 발생하지 않았다고 한다.
물론 이 문제는 Windows NT 계열을 넘어 16비트 프로그램 자체가 존재하지 않는 64비트 운영체제의 관점에서는 더욱 무의미한 지나간 옛날 추억이 되었을 뿐이다.

16비트에서 32비트로 넘어갈 때는 16비트 환경에서도 far이니 huge니 하면서 어떻게든 16비트 코드에서 64KB를 초과하는 메모리 영역을 다루려고 애썼으며, 반대로 32비트 주소 공간에서 16비트 코드를 수용하고 실행하려고 온갖 발악을 했었다. 하지만 32비트와 64비트는 서로 완벽하게 격리된 채 공존할 뿐, 상대방 영역을 전혀 건드리지 않는다는 차이가 있다.

이상이다.
여담이지만 날개셋 한글 입력기의 소스를 뒤져 보니.. 어떤 DLL을 datafile 방식으로 읽어들인 상태에서는 그 DLL에 대해서 VerQueryValue 같은 버전 정보 확인 API도 제대로 동작하지 않았다는 주석이 적혀 있다. 그래서 버전 리소스를 수동으로 직접 파싱하는 방식으로 기능을 구현했다.
Windows Vista 이상 또는 심지어 9x 계열에서도 괜찮았으며 2000/XP에서만 문제가 발생했다고 하는데.. 이 역시 LoadLibraryEx 함수의 부작용이 아니었나 추측해 본다. 과거에 일반 로딩과 datafile 특화 로딩은 내부 동작이 여러 모로 차이가 컸던 모양이다.

Posted by 사무엘

2021/10/15 08:34 2021/10/15 08:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1943

Windows에서 리스트뷰 컨트롤은 아이템 기반의 정보를 나열하는 용도로 굉장히 편리하고 유용한 물건이다. 본인은 수 년 전에 얘에 대해서 전문적으로 리뷰를 한 적도 있다. (☞ 이전 글 보기)
하지만 그럼에도 불구하고 얘에 대해서 본인은 Windows 프로그래머로서 다음과 같은 점을 아쉽거나 의아하게 생각한다.

(1) 먼저, ‘작은 아이콘’ 모드라는 게 정체성이 너무 어정쩡 모호한 건 둘째치고라도, 아이템 배치와 관련된 제어가 제대로 안 된다는 것이다.
얘는 작은 아이콘의 옆에 텍스트가 한 줄 붙는다는 점에서는 목록 모드와 매우 비슷하다. 하지만 얘는 그래도 횡대와 종대 아무 방향으로나 align이 되고, group도 적용된다는 것이 목록 모드와의 차이점이다. 즉, 나름의 쓸모도 있다는 것이다.

그런데.. 얘는 아이템의 간격 내지 폭을 어떻게 조절해야 할지 모르겠다.
LVM_SETICONSPACING은 큰 아이콘 모드의 간격이고 LVM_SETCOLUMNWIDTH는 목록 모드의 폭이고, LVM_SETTILEVIEWINFO는 타일 모드에서의 크기이다.
그런데 작은 아이콘 모드는 내가 아는 한 아무리 눈을 씻고 찾아 봐도 이런 API가 존재하지 않는다.

사용자 삽입 이미지

아이템이 이렇게 제멋대로 막장으로 출력되는 걸 막을 길이 없더라.
내가 날개셋 제어판의 외부 모듈 목록에다가 ‘작은 아이콘’ 모드도 추가해 볼까 하다가 이걸 보고는 단념했다.

(2) 그리고 악명 높은 화면 잔상 버그 말이다.
check list를 건드렸을 때 선택 막대가 진해지는 문제는 이미 이전 글에서도 지적했던 바 있다.
스타일을 바꿔서 우회하는 방법도 있긴 하지만 완전한 해결책이 아니며, 원래는 그렇게 우회하지 않더라도 그런 문제가 발생하지 않아야 한다. Windows 10 이전에는 문제가 없었기 때문이다.

사용자 삽입 이미지
(자세히 모드에서 칼럼의 폭을 조절해도 이렇게 해당 칼럼이 덧칠되는 문제가..)

게다가 ‘작은 아이콘’ 모드일 때는 아이템에다가 마우스를 가져가기만 해도 글자가 사라져 버리는 치명적인 문제가 존재한다.
이건 뭔가 내부 계산 로직이 대놓고 잘못된 거나 마찬가지이다. 마소에서도 자체적으로 작은 아이콘 모드를 쓰지를 않기 때문에 버그를 인지하지 못하는 게 아닌가 하는 생각이 들 지경이다.

사용자 삽입 이미지

Windows는 내 컴퓨터 내지 탐색기 UI에서 전통적으로 리스트뷰 컨트롤을 사용해 왔다.
초창기 Windows 95/98은 이 컨트롤이 제공하는 ‘큰 아이콘, 작은 아이콘, 목록, 자세히’라는 네 가지 보기 모드를 그대로 제공했었다.
그러다가 2000/ME에서는 그림이나 문서의 내용 thumbnail이 표시되는 미리 보기 모드가 추가되어 5개가 되었다. 이건 기술적으로는 물론 ‘큰 아이콘’의 확장판이었다.

XP에서는 정체성이 어정쩡한 작은 아이콘 모드가 삭제되고, 제5의 모드인 ‘타일 모드’가 ‘큰 아이콘’이라는 이름으로 등장했다. 기존의 ‘큰 아이콘’은 그냥 ‘아이콘’으로 바뀌었고.. 그러니 전체 개수는 5개로 변함없었다. 원래 있던 작은 아이콘 모드는 마소에서도 완전히 버린 자식 취급하고 있는 게 틀림없다.

사용자 삽입 이미지

그 다음 Vista/7 이후부터는 아이콘의 크기를 매우 다양하게 조절할 수 있게 되었다. 게다가 개념적으로 예전의 ‘작은 아이콘’에 해당하는 모드도 부활했다.
하지만 이건 리스트뷰 컨트롤이 제공하는 ‘작은 아이콘’으로 구현한 게 아니다. 내부적으로는 그냥 ‘타일 모드’에다가 아이콘만 작은 걸 준 게 아닐까..?? 작은 아이콘에서는 이렇게 길다란 아이템을 뒷부분을 생략해서 표시하는 게 가능하지 않다. 타일에서만 가능하다.

사용자 삽입 이미지

그리고 굉장한 뒷북인 놀라운 사실이 있다.
무려 Windows 7부터는 운영체제의 탐색기와 파일 공용 대화상자에서 애초에 리스트뷰 공용 컨트롤을 사용하지 않고 있다. 일반인에게 스펙이 공개되지 않은 별개의 자체 구현 컨트롤을 쓴다..!!

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

아.. 어쩐지~~
(1) 원래 리스트뷰 컨트롤의 '목록' 모드는 이렇게 카테고리라고 해야 하나 그룹 분류 기능을 지원하지 않는다. 목록 모드라는 것은 스크롤바가 세로가 아니라 가로로 난 것을 통해 알 수 있음..
탐색기의 저런 모양은 공용 컨트롤로는 구현 불가능하다. 또한, 저렇게 칼럼마다 폭이 유동적으로 다른 목록도 공용 컨트롤은 지원하지 않는다.

사용자 삽입 이미지

(2) 아울러, '내용'이라는 이 특이한 보기 모드도 기존 리스트뷰 컨트롤로는 구현 불가능하다.
이런 걸 어떻게 구현했을지, 온통 owner draw 개조로 도배했을지 궁금했는데.. 답은 간단했구나. 그냥 자체 컨트롤을 만든 것이었다.;; 영문 위키백과에서도 Windows 7에서부터 바뀌거나 사라진 기능 중 하나로 다음과 같이 언급되어 있다.

An undocumented incompatible Item view control replaces the List view control used in Windows Explorer... The Item view also does not support custom positioning, custom ordering, or hyperlinks, which the Windows Vista list view did support.


아.. 옛날엔 Office 팀에서 파일 열기/저장 대화상자들 자체 제작해서 썼더니만, 그건 없어졌고 이젠 운영체제 셸 팀에서 뷰 컨트롤을 자체 제작해서 쓰기 시작했군...

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

Windows Vista와 7의 탐색기는 외형상 굉장히 비슷해 보인다.
그런데 "Hard Disk Drives / Devices with Removable Storage"라고 아이템을 분류해 놓은 카테고리 부분을 보자.
아이템들을 몽땅 숨기거나 펼칠 수 있는 삼각형 마커가 Vista는 오른쪽 끝에 있는 반면, 7과 그 이후(현재의 10까지 포함)부터는 왼쪽 끝에 있다. 왼쪽 끝에 있으니 무슨 트리 컨트롤을 다루는 것 같은 느낌이 든다.

(3) 마커가 왼쪽에 있을 뿐만 아니라 일반 아이템보다 글자 크기도 더 큰 건.. 내가 알기로 공용 컨트롤 리스트의 기본 옵션만 바꿔서 구현할 수 있지 않다. 비슷해 보여도 Vista는 리스트뷰 컨트롤을 썼지만 7부터는 그렇지 않다는 것이다.

트리와 리스트뷰 컨트롤을 적당히 배합해서 '탐색기' 짝퉁을 만드는 게 Windows UI/셸 프로그램의 정석 코스로 통용되곤 했는데.. 이젠 100% 동일하게 동작하는 탐색기를 이런 식으로 만드는 건 근본적으로 불가능해졌다.

단, 바탕 화면은 그냥 아이콘들만 늘어놓으면 되니 지금도 여전히 기존 리스트뷰 컨트롤 기반이다.
결론을 내리자면.. 마소에서는 자기들도 리스트뷰 컨트롤의 한계를 인지했는지 탐색기에서 얘를 더는 사용하고 있지 않다. 작은 아이콘 모드의 활용성을 좀 강화하고, 목록 모드에서도 그룹 구분을 지원하고, 화면 잔상 버그들을 좀 고쳤으면 좋겠다.

Posted by 사무엘

2021/10/06 08:36 2021/10/06 08:36
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1940

2021년이 된 와중에 문득 생각을 해 보니.. Windows 8과 8.1은 정말 존재감 없이 소리소문 없이 싹 묻히고 사라진 것 같다.
XP, 7 다음에 바로 10이다. 8 계열은 20년 전의 ME만치 망한 건 아니지만 Vista보다는 더 흑역사이다.

사실 난 개인적으로는 옛날에 XP 다음의 Vista는 그 정도로 욕 먹을 퀄리티는 아니었으며, 일부는 오해와 누명을 쓴 것도 있다고 생각한다. 5년 만에 출시됐다 보니 갑자기 시스템 요구 사항이 너무 높아지고 일부 하드웨어와 호환이 깨지고, 사용자 계정 컨트롤이 도입된 게 반발을 일으켰을 뿐.. 그건 시간이 해결해 줄 수 있고 사후 재평가의 여지가 있는 이질감이었다. 7이라도 XP 다음에 바로 갑툭튀였다면 맞았을 질타를 미리 맞아 준 것의 비중이 크다.

그럼 8 계열은 사정이 어땠을까? 잠시 10여 년 전 당시 상황으로 되돌아가 보자.
이때는 나름 격변기였다. 아이폰이니 안드로이드니 하면서 본격적으로 스마트폰이라는 새로운 컴퓨팅 생태계가 조성되고 있었으며, 마소에서도 초대 원로 경영진(빌 게이츠, 스티브 발머)이 대거 은퇴하고 세대가 교체되고 있었다. 사내 분위기가 크게 요동칠 수밖에 없었을 것이다.

이 와중에 얼리어답터들은 “Windows가 7 이후로 2010년대엔 도대체 어디까지 변모할까? 마소는 이 시점에서 어떤 결정을 내릴 것이며 PC와 모바일과의 접목은 어떻게 이룰까?”라고 기대와 주목을 잔뜩 하고 있었다. 그때는 다음 버전이 심지어 재래식 NT 커널을 버리고 처음부터 다시 개발된다는 루머까지 나돌았다. (실제로는 그럴 리가.. NT 커널은 지금 Windows 10에서도 건재함! 없어질 수가 없다)

그랬는데.. Windows 7의 차기 버전인 8은 결과적으로 뭔가 구심점 없고 나사 빠진 듯한 망작이 되었다.
비주얼이 다시 심플해진 것은 그렇다 친다만, 갑자기 시작 메뉴가 없어지고 전체 화면으로 바뀐 것은.. PC와 스마트폰의 크기와 용도의 차이에 대한 고찰이 결여된 자충수였다. 게다가 8은 시작 메뉴 버튼 자체가 없었다~! 이런 제품이 어째 QA나 사용성 테스트를 통과하고 출시됐는지 모르겠다.

더 옛날인 2000년대엔 새 밀레니엄 컴퓨팅을 표방하면서 인텔 Itanium이라는 64비트 프로세서가 출시되었고 Windows는 2000뿐만 아니라 ME도 나왔었는데.. 아시다시피 Itanium과 ME는 대차게 망했다. 그런 것처럼 2010년대를 개막했던 Windows 8은 사용성 면에서 큰 혹평을 받으면서 XP와 7의 아성을 넘지 못했다.

그리고 사실은 스마트폰 OS의 쟁탈전도 매우 싱겁게 끝났다. 지금 다들 경험하시는 바와 같이 안드로이드 아니면 iOS로 양분되었고, 마소는 이 바닥에서의 주도권을 완전히 빼앗겼다. 심지어 웹 브라우저마저 IE 독점은 진작에 물 건너갔고 Edge 브라우저도 자체 개발을 포기하고 크로뮴 엔진에 흡수되지 않았는가?
국내로 치면 모바일에서 주도권을 완전히 주도권을 빼앗기고 삼성 전자와는 정반대의 처지가 된 LG 전자와 비슷하다고 하겠다..;

그러니 8 시절부터 Windows에 도입된 Metro 앱이니 하는 것 역시 폰에서는 볼 일이 없어졌다. Universal이라는 수식어가 무색해졌고 지금은 그냥 스마트폰 같은 큼직한 글자의 UI 엔진이 제공되는 특이한 프로그래밍 플랫폼? 가상 머신?처럼 됐을 뿐이다. NT 커널 없이 새로 개발된다는 엔진이 이런 곳에 적용된 거라고 보면 되겠다.

그렇게 결판이 난 뒤 마소에서는 스마트폰용 OS가 아니라 그냥 기존의 데스크톱용 Windows 10을 스마트폰용 CPU인 ARM64에다가 포팅하는 식으로.. 즉, 자기 정체성을 유지하면서 모바일 환경에다 손을 내민 상태이다. 요즘 PC와 스마트폰의 경계가 많이 모호해지긴 했지만 그래도 PC는 안 쓸 때도 24시간 내내 켜 놓는 물건은 아니며, 떨어뜨려도 될 정도로 튼튼한 물건도 아니라고 여겨진다. 그리고 스마트폰은 프린터나 재래식 하드디스크 같은 장치와는 여전히 친숙하지 않다.

그렇게 Windows 8 내지 8.1은 2010년대 초반 마소의 시행착오가 깃들어 있는 비운의 작품이 됐다.
개인적으로는 Windows 제어판이 Metro 기반인 ‘설정’으로 야금야금 대체된 건 글쎄.. 그냥 reinvent the wheel 삽질 같고 멀쩡한 인도 보도블록을 교체하는 듯한 느낌이다. 기존 제어판에 익숙한 사용자를 괜히 헷갈리게만 하고 말이다.
지금도 키보드의 반복 속도를 최대로 조절하는 것만 해도 설정에서 가능하지 않기 때문에 재래식 제어판으로 가야 한다.

그리고 프로그램의 제목 표시줄에 제목이 가운데 정렬로 표시되었던 Windows는 과거의 16비트 시절 1~3.x 아니면 후대의 8/8.1밖에 없다~!! 거기 말고는 MS Office가 뜬금없이 2007부터 현재까지 가운데 정렬을 하며 따로 노는 중이다.

나머지 Windows 95, NT4부터 7, 그리고 지금의 Windows 10은 다 왼쪽 정렬이다.
Windows의 역사상 32/64비트 시대에 프로그램 제목을 가운데에다 표시했던 버전, 그리고 시작 버튼이 없던 버전이 있었다는 걸 기억하는 사람이 얼마나 있을까? 문득 궁금해진다.

한글 IME의 개발이라는 관점에서 보면 Windows 8 계열은 메트로 앱에 대해서는 키보드 포커스를 얻었을 때 가/A 한영 상태를 IME가 근처에다 팝업 창으로 수동으로 잠깐 띄워주고, 상태가 바뀌었을 때도 그걸 띄우는 수고를 해 줘야 하는.. 약간 원시적인 조치가 필요하던 유일한 운영체제였다.
메트로 앱은 전체 화면에서 실행되는 관계로, 운영체제의 기존 도구모음줄(language bar)의 보조를 전혀 받을 수 없기 때문이다.

이것도 아무리 생각해 봐도 뻘짓이었다. 도구모음줄을 IME가 제각각으로 구현해야 했던 1990년대도 아니고.. Windows 10부터는 메트로 앱도 창 모드로 실행 가능하고, 입력기의 상태를 작업 표시줄(task bar)에서 확인 가능하기 때문에 저렇게 할 필요가 없어졌다. 즉, 저 관행 역시 일회적인 이벤트로 끝났다는 것이다.

이상. 이 글에서는 Windows 8 계열에 대해 주로 다뤘지만, 걔네 말고 Windows라는 운영체제 제품에 대해서 개인적으로 의아하거나 궁금한 점을 더 꼽자면 다음과 같다.

1. 비현실적인 권장 사양

마소에서는 Windows 7 64비트 정도의 시점을 마지막으로.. 자기 운영체제의 동작을 위한 최소 PC 사양과 권장 사양을 더 올려서 기재하지 않고 있다.
7이 램 최소 1GB 이상, 권장 2GB 이상인데.. 이게 Windows 8과 10까지 동일하다. 하지만 이건 개인적으로 생각하기에 완벽한 허위· 과장 광고라고 여겨진다.

도대체 어느 정도 돌아가는 게 최소 사양이고 어느 정도로 여유가 남는 게 권장인지 정확한 정의는 잘 모르겠다만.. 요즘 Windows 10은 램 8GB에서 돌려도 부팅 직후에 크롬 브라우저 창 몇 개나 MS Office 프로그램, Visual Studio 같은 걸 열면 메모리가 거덜난다. 정말 못 해도 16GB는 돼야 넉넉하다는 느낌이 든다.

Windows 7에서야 최소 1GB, 권장 2GB는 수긍할 만하며 램 8GB는 펄펄 날아다니는 용량이다. 하지만 10은 절대 그렇지 않다. 아무리 못 해도 최소 2, 권장 4 이상으로는 수정돼야 하리라 여겨진다. 그리고 요즘도 32비트 에디션이 나오고 있나 모르겠는데.. 걔는 이제 단종시켜도 될 것이다.

2. 서버 제품군의 존재감

Windows의 개인 사용자용 제품군은 아시다시피 2015년부터 10이라는 단일 브랜드 하에서 주기적인 업데이트를 통해 프로그램을 진화(?)시키는 형태로 바뀌었다. 하지만 서버 제품군은 여전히 2008, 2012, 2016 등의 연도 브랜드를 쓰고 있어서 다소 이질적이다. 이런 이원화된 버전 넘버링이 언제까지 계속될지.. 마치 Office 365와 기존 Office 20xx 패키지의 관계만큼이나 궁금해진다.

그리고 더 결정적으로는 기업 같은 데서 서버 제품군을 쓰는 곳이 있긴 한지..?? 이건 마치 Itanium 컴퓨터만큼이나 본인이 태어나서 지금까지 한 번도 구경해 보지 못했다.
왜냐하면 서버 제품군을 쓸 정도의 컴퓨터라면 차라리 유닉스 터미널이 내장돼 있는 리눅스나 맥OS를 쓰고 말지 Windows를 쓰지는 않기 때문이다.

서버 에디션은 원래 Windows NT나 2000까지는 동일 버전의 바리에이션 형태로만 존재해 왔다. 그랬는데 XP 때는 웬일인지 Server 2003이라고 제품명은 물론 내부 버전 번호까지 다르게 붙일 정도로(5.1 vs 5.2) 차별화를 했었다. 그 대신 XP는 개인용 에디션이 홈 vs 프로로 더 세분화됐다.
그 뒤로 서버 제품군은 버전 번호는 클라이언트와 동일하지만 제품명은 저렇게 Server 20xx 식으로 나가고 있다.

3. 자잘한 UI 버그

  • 가끔씩 재래식 TSF 입력 도구모음줄과, 작업 표시줄 쪽의 Windows 10 스타일 입력 상태 아이콘이 동시에 나타나는 버그는 꽤 유명했는데 19xx나 20xx 사이에서 드디어 고쳐진 것 같다. 요즘은 눈에 띄지 않는다.
    하지만 caret (cursor)이 여섯 번 정도 깜빡이다가 깜빡임이 중단되는 버그는 여전히 건재한 듯하다. Windows 3.1 이래로 이런 특이한 현상은 처음이다.

  • 가~~~끔. 컴을 절전 모드에서 꺼냈을 때나 프로그램 창을 전환했을 때.. 이미 띄워져 있는 프로그램 창들이 부르르 다시 그려지면서 깜빡이고 떨리는 현상.
    내 개인용 컴과 회사 컴이 모두 그러는데 좀 보기 거슬린다.
    윈10 초창기에는 이런 현상이 없었고, 특정 컴에서만 그러는 것 같지는 않은데 왜 그러나 모르겠다. (2004대 버전 기준)

  • 컴퓨터에 따라서 케바케이긴 하지만.. 메뉴가 '파일, 편집' 같은 라벨의 우측 하단 ┗ 모양이 아니라 ┛ 왼쪽으로 펼쳐지는 기괴한 현상이 왜 발생하는지 모르겠다. 아랍권도 아닌 멀쩡한 한국어/영어에서 말이다.
    로컬라이즈나 사용 접근성을 위해 필요한 옵션이라면 제어판에라도 옵션을 정식으로 갖다 놓든지..? 저런 동작이 왜 존재하는지도 모르겠고, 또 사용자의 동의 없이 불쑥 바꿔 놓고는 원상복구를 왜 레지스트리 수정을 통해서 해야 하는지 모르겠다.

Posted by 사무엘

2021/06/02 08:36 2021/06/02 08:36
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1894

1. 비트 연산 관련 버그

프로그래머가 살면서 설마 컴파일러의 버그를 볼 일이 얼마나 될까? 이건 마치 버스· 트럭· 택시 등 운전으로 먹고 사는 기사 아저씨가 잘 가다가 차량의 엔진 결함이나 급발진을 경험하는 것만큼이나 끔찍한 경험일 것이다.

본인은 최적화 옵션을 빡세게 주고 나면 Visual C++ 컴파일러가 비트 연산 쪽으로 유난히도 말귀를 못 알아먹는 현상을 종종 목격했다.
7년쯤 전에 VC++ 2010 기준으로 (1) bit rotate 연산을 <<, >> | 따위로 구현한 게 제대로 동작하지 않는 것을 목격했다. 그 함수만 #pragma를 줘서 최적화를 강제로 꺼야 오류가 발생하지 않았다.

그리고 2019년쯤에는 (2) WORD, BYTE 따위를 비슷한 연산으로 한데 합쳐서 DWORD를 만들려고 했는데.. 이것도 변수 내용을 강제로 로그를 찍으면 문제가 없지만 간단하게 값만 되돌리게 하면 틀린 값이 돌아왔다.
인라인 함수, 매크로 함수, 최적화 강제 해제 등 별별 방법을 써도 소용없어서 결국은 무식하게 memcpy로 값을 오프셋별로 강제 복사해서 문제를 회피해야 했다.

그 뒤, 19.5.x급으로 그 당시로서는 최신 업데이트가 적용됐던 Visual C++ 2019에서 더욱 황당한 일을 겪었다.
내가 하고 싶은 일은 8비트 char 값을 그대로 부호 없는 형태로만 바꿔서.. 즉, -3을 253으로만 바꾼 뒤 다른 산술 연산 처리를 하는 것이었다. 그런데 (3) 컴파일러가 말귀를 못 알아듣고 숫자를 32비트로 취급하면서 앞에 0xFFFFFF00를 제멋대로 붙였다.

숫자는 내가 기대한 것보다 엄청나게 큰 값으로 바뀌었으며, 프로그램은 이 때문에 오프셋 계산을 잘못해서 메모리 오류가 발생했다. 내가 아무리 강제 형변환 연산을 집어넣어 줘도 오류는 없어지지 않았다. 계산값에다가 원래는 할 필요가 없는 &0xFF 필터링을 강제로 하거나, 이 역시 최적화를 꺼야만 오류가 사라졌다. 이런..

이 세 사례는 모두 비트 연산 + 최적화와 관련된 컴파일러의 난독증이라는 공통점이 있었다. 2010으로 32비트 코드를 빌드하던 시절이나, 2019로 64비트 코드를 빌드하던 시절이나 마찬가지이니.. 딱히 버전과 아키텍처를 가리지도 않는 것 같다.

더 자세한 정황을 나열하지 못하는 이유는 이것들이 전부 방대한 회사의 코드를 취급하다가 발생한 일이기 때문이다. 그래서 동일 문제를 재연할 수 있는 최소한의 케이스를 따로 분리할 수가 없다. 그 함수만 텅 빈 프로젝트에다가 떼어내서 돌리면 당연히 문제가 발생하지 않는다.
하지만 동일 코드를 사용하여 macOS, 안드로이드 등 타 플랫폼에서 돌아가는 제품에서는 버그가 발생하지 않으니 이건 일단 Visual C++만의 문제라고 봐야 할 듯하다.

2. UTF-8 지원 여부와 미스터리한 오동작

Windows는 전통적으로 ANSI 인코딩(?) 천국이던 운영체제였다. 그래서 유니코드 자체는 진작부터 지원했지만 UCS-2 내지 UTF-16 같은 별도의 2바이트 단위 인코딩 형태로만 지원하는 것을 선호했다. 1바이트 단위 인코딩인 UTF-8의 형태로 지원하는 것에는 대단히 보수적이고 인색했다.

오죽했으면 Visual C++이 취급하는 리소스 스크립트 *.rc라든가 resource.h의 기본 포맷도 유니코드 기반으로 바뀌긴 했는데.. UTF-8이 아니라 UTF-16으로 바뀌었다. 거 참..

그래도 세월이 흐르니 마소에서도 대세를 거스를 수 없는지라, 명령 프롬프트에서 제한적이나마 65001 UTF-8 코드 페이지를 지원하기 시작했다. Windows 10 19xx 버전부터는 메모장이 기본으로 지정하는 텍스트 저장 인코딩이 UTF-8로 바뀌기도 했다.
심지어 Visual C++ 컴파일러 역시 UTF-8 인코딩의 소스 코드를 인식하기 시작했다. 단...!! 이건 2% 부족한 아쉬운 면모가 좀 있다.

바로.. 파일 앞부분에 BOM이 있을 때만 UTF-8로 인식한다는 것이다. 그렇지 않으면 그냥 ANSI이다.
소스 코드의 인코딩을 강제로 지정하는 옵션이 소스 코드 내부에 #pragma 같은 형태로 좀 있었으면 좋겠지만 그렇지는 않다. #pragma code_page라는 게 있긴 한데, C 문법을 일부 빌려 온 리소스 스크립트에만 쓰인다.
파일 내부 대신, 컴파일러의 옵션으로 /source-charset:utf-8 요런 게 존재하고, 줄여서 그냥 /utf-8이라고만 해도 된다.

생각해 보면 설정이 하나만 있는 것으로 충분하지 않다. 소스 코드 자체는 인코딩이 UTF-8인데 그 안에서 L로 둘러싸이지 않은 "한글"이라는 문자열 리터럴은 KS X 1001로, 즉 길이가 4바이트이고 전체 크기가 5바이트인 문자열을 의도한 것일 수 있다. 그렇게 실제로 의도된 인코딩을 지정하는 옵션은 /execution-charset이라고 따로 있으며, /utf-8은 두 charset을 모두 utf-8로 지정한 것과 같은 효과를 낸다.

그런데 컴파일러는 그렇게 인식시키면 되지만 에디터의 동작에 여전히 함정이 남아 있다.
BOM도 없고 딱히 한글· 한자 같은 문자도 없이 모든 문자열이 간단한 1바이트 숫자· 알파벳 따위로만 구성된 평범한 파일의 경우, Visual Studio IDE는 얘를 기본적으로 ANSI 인코딩 파일로 간주한다. 그 파일에 나중에 한글· 한자가 부주의하게 추가된다면 인코딩이 영락없이 잘못 지정될 수 있다. 이 기본 동작을 고치는 방법이 있는지는 난 아직 모르겠다.

그런데 그렇다고 BOM을 넣어 버리면..?? BOM은 Windows 동네에서나 통용되지, 리눅스 등 타 운영체제에서는 그냥 민폐 덩어리인 문자이다. 소스 파일의 앞에 저런 문자가 떡 있으면 컴파일러가 잘못 먹고 체하는 수가 있다.
그러니 한 소스를 여러 플랫폼에서 공유하는 경우, 모든 코드의 인코딩은 그냥 닥치고 BOM 없는 UTF-8로 통일하는 게 안전하다. 이 문제에 관한 한은 Visual C++이 타 빌드 툴들의 표준 관행에 맞춰 줘야 한다. BOM는 이식성을 저해하기 때문이다.

모종의 이유로 인해 Visual C++에서 소스 코드의 인코딩이 잘못 인식되면 빌드 과정에서 깨진 문자가 있다고 C4819라는 경고가 발생한다. 깨진 문자가 주석 내지 조건부 컴파일에 걸려서 어차피 빌드되지 않는 영역에 있을 때는 저게 딱히 문제될 게 없다. 단지, 문자열 리터럴 내부에 들어있던 한글· 한자가 깨지면 심각한 문제가 될 것이다.

그런데 내 경험상.. 주 번역 단위에 해당하는 소스 파일과, 걔가 인클루드 하는 헤더 파일 간에 인코딩이 다를 때도 상당히 골치 아픈 문제가 발생하곤 했다.
C4819 말고도 C4828이라고 파일의 줄 수가 아닌 오프셋 운운하면서 굉장히 기괴한 경고가 떴다. 최신 컴파일러에서는 이 경고가 삭제되었는지 조회되지도 않더라.

그리고 정말 믿을 수 없지만 컴파일러가 완전히 뜬금없는 에러를 내면서 동작을 멈췄다. 실제로 문법 오류가 전혀 없는 구문에서도 쓸데없는 에러가 발생했으며, 그 소스 파일에 실제로 존재하지 않는 칸 번호를 언급하기도 했다.
이렇게만 말하는 나도 황당하고 읽는 분들도 상황을 받아들이지 못하시겠지만.. 내가 실제로 겪은 상황이 저랬다.

이 역시 회사에서만 겪었기 때문에 정확· 엄밀하게 재연 케이스를 만들지는 못하겠다. 아까 얘기했듯이 (1) /utf-8 옵션을 global하게 준 상태에서 소스와 헤더 파일들의 인코딩이 충돌 난 것, 그리고 아마도 (2) precompiled 헤더를 쓰는 소스와 그렇지 않은 소스가 한 프로젝트 안에서 좀 뒤섞여 있는 것, (3) namespace와 using이 좀 복잡하게 얽혀서 인텔리센스도 오락가락 하는 상황인 것이 다 조금씩 영향을 주지 않았을까 생각된다.

이 난국은 모든 코드의 인코딩을 BOM 없는 UTF-8로 정리하고, 모든 코드에다가 한글로 dummy string을 만들어서 Visual Studio IDE가 파일을 ANSI (cp949) 인코딩으로 잘못 저장하는 일이 없게 조치를 취함으로써 해결되긴 했지만..
그때 그 문제가 왜 발생했으며 그 상황을 어떻게 재연할 수 있는지는 모른 채 미스터리로 남게 되었다.

회사에서는 길지 않은 기간 동안에도 이 정도의 이상한 버그를 몇 차례 경험했는데.. 개인적으로 날개셋 한글 입력기를 20여 년 가까이 만들어 온 동안은 컴파일러의 버그를 경험한 적이 거의 없다는 것이 참 신기하다. IDE야 불필요하게 다운되거나 뻗는 버그를 여럿 경험했지만 컴파일러가 문제를 일으킨 적은 없었다.
모든 코드가 깔끔하게 KS X 1001 레거시 인코딩이고, 회사 코드보다는 규모가 작고 모듈 구조가 깔끔하고, 전부 precompiled 헤더를 사용하기 때문이 아닌가 생각한다.

소스 코드의 인코딩이 UTF-8이 아니거나, UTF-8이더라도 앞에 BOM이 있는 것 자체를 경고로 처리하는 건 너무 과격할까? 그리고 #include에서 경로 지정을 /가 아닌 \로 한 걸 경고로 처리하는 옵션도 있으면 좋겠다. 이런 건 Windows 환경에서나 통용되지 밖에서는 전부 민폐 에러 요인이 되기 때문이다. 본인이 직장의 공동 작업 과정에서 종종 실수했던 적도 있는 사항들이다.

3. 인텔리센스의 오동작

끝으로, 이건 실제로 생성된 exe/dll의 동작과 관계 있는 치명적인 문제는 다행히 아니지만.. Visual C++ IDE가 텍스트 에디터에서 사용하는 인텔리센스도 일부 특이한 상황에서는 말귀를 못 알아듣고 오동작할 때가 있다.

본인이 겪은 경우는 클래스(가령 A)의 선언 내부에 MFC의 DECLARE_DYNAMIC 같은 복잡한 custom 매크로를 넣은 뒤, 곧장 private/public/protected 같은 접근 권한 지정자가 나올 때이다. 그러면 인텔리센스가 그 뒤에 이어지는 멤버 및 내부 enum/class (가령 B) 따위 선언을 파싱을 제대로 못 한다. ClassView를 보면 A의 멤버 목록에 B의 멤버들이 잘못 표시되며, B 선언 이후에 등장하는 A의 진짜 멤버들은 전혀 인식되지 않는다.

ClassView뿐만 아니라 텍스트 에디터에다 불러온 소스 코드에서도 각종 경고와 에러 밑줄이 A의 멤버들이 누락된 것처럼 쭈루룩 뜬다.
그렇기 때문에 A 클래스의 구현부에서는 인텔리센스와 자동 완성, 심벌 위치 조회 같은 기능들을 활용하지 못하면서 코딩을 꽤 불편하게 해야 한다.

이런 초보적인 문제는 Visual C++ 6 ncb 시절에나 보던 게 아니었나? 왜 발생하는지 모르겠다.
최신 업데이트를 적용한 Visual C++ 2019에서도 동일하게 발생한다. 본 컴파일러가 아니라 인텔리센스 컴파일러이니 딱히 특정 Visual C++ 컴파일러 툴킷만의 문제도 아닐 것이다.

뾰족한 해결책은 없고, 인텔리센스를 헷갈리게 하는 그 문제의 매크로를 클래스 선언의 맨 앞이 아니라 맨 뒤로 옮김으로써 문제를 회피할 수 있었다. 흠...

4. 도킹 하다가 뻗음

역시 컴파일러가 아닌 IDE 얘기이고, 옛날 버전에서만 발생하는 문제이기 때문에 지금 큰 의미는 없지만..
Windows 10 19xx대 버전부터인가 Visual Studio 2013 (그리고 아마 2015도)에서 각종 문서 편집 창이나 보조 윈도우(출력, 속성, 디버그 등등)를 어디에든지 도킹을 해서 붙이면 프로그램이 뻗어 버린다.

2010이 언제부턴가 실행될 때 Microsoft.Vsa.tlb 파일이 없다는 에러를 내는 것과 비슷한 현상인 것 같다. 그래도 얘는 정상 실행은 되고 프로그램 사용에 문제가 없는 반면, 저건 창을 내 마음대로 배치할 수 없게 만들고 프로그램이 뻗기까지 하기 때문에 상당히 심각한 문제이다.
저런 단순 UI는 운영체제건 VS건 한번 만들고 나서는 고칠 일이 없는 기능일 것 같은데.. 둘 다 내부적으로 뭘 건드리길래 이런 부작용이 발생하는 걸까..??

하긴, 더 옛날엔 Visual Studio 2005도 Windows Vista에서 실행하려면 sp1에다가 Vista 지원 추가 패치까지 설치해야 겨우 돌릴 수 있었다. 아래아한글 2005와 2007도 Vista 이후의 운영체제에서 실행하려면 업데이트부터 대판 설치해야 했었으니 이런 예가 전혀 없지는 않구나.

어떤 프로그램이 후대의 운영체제에서 단순히 GUI나 외형의 glitch 정도가 발생하는 걸 넘어 아예 뻗고 실행이 안 되는 건.. 대부분 보안 강화 때문이지 싶다. 문서화되지 않고 미래에 얼마든지 달라질 수 있는 특성이나 동작에 의존하게 프로그램이 만들어진 경우야 걔의 잘못이겠지만, 흔한 경우는 아닐 것이다.

Posted by 사무엘

2021/05/19 08:35 2021/05/19 08:35
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1889

프로그래밍에서 메모리를 가리키는 포인터라는 건.. 그 특성상 돌아가는 컴퓨터의 machine word와 크기가 동일하다. 하지만 현실에서 포인터(= 메모리 주소)를 구성하는 모든 비트가 골고루 쓰이는 일은 몹시 드물었다.

먼저, 컴퓨터의 실제 메모리 양이 포인터가 가리킬 수 있는 범위보다 훨씬 적다. Windows의 경우, 32비트 시절에는 user mode에서는 대부분의 경우 포인터의 상위 비트가 언제나 0이었던 것이 잘 알려져 있다(하위 2GB까지만 사용).
하물며 64비트는 공간이 커도 너무 크기 때문에 가상 메모리 관리 차원에서도 아직은 40~48비트까지만 사용한다. 상위의 무려 16비트가량이 쓰이지 않는다는 것이다. 램이 32GB여도 겨우 35비트면 충분하니까..

가난하고 배고프던 20세기 16비트 시절에는.. 반대로 포인터 하나만으로 겨우 몇백 KB~수 MB 남짓한 메모리도 한번에 다루지 못했다. 그래서 far 포인터니 huge 포인터니 별 삽질을 다 해야 했는데 그때에 비하면 지금은 격세지감이 따로 없다.

저렇게 상위 비트뿐만 아니라 하위 비트도 마찬가지이다. padding, align 같은 이유로 인해, 메모리 할당 함수의 포인터 리턴값이 홀수가 될 일은 일반적으로 없다. 아니, 겨우 2의 배수가 아니라 4나 8의 배수가 될 수도 있으며, 이 경우 하위 2~3개 비트도 0 이외의 값을 가질 일이 없게 된다.

그러니 포인터를 저장하는 공간에서 0 이외의 값이 들어올 일이 없는 비트에다가 자신만의 정보를 넣는 꼼수를 부리는 프로그램이 예로부터 줄곧 존재해 왔다.
이거 무슨 변태 같은 짓인가 싶지만.. 이제 막 32비트로 넘어가긴 했지만 아직 가정용 컴퓨터들의 평균적인 메모리 양이 수 MB대밖에 안 됐던 시절이 있었다. 이때는 메모리가 부족해서 하드디스크 스와핑이 일상이었다. RAM을 1바이트라도 더 아끼는 최적화가 필수였다.

가령, 다재다능한 자료구조인 빨강-검정 나무를 생각해 보자.
노드의 색깔을 나타내는 겨우 1비트짜리 정보를 위해서 굳이 bool 멤버를 추가하는 건 굉장한 낭비라는 생각이 들지 않는가? 단 1비트 때문에 구조체 패딩까지 감안하면 무려 2~4바이트에 달하는 공간이 매 노드마다 허비되기 때문이다.
안 그래도 노드의 내부엔 left/right 같은 딴 노드 포인터가 있을 것이고, 포인터 내부에 쓰이지 않는 1비트 공간이 있으면 거기에다 색깔 정보를 박아 넣고 싶은 생각이 들 수밖에 없다. 비트필드와 포인터의 union 써서 말이다.

물론, 그렇게 0으로만 채워지던 공간을 운영체제에서도 나중에 유의미하게 사용하기 시작하면.. 그 꼼수 프로그램은 재앙을 맞이하게 된다.
대표적인 예로 마소에서는 32비트 기준으로 사용자:커널이 통상적인 2GB:2GB가 아니라 3GB:1GB로 주소 공간을 분할하는 기능을 Windows에다가 추가했다.

이러면 사용자 모드의 포인터도 2GB가 넘는 영역에 접근할 수 있으며 최상위 비트가 1이 될 수 있다. 그런데 포인터의 최상위 비트를 자기 멋대로 사용하고 있는 프로그램은.. 뭐 메모리 뻑나고 죽을 수밖에 없다.
64비트 환경에서는 겨우 1비트가 아니라 상위 word 전체를 다른 용도로 전용해도 당장 이상이 없으며 이 추세가 앞으로 몇 년은 가지 싶다. 컴퓨터의 램이 256~512GB나 1테라까지 간다면 모를까..

요즘 컴퓨터야 메모리가 워낙 많고 풍족하니, 굳이 저런 꼼수를 동원하는 프로그램은 별로 없을 것이다.
하지만 저 때가 되면 또 꼼수 부리는 말썽꾸러기 프로그램과의 호환성 때문에 주소 공간을 옛날처럼 상위 16~32GB까지로 봉인하는 옵션 같은 게 또 등장할지도 모른다.;;; HIGH_DPI_AWARE처럼 LARGE_ADDRESS_AWARE 시즌 2 말이다.

여담이지만 Windows의 경우, 실행 파일은 시작 주소가 언제나 64KB의 배수 단위로 부여되기 때문에 HINSTANCE/HMODULE은 아래쪽은 무려 word 덩어리가 언제나 0이 된다. 이 특성을 이용해서 운영체제의 LoadLibraryEx 함수도 하위 몇 비트를 자기 마음대로 활용하기도 한다.

※ 나머지 메모

(1) unsigned 타입에 대해서 단항 연산자 -를 적용해서 -a 이런 값을 구하는 코드를 우연히 보고는 개인적으로 신박하다는 생각이 들었다. 흐음~ Visual C++의 경우 이건 원래 경고인데, 요즘 버전에서는 더 엄격하게 에러로 처리하는가 보다.
-a는 2의 보수의 특성상 ~a+1과(비트 not보다 1 크게) 완전히 동일한 효과를 내며, 앞에 0을 붙여서 이항 연산자로 만들어도 에러를 회피할 수 있다.

(2) ANSI C에서는 함수의 prototype을 선언할 때 매개변수 리스트에 타입만 써 넣고 이름을 빼먹으면 안 된다는 걸 최근에야 알게 됐다.
아니 도대체 왜..? 거기서 매개변수의 이름은 거의 잉여 옵션에 불과할 텐데.. void func(int);라고만 쓰면 틀리고 void func(int x);라고 아무 이름이라도 붙여야 된다는 것이다.
이건 먼 옛날에 C언어에서 void func(a) int a; 같은 구닥다리 문법이 쓰이던 시절의 잔재인 것 갈다.

Posted by 사무엘

2021/05/15 08:35 2021/05/15 08:35
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/1887

« Previous : 1 : 2 : 3 : 4 : 5 : ... 20 : Next »

블로그 이미지

그런즉 이제 애호박, 단호박, 늙은호박 이 셋은 항상 있으나, 그 중에 제일은 늙은호박이니라.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/03   »
          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:
2620003
Today:
3002
Yesterday:
1544