요번에 만든 프로그램은 지정된 폴더의 모든 하위 폴더를 뒤져서, 폴더 별 소스 라인 수를 구하는 프로그램입니다. 개인적으로 회사 내 프로젝트의 라인 수를 구하기 위해 어제 저녁에 잠깐 동안 만든 프로그램이라, 완성도는 상당히~~ 떨어집니다 ^^;
프로그램 간단 설명
설명이라고 특별히 할만한 내용이 없습니다^^; 폴더 위치 입력기에 프로젝트의 소스 폴더를 입력하시고, [소스 라인 구하기]를 쿡 눌러주면 됩니다.
해당 폴더의 모든 하위 폴더를 재귀적으로 탐색 한 뒤에는 결과를 프로그램이 실행된 폴더의 result.csv 파일로 저장한 후 엑셀을 실행하도록 되어 있습니다. (결과 리스트에 내용을 채울까 하다가 리스트 컨트롤 만드는 법도 거의 다 잊었고, 어차피 결과를 한눈에 보고 필터링하기에는 엑셀이 좋을 것 같아서 텍스트 포맷인 확장자 .CSV 로 저장하고, 엑셀에서 읽어 들이도록 했습니다)
결과 엑셀 파일은
- Folder – 현재 폴더 명
- Depth – 하위 폴더로 내려갈수록 Depth 가 1 증가합니다.
- Lines(#) – 해당 폴더에 있는 소스 파일(*.cpp, *.c, *.h) 의 라인 수를 의미합니다. 공백 라인, 주석라인(// 및 /* ~ */) 은 제외한 라인 수입니다.
- CPP(#) – 현재 폴더의 C 또는 CPP 파일 개수 입니다.
- H(#) – 헤더 파일의 개수입니다.
- Sub Total Lines(#) – 해당 폴더의 하위 폴더의 라인 합계 입니다.
- Sub Total CPP(#) – 해당 폴더의 하위 폴더에 있는 C 또는 CPP 폴더 합계입니다.
- Sub Total H(#) – 해당 폴더의 하위 폴더에 있는 헤더 파일 합계입니다.
마지막으로 엑셀 파일의 맨 끝에는 전체 라인 수와 전체 소스 파일 개수를 넣어주도록 했습니다. 자세한 내용은 프로그램을 잠깐 써 보시면 금방 아시리라 봅니다.
프로그램 다운로드
프로그램 소스 뒷담화
작업하면서 간단하게나마 헤맸던 부분(어제 저녁에 몇 시간 작업한 걸로 헤맸다고 볼 순 없을 것도 같지만 ^^)이나 생각할만한 내용들입니다.
혼동되는 유니코드 사용 문제
얼마 전 유니코드로 개발하는 방법에 대한 글을 포스팅 한 적이 있습니다.
http://eslife.tistory.com/entry/유니코드로-개발하기
아직 유니코드에 대해서 초짜이다 보니, 파일 관련 작업을 하면서 헤매는 부분이 많습니다. 기존에 MBCS 용으로 만든 소스를 가져다 사용했는데(CStdioFile 대용으로 만든 작은 클래스) 역시나 파일의 내용이 제대로 읽혀지지 않더군요. 기존에 char 로 되어 있던 부분을 대부분 TCHAR로 무식하게 변경한 소스는 아래와 같습니다.
int CSourceLines::GetMBCSLineCnt(const TCHAR* lpFile)
{
int nLineCnt = 0;
HANDLE hFile;
hFile = CreateFile(lpFile,GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
int nFileSize = GetFileSize(hFile, NULL);
TCHAR* pContent = new TCHAR[nFileSize + sizeof(TCHAR)];
pContent[nFileSize ] = 0;
DWORD dwRead;
ReadFile(hFile, pContent, nFileSize, &dwRead, NULL);
CloseHandle(hFile); // 파일닫기
// 중략
}
이와 같이 했을 때 pContent 에는 예상대로라면은 읽어들인 소스 파일의 내용이 들어 있어야 하는데, 실제 들어가 있는 내용은 깨져 있었습니다. 그도 그럴 것이, ReadFile API 는 파일의 내용을 그대로 읽어들여 메모리에 채우기 때문에, 소스 파일이 MBCS 인 경우에는 TCHAR 가 아닌 일반 char로 읽어들여야 했습니다. 여기서 제가 생각했던 룰이 깨지더군요. 유니코드라고 해서 무조건 char 를 TCHAR 로 변경하면 안된다였습니다 --; 언제나 그렇듯이 프로그램이 그리 호락호락한 적은 드물었던 것 같습니다.
제대로 읽어들이는 소스는 아래와 같습니다. (결국 원래 소스 그대로 두면 되는 거였는데 엉뚱한 삽질을 --)
int CSourceLines::GetMBCSLineCnt(const TCHAR* lpFile)
{
int nLineCnt = 0;
HANDLE hFile;
hFile = CreateFile(lpFile,GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
int nFileSize = GetFileSize(hFile, NULL);
char* pContent = new char[nFileSize + sizeof(char)];
pContent[nFileSize ] = 0;
DWORD dwRead;
ReadFile(hFile, pContent, nFileSize, &dwRead, NULL);
CloseHandle(hFile); // 파일닫기
// 중략
}
메시지 루프와 간단한 Sink 인터페이스
하위 폴더의 모든 소스를 읽어 들여 소스 내 라인 수를 계산하다 보니, 폴더 내 소스 파일이 많을 경우 제법 오랜 시간 대화상자가 먹통이 되는 경우가 생겼습니다. 이를 해결하기 위해 while 루프 중간에 간단한 메시지 루프를 넣었습니다.
// 간단한메시지루핑
while(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
그리고, 요청을 한 메인 대화상자에 현재 작업 중인 폴더 정보를 전달하기 위해 간단한 Sink 인터페이스를 하나 만들고
interface ISourceLineSink
{
virtual ~ISourceLineSink(){}
virtual void SetCurFolder(const TCHAR* lpFolder) = 0;
};
대화상자가 이 인터페이스를 상속받게 한 다음
class CSourceCountDlg :
public CDialog,
public ISourceLineSink
인터페이스를 통해 현재 검색 중인 폴더의 정보를 대화상자로 전달하도록 코드를 만들었습니다. 대화상자는 인터페이스 구현부에서 전달된 문자열을 단순히 대화상자에 있는 상태 표시줄에 표시하도록 한 줄만 코딩 했습니다.
void CSourceCountDlg::SetCurFolder(const TCHAR* lpFolder)
{
GetDlgItem(IDS_STATUS)->SetWindowText(lpFolder);
}
맑은 고딕 지원 문제
대화상자를 처음에는 ‘맑은 고딕’ 폰트를 기본으로 지정했습니다. 그래서 비스타에서는 위와 같은 대화상자가 보이지만(비스타에서도 역시 어설픕니다) ‘맑은 고딕’ 폰트가 없는 윈도우에서는 대화상자가 형편없이 깨져버립니다.
일반적으로 이와 같은 경우엔 대화상자의 기본 폰트를 ‘돋움’ 이나 ‘굴림’ 등의 일반적인 폰트를 사용하면 되는데, 그냥 조금 오버해서, ‘맑은 고딕’ 용 대화상자와 ‘돋움’ 용 대화상자 2개를 만들었습니다. (단순히 리소스 뷰에서 대화상자를 복사하고, 속성에서 폰트만 변경했습니다)
그런 다음 대화상자를 시작하기 전에 시스템에 ‘맑은 고딕’ 폰트가 있는 지 체크하는 함수를 하나 두고 폰트의 존재 여부에 따라 리소스 아이디를 다르게 지정하도록 했습니다.
HDC dc = GetDC(GetDesktopWindow());
UINT uDlgID = IDD_SOURCECOUNT_DIALOG;
uDlgID = (::EnumFontFamilies(dc, _T("맑은고딕"), (FONTENUMPROC) EnumFontFamProc, 0) == 0) ? IDD_SOURCECOUNT_DIALOG :IDD_SOURCECOUNT_DIALOG1;
ReleaseDC(GetDesktopWindow(), dc);
CSourceCountDlg dlg(uDlgID, NULL);
대화상자의 생성자로 전달된 리소스 ID 를 이용할 수 있도록 추가적인 생성자를 아래와 같이 만들었습니다.
CSourceCountDlg::CSourceCountDlg(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
: CDialog(nIDTemplate, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
사실 소스 라인 구하는 프로그램에 이런 삽질은 그리 할만한 작업은 아니지만, 앞으로 시스템에 없는 폰트로 대화상자를 개발할 때는 참고할만한 내용일 듯 해서 적어 봤습니다.
마지막으로
열심히 만들고 보니, 아니나 다를까 이미 훌륭하게 소스 코드 라인 수를 구해 주는 좋은 프로그램이 나와있네요
회사 동료가 알려준 Line Counter 라는 Visual Studio 플러그 인 프로그램이 있는데, 써보니 아주 훌륭합니다.
제가 만든 거랑 차이점이 있다면, 폴더가 아닌 솔루션에 들어 있는 프로젝트의 소스파일, RC 파일, 텍스트 파일 등의 모든 라인 수를 합쳐주고, 결과창도 훨씬 상세하게 보여주는 군요. 관심 있으신 분들은 이 프로그램도 한번 사용해 보시길.
'개발' 카테고리의 다른 글
AfxGetInstanceHandle() 함수가 NULL 을 리턴하는 경우 (0) | 2008.05.13 |
---|---|
XmlLite 을 이용한 메뉴 XML 로딩하기 (13) | 2008.04.16 |
Visual Studio 2008 Profiler (7) | 2008.03.25 |
Visual Studio 2005의 Code Analysis 기능 (3) | 2008.03.11 |
마이너가 된 IE 8, 그리고 생각해 봐야 할 하위호환성 문제 (4) | 2008.03.07 |
유니코드로 개발하기(MFC 프로젝트) (14) | 2008.03.05 |
댓글