65.9K
CodeProject 正在变化。 阅读更多。
Home

计算 C 或 C++ 代码的行数

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.40/5 (11投票s)

2005 年 12 月 29 日

2分钟阅读

viewsIcon

80085

downloadIcon

954

一篇关于统计代码行数的文章。

Sample Image

主窗口最初为 760x550(为文章而缩小)。

引言

这个程序只统计代码行数,但具有一些非常有用的功能。

  1. 我使用了 MFC 类 CFileFind 来递归地查找所选文件夹中的文件。
  2. 此版本识别的文件类型定义在 BOOL IsCplusplusFile(CString &lpszFileName) 中。
  3. 主窗口可以使用滚动条箭头或键盘按键(上、下、左、右、Home 和 End)滚动。
  4. 我使用了宏 _MSC_VER 进行条件编译,以便可以使用 VC++ 6.0 或 VC++ 8.0 编译代码。
  5. 主窗口居中显示在桌面上。
  6. 用户第一次运行程序时,浏览器将从项目目录启动。当程序退出时,上次选择的文件夹将保存到 ini 文件中,从而记住您上次的选择。
  7. 在程序会话期间,浏览器启动目录 m_szDirectory 会被更新并传递给函数 CALLBACK BrowseCallbackProc(....); 。“浏览文件夹”对话框也在 CALLBACK 函数中居中显示在桌面上。

背景

在 Microsoft Visual C++ 8.0 中,几个较旧的 C 函数被声明为已弃用;在大多数情况下,函数定义末尾添加了 _s,并且函数内部需要一个 size_t 变量。

例如

    // 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 ]             ]
        [__________________________________________]

预定义宏 _MSC_VER 定义了编译器版本:对于 Microsoft Visual C++ 6.0 定义为 1200,对于 Microsoft Visual C++ 8.0 定义为 1400。

使用代码

  1. 查找文件夹中的所有文件
    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. 获取 C 或 C++ 文件类型
    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. 键盘滚动
        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. 条件编译
    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. 居中显示主窗口
    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. 保存上次选择的文件
         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. 更新启动目录并居中显示窗口
    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;
    }

关注点

滚动条不会自动重绘,所以我添加了自己的消息处理程序。

#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;

历史

  • 2005 年 12 月 29 日 - 版本 1.0。
© . All rights reserved.