Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Count Lines of C or C++ Code

0.00/5 (No votes)
29 Dec 2005 1  
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