Click here to Skip to main content
15,890,845 members
Articles / Desktop Programming / MFC
Article

Count Lines of C or C++ Code

Rate me:
Please Sign up or sign in to vote.
3.40/5 (11 votes)
29 Dec 20052 min read 79K   952   17   10
An article on counting lines of code.

Sample Image

The main window is initially 760x550 (reduced for the article).

Introduction

This program only counts lines of code, but has some very useful features.

  1. I used the MFC class CFileFind to recursively find the files in the selected folder.
  2. The file types recognized by this version are defined in BOOL IsCplusplusFile(CString &lpszFileName).
  3. The main window is scrollable using the scroll bar arrows or the keyboard keys, up, down, left, right, Home, and End.
  4. I used the macro _MSC_VER in a conditional assembly so the code can be assembled using VC++ 6.0 or VC++ 8.0.
  5. The main window is centered on the desktop.
  6. The first time the user runs the program, the browser starts in the project directory. When the program exits, the last folder selected is saved to an ini file thus remembering what you selected last.
  7. During a session with the program, the browser start directory m_szDirectory is updated and passed to the function CALLBACK BrowseCallbackProc(....); . The Browse for Folder dialog is also centered on the desktop in the CALLBACK function.

Background

In Microsoft Visual C++ 8.0, several older C functions are declared as deprecated; in most cases, _s is added to the end of the function definition and a size_t variable is required inside the function.

For example:

// The char array is created on the stack.
char title[10]; // should have been MAX_PATH.
// VC++ 6.0 function.
strcpy(title, "Open Dialog Box");
// strcpy(..); has no array bounds checking, the string literal is 16 bytes.
// The next 6 bytes in stack are overwritten and the program crashes.

// VC++ 8.0 function.
strcpy_s(title, MAX_PATH, "Open Dialog Box");
// This will throw and exception in the Debug version and a Message Box
// will be displayed.

     __________________________________________
    [ Microsoft Visual Studio                  ]
    [------------------------------------------]
    [                                          ]
    [ Run-Time Check Failure #2 - Stack around ]
    [ the variable 'title' was corrupted.      ]
    [                                          ]
    [    [ Break ]    [ Continue ]             ]
    [__________________________________________]

The predefined macro _MSC_VER defines the compiler version: defined as 1200 for Microsoft Visual C++ 6.0 and defined as 1400 for Microsoft Visual C++ 8.0.

Using the code

  1. Find all files in a folder:
    VOID GetFileNamesInDirectory(CString &lpszDirectory)
    {
        CFileFind finder;
        CString   strFullPath;
        BOOL      bWorking = FALSE;
        CString   strWildcard(lpszDirectory);
        if (strWildcard.Right(1) != "\\")
            strWildcard += "\\*.*";
        bWorking = finder.FindFile(strWildcard);
        while (bWorking)
        {
            bWorking = finder.FindNextFileA();
            if (finder.IsDots())
                continue;
            if (finder.IsDirectory())
            {
                strFullPath = finder.GetFilePath();
                GetFileNamesInDirectory(strFullPath);
            }
            else
            {
                strFullPath = finder.GetFilePath();
                szaFiles.Add(strFullPath);
            }
        }
        finder.Close();
    }
  2. Get the C or C++ file type:
    BOOL IsCplusplusFile(CString &lpszFileName)
    {
        char    szExt[_MAX_EXT];
    #if _MSC_VER < 1400
        _splitpath(lpszFileName, NULL, NULL, NULL, szExt);
    #else
        _splitpath_s(lpszFileName, NULL, 0, NULL, 
                    0, NULL, 0, szExt, _MAX_EXT);
    #endif
        if (!_stricmp(szExt, ".c")    ||
            !_stricmp(szExt, ".cpp")||
            !_stricmp(szExt, ".dsp")||
            !_stricmp(szExt, ".dsw")||
            !_stricmp(szExt, ".h")    ||
            !_stricmp(szExt, ".hpp")||
            !_stricmp(szExt, ".rc") ||
            !_stricmp(szExt, ".vcproj"))
        {
            return TRUE;
        }
        return FALSE;
    }
  3. Keyboard scrolling:
    case WM_KEYDOWN:
        switch(wParam) {
            case VK_LEFT:
                SendMessage(hwnd, WM_HSCROLL, SB_LINELEFT, 0);
                break;
            case VK_RIGHT:
                SendMessage(hwnd, WM_HSCROLL, SB_LINERIGHT, 0);
                break;
            case VK_DOWN:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
                break;
            case VK_UP:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
                break;
            case VK_HOME:
                SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
                break;
            case VK_END:
                SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
                break;
            }
      return 0;
    
  4. Conditional assembly:
    int GetNumberOfLinesInFile(CString &lpszFileName)
    {
        FILE    *lpFile;
        int        nNumberOfLines;
    #if _MSC_VER < 1400
        lpFile = fopen(lpszFileName, "r");
    #else
        errno_t ernum;
        ernum = fopen_s(&lpFile, lpszFileName, "r");
    #endif
        nNumberOfLines = 0;
        if (lpFile != NULL)
        {
            char ch;
            do
            {
                ch = fgetc(lpFile);
                if (ch == '\n')
                {
                    nNumberOfLines ++;
                }
            }while (ch != EOF);
        fclose(lpFile);
        }
        return nNumberOfLines;
    }
  5. Center main window:
    void CenterWindow(HWND hwnd)
    {
        int x, y;
        HWND hwndDeskTop;
        CRect rcWnd, rcDeskTop;
        // Get a handle to the desktop window
        hwndDeskTop = ::GetDesktopWindow();
        // Get dimension of desktop in a rect
        ::GetWindowRect(hwndDeskTop, &rcDeskTop);
        // Get dimension of main window in a rect
        ::GetWindowRect(hwnd, &rcWnd);
        // Find center of desktop
        x = rcDeskTop.Width() / 2;
        y = rcDeskTop.Height() / 2;
        x -= rcWnd.Width() / 2;
        y -= rcWnd.Height() / 2;
        // Set top and left to center main window on desktop
        ::SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
    }
  6. Save last selected file:
    case WM_DESTROY:
         WritePrivateProfileString("CountLinesApp",
           "DIRECTORY", m_szDirectory, szAppIniFile);
         PostQuitMessage (0) ;
    
         return 0 ;
    
    case WM_CREATE:
       GetCurrentDirectory(MAX_PATH, m_szDirectory);
       szAppIniFile = m_szDirectory;
       szAppIniFile += "\\CountLinesApp.ini";
       dwNum = GetPrivateProfileString("CountLinesApp",
           "DIRECTORY", "C:\\", m_szDirectory,
           MAX_PATH, szAppIniFile);
       if (dwNum == 3)
           GetCurrentDirectory(MAX_PATH, m_szDirectory);
    
  7. Update start directory and center window:
    int CALLBACK BrowseCallbackProc(HWND hwnd, 
                 UINT uMsg, LPARAM lParam, LPARAM pData)
    {
        switch(uMsg)
        {
            case BFFM_INITIALIZED:
            {
                int x, y;
                HWND hwndDeskTop;
                CRect rc, rcDeskTop;
                // Get dimension of dlg window in a rect
                ::GetWindowRect(hwnd, &rc);
                // Get a handle to the desktop window
                hwndDeskTop = ::GetDesktopWindow();
                // Get dimension of desktop in a rect
                ::GetWindowRect(hwndDeskTop, &rcDeskTop);
                // Find center of client
                x = rcDeskTop.Width() / 2;
                y = rcDeskTop.Height() / 2;
                x -= rc.Width() / 2;
                y -= rc.Height() / 2;
                // Set top and left to center dlg window on Desk Top
                SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
                LPCTSTR lpszPath = m_szDirectory;
                TCHAR szTemp[MAX_PATH];
                if(lpszPath==NULL)
                {
                    ::GetCurrentDirectory(MAX_PATH, szTemp );
                    lpszPath = szTemp;
                }
                // WParam is TRUE since you are passing a path.
                // It would be FALSE if you were passing a pidl.
                ::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,
                    (LPARAM)lpszPath);
                break;
            }
            case BFFM_SELCHANGED:
            {
                char szSelection[MAX_PATH];
                if(!::SHGetPathFromIDList((LPITEMIDLIST)lParam, szSelection) ||
                    szSelection[1]!=':')
                {
                    szSelection[0] = '\0';
                    ::SendMessage(hwnd, BFFM_ENABLEOK, 0, FALSE);
                }
                else
                {
                    ::SendMessage(hwnd, BFFM_ENABLEOK, 0, TRUE);
                }
                ::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)szSelection);
    
                break;
            }
        default:
            break;
        }
        return 0;
    }

Points of Interest

The scroll bars are not automatically redrawn so I added my own message handler.

#define    WM_DRAW_BAR WM_USER + 1
     case WM_DRAW_BAR:
         // Set vertical scroll bar range and page size
         si.cbSize = sizeof (si) ;
         si.fMask  = SIF_ALL ;
         si.nMin   = 0 ;
         si.nMax   = nVMax ;
         si.nPage  = nVPage;
         si.nPos   = 0 ;
         si.nTrackPos = 0;
         SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
         // Set horizontal scroll bar range and page size
         si.cbSize = sizeof (si) ;
         si.fMask  = SIF_ALL ;
         si.nMin   = 0 ;
         si.nMax   = nHMax ;
         si.nPage  = nHPage ;
         si.nPos   = 0 ;
         si.nTrackPos = 0;
         SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
         return 0;

History

  • December 29, 2005 - Version 1.0.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Jeff Onsager7-Jul-16 6:12
Jeff Onsager7-Jul-16 6:12 
QuestionIt is quite useful tool and easy to use, Thanks for publishing Pin
Ravikishore.Pochiraju12-Apr-13 3:00
Ravikishore.Pochiraju12-Apr-13 3:00 
Generalvery handy, I made one change to ignore blank lines Pin
wgsarver12-Jan-09 9:46
wgsarver12-Jan-09 9:46 
Generalbooyakasha Pin
mark-w8-Feb-07 5:24
mark-w8-Feb-07 5:24 
Generalgood work! I think multithread maybe better for large project! Pin
angelbear4-Jan-06 15:55
angelbear4-Jan-06 15:55 
GeneralVery nice, i jut made such code a few days ago... Pin
Roey C3-Jan-06 21:44
Roey C3-Jan-06 21:44 
GeneralQuestion Pin
Stanciu Vlad29-Dec-05 10:05
Stanciu Vlad29-Dec-05 10:05 
GeneralRe: Question Pin
Roger6529-Dec-05 10:38
Roger6529-Dec-05 10:38 
GeneralA screenshot would have been nice! Pin
Nish Nishant29-Dec-05 5:34
sitebuilderNish Nishant29-Dec-05 5:34 
GeneralRe: A screenshot would have been nice! Pin
Nish Nishant29-Dec-05 5:40
sitebuilderNish Nishant29-Dec-05 5:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.