대학원에 간 뒤부터 컴퓨터나 프로그래밍 쪽 글은 눈에 띄게 줄고, 확실히 언어 쪽 글이 늘었다. 물론 철도 글은 예나 지금이나 비슷한 빈도로.. ㅋㅋㅋ
그래도 언젠가 한 번쯤 이런 글을 올리고 싶었다.

비주얼 C++ 4.2는 본인이 고등학교에 진학하고서 도스용 프로그램으로 정올 공모를 한 번 마친 후, 그때부터 공부하기 시작한 툴이다. 이때 윈도우 API, MFC, 심지어 C++ 객체 지향 개념까지 전부 뭉뚱그려서 동시에 공부를 시작한 셈이었다.
그로부터 10년이 지난 지금까지도 비주얼 C++은 나의 소중한 친구이고 내 마음의 고향이다. ^^;; <날개셋> 한글 입력기 1.0이 VC++ 4.2로 개발되었다.

참고로 4.2 버전은 4.0 버전이 몇 차례 마이너 업그레이드를 거친 것이었다. 4.2가 따로 4.0처럼 별도의 패키지로 출시되지는 않았으며, MSDN 구독자에게만 비공식적인 경로로 배포되었다고 한다. 이 점에서 4.2는 윈도우 95로 치면 마치 OSR2 업그레이드 에디션과 비슷한 위상이다.
지금은 윈도우든 개발툴이든 오피스든 MS에서 나오는 제품들은 다 서비스 팩이라는 개념으로 업데이트 방식이 통일되었지만 말이다.

그러나 4.2라는 버전은 굉장히 의미가 크다. 윈도우 운영체제가 시스템 차원에서 MFC 라이브러리의 하위 호환성을 보장해 주고 있는 최후 버전이 4.2이기 때문이다. 그 이름도 유명한 MFC42.DLL이다. MFC40.DLL이 아니다.

사용자 삽입 이미지

(<날개셋> 한글 입력기 1.0이 바로 윈도우 95 + 800*600 화면 + 비주얼 C++ 4.2 환경에서 개발됐다.)

그 전부터도, PC 환경에서 이제 윈도우가 대세로 넘어갔으니 윈도우 프로그래밍을 공부하려고 마음을 안 먹은 건 아니었다. 그러나 그때는 비주얼 베이직이나 델파이, C++ 빌더처럼 RAD 툴 수준에 머물러 있었다. 그러던 차에 접한 비주얼 C++은 굉장히 충격적이었다.
이 툴로는 다른 툴과는 달리, 운영체제와 직통으로 대화하고 다른 이상한 런타임이 필요하지 않은 가볍고 빠른 프로그램을 만들 수 있었기 때문이다.

비주얼 C++ 4.2 자체도 요즘 최신 버전에 비하면 정말 미치도록 작고 가볍다. ^^;;; 도움말은 RTF 기반이었고, C/C++ + 윈도우 API + MFC 레퍼런스를 전부 합해서 용량이 150MB 남짓밖에 안 했다. 그 당시 왼쪽의 class view는 실시간 업데이트가 되지 않았으며, 소스 코드를 저장해야 업데이트 됐다. ^^;;;
또한 프로그램 파일들이 압축되지 않은 형태로 CD에 그대로 들어있었기 때문에, 프로그램을 설치하지 않고도 CD에서 곧바로 MSDEV.EXE를 실행해서 프로그램을 실행할 수도 있었다. 물론 전기능을 제대로 활용할 수는 없었지만.

한동안 MS 오피스와 비주얼 C++은 요즘 서울 버스 색깔처럼 빨-노-초-파가 어우러진 4색 고리 모양 아이콘을 사용해 왔다. 4.2도 그랬다. 그런데 2010 버전은 둘 다 단색 아이콘으로 돌아갔으니 이 또한 흥미로운 점. 오피스는 노랑-주황색 사각형 창 4개 모양이 됐고, 비주얼 스튜디오(C++ 포함)는 보라색 ∞ 모양이 돼 있다.

세월이 흘러, 현재 <날개셋> 한글 입력기는 9개 모듈을 모두 비주얼 C++ 2008로 개발되는 중이다. 그러나 타자연습과 파워업은 적극적인 개발보다는 유지 보수만 하는 만큼 여전히 2003을 사용 중이다.

Posted by 사무엘

2010/10/29 16:10 2010/10/29 16:10
,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/400

아래아한글이 윈도우 비스타부터 키매크로를 지원 안(못) 하는 이유는?
(관련 글: 아래아한글의 키매크로 )
이것과 관련하여 갑자기 떠오른 생각이 있어서 글을 남긴다. 본인은 한컴에 입사한 개발자도 아니고 아래아한글의 소스 코드를 본 적도 없지만, 본인이 보기에 이것 때문이 거의 확실하다.

윈도우 훅 중 WH_JOURNALRECORD와 WH_JOURNALPLAYBACK 훅이 비스타에서부터는 보안 강화를 이유로 차단되었기 때문이다. MSDN을 보면 알 수 있지만 저건 완전 키매크로를 구현하라고 만든 훅이다.
(관련 글: 훅킹 프로그래밍 )
실패 사유를 나타내는 에러 코드는 5(access denied)가 들어온다.
심지어는 프로그램을 관리자 권한으로 실행해도 차단은 풀리지 않는다. 이건 좀 너무 심하지 않았나?

물론, 키매크로를 구현하는 방법이 저 훅만 있는 건 아니기 때문에 다른 키보드/마우스 훅을 사용하여 동일 기능을 우회 구현할 수도 있다. 하지만 이래저래 개발자에게는 귀찮고 짜증나는 일이 하나 더 생긴 게 틀림없다.

참고로 이렇게 차단을 하는 주체는 사용자 계정 컨트롤(UAC)이다. 그렇기 때문에 이걸 끄면 비스타도 XP와 완전히 동일하게 동작은 한다. 하지만 보안상으로는 위험하기 때문에 개발자가 사용자에게 UAC를 끌 것을 강요해서는 안 된다.
UAC는 안전을 위해 프로세스 간 의사소통을 하는 메커니즘에도 상당한 제약을 부과했다. 단적인 예로, 권한이 낮은 프로그램이 권한이 높은 프로그램에게 임의의 메시지를 보낼 수 없다.

이미 아시는 분도 있겠지만 <날개셋> 한글 입력기는 5.3부터 입력 패드라는 프로그램을 제공하고 있다. 윈도우 IME 훅킹을 통해, 정식 외부 모듈이 아니면서도 외부 모듈의 동작을 흉내 내어 주는 프로그램인데, 이 프로그램을 제대로 사용하려면 관리자 모드로 실행해 줘야 한다. 그렇지 않으면 입력 패드보다 권한이 높은 프로그램(관리자 권한으로 실행된)에다가는 글자 입력을 할 수 없게 된다.

그런데, UAC 하에서도 예외적으로 실행 중인 모든 프로세스의 윈도우에다가 메시지를 보낼 수도 있고 심지어 봉인된 WH_JOURNAL* 훅까지 구사할 수 있는 만능 권한 등급이 없는 건 아니다. MS에서는 대표적인 예 중 하나로 장애인의 UI 접근성 개선을 위해 쓰이는 프로그램에게나 그런 만능 권한을 주고 있다.
예를 들어 화면 키보드 같은 프로그램이야 권한을 초월하여 아무 프로그램에게나 문자 입력 메시지를 전달할 수 있어야 하고 심지어 운영체제 로그인 UI에도 존재해야 하기 때문이다. (ID/패스워드 입력할 때)

단지 그 권한을 얻기가 더럽게 까다로워서 문제이다. 만능 권한을 얻을 수 있는 프로그램은 사용자의 컴퓨터에 반드시 관리자 권한으로 정식으로 설치되어 EXE 파일이 Program Files 같은 특정 경로에만 존재해야 한다. 잘 알다시피 UAC 하에서는 평소에는 Program Files 디렉터리 밑에다가 파일을 만들지도 못한다.

또한, 결정적으로 EXE 내부에 디지털 서명이 되어 있어야 한다. 과거에 마치 ActiveX를 배포할 때 안전한 코드 인증을 받는 것처럼 말이다. 이거 서명을 받으려면 $이 필요하고, 무엇보다도 사업자 등록 번호가 있어야 한다. 즉, 듣보잡 개인 개발자는 서명을 받지도 못한다는 뜻.
따지고 보면 <날개셋> 입력 패드도 일종의 화면 키보드처럼 UI 접근성 개선 프로그램의 성격이 강하기 때문에 저런 권한이 있어야 할 텐데... 그러지는 못하고 있고 그저 사용자가 알아서 충분히 높은 권한을 줘서 프로그램을 실행해 주길 바랄 뿐이다.

이래저래 보안이 안전을 빌미로 세상을 많이 복잡하게 만들고 있다.
(관련 글: 프로그램의 권한 )

Posted by 사무엘

2010/09/13 18:34 2010/09/13 18:34
, , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/372

※ CreateFont

받는 인자가 무려 14개나 되어, Win32 API 전체를 통틀어 손꼽힐 정도로 받아들이는 인자가 많다. MSDN 레퍼런스 없이는 함부로 꺼내 쓰지도 못한다.
그렇다고 해서 딱히 포인터를 전달한다거나, 결과값을 받는다거나 하는 것도 아니고 인자들은 다 단순한 int 아니면 문자열들이다. 이 값들을 LOGFONT라는 구조체에다 담아서 한꺼번에 전달하는 CreateFontIndirect라는 함수가 별도로 있기도 하다.

하지만 실제로 쓰이는 인자는 글꼴 이름과 크기(가중치 100), 그리고 아주 가끔 빌드/이탤릭 여부(가중치 20) 정도가 진짜 전부이다.
전문적인 워드/그래픽 프로그램이 아니고서야 장평값이 다르다거나 기울여진 상태로 글자를 찍을 일이 있기나 할까? 운영체제가 기본으로 지정한 퀄리티(안티앨리어싱 여부) 말고 다른 걸 지정할 일도 거의 없기는 마찬가지. 좀더 간단한 형태의 함수가 있었으면 좋겠다. MFC의 CFont::CreatePointFont()처럼 말이다.

※ CreateProcess

인자 개수 10개. 프로그램을 실행하는, 좀더 정확히 말하면 프로세스를 생성해 주는 가장 저수준 함수이다.
실생활에서는 파일 이름과 매개변수(가중치 100)만 있으면 거의 다 통용되고, 거기에다 초기 시작 디렉터리와 윈도우 초기 배치 방식을 나타내는 SW_* 값 정도만 있으면 될 것이다(가중치 20).

하지만 이 함수 역시 사용하기는 무진장 까다롭다. 파일 경로를 나타내는 버퍼는 read only여서는 안 되고 write 가능한 영역에 있어야 한다. 운영체제가 이 문자열을 그대로 strtok 스타일로 tokenize를 했다가 다시 원래 형태로 되돌려 주기 때문이다. null 문자를 잠시 삽입하므로 이는 버퍼를 수정하는 행위임.
디버거 붙일지 여부, 각종 보안 설정, 환경 변수 값, 심지어 멀티모니터 환경에서 프로그램을 초기에 띄울 모니터 위치 등등.. 별별 정보를 다 지정 가능하기 때문이다.

게다가 리턴값으로는 새로 생긴 프로세스 및 스레드의 식별자와 핸들 같은 것도 돌아오는데, 그 핸들은 이 함수를 호출한 프로세스가 알아서 Close 해 줘야 된다. 여간 손이 많이 가는 게 아니다.
MS는 프로그램을 실행할 때 16비트 시절의 잔재인 WinExec 함수를 사용하지 말고 이 함수를 사용할 것을 권하나, WinExec의 간편함(달랑 명령줄과 윈도우 배치 방식만 넘겨주면 됨!)의 유혹을 뿌리치기는 쉽지 않을 것이다. 사실 WinExec도 이제는 내부적으로 CreateProcess를 호출하는 방식으로 실행된다.

※ CreateFile

인자 개수 7개. C 표준 함수에 있는 fopen처럼 파일 이름과 용도(많지도 않다. 읽기 아니면 신규 생성)만 있었으면 좋겠지만, 역시 운영체제 API이다 보니 보안 관련, 파일 공유 여부 같은 잡다한 정보들이 엄청 많이 들어간다. 파일을 그저 문서의 열기/저장 기능 구현 용도로나 쓴다면 대부분의 세부 옵션들은 필요하지 없으며, 그냥 디폴트만 넘겨 주면 된다.
사실 이 함수는 디스크 상의 파일뿐만 아니라 파일의 형태로 표현 가능한 각종 운영체제 오브젝트들을 생성할 수도 있는 상당히 다재다능한 녀석이다. 피타고라스는 세상만물이 수라고 말했다면, 유닉스에서는 모든 게 파일이라고 한다. 윈도우 운영체제도 그런 철학이 아주 없지는 않다.

※ GetOpenFileName

우리에게 친근한 파일 열기 대화상자를 꺼내 주는 함수. 이제 얘는 인자로 일일이 입력 정보를 받는 방식을 애초부터 포기하고, 대신 크고 아름다운 구조체만을 받는 형태이다. C++로는 응당 클래스를 만들어 쓴다. MFC의 CFileDialog처럼.

하지만 실제로 자주 쓰이는 정보는 열기/저장 여부, 시작 디렉터리, 파일 포맷 필터 말고 있나? (가중치 100)
거기에다가 아주 가끔씩 파일 복수 선택 여부라든가 '읽기 전용으로 열기' 같은 자잘한 옵션만.. (가중치 30)
이렇게 굳이 구조체 만들 필요 없이 간단하게 실행이 끝나는 함수도 좀 있었으면 좋겠다는 생각을 꽤 자주 하곤 했다.

※ TaskDialogIndirect

너무나 클래식한 API인 MessageBox 함수를 대체하는 새로운 UI 대화상자 함수로, 윈도우 비스타에서 처음으로 추가된 걸로 잘 알려져 있다.
다른 건 '메시지 박스' 그대로 쓰면 되는데 "다음부터 이 메시지 표시하지 않음" 체크 같은 딱.. 2% 아쉬운 면모만 보충하고 싶을 때... 이 함수가 가히 구세주이다. 참고로 이 함수를 쓰려면 공용 컨트롤 6.0 매니페스트가 필수라는 걸 잊지 말자.

물론 얘는 TaskDialog와 TaskDialogIndirect로 아예 두 버전으로 나뉘어 있고, 쓸데없이 괜히 함수 프로토타입만 복잡한 게 아니라 진짜로 사용자가 실제로 쓸 수 있는 기능이 엄청 많은 게 맞다. 최근에 만들어진 만큼 API를 비교적 잘 설계했다는 생각이 든다. 하지만 정확하게 기억은 안 나는데 TaskDialog에는 정작 내가 꼭 써야 하는 customize 기능이 빠져 있어서 어쩔 수 없이 훨씬 더 복잡한 TaskDialogIndirect 함수만 써야 했던 것 같다. 그건 아쉬운 점.

대화상자의 제목, 큰 제목, 본문, 각 버튼의 모양, 링크, 라디오 상자 등등등...
이건 구조체가 아니라 아예 대화상자 생성 스크립트(XML 기반)를 받게 해도 이상할 게 없어 보인다.
최신 MFC에는 이 task dialog를 감싸는 클래스가 "당연히" 추가되어 있으며, 각종 상업용 GUI 패키지들에는 XP 이하 운영체제에다가도 task dialog를 자체 구현한 솔루션이 있기도 하다.

Posted by 사무엘

2010/06/29 08:53 2010/06/29 08:53
,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/306

파일 대화상자

1. 파일 대화상자라는 개념

윈도우 운영체제.. 사실 이뿐만이 아니라 플랫폼을 불문하고 현대적인 GUI 프레임워크들은
불러오기/저장하기(파일 선택) 기능을 공통 기능으로 제공한다.

어떤 형태로든 사용자가 작업하는 문서(데이터)를 파일로 읽고 쓰는 소프트웨어치고 이 기능을 안 쓰는 녀석은 없으므로, 이건 가히 필수 공통 기능이라 하기에 손색이 없다. 도스 시절에는 불러오기/저장하기 UI도 프로그래머가 제각각으로 직접 구현해야 했으니 얼마나 번거로웠는지 모른다. 이거 하나만 운영체제가 고수준 API를 제공해 줌으로써 응용 프로그램이 직접 FindFirstFile, FindNextFile 같은 파일 탐색 함수를 호출해야 할 일은 상당수 없어졌다.

물론 이것 말고도 색깔을 찍는 대화상자, 인쇄 대화상자 같은 것도 공통 대화상자에 속하며 운영체제가 제공해 주는 게 있다. 그리고 필요한 경우, 대화상자의 일부 요소나 동작 방식을 프로그래머가 자기 입맛에 맞게 customize하는 테크닉도 응당 제공된다.

2. 윈도우 운영체제의 파일 대화상자의 역사

윈도우 3.x의 파일 대화상자는 파일 목록과 디렉터리 목록이 리스트 박스의 형태로 좌우로 나란히 나오고, 그 아래에는 저장하거나 열려는 파일의 format 그리고 드라이브 목록이 콤보 박스의 형태로 나란히 나와 있었다. 드라이브-디렉터리-파일이라는 클래식한 형태에 충실한 디자인이라 하겠다. 가정에서는 네트웍이 아직 많이 생소하던 시절이었기 때문에, 네트웍 드라이브에 접근하기 위해서는 별도의 버튼을 눌러야 했다.

사용자 삽입 이미지

그러던 것이 윈도우 95/NT4에 와서는 크게 바뀌었다. 사실 윈도우 95부터는 쉘의 디자인의 근간이 싹 바뀌어 오늘날까지 이어져 오고 있다. 드라이브를 제공하는 장치간의 구분이 완화되었고, 가장 최상위 계층인 바탕 화면 아래로 내 컴퓨터, 휴지통 등등이 따르는 컨셉이 이때 도입된 것이다. 파일 리스트는 구닥다리 리스트 박스 대신, 소위 공용 컨트롤이라고 불리는 리스트뷰 컨트롤 기반으로 바뀌고 디렉터리와 드라이브는 폴더라는 개념으로 바뀌었다. 그리고 탐색기가 제공하는 쉘 기능(파일 복사, 붙여넣기, 삭제, 개명, 각종 우클릭 메뉴)은 파일 대화상자 내부에서도 그대로 쓸 수 있게 된 것 역시 큰 변화이다.

사용자 삽입 이미지

윈도우 98부터는 파일 대화상자의 크기 조절이 가능해졌다. 상당히 바람직한 변화이다.

윈도우 2000/ME부터는 파일 리스트 왼쪽에 바탕 화면, 내 문서 등 주요 폴더로 곧바로 이동하는 아이콘이 추가되었다(일명 favorite bar). 그리고 아마 이 무렵부터, 파일이나 디렉터리 이름의 처음 몇 자를 입력하면 자동 완성 후보가 뜨는 아주 편리한 기능이 생겼다.

이 구조가 윈도우 XP까지 이어지다가 비스타/7부터는 또 파일 대화상자가 싹 바뀌었다. 변화의 양상에 대해 한 마디로 요약하자면, 탐색기와의 경계가 더욱 모호해지고 좀더 "웹처럼"(webby) 바뀌었다. 웹 페이지 탐색하는 기분으로 내 컴퓨터의 폴더를 탐색하게 되었는데, 이는 IE4부터 MS에서 부르짖은 캐치프레이즈이기도 하다. 물론 탐색기와 IE의 완전 통합은 보안상의 이유로 인해 IE7부터는 좀 지양되었지만 말이다.

얼마나 바뀌었냐 하면, 파일 대화상자에도 웹브라우저처럼 "뒤로, 앞으로" 버튼이 생겼고, 검색란이 생겼다. 자주 쓰는 폴더 목록은 마치 인터넷 즐겨찾기처럼 뜨기 시작한 것이다. 그런 디자인이야 디자이너의 취향 나름이라 하지만, 기본 크기가 좀더 큼직해지고 마우스로 두세 단계의 폴더를 바로 건너뛰어 상위 단계로 갈 수 있어서 디렉터리 변경이 좀더 편해진 게 매우 좋다.

3. 과거 호환성

이렇게 운영체제가 버전업되면서 파일 대화상자의 디자인은 몇 차례 변화를 겪었다.
응용 프로그램이 아무 조작을 안 가하고 기계적으로 MSDN에 명시된 input과 output만 FM대로 잘 활용한다면, 파일 대화상자의 디테일은 응용 프로그램의 본질적인 동작에 전혀 영향을 끼치지 않는다. (당연한 말이지만)

그렇기 때문에 윈도우 비스타 이전에 개발된 프로그램이라 할지라도, 파일 대화상자 API만 곱게 쓴다면 비스타에서 실행했을 때 최신 디자인의 파일 대화상자가 뜬다. 이게 무슨 공용 컨트롤 매니페스트도 아니고, 최신 대화상자를 쓰기 위해서 응용 프로그램이 뭔가 조치를 취해야 할 필요는 없다.

그럼에도 불구하고 운영체제들은 옛날 버전 파일 대화상자들의 디자인도 갖고는 있다.
응용 프로그램이 파일 대화상자에다가 자신만의 컨트롤을 추가하고, 동작 방식을 제어하고 특정 컨트롤을 서브클래싱까지 하는 경우, 그 파일 대화상자의 디자인이 최신 운영체제에서 바뀌어 버린다면 동작 방식에 심각한 문제가 생길 수 있기 때문이다.

이미지 프로그램의 경우 열기 대화상자에다가는 그림 preview를 추가하기도 하고, 저장 대화상자에다가는 JPG로 저장할 경우 화질을 지정하는 추가 옵션을 대화상자에다 덧붙이곤 했다.
압축 유틸리티나 파일 변환 프로그램은 아예 파일 열기 대화상자에다가 자기네 동작 옵션을 한 보따리 가득 집어넣어 그걸로 프로그램 메인 화면을 만들기도 했다는 걸 알 필요가 있다.

최신 디자인을 적용할 수 없는 경우, 운영체제는 여전히 윈도우 2000이나 심지어 98(favorite bar까지 없어진) 스타일로 파일 대화상자를 출력해 준다.
특히 MFC를 써서 프로그램을 개발하는 경우 이 점을 매우 신경써야 한다. MFC는 프로그래머가 원하는 타이밍에 손쉽게 이벤트를 날려 주기 위해서.. 쉽게 말해 개발자의 편의를 위해서 윈도우에다가 온갖 보이지 않는 훅킹과 서브클래싱을 남발하는데, 이런 이유로 인해서 구형 버전의 비주얼 C++로 CFileDialog 클래스를 쓰면, 아무리 내가 customize를 하는 게 없어도 최신 운영체제에서 파일 대화상자가 최신 디자인으로 나오지 않는다!

까놓고 말해 본인이 개발한 <날개셋> 편집기의 열기 대화상자와, <날개셋> 타자연습의 연습글 추가 대화상자를 윈도우 비스타/7에서 살펴보기 바란다. 차이가 느껴질 것이다.
이런 이유로 인해, 최신 운영체제의 GUI 혜택을 입으려면 새로운 기능을 쓰는 게 없어도 개발툴도 최신으로 써야 하는 경우가 있다. 싫으면 MFC 클래스 쓰지 말고, 불편하더라도 GetOpenFileName처럼 윈도우 API를 직접 호출하든가 말이다. ^^

4. 파일 대화상자의 중복 개발

그런데 MS 오피스 제품들은 전통적으로 운영체제의 표준 API를 쓰지 않고, 무려 자기네가 따로 자체 개발한 파일 대화상자를 사용해 왔다. 가령, 운영체제의 표준 파일 대화상자는 윈도우 2000/ME대에 가서야 favorite bar가 추가된 반면, MS 오피스의 대화상자는 꽤 일찍부터.. 최소한 오피스 97 시절부터 그런 걸 갖추고 있었다.

이런 식으로 MS 내부에서 운영체제 GUI가 오피스 GUI를 뒤쫓아가는 양상은 일상적인 모습 같다. 하다못해 윈도우 3.1 시절에도 오피스가 자체 구현했던 도구모음줄(toolbar), 상황선(status bar), 은빛 3D 효과 대화상자, 프로퍼티 시트와 위저드 같은 게 95로 넘어가면서 운영체제 표준 GUI로 나중에야 편입되었다. 그러던 관행이 세월이 흘러 윈도우 7에서는 리본 인터페이스가 워드패드와 그림판에까지 적용되어 있다.

오피스 제품 중에서도 무려 1980년대부터 개발되어 온 워드와 엑셀은, 겉모습은 별 차이 안 나지만 사실 일반 대화상자들마저도 윈도우 운영체제의 표준 대화상자가 아니며 마치 아래아한글처럼 자체 구현 대화상자이다! 걔네들이 처음부터 아주 크로스 플랫폼으로 개발되어서 그런 건 아니고, 애초에 개발 컨셉이, 빈약한 운영체제의 기본 컴포넌트에 의존하지 않고 자기네 컴포넌트로 UI를 자체 구상하겠다는 방향이었던 것 같다. 그 부자 기업이 하고 싶은 걸 뭘 못 하겠는가?

90년대 이후부터 개발된 파워포인트나 액세스는 그런 수준까지는 아니지만 열기/저장 대화상자라든가 About 대화상자만은 여전히 MS 오피스 공용 라이브러리의 것을 사용해 왔다.
개발툴인 비주얼 스튜디오도 닷넷급부터는 MS 오피스의 GUI 엔진을 이어받고 있는 중이다. 순수 MFC만으로 MS 오피스 짝퉁 GUI을 구현했던 비주얼 C++ 5와 6의 계보는 흑역사가 된 지 오래.

그런데, 그러던 정책이 이제는 바뀌었다.
윈도우 비스타와 동일한 timeline에 개발된 제품인 비주얼 스튜디오 2008과 MS 오피스 2007부터는
놀랍게도 운영체제의 표준 파일 대화상자를 사용하며, 앞으로도 계속 그런 구도가 유지될 것이다!
비주얼 스튜디오 2005와 MS 오피스 2003은 그렇지 않다는 소리.

다만, 이건 비스타 이상에서 실행됐을 때에 한해서이다. 윈도우 XP에서 돌아가는 비주얼 스튜디오 2008이나 MS 오피스 2007은 여전히 자체 대화상자를 사용한다. -_-;; 이것도 무지 신기한 점임.

비스타부터는 이제 운영체제의 파일 대화상자도 기능이 매우 향상되었고 customize하는 수단도 충분히 깔끔해졌기 때문에 굳이 운영체제 따로, 오피스 따로 노선을 갈 필요를 느끼지 않게 되어 그렇게 바뀐 것 같다.
비스타에서는 비주얼 스튜디오 2005의 옛날식 파일 대화상자가 오히려 더 촌스럽게 느껴진다. ^^;;

Posted by 사무엘

2010/05/13 07:34 2010/05/13 07:34
, ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/267

멀티태스킹 운영체제에는 한 주소 공간에 여러 프로그램들이 동시에 실행될 수 있다. 그렇기 때문에 그런 환경에서 동작하는 프로그램이라면 자기가 메모리의 어느 위치에 적재되는지를 신경써야 하며, 임의의 위치에 올라가더라도 잘 실행될 준비가 되어 있어야 한다.

과거 도스 시절에는 EXE말고 COM이라는 실행 파일이 있었는데, 이것은 최소한의 헤더 같은 것도 없고 코드와 데이터가 다 16비트 공간 안에 막혀 있으며(과거 메모리 모델로 치면 제일 작은 tiny와 동일), 메모리 주소도 고정 붙박이식이어서 오늘날의 컴퓨터 환경과는 도저히 어울릴 수 없는 과거 유물이 되어 있다.

윈도우 운영체제의 실행 파일이 사용하는 PE 포맷은 position dependent code 방식이다. 즉, preferred base라는 개념이 존재하여 자기는 32비트 주소 공간에서 어디에 적재되는 게 이상적인 경우라고 이미 가정하고 만들어져 있다는 뜻이다. 어떤 EXE나 DLL이 거기에 바로 적재가 가능하다면 가장 빠르고 좋지만, 만약 이 선호하는 주소에다 적재가 못 된다면 별도의 재배치 작업이 필요하다. 즉, 마치 퀵 정렬처럼 잘 될 때와 못 될 때의 성능 편차가 크며 일종의 모험이 동반된다는 뜻이다.

이는 유닉스의 shared library와는 다른 디자인이다. 그쪽은 이렇다할 preferred base 주소가 없으며, 코드 자체가 어느 메모리 주소에 적재되든 동일한 성능으로.. 하지만 딱히 최적의 성능을 발휘하지는 않는 구도로 동작한다. 일종의 힙 정렬이나 병합 정렬처럼 동작한다는 뜻.

윈도우 PE 포맷에는 실제로 실행되는 기계어 코드인 code 섹션도 있고 data라든가 리소스(rsrc)와 더불어 재배치 정보를 나타내는 섹션(reloc)도 있다. preferred base에서 동작하지 못할 때, 코드의 어느 부분을 쫙 수정해 주면 preferred base가 아닌 다른 지점을 기준으로 이 프로그램이 잘 돌아갈 수 있는지를 따로 기록해 놓은 것이다. 사실 이런 개념의 재배치 정보는 도스 EXE나 16비트 윈도우 EXE에도 있긴 했다.

그런데 32비트 윈도우 EXE만은(물론 64비트도 포함) 원칙상 이런 재배치 정보가 필요하지 않게 됐다. 32비트 환경부터는 모든 EXE들이 나만의 독립된 주소 공간을 가지기 때문에, preferred base가 무엇이든지에 무관하게 EXE는 무조건 내가 원하는 위치에 가장 먼저 적재되는 게 보장되기 때문이다.
그래서 통상 EXE들은 재배치 정보를 넣지 않는다. 필요가 없기 때문에, 넣지 않는 게 파일 크기를 줄이는 데도 도움이 되기 때문이다.

그럼에도 불구하고 재배치 정보가 필요한 경우는 다음과 같다.

1. EXE가 아닌 DLL은 반드시 필요하다. DLL은 다른 EXE의 밑에 붙어서 동작한다는 특성상 자신만의 주소 공간을 갖고 있지 않다. 나의 preferred base에 EXE라든가 다른 DLL이 이미 선점되어 있다면 응당 재배치가 필요하다. DLL은 그런 상황을 언제든지 대비해야 한다.

2. Win32s에서 돌아가는 32비트 프로그램이라면 EXE라도 재배치 정보가 반드시 있어야 한다. Win32s는 과거 윈도우 3.1에서 일부 32비트 프로그램을 구동하기 위해 제공되었던 일종의 운영체제 익스텐더로, 32비트 프로그램을 실행만 해 줄 뿐, 16비트 윈도우 3.1이 지니고 있던 시스템적인 한계는 그대로 답습하고 있다.
멀티스레딩을 지원하지 않으며 32비트까지 포함해 모든 EXE가 독립이 아니라 단일 주소 공간을 공유한다!
프로그램을 실행할 때마다 EXE의 핸들이 제각각인 값이 들어오며, 로딩도 preferred base에 정확하게 절대로 되지 않는다. 여기에 대해서는 추후 실험해 볼 예정.
실제로 테스트를 해 보면 핸들이 0x1xxx 이렇게 아주 작은 값으로 들어온다는 게 매우 흥미롭다. 윈도우 NT에서는 그렇게 낮은 주소는 아예 무조건 에러로 간주하는 반면, Win32s에는 그게 여전히 쓰인다는 소리이다. 포인터가 아니라 진짜로 다른 번호에 가깝다.

3. 비주얼 C++ 2008에서 추가된 '시작 주소 랜덤화' 기능을 사용하려면 재배치 정보가 필요하다.
보안을 위해 성능을 희생하듯, 윈도우 비스타부터는 PE 실행 파일도 일종의 position independent (과거의 dependent가 아닌) code처럼 써먹으려는 것 같다.
이 방식으로 빌드된 EXE는 운영체제가 일부러 EXE의 preferred base와는 다른 임의의 위치에다가 로딩을 해 준다. 즉, 이 EXE의 특정 지점의 코드나 데이터를 딱 맞춰 수정하는 프로그램이 제대로 동작하지 않게 위해서이다. (스타크로 치면 맵핵 같은 프로그램)

Posted by 사무엘

2010/05/04 08:36 2010/05/04 08:36
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/258

윈도우 운영체제가 인식하는 실행 파일 포맷인 PE(portable executable)의 헤더를 보면,
이 EXE/DLL이 실행되는 플랫폼(x86, x64, IA64 등등)이라든가, 이 실행 파일의 특성을 나타내는 플래그 등 여러 정보가 존재한다.
그런데 그 플래그 중에는 'Large address aware' 여부를 나타내는 플래그가 있다.
이건 무엇을 뜻하며, 왜 만들어진 것일까?

윈도우 NT는 도스의 잔재 없이 처음부터 순수 32비트로 개발된 운영체제이다.
32비트 공간에서는 최대 2^32 = 4GB 크기의 가상 메모리를 사용할 수 있는데, MS는 전통적으로 하위 2GB는 응용 프로그램이, 상위 2GB는 커널이 사용하는 구도로 운영체제를 설계했다.

그때는 램은커녕 하드디스크 용량도 4GB보다 훨씬 적던 시절. 그러니 그때 32비트는 가히 무한대에 가까운 공간이었으며, 메모리 분배를 어떻게 한다고 해도 이상할 게 없었다.
응용 프로그램은 언제나 하위 2GB만을 사용하다는 게 무슨 뜻일까?
포인터에서 32비트 크기가 다 쓰이는 게 아니라, 최상위 1비트는 절대로 1이 될 일이 없다는 말이다.

그래서 일부 잔머리 잘 굴리는 프로그래머들은 포인터에다가도 자신만의 1비트짜리 boolean 정보를 최상위 비트에다 얹고, 포인터를 쓸 일이 있으면 그 값을 잠시 제거한 후 사용했다고 한다. 흠좀무.

그런데 세상이 변해서 이제 램이 기가바이트급 스케일이 되었고, 32비트 공간만으로는 부족한 시대가 왔다. 본격적으로 64비트 시대가 도래하기 전부터 데이터베이스처럼 아주 memory-intensive한 프로그램을 돌리던 업계에서는, 유저와 커널을 2:2로 가르지 말고 3:1로 갈라서 응용 프로그램에다가 메모리를 좀더 많이 얹어 달라고 MS에다 끊임없이 요구했다. 그래서 MS는 '물리 주소 확장' 모드라는 걸 만들어 줬다.

사실, 커널도 메모리, 좀더 정확히 말하면 주소 공간이 의외로 많이 필요하다. 2:2도 오히려 부족한 감이 있다. 커널 코드를 얹고 각종 커널 오브젝트를 관리하는 메모리만 필요한 게 아니기 때문이다. 가상 메모리라는 시스템은 그 개념상 메모리를 관리하기 위한 메모리도 요구하는 법. 그와 관련되어 방대한 공간이 필요하며, 디바이스 드라이버를 얹고 돌리기 위한 메모리 등등도 따지면 결코 만만한 수준이 아니다.

3:1로 가르면 응용 프로그램이야 사용 가능한 메모리가 좀더 늘며, 종전에는 응용 프로그램이 한번에 약 1GB 남짓밖에 매핑을 못 하는 memory mapped file도 훨씬 더 큰 크기까지 확장할 수 있다. 하지만 만들 수 있는 프로세스/스레드 수가 감소하며 네트웍이라든가 운영체제의 전반적인 기능상의 한계가 매우 커지고, 운영체제가 이론적으로 관리 가능한 총 물리 메모리의 양도 줄어든다! 이 tradeoff를 반드시 잊지 말아야 한다.

그런데 문제는...
그렇게 3:1로 응용 프로그램의 메모리 주소를 확장하면...
드디어 최상위 비트가 1인 포인터 값이 응용 프로그램으로 오는 게 가능해진다는 것.
그렇다면, 예전에 놀고 있던 최상위 비트를 다른 용도로 활용하던 프로그램을 이런 확장 환경에서 돌리면.....;;; 더 이상의 자세한 설명은 생략한다.

그래서 호환성을 목숨처럼 1순위로 강조하는 MS는, 아무 프로그램이나 일방적으로 넓어진 포인터를 주는 게 아니라, 넓어진 포인터를 줘도 안전하다고 플래그가 따로 지정되어 있는 프로그램에 대해서만 제 기능을 다하도록 하는 정책을 선택했다. 그것이 바로 large address awareness이다. 이 플래그가 없이 빌드된 프로그램은 여전히 메모리를 2GB씩밖에 못 쓴다. 마치 윈도우 XP 이후에도, 별도의 매니페스트를 내장하고 있지 않은 옛날 프로그램들은 비주얼 스타일 테마가 적용되지 않는 것과 같은 맥락으로 말이다.

단, 이건 EXE에 한해서이다. DLL은 그런 선택의 권리가 없다. 확장 주소가 지원되는 EXE에 붙을 수도 있고 지원 안 되는 EXE에 붙을 수도 있으며, 어느 때건 동작을 잘 해야 한다. 따라서 DLL은 반드시 확장 주소를 지원하도록 작성되어야 한다.

본격적으로 64비트 환경이 되면서 확장 주소의 진정한 의미가 드러났다. 이제는 상위 1비트 정도가 아니라 아예 테라바이트급 메모리 주소에도 접근 가능해야 하며, 64비트 프로그램은 '확장 주소 지원' 플래그가 반드시 있어야 한다. 이 플래그가 없으면, 비록 x64 내지 IA64 아키텍처용으로 만들어진 64비트 프로그램이라 할지라도 포인터의 주소로는 여전히 무려 2GB 이내의 값만 들어온다. -_-
포인터 크기를 4바이트 int 크기로 하드코딩하고 제작된 무개념 프로그램을 최대한 쉽게 64비트로 포팅할 수 있게 배려한 것이다. 물론 이 역시 EXE에 한해서이지만 말이다.

large address aware 옵션은 비주얼 C++의 x86 플랫폼에서는 호환성 차원에서 디폴트로 꺼져 있다. 즉, 사용자가 별도로 옵션을 켜지 않으면, 2GB까지만 인식하는 프로그램을 만든다.
하지만 x64/IA64 플랫폼에서는 사용자가 별도로 이 옵션을 끄지 않으면 디폴트로 켜져 있으며, 코드가 2GB 정도가 아니라 4GB 이상의 공간도 안전하게 인식하는 것으로 간주한다. 둘이 묘한 차이가 있다는 것을 기억하자.

물론 굳이 램이 4GB가 아니더라도 64비트는 CPU가 한번에 정보를 처리하는 단위 자체가 더 크다는 점 하나만으로 32비트보다 대용량 데이터를 처리하는 성능이 더 뛰어나다. double 실수형을 하나 스택에 얹을 때만 해도 32비트에서는 CPU 명령을 최소 둘 이상 써야 하는데 64비트에서는 한 번만에 끝난다는 소리이지 않은가. 그렇기 때문에 램 용량이 32비트 크기를 초과하기 전부터도 64비트 프로세서가 개발되어 일부 제한된 영역에서 쓰이기도 했던 것이다.

잘 알다시피 64비트 윈도우는 과거 16-32비트가 그랬던 것처럼 그 정도로 지저분한 호환 계층은 제공하지 않으며, 한 프로세스 공간에 64-32비트 코드가 공존하는 것을 허용하지 않는다. 그래도 윈도우 핸들값은 여전히 32비트 범위 안에만 존재하며 32와 64비트가 값을 그대로 공유 가능하다는 게 신기하다. 하긴, 윈도우 9x에서는 윈도우 핸들값이 아예 16비트 범위에 있었지만 말이다. ^^ 썽킹이라는 말도 참 오랜만에 다시 듣는다.

Posted by 사무엘

2010/04/12 09:12 2010/04/12 09:12
, ,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/242

C/C++ 언어는 언어 차원에서 자체 제공하는 문자열 타입이란 게 없다.
문자열은 단순히 문자의 배열 내지 이를 가리키는 포인터로 취급되며, 코드 번호 0인 문자가 문자열의 끝을 나타내는 매우 원시적이고 단순한 방법을 사용하고 있다. 그 이름도 유명한 null-terminated string 기법이다. (이하 편의상 NTS로)

깔끔하고 쓰기 편한 문자열 처리를 제대로 구현하는 데 드는 비용을 감안한다면, 이것은 그렇게 나쁜 방법은 아니다. string은 int보다야 처리하기가 훨씬 무거운 건 사실이다. 더구나 C/C++은 정말 1바이트라도 더 아끼고 한 클럭이라도 더 줄여야 하는 시스템 프로그래밍 용도로 개발된 언어인 것이다.

NTS 방식으로 문자열을 다루지 않는 언어도 있다. 이런 언어는 문자열의 길이는 별도의 장소에 보관하며 스트링의 내부에 0번 문자가 있을 수도 있다. 그런데 이런 언어로 프로그램을 짜더라도, NTS를 받는 운영체제 API에다 문자열을 넘겨줄 때는 뒤에다 0번 문자를 손수 추가하여 문자열을 변환하곤 한다. 운영체제 커널이야말로 C 언어 기반인 경우가 태반이기 때문이다.

하지만 다른 모든 곳에는 NTS 문자열 포인터만 받더라도, 그래픽 API는 비록 C언어 스타일이라 할지라도 이례적으로 문자열 포인터뿐만 아니라 문자열의 길이를 별도의 인자로 따로 받는 경우가 많다. 윈도우 API도 그 대표적인 예인데, 그 이유는 명백하다. 그래픽 처리의 특성상 NTS 문자열을 일부 몇 글자만 찍어야 하는 경우도 빈번하게 발생하기 때문이다.

NTS는 간단하고 효율적인 대신, 문자열의 끝을 언어나 프레임워크 차원에서 보장을 하지 않는다는 특성 때문에 오늘날 버퍼 초과 같은 보안 문제의 온상이 되어 있다. 가령, C 표준 함수 중에 strcpy는 잠재적으로 굉장히 위험한 함수이거니와, 파일도 아니고 키보드 입력을 받는데도 버퍼 크기 한계 지정도 없이 설계되어 있는 gets는 정말 도저히 그대로 쓸 수 없는 지경이 되어 있다.

이 문제를 해결하기 위해 윈도우 SDK에는 꽤 오래 전부터 StringCb* 함수들을 도입했다. write 버퍼의 포인터뿐만 아니라 버퍼 크기도 별도로 지정하여 복사가 그 영역 밖으로는 되지 않게, 즉 문자열이 잘리도록 조치를 취한 것이다. 이것들은 그냥 static 링크되는 코드이지 운영체제 커널 DLL에 있지는 않다는 점에서 lstrcpy 같은 함수와는 위상이 다르다. 사실 저 함수 자체가 기존 커널 l* 함수보다 더 안전한 솔루션을 제공하기 위해 추가된 것이다.

비주얼 C++ 2005부터는 보안 문제를 더욱 적극적으로 대처하는 함수군이 생겼다. 아예 _s 접미사가 붙은 strcpy_s 류이다. 이것도 하는 일은 기존 strcpy보다 더욱 안전하게 문자열을 복사하는 것이지만, 다음과 같은 면에서 StringCb*와는 동작 방식이 약간 다르며, 쓰임이 더 엄격하다는 것을 알 필요가 있다.

첫째, StringCb*는 버퍼 크기가 걸리면 문자열을 그냥 자르고 null-terminate까지 알아서 시켜 주는 반면, *_s는 런타임 에러를 발생시킨다. 그러므로 전자는 단순히 로그를 찍는 것과 같이 문자열이 꼭 다 복사되지는 않아도 되고 버퍼 초과 에러만 예방하고 싶을 때 쓰면 되며, 후자는 어떤 경우에도 버퍼가 초과하는 일 자체가 없이 문자열이 100% 정확하게 복사되는 게 보장되어야 할 때 쓰면 된다.

둘째, 특히 디버그 버전의 *_s 함수는 지정해 준 버퍼 영역을 다 검사한다. 비록 지금 한 글자밖에 복사할 게 없더라도 사용자가 100글자를 지정했다면 그 메모리가 다 안전한지 일일이 검사한다는 뜻이다. 그렇기 때문에
char a[8];
strcpy_s(a, 12, "ABC"); //12>8

는 비록 전혀 해롭지 않은 코드임에도 불구하고 에러를 일으킨다. 디버그 빌드에서는 말이다.

strcpy 같은 간단한 함수뿐만 아니라 scanf 함수도 쓰임이 강화되었다. 가령,
char s[64];
scanf_s("%s", s, 64);

이런 식으로 %s도 버퍼뿐만 아니라 버퍼의 크기까지 덧붙이도록 동작이 수정되었다는 뜻이다.

*_s 함수는 일부 템플릿 오버로드 버전도 존재하기 때문에, 기존 코드에다 *_s 함수를 넣는 리팩터링 작업을 할 때 일일이 배열 사이즈를 집어넣어야 하는 수고를 상당수 덜어 준다. 가령, 쓰기 버퍼가 포인터가 아니라 static 배열인 경우,

template<int N>
.... strcpy_s(char (&dst)[N], const char *src)
{
 return strcpy_s(dst, N, src);
}

이런 템플릿 오버로드가 이미 구비되어 있는 덕분에, strcpy를 strcpy_s로 바꾸기만 해도 알아서 배열의 크기가 전달된다. 물론 컴파일 타임에 말이다.
물론, 쓰기 버퍼가 임의의 포인터인 경우에는 이런 방법을 쓸 수 없고 사용자가 버퍼 크기를 수동으로 전달해 줘야 할 것이다.

본인도 어지간하면 이런 안전한 함수를 쓰고 싶은데, strchr/strstr 같은 함수의 결과값에다가 또 strcpy를 해야 할 때는 남은 버퍼 크기를 지정해 주는 게 좀 난감하다.

덧.
1. C언어에서 정적 배열의 원소 개수를 구할 때는 통상
#define ARRAYSIZE(x)  sizeof(x)/sizeof(x[0])
과 같은 형태로 정의하며, 이렇게만 써 놔도 이 식은 최적화 과정에서 모두 컴파일 타임 때 고정된 값이 계산되어 들어간다. 하지만 ARRAYSIZE 매크로도 템플릿을 이용한 매크로로 바꾸면 나눗셈 연산도 없고 더구나 배열이 아닌 일반 포인터를 넘겨주면 에러까지 나는(더욱 type-safe하기까지 한) 버전을 만들 수 있다.

템플릿으로 컴파일 타임 때 정말 별 걸 다 할 수 있다. ^^;; strcpy_s의 템플릿 오버로드 버전을 보니까 문득 이게 생각이 났다.

2. 아울러 어떤 구조체 멤버가 메모리 상으로 몇째 오프셋을 나타내는지 가리키는 매크로도 NULL 포인터로부터 ->로 참조한 멤버 값의 주소를 가리키는 방식으로 만들 수 있다. NULL 포인터는 -> 연산자를 적용하기만 하면 바로 에러가 날 것 같지만 주소 연산자 &는 메모리 위치에 상관없이 컴파일 타임 때 값이 계산되는 연산자이기 때문에 그런 일은 발생하지 않는다.

3. C/C++에서 0이라는 수치는 숫자와 포인터에 모두 아무런 형변환 없이 적용 가능한 녀석이어서 문제이다. true, false는 이미 진작부터 예약어가 생겼는데 C++도 nil이나 null 같은 예약어가 있어야 하지 않나 싶다. 본인은 전에도 이런 언급을 한 적이 있을 것이다.

Posted by 사무엘

2010/03/13 14:56 2010/03/13 14:56
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/210

내가 옛날에 만든 프로그램들

1. PentaCombat (마지막 빌드 2000): 2000년대 이후로 개발이 중단됐다. (그 당시 이 프로젝트 이후 곧장 <날개셋> 한글 입력기 개발로..) 나름 3*3과 4*4 판단 알고리즘을 굉장히 정교하게 구현해 냈고 오목은 AI 연구용으로도 굉장히 재미있는 주제라고 생각했는데, 더 개발을 못 하게 된 게 무척 아쉽다. 지금 공개되어 있는 컴파일 EXE, DLL은 무려 비주얼 C++ 4.2로 빌드되었으며, 날짜도 1999년~2000년대이다. ㅎㄷㄷ

2. WordTech (마지막 빌드 2007): 이것도 굉장한 애착을 갖고 있는 프로그램이다. 국내에서 스크래블/업워드 크로스워드 게임을 자체 개발한 사례는 이 프로그램이 유일하기 때문이다. 그것도 컴퓨터 AI에다 네트워크 기능까지 말이다.
지금은 10년 전보다 더 효율적인 단어 목록 자료구조와 더 빠르고 똑똑한 AI 알고리즘을 만들 수도 있다. 그리고 네트워크 쪽도 구닥다리 DirectPlay 대신 저수준 네트웍 API로 새로 짤 필요도 있다. 하지만 본인은 이제 이걸 도저히 손댈 수 없는 처지가 됐다.

3. <날개셋> 타자연습 (마지막 빌드 2009): 더 무슨 말이 필요하리요? 게임은 좀 3D로 고쳐야 하고 각종 바이러스들의 비주얼 효과도 더욱 현란하게 고쳐야 한다. 윈도우 비스타부터는 운영체제의 기본 내장 게임조차 Direct3D를 쓰는 세상이 되지 않았던가.
그리고 네트워크 기능을 적극 도입하여 온라인 타자방, 실시간 연습글 업데이트 같은 기능도 넣어야 한다.
하지만 타자연습도 작년 말 3.21을 끝으로, 더는 내가 더 손을 볼 수 없는 사실상 개발 중단 상태가 되지 않을까 싶다. (지원 중단이라는 뜻은 아님. 여건상 새로운 기능을 추가하지는 못하지만, 버그 패치나 보안 업데이트 정도만. ㅎ)

4. <날개셋> 한글 입력기: 그나마 지금까지 독자적인 아이템으로, 10년간 가장 열정적으로 기능 연구와 개선을 해 온 프로그램. 엔진 쪽도 사실 최하 6.0까지는 더 만들고 싶지만 현실은 5.7, 혹은 5.53에서 끝날지도 모르겠다. 엔진 차원에서 더 고차원적인 개념을 생각하자면 끝도 없지만, 일반 사용자의 관점에서는 지금 엔진만으로도 기능은 이미 너무 많아서 미처 다 활용도 못 할 수준이리라.
지금의 5.5x대 엔진을 바탕으로 아무래도 여타 운영체제 포팅을 할 가능성부터 먼저 찾는 걸로 계획을 수정해야 할 것 같다. 그것부터 된 후에 여건이 남으면 엔진 작업도 더 할 것이다.

본인에게는 <날개셋> 한글 입력기만큼이나, 한글과 관련된 또 완전히 다른 솔루션을 연구하고 싶은 게 있다. 시기가 시기이니만큼 이 카드도 슬슬 꺼내 봐야 할 것 같다. 그러니 언제까지나 기존 아이템의 유지 보수에만 매달려 있을 수가 없다. 지저분한 윈도우 IME 쪽 버그 살펴보는 것도 한계가 있다.

이런 식으로 사람은 점점 발전하는 것 같다.
역시 어렸을 때, 실패에 대한 위험 부담 내지 사회적 책임이 적을 때 하고 싶은 일을 실컷 해 놔야 한다. 게임으로 허비하기엔 인생은 너무나 아깝다.

고등학교 3학년 때 과감하게 <날개셋> 한글 입력기 1.0을 만들었기 때문에 10년 뒤에 이것이 5.5까지 버전이 오를 수 있었다.
그리고 그 전에 허접하게나마 저 두 보드 게임을 만들었기 때문에 그 기술과 경험을 근거로 이듬해에 <날개셋> 한글 입력기 1.0이 만들어질 수 있었다.

저 프로젝트들 생각만 하면 그나마 프로그래머다운 기질이 팍팍 살아나는 걸 느낀다. 하지만 나는 순수 공돌이나 전산학도는 아니기에, 내 경쟁력을 위해서는 아무 프로그램이나 짜서는 안 되고, 컴퓨터를 수단으로 삼아 다른 특정 분야에서 활로를 찾아야겠다.

Posted by 사무엘

2010/02/26 09:05 2010/02/26 09:05
, , , ,
Response
No Trackback , 5 Comments
RSS :
http://moogi.new21.org/tc/rss/response/198

비주얼 스튜디오로 C# 프로젝트를 하나 만든 후, 마법사가 생성해 준 기본 폼 애플리케이션을 debug와 release로 제각기 모두 빌드하여 실행해 본다.
그 후 이 프로젝트 디렉터리의 크기를 측정해 보라. 본인은 200KB가 채 나오지 않았다.

그런데 C/C++ 프로젝트를 만들고(MFC는 쓰지도 않고), 그냥 간단한 창만 하나 띄우는 Win32 프로그램을 debug와 release로 모두 빌드해서 실행한 후 디렉터리 크기를 재 보라.
프로젝트가 차지하는 용량은 무려 20MB가 넘는다. (비주얼 스튜디오 2008 기준)

그렇다. C/C++은 프로젝트를 만들고 빌드를 좀 하면, 잡다하게 생기는 중간(intermediate) 파일이 엄청 많다. 게다가 용량도 상당히 많이 잡아먹는다.

<날개셋> 한글 입력기 프로젝트도 32비트 디버그/릴리스, 그리고 64비트까지 빌드하다 보니 디렉터리 전체 크기가 무려 800MB에 달해 있다. 하지만 그 중 실제로 빌드에 쓰이는 소스나 데이터 파일의 합은 20~30MB대는 절대 안 넘을 것이다. -_-;;

OBJ 파일이 생기는 것이야 C/C++ 자체가 링크를 염두에 두고 만들어진 언어이니 어쩔 수 없고 그건 어차피 그렇게 크지도 않다. ..... 라고 말하려고 했는데, 오브젝트 파일도 각종 디버깅 내지 고급 최적화와 관련된 메타정보가 첨가되다 보면 단순히 소스 코드의 기계어 번역이라고 볼 수 없을 정도로 덩치가 은근히 커지긴 한다.

OBJ 말고도 디스크 용량을 상당히 차지하는 주범은 잘 알다시피 pre-compiled header이다(*.PCH) 겨우 몇몇 개의 헤더 정도나 인클루드하면 되는 정올 답안 수준의 프로그램이 아니라 특정 운영체제/플랫폼이나 거대한 라이브러리의 프로그래밍 요소를 다 인클루드하는 프로그램이라면, 그렇게 고정불변이고 덩치가 많은 요소들은 미리 컴파일을 좀 시켜 놔야지 프로그램의 빌드 시간을 줄일 수 있다.

본인이 비주얼 C++을 처음으로 쓴 게 4.2 시절부터이다. 그때엔 MFC 심볼들을 다 빌드해 놓은 pch 파일도 이미 3~5MB 정도 했던 것 같다. 하지만 지금은 그것보다 덩치가 훨씬 더 커져 있고 한 빌드 configuration당 10MB는 훌쩍 넘어간다. 프로젝트 하나 만들 때마다 50~100MB씩은 잡아야 한다. 오로지 C/C++ 언어 프로젝트만이 이런 삽질이 필요하다.

윈도우 SDK나 MFC처럼 매 프로젝트마다 일일이 빌드가 필요하지 않은 것들은 공용 PCH라는 개념으로 공유만 좀 하게 해 놔도 이런 파일의 크기를 상당 부분 줄일 수 있을 텐데 너무 낭비라는 생각이 든다. 하지만 요즘은 하드디스크 용량이 워낙 많다 보니, 빌드 시간을 네트워큭 분산 기술을 줄이려는 연구는 해도 PCH 파일 크기를 줄이려는 연구는 거의 행해지지 않는 것 같다.

이외에도 인텔리센스 데이터베이스인 NCB 파일도 은근히 크고, 매 빌드 때마다 생기는 심볼 디버그 데이터베이스인 PDB 파일도 무시 못 한다.
왜 이런 일이 생기는 것일까? 대략 다음과 같은 관점에서 살펴볼 수 있다.

첫째, C/C++은 굉장히 작은 언어이고, 프로그래밍에 필요한 요소들을 전적으로 소스와 동일한 텍스트 포맷인 헤더 파일의 파싱(느림!!)에 의존하여 읽어들이는 형태이기 때문이다. 라이브러리 링크는 별도의 계층으로 따로 존재하지, 즉시 읽어들일 수 있는 바이너리 유닛/패키지라든가(파스칼, 자바, C# 등) 언어가 자체 내장하고 있는 요소가 없다. 원천적으로 이식성을 택했지, 속도를 배려하지는 않은 느린 프로세스를 사용하는 셈이다.

둘째, C/C++은 전처리기라든가 링크 과정으로 인해 빌드가 더욱 느리고, 언어의 해석이 더디기 때문이다. 모든 토큰은 그냥 토큰이 아니라 전처리기를 재귀적으로 거치면서 다 까 봐야 실체가 드러난다. ^^;;; 헤더 파일의 글자 하나를 고치면 이 여파가 수백, 수천 개의 소스 파일에 동시에 파급되고 프로그램의 의미가 전혀 다르게 바뀔 수 있다. 이것은 장점도 있지만 똑똑한 개발 환경이나 빠른 빌드 환경을 만드는 데는 불리한 구조이다.
그러니, 소스 코드를 조금이라도 빨리 분석하기 위해서는 소스코드 자체뿐만 아니라 온갖 메타정보들을 ‘별도의 파일’로 보관할 수밖에 없다. NCB처럼 말이다.

셋째, C/C++은 그 어느 에뮬레이터나 가상 기계 계층이 없이, CPU 차원에서 기계가 직통으로 알아듣는 프로그램을 만들 수 있다. 잘 최적화된 프로그램은 사람이 원래 짠 소스 코드와는 전혀 다른 형태가 될 수도 있다. 그러니 이런 코드의 디버깅은 어려울 수밖에 없다. 변수의 내용을 확인하거나 한 줄씩 실행하는 것까지는 못 하더라도 프로그램이 뻗었을 때 스택 덤프라도 보려면 빌드된 프로그램과 소스 코드 사이의 관계를 설명하는 최소한의 정보라도 있어야 한다. 소스 코드와 형태가 전혀 딴판인 코드를 생성하는 컴파일러일수록 그런 정보는 더욱 세부적이고 양이 많아질 수밖에 없다.

한 마디로 C/C++이 정말 강력하고 포괄적이고 대인배 같은 언어이다 보니 주변에 붙는 군더더기도 많은 모양이다. ^^ 코드 생성이 여러 단계를 거치면서 매우 번거롭고 어려운 대신, 한번 만들어 진 코드는 그 어느 언어보다도 강력하다.

Posted by 사무엘

2010/02/22 21:40 2010/02/22 21:40
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/194

내 프로그램의 중복 실행 여부를 판단하려면? (물론 윈도우 프로그래밍 기준)

실행 직후에 자신만 식별할 수 있는 이름으로 커널 오브젝트를 만들어서, 이놈의 생성 여부로 판단하는 게 제일 무난하고 안전하다. 커널 오브젝트라 함은 메모리 맵드 파일, 뮤텍스, 이벤트 등 이름의 scope가 전역적인 어느 것이라도 될 수 있겠다.

다른 방법으로 중복 실행을 판단하는 방법은 크게 윈도우 아니면 파일로 식별하는 것으로 나뉘는데, 커널 오브젝트만치 완전하지는 못하다. 그 이유를 지금부터 설명하겠다.

※ 응용 프로그램이 생성한 윈도우로 판단하는 법

FindWindow 함수로 나만이 지정하는 윈도우 클래스 이름이나 윈도우 캡션 이름을 검색하여 그게 존재하면 그 윈도우로 포커스를 옮겨 버리고 나는 실행을 종료한다. 대개, 이미 존재하는 인스턴스로 포커스를 옮겨 주는 작업이 필요할 것이므로 윈도우로 검색하는 방법은 어지간해서는 상당히 간편하고 직관적이고 좋은 방법이긴 하다. 다만,

만약 MFC 같은 프레임워크로 프로그램을 개발하고 있었다면, 메인 윈도우의 클래스 이름을 나만의 명칭으로 변경하기 위해 PreCreateWindow 같은 함수를 번거롭게 오버라이드해야 한다.

또한 클래스 이름이 아니라 캡션 이름으로 검색하는 것은 어지간해서는 피해야 한다. 캡션 이름 검색은 모든 top-level 윈도우들에 WM_GETTEXT 메시지를 보내는 방법으로 행해지기 때문에 오버헤드가 클 뿐만 아니라, 이미 실행된 내 프로그램 윈도우가 작업 중이어서 응답을 안 하고 있다면 프로그램 실행이 의도대로 되지 않을 우려가 크다.

윈도우로 검색하는 방법은 근본적으로 큰 약점이 있다. 일반적으로 프로그램이 실행된 직후 로딩, 각종 초기화를 끝내어 메인 윈도우를 생성하기까지는 적지 않은 시간이 소요된다는 것이다. 커널 오브젝트를 생성하는 것처럼 즉시 생성되는 게 아니다. 그렇기 때문에 첫 인스턴스가 아직 메인 윈도우를 만들기 전에 사용자가 실수나 고의로 또 엔터를 눌러서 둘째 인스턴스까지 실행한 경우 여전히 프로그램이 두 개가 실행되어 버릴 수가 있다. 프로그램이 어떤 경우에도 절대로 두 인스턴스 이상이 실행돼서는 안 되는 중요한 프로그램인 경우 윈도우 검색의 결과에만 의존해서는 안 된다.

※ 파일 차원에서 판단하는 법

윈도우 3.1 시절에는 WinMain 함수의 둘째 인자인 hPrevInstance를 살펴보는 것만으로도 내 프로그램의 중복 인스턴스를 판단할 수 있었다.
32비트 이후의 운영체제에서는 인스턴스 핸들의 의미가 한 주소공간 안의 포인터로 완전히 바뀌어 버렸기 때문에, 주소공간 자체가 독립적인 프로세스를 식별할 수는 없게 되었다. 오로지 그 주소공간 안에 로드되어 있는 여러 DLL 같은 모듈들만 식별할 수 있다.

그 반면, 지금도 EXE 내지 DLL 내부에 공유 가능한 섹션을 따로 생성하여 여기에 중복 인스턴스와 관련된 정보를 간단하게 집어넣을 수도 있다. 즉,
#pragma data_seg()
#pragma comment(linker, "/Section:SHARED,RWS")
이런 지시문 안에다가 전역변수를 선언하면 그 변수는 운영체제의 가상 메모리 상으로 나의 모든 인스턴스들이 공유하게 된다는 뜻이다. 자세한 것은 MSDN 참고. 번거롭게 메모리 맵드 파일 API를 호출할 필요 없이 간단한 데이터 공유에는 이 방법이 굉장히 편리하다.

이렇게 파일 차원에서 식별하는 방법은 윈도우 차원에서 식별하는 방법이 잠재적으로 갖고 있는 부작용들이 전혀 없어서 좋으나, 말 그대로 파일에 전적으로 종속적이라는 큰 한계가 있다.
같은 EXE를 이름만 바꿔 복사해서 실행한 것은 중복 인스턴스로 전혀 판단하지 못한다는 것이다. 이 점이 매우 중요하며, 이는 대부분의 경우 원치 않는 결과일 것이다. 결국 실행 파일 그 자체가 아니라 그 실행 파일이 만들어 놓은 결과를 추적해서 중복 실행을 판단하는 접근 방식이 필요하게 된다.

Posted by 사무엘

2010/02/08 22:38 2010/02/08 22:38
,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/176

« Previous : 1 : ... 7 : 8 : 9 : 10 : 11 : 12 : 13 : Next »

블로그 이미지

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

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2026/10   »
일 월 화 수 목 금 토
        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:
4199055
Today:
2697
Yesterday:
2133