本章目录:
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