notepad2 컴파일 삽질기 1 : 기본적인 오류 수정

윈도우의 메모장을 대치할 수 있는 간단한 텍스트 편집기 중에 notepad2가 있습니다.
이 프로그램의 장점은 무엇보다 실행파일 하나로 구성되어 있어 언제나 갖고다니기 편하단 점입니다.
또한 널리 쓰이는 Scintilla를 기반으로 되어있어 안정성도 어느정도 검증되었다는 장점도 있습니다.

사용자 삽입 이미지

하지만, notepad2에는 다소의 문제점이 있습니다.

1. 한글 IME를 제대로 인식하지 않아 한글 입력 화면이 부자연스러움
2. 원 소스 자체에 약간의 에러 및 버그가 있음 (실행파일을 다운받을 땐 상관 없습니다)

이 외에도 VS.Net(7.0)으로 개발되어 이후 버전의 컴파일러로 컴파일시 약간 불편하단 점도 있더군요.


그래서, Visual Studio 8.0 Express Edition에서 notepad2의 소스를 수정하고 컴파일 해봤습니다.
기본적인 IME 부분 수정은 codewiz님의 방법을 참고했으며, 나머지 부분들은 사용 중에 문제가 있는 부분들 중심으로 수정했습니다.

사전 준비

1. Visual Studio 준비

Visual Studio 8.0 Express Edition에서 다운받을 수 있습니다.
※ 다른 버전(VS.Net 2003, VS 2005 등)에서는 일부 수정 방법이 아주 조금씩 다를 수 있습니다.


2. 소스 다운로드 및 적절한 폴더 설정

Notepad2, Scintilla 에서 각각 소스 파일을 다운로드 받습니다.
이 글을 쓰는 현재 기준으로 notepad2는 3.0.20이, Scintilla는 1.77이 최신버전입니다
일단 Notepad2의 소스의 압축을 풉니다.
다음으로, Scintilla의 소스를 notepad2의 소스 폴더 내에 압축을 풉니다.

사용자 삽입 이미지

이런 구성으로 폴더를 구성하면 됩니다


3. 솔루션 파일 변환

notepad2 소스의 루트 폴더에 Notepad2.sln이라는 솔루션 파일이 있습니다.
이 파일을 VS2008에서 읽어주면 변환은 끝납니다.
여기서 작업이 시작합니다.


step1. 각종 오류/버그 수정

오류/버그 수정은 파일 단위로 적겠습니다.

1. Dlapi.h

Dlapi.h 파일의 맨 마지막 부분에 아래의 3줄을 추가합니다.
#ifndef LPSHELLICON
typedef struct IShellIcon IShellIcon, *LPSHELLICON;
#endif


2. Edit.c

void EditTitleCase(HWND hwnd) 메쏘드에서
if (!IsCharAlphaNumericW(pszTextW[i]) && !StrChr(L"'?,pszTextW[i])) {
라는 행을 찾아
if (!IsCharAlphaNumericW(pszTextW[i]) && !StrChr(L"'",pszTextW[i])) {
로 바꿔줍니다.
이건 소스 쪽 오타입니다. ㅠ.ㅠ


3. Helpers.c

void FormatBytes(LPWSTR lpOutput,int nOutput,DWORD dwBytes)에서
int i;
라는 행을 찾아
int i, iLen;
로 바꿔줍니다.

다음으로
swprintf(tch,L"%.2f",dBytes);
라는 행을 찾아
iLen = wsprintf(tch,L" %i",(DWORD)(dBytes*100));
memcpy(tch, tch+1, (iLen-3)*sizeof(WCHAR));
tch[iLen-3] = L'.';
의 세 줄로 수정합니다.


4. Notepad2.rc

"afxres.h"가 들어간 두 행을 찾습니다.
#include "afxres.h"

"#include ""afxres.h""\0"
입니다.


이 두 줄을 각각
#include "windows.h"
#ifndef IDC_STATIC
#define IDC_STATIC              (-1)
#endif

"#include ""windows.h""\0"
으로 바꿔줍니다.
Express Edition에는 afxres.h 파일이 없기 때문입니다.
(VS2005/VS2008의 리테일 버전에서는 이 작업은 필요없습니다)


다음으로,
IDR_RT_MANIFEST         RT_MANIFEST             "..\\res\\Notepad2.exe.manifest"
라는 manifest 지정행이 있는데, 이것을
//IDR_RT_MANIFEST         RT_MANIFEST             "..\\res\\Notepad2.exe.manifest"
과 같이 주석처리합니다.


5. Notepad2.h

VS2008에선 앞의 4번의 아랫부분과 같이 주석처리해도 이상없이 마니페스트 파일을 링크하던데, VS2005는 좀 다르더군요.
VS2005에서 제대로 마니페스트 파일을 링크하려면 Notepad2.h의 맨앞에
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
를 추가해야 합니다.


6. KeyWords.cxx
#define LINK_LEXER(lexer) extern LexerModule lexer; forcer += lexer.GetLanguage();
를 찾아서 다음과 같이 수정합니다.
//#define LINK_LEXER(lexer) extern LexerModule lexer; forcer += lexer.GetLanguage();
#define LINK_LEXER(lexer)

LINK_LEXETR에서 링크하는 외부모듈의 역할은 정확히 모르겠지만,
링크를 원천적으로 차단해버려야 제대로 링크가 되더군요... ㅡㅡ;;;


7. Styles.c

Notepad2/Scintilla에는 약간 이상한 버그가 하나 있습니다.
글꼴을 지정하는 과정에서 charset이 0으로 기록되면 다른 프로그램으로의 복사/붙이기가 정확하게 이루어지지 않는다는 점입니다.
(okto님께서 Notepad2 제작자에게 문의를 하셨지만, Scintilla 쪽 버그라 손을 안 대겠단 답을 하셨답니다)

이 문제를 간단히 해결하기 위해 charset이 0인 경우는 기록하지 않도록 수정하겠습니다.

BOOL Style_SelectFont(HWND hwnd,LPWSTR lpszStyle,int cchStyle,BOOL bDefaultStyle) 메쏘드의 두 군데를 수정하면 됩니다.
우선, 아래와 같은 부분을 찾습니다.
if (bDefaultStyle &&
    lf.lfCharSet != DEFAULT_CHARSET &&
    lf.lfCharSet != iDefaultCharSet) {
  lstrcat(szNewStyle,L"; charset:");
  wsprintf(tch,L"%i",lf.lfCharSet);
  lstrcat(szNewStyle,tch);
}
이곳을 아래와 같이 고쳐줍니다.
if (bDefaultStyle &&
    lf.lfCharSet != DEFAULT_CHARSET &&
    lf.lfCharSet != iDefaultCharSet &&
    lf.lfCharSet) {
  lstrcat(szNewStyle,L"; charset:");
  wsprintf(tch,L"%i",lf.lfCharSet);
  lstrcat(szNewStyle,tch);
}


다음으로 이렇게 생긴 부분을 찾습니다.
if (Style_StrGetCharSet(lpszStyle,&iValue))
{
  if (lstrlen(szNewStyle))
    lstrcat(szNewStyle,L"; ");
  wsprintf(tch,L"charset:%i",iValue);
  lstrcat(szNewStyle,tch);
}
이곳은 이렇게 고칩니다.
if (Style_StrGetCharSet(lpszStyle,&iValue) && iValue)
{
  if (lstrlen(szNewStyle))
    lstrcat(szNewStyle,L"; ");
  wsprintf(tch,L"charset:%i",iValue);
  lstrcat(szNewStyle,tch);
}