当前位置:C++技术网 > 资讯 > MFC学习实践之简易音乐播放器(五)添加目录扫描、播放模式及最小化等功能(终章)

MFC学习实践之简易音乐播放器(五)添加目录扫描、播放模式及最小化等功能(终章)

更新时间:2017-04-04 10:14:52浏览次数:1+次

    本章目录:

    1.音乐文件扫描

    2.播放模式功能

    3.最小化功能

    4.鼠标右键菜单及文件定位

    5.结束语

        从第一章到现在,功能慢慢地添加完善,中间也耽搁了好长时间,趁着清明,时间充足,把它写完了!

        到现目前为止,主界面如下:

    

    

    1.音乐文件扫描功能

        文件扫描主要用到FindFirstFile、FindNextFile及FindClose三个API函数。

        函数原型为(以UNICODE版本为例

    

WINBASEAPI __out HANDLE WINAPI FindFirstFileW(
 __in LPCWSTR lpFileName,_
 _out LPWIN32_FIND_DATAW lpFindFileData);

WINBASEAPI BOOL WINAPI FindNextFileW(
 __in HANDLE hFindFile,
 __out LPWIN32_FIND_DATAW lpFindFileData );

WINBASEAPI BOOL WINAPI FindClose( __inout HANDLE hFindFile);

    (1)FindFindFirstFile有两个参数:

    第一个参数lpFileName String,欲搜索的文件名。可使用*通配符。例如搜索D:\KuGou目录下所有的MP3文件,可设置为:D:\KuGou\*.mp3。

    第二个参数lpFindFileDataWIN32_FIND_DATA,这个结构用于装载与找到的文件有关的信息。该结构可用于后续的搜索。

    

 typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;

    

    函数返回值:

    一个HANDLE,如执行成功,返回一个搜索句柄。如果出错,返回一个INVALID_HANDLE_VALUE常数,一旦不再需要,应该用FindClose函数关闭这个句柄。

    

    (2)FindNextFile

     根据调用FindFirstFile函数时指定的一个文件名查找下一个文件。

    函数参数类型及说明:
hFindFile Long,上一次查找返回的文件句柄
lpFindFileData WIN32_FIND_DATA,这个结构用于装载与找到的文件有关的信息。该结构可用于后续的搜索
函数返回值:

    一个BOOL,如执行成功,返回TRUE。否则为FALSE。

    

    (3)FindClose

    关闭FindFirstFile创建的搜索句柄
函数参数类型及说明:
HANDLE hFindFile;// FindFirstFile创建的句柄
函数返回值:
调用成功 返回一个非0值
调用失败 返回0 可利用GetLastError来得到错误信息

    具体的用法可自行搜索。

    (4)音乐文件扫描函数

    利用函数递归,循环遍历选择的文件目录下的搜索MP3文件。(可另开线程搜索,利用多线程提升搜索速度)

    

/*
函数功能:
遍历dirpath文件夹且包含子文件夹下的所有mp3文件,文件大小限制:2M以上限定,可修改
@param
TCHAR *dirpath 待搜索的文件夹路径
返回值int类型
返回搜索到的音乐文件数量
*/
    int CMusicPlayerDlg::EnumFileToList(TCHAR *dirpath)//音乐文件遍历
    {
        WIN32_FIND_DATA wfd;
        int sum = 0;
        TCHAR pathname[MAX_PATH];
        ZeroMemory(pathname, MAX_PATH);
        wsprintf(pathname, L"%s\\*.*", dirpath);//搜索的字符串组合
        HANDLE hfd = FindFirstFile(pathname, &wfd);//开始查找文件
        if (hfd == INVALID_HANDLE_VALUE)
        {
            return sum;
        }
        while (FindNextFile(hfd, &wfd))//循环搜索
        {
            //判断是否属于当前路径或上一级路径 .代表当前路径 ..代表上一级路径
            if (lstrcmp(wfd.cFileName, L".") != 0 && lstrcmp(wfd.cFileName, L"..") != 0)
            {
                if (wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)//判断是否属于文件夹
                {
                    TCHAR tm[MAX_PATH];
                    ZeroMemory(tm, MAX_PATH);
                    wsprintf(tm, L"%s\\%s", dirpath, wfd.cFileName);
                    sum += EnumFileToList(tm);//如果是文件夹继续搜索子文件夹
                }
                //如果是文件
                else
                {
                    DWORD size = wfd.nFileSizeHigh*(MAXDWORD)+wfd.nFileSizeLow;//获取文件大小
                    if (StrStr(wfd.cFileName, L".mp3") && size>(2 * 1024 * 1024))//搜索的条件:MP3后缀名的文件且文件大小大于2M
                    {
                        TCHAR temp[MAX_PATH];
                        ZeroMemory(temp, MAX_PATH);
                        wsprintf(temp, L"%s\\%s", dirpath, wfd.cFileName);
                        AddMusicToList(temp);//添加只音乐文件列表
                        sum++;
                    }
                }
            }
        }
        FindClose(hfd);//关闭句柄
        return sum;
    }

    

     2.播放模式功能

    

    这里只设置了四个常用的模式:顺序播放,列表循环,随机播放,单曲循环。

    其中随机播放利用了随机数的方法,随机生成0到音乐总数之间的随机数。

    其中模式控制音乐播放的功能函数为:

    

int CMusicPlayerDlg::PlayMusic(bool next)//next是否为下一曲
    {

        int count = m_ctrMusicList.GetItemCount();//获取音乐列表文件总数
        int ModeIndex = m_ctrMode.GetCurSel();//取得当前模式设置
        switch (ModeIndex)//模式分支
        {
        case 0://顺序播放
            if (m_nowPlayIndex < 0) break;//如果整个列表播放结束,则停止
        case 1://列表循环
        {
            if (next)
            {
                
                if (m_nowPlayIndex < (count - 1))//音乐边界判断
                {
                    //m_nowPlayIndex += 1;
                    m_ctrMusicList.SetItemState(m_nowPlayIndex + 1, LVIS_SELECTED, LVIS_SELECTED);
                    OnClickedIdplay();
                }
                else
                {
                    MessageBox(_T("当前已是最后一首音乐!"));
                }
            }
            else
            {
                if (m_nowPlayIndex >0)//音乐边界判断
                {
                    //m_nowPlayIndex -= 1;
                    m_ctrMusicList.SetItemState(m_nowPlayIndex - 1, LVIS_SELECTED, LVIS_SELECTED);
                    OnClickedIdplay();
                }
                else
                {
                    MessageBox(_T("当前已处于第一首音乐!"));
                }
            }
            
        }
        break;
        case 2://随机播放
        {
            srand(time(NULL));
            int rd = rand() % count;//产生随机数
            m_ctrMusicList.SetItemState(rd, LVIS_SELECTED, LVIS_SELECTED);
            OnClickedIdplay();
        }
        break;
        case 3://单曲循环
        {
            OnClickedIdstop();
            m_ctrMusicList.SetItemState(m_nowPlayIndex, LVIS_SELECTED, LVIS_SELECTED);
            OnClickedIdplay();
        }
        default:
            break;
        }
        return 0;
    }

    

    3.程序最小化

    程序最小化设计主要有两个步骤:

    (1)添加程序托盘图标,类似于任务栏中的QQ,360等小图标。

    (2)添加托盘响应消息,来达到隐藏窗口及显示窗口的目的

    程序最小化功能主要用到Shell_NotifyIcon   API函数。

    Shell_NotifyIcon,主要用于向任务栏的状态栏发送一个消息。

    函数原型:

    

BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA lpdata
);

    使用方法详情链接:百度百科

    可在对话框初始化函数中添加托盘图标。

    功能函数为:

    

int CMusicPlayerDlg::AddMinIcon()
{
    m_min.cbSize = sizeof(NOTIFYICONDATA);
    m_min.hIcon = AfxGetApp()->LoadIconW(IDR_MAINFRAME);
    m_min.hWnd = this->m_hWnd;
    wsprintf(m_min.szTip, L"音乐播放器");
    m_min.uCallbackMessage = WM_USER_MIN;
    m_min.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    Shell_NotifyIcon(NIM_ADD, &m_min);
    return 0;
}

    

    

    WM-USER_MIN 为宏定义常量

    #define WM_USER_MIN (WM_USER+100)

    消息响应函数为:

    

LRESULT CMusicPlayerDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: 在此添加专用代码和/或调用基类
    switch (message)
    {
    case WM_USER_MIN:
        switch (lParam)
        {
        case WM_LBUTTONDOWN://鼠标左键点击 窗口显示与隐藏
        {
            if (this->IsWindowVisible())
                this->ShowWindow(SW_HIDE);
            else
                this->ShowWindow(SW_SHOW);
        }
            break;
        case WM_RBUTTONDOWN://鼠标右键点击 弹出功能菜单
        {
            CMenu cm;
            cm.LoadMenu(IDR_MENUMIN);//加载预定义菜单
            CMenu *pm = cm.GetSubMenu(0);
            POINT p;
            GetCursorPos(&p);
            pm->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, AfxGetMainWnd());//弹出菜单
        }
        break;
        default:
            break;
        }
    default:
        break;
    }
    return CDialogEx::WindowProc(message, wParam, lParam);
}

    

    程序托盘的功能菜单预定义为:

    

    

     4.鼠标右键菜单弹出及文件定位

    (1)右键菜单弹出常用方法如下:

    

CMenu cm;//定义菜单变量
 cm.LoadMenu(IDR_MENUMAIN);//加载菜单资源
 CMenu *pm = cm.GetSubMenu(0);//取得菜单功能组
 m_ctrMusicList.ClientToScreen(&p);//坐标变换,取得鼠标当前所在为位置
 pm->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, AfxGetMainWnd());//在鼠标当前位置弹出功能菜单

    这里比较复杂的一点就是获取鼠标所在的坐标位置,坐标定位与变换方法可自行搜索。

    (2)文件定位

    在资源管理器中进行文件定位主要是利用explorer.exe系统程序自带的一些命令参数。命令行详细参数说明在百度百科

    具体程序代码为:

    

void CMusicPlayerDlg::OnMenushowinexp()
    {
        // TODO: 在此添加命令处理程序代码
        int index = m_ctrMusicList.GetNextItem(-1, LVIS_SELECTED);//列表选项判断
        CString* str = (CString*)m_ctrMusicList.GetItemData(index);
        TCHAR szParam[MAX_PATH] = L"";
        wsprintf(szParam, L"/select,\"%s\"", *str);
        ShellExecute(NULL, L"open", L"explorer",szParam, NULL, SW_SHOW);
    }

    

    

    5.结束语

        到这儿,这个音乐播放器也是告一段落了,对于我的编程学习之路,总结下来也就是这么几句话。书中代码看百遍不如照着代码敲一遍,照着代码敲一遍不如举一反三自己写一遍,写一遍不如学习之后总结归纳,从策划设计优化,完整的工程项目走一遍。

        整个播放器的功能基本上是完成了,但还是有许多缺陷,许多bug,例如文件扫描功能只是识别了MP3文件,有兴趣的可自行在函数中添加wmv等格式。播放器界面也有待提升,每次打开都是一如既往的丑(自我吐槽下),我已经在百度网盘上分享了,有兴趣的可自行完善添加或修改。

        至此,结束!

    链接:http://pan.baidu.com/s/1nvltSql 密码:4uq4