2024年2月25日发(作者:七天退换)
基于MFC运用windows media player控件实现的多媒体播放器
设计平台
PC(windows 7 x64),Visual C++ 6.0
设计框图
打开MediaPlayer
MediaPlayer初始化
打开文件
列表
关于
播放
顺序播放
导入/出 选中项上/下移动 删除选中/所有项
循环模式
播放模式
暂停 音量 快进退 上下曲
停止
随机播放
退出
静音
一.工程创建
本程序基于MFC的对话框模式,起名为MediaPlayer。
二. 添加Windows Media Player 控件
在对话框的空白处点击右键选择“Inrt ActiveX control”,在弹出的对话框中找到Windows Media Player。(因为Win7系统中没有ActiveMovieControl控件所以就用以上控件代替了。)
2 / 24
三.为Windows Media Player 控件设置变量
在已经添加好的控件上单击右键选择“ClassWizard”,在弹出的窗口中切换到“Member Variables”一栏,找到刚刚添加的控件的ID,选中它然后在右侧的按钮中点击“Add Variables”,然后如图所示:
这样就添加成功了。此时在源文件的对话框中,系统会自动生成必需的媒体实现文件。
3 / 24
四.播放器界面设计
设计的界面如下图所示,其中播放控制按钮有打开文件,播放,暂停,停止,快退,快进,播放列表,全屏,退出,关于。然后还有音量控制,播放模式,播放进度,列表选项的相关控制器件。
4 / 24
控件ID与变量关联如下:
控件
WMPlayer
ListControl
Slider
Slider
ID 变量类型
CWMPPlayer4
CListCtrl
CSliderCtrl
CSliderCtrl
int
变量名称
m_player
m_listCtrl
m_schedule
m_VOLUME
m_order
响应函数名称
OnOpen()
OnPlay()
OnPau()
OnStop()
OnRever()
OnForward()
OnPlayList()
OnFullScreen()
OnExit()
OnSub()
OnAdd()
OnLast()
OnNext()
OnAbout()
OnSlience()
OnListchooup()
OnListchoodown()
OnListchoodelet()
OnListdeleteall()
OnListimport()
OnListexport()
IDC_OCX1
IDC_LIST1
IDC_SLIDER1
IDC_SLIDER2
Radio Button IDC_ORDER
按钮控件的ID和对应的响应函数名称如下:
控件名称
打开文件
播放
暂停
停止
快退
快进
播放列表
全屏
退出
-
+
上一曲
下一曲
关于
静音
选中项上移
选中项下移
删除选中项
删除所有项
导入列表
导出列表
ID
IDC_OPEN
IDC_PLAY
IDC_PAUSE
IDC_STOP
IDC_REVERSE
IDC_FORWARD
IDC_PLAYLIST
IDC_FULLSCREEN
IDC_EXIT
IDC_SUB
IDC_ADD
IDC_LAST
IDC_NEXT
IDC_ABOUT
IDC_SILENCE
IDC_LISTCHOOSEUP
IDC_LISTCHOOSEDOWN
IDC_LISTCHOOSEDELETE
IDC_LISTDELETEALL
IDC_LISTIMPORT
IDC_LISTEXPORT
五.功能实现
(1)播放控制
Windows Media Player 控件初始化
在添加该控件的时候自动生成的多个库文件,不同的文件实现播放器各个方面的功能,所以需要我们手动将他们联系起来。所以我们要在MediaPlayerDlg.h头文件中添加如下内容:
添加头文件:
#include "wmpcontrols.h"
#include "wmpttings.h"
#include "wmpplaylist.h"
5 / 24
#include "wmpmedia.h"
添加公共成员:
CWMPControls m_control;
CWMPSettings m_tting;
CWMPMedia m_media;
在的初始化函数BOOL CMediaPlayerDlg::OnInitDialog()中添加
m_control=static_cast
m_tting=m_tings();
m_list=static_cast
m_media=static_cast
将相应媒体播放控制文件联系起来。
打开文件到播放列表
void CMediaPlayerDlg::OnOpen()
{
m_us();
CString str;
POSITION pos;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
m_mState(IndexInFieldList,~LVIS_SELECTED,
LVIS_SELECTED);
TCHAR szFilter[] =
_T("Mp3 File(*.mp3)|*.mp3
|Wma File(*.wma)|*.wma
|Video File(*.dat)|*.dat
|Wave File(*.wav)|*.wav
|AVI File(*.avi)|*.avi
|Movie File(*.mov)|*.mov
|Media File(*.mmm)|*.mmm
|Mid File(*.mid;*,rmi)|*.mid;*.rmi
|MPEG File(*.mpeg)|*.mpeg
|All File(*.*)|*.*||");
CFileDialog fileDlg(TRUE,NULL, NULL,OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT, szFilter, this);
CString strFilePath;
if (IDOK == l())
{
strFilePath = hName();
per();
m_(strFilePath);
m_media=static_cast
m_Item(m_media);
m_Item(0,m_e());
6 / 24
m_mText(0,1,_T(m_mInfo("FileType")));
m_mText(0,2,_T(m_ationString()));
m_mText(0,3,_T(strFilePath));
m_mText(0,4,_T(m_mInfo("Duration")));
m_(m_mText(0,3));
m_mState(0,LVIS_SELECTED,LVIS_SELECTED);
}
}
打开功能实现分为了两个部分,第一部分通过m_()来取得媒体文件,第二部分是将媒体文件的相关信息添加到右侧的播放列表中去,让列表中的光标始终停留在第一栏的位置。
播放按钮
void CMediaPlayerDlg::OnPlay()
{
if(m_nt()<=0)
{
MessageBox("列表无文件!","Warning",MB_OK);
spaceflag=1;
}
el
{
if(!le(m_()))
{
MessageBox("文件不已存在,该列项将被删除!",
"Warning",MB_OK);
CMediaPlayerDlg::OnListchoodelete();
}
el
{
KillTimer(0);
m_();
if(m_rentPosition()==0)
m_rentPosition(0.01);
SetTimer(0,20,NULL);
spaceflag=0;
}
}
}
播放功能主要是通过控件提供的m_()来实现的。函数中Timer的设置和后面的播放模式有关,m_rentPosition(0.01)的目的是为了将未播放状态与播放后的停止状态区分开来。
暂停按钮
void CMediaPlayerDlg::OnPau()
7 / 24
{
m_();
}
停止按钮
void CMediaPlayerDlg::OnStop()
{
KillTimer(0);
m_();
}
全屏模式
void CMediaPlayerDlg::OnFullscreen()
{
if(m_rentPosition()!=0&&!m_lScreen())
{
m_();
m_lScreen(true);
m_();
}
}
全屏模式存在着bug,就是进入到全屏后不能够接收键盘输入字符,否则整个程序会变为未响应状态。后来有添加了对键盘信息的获取和控制函数来解决这个问题,后面会提到。
快进/退
void CMediaPlayerDlg::OnRever()
{
double t = m_rentPosition();
m_rentPosition(t=t-10); }
void CMediaPlayerDlg::OnForward()
{
double t = m_rentPosition();
m_rentPosition(t=t+10);
}
虽然"wmpcontrols.h"中有直接快进、退的相关函数,但是视频播放时是无效的,所以就没有用自带的函数来实现。这里是通过m_rentPosition()来获得播放媒体的当前位置,然后通过m_rentPosition()来设置播放媒体的当前位置为原位置+10或-10来实现快进、退功能。
播放列表的显示和隐藏
void CMediaPlayerDlg::OnPlaylist()
{
flag=!flag
if(flag)
{
SetWindowPos(NULL,0,0,440,505,SWP_NOMOVE);
}
8 / 24
el
{
SetWindowPos(NULL,0,0,760,505,SWP_NOMOVE);
}
}
从函数内容可以看出,播放列表的显示和隐藏是通过设置主窗体的大小来实现的。其中flag为bool型的全局变量,初值为fal。当按下播放列表的按钮时会执行SetWindowPos(NULL,0,0,420,454,SWP_NOMOVE);无相对移动,窗口缩小列表隐藏。执行另一语句时列表又会显示出来。
(2)音量控制
静音
void CMediaPlayerDlg::OnSilence()
{
if(!m_e())
m_e(true);
el
m_e(fal);
}
通过调用函数m_e()获取当前是否处于静音状态,通过m_e(fal/true)设置是否静音。
加减音量
void CMediaPlayerDlg::OnAdd()
{
m_ume(m_ume()+10);
//获得当前音量大小,并设置当前音量为原音量大小+10
m_(m_ume());
//设置滑动条位置
}
void CMediaPlayerDlg::OnSub()
{
int Volume=m_ume();
m_ume(Volume=Volume-10);
//获得当前音量大小,并设置当前音量为原音量大小+10
m_(m_ume());
//设置滑动条位置
}
加减音量也可以通过调用相应的成员函数来实现。在按下“+”、“-”按钮时,滑动条也会做出相对应的动作,滚动条需要初始化,会在下面的播放进度中详细说明。
9 / 24
(3)播放模式
上/下一曲
void CMediaPlayerDlg::OnLast()
{
// TODO: Add your control notification handler code here
m_us();
POSITION pos;CString strName;
UINT flag1 = LVIS_SELECTED|LVIS_FOCUSED;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
if(IndexInFieldList == -1)
{
AfxMessageBox("列表内没有被选中项!");
return;
}
if(IndexInFieldList == 0)
{
AfxMessageBox("已经到了列表头部!");
m_mState(IndexInFieldList, flag1, flag1);
return;
}
m_mState(IndexInFieldList,~LVIS_SELECTED,LVIS_SELECTED);
m_mState(IndexInFieldList-1, flag1, flag1);
strName=m_mText(IndexInFieldList-1,3);
CMediaPlayerDlg::OnStop();
m_(strName);
CMediaPlayerDlg::OnPlay();
}
void CMediaPlayerDlg::OnNext()
{
// TODO: Add your control notification handler code here
m_us();
POSITION pos;CString strName;
UINT flag1 = LVIS_SELECTED|LVIS_FOCUSED;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
if(IndexInFieldList == -1)
{
AfxMessageBox("列表内没有被选中项!");
return;
}
if(IndexInFieldList == m_mCount()-1)
10 / 24
{
AfxMessageBox("已经到了列表末尾!");
m_mState(IndexInFieldList, flag1, flag1);
return;
}
m_mState(IndexInFieldList,~LVIS_SELECTED,LVIS_SELECTED);
m_mState(IndexInFieldList+1, flag1, flag1);
strName=m_mText(IndexInFieldList+1,3);
CMediaPlayerDlg::OnStop();
m_(strName);
CMediaPlayerDlg::OnPlay();
}
上/下一曲的功能主要是通过移动播放列表中的光标来实现的。如上一曲的功能实现过程为:IndexInFieldList获得列表光标当前位置,通过析构函数m_mState(IndexInFieldList,~LVIS_SELECTED,LVIS_SELECTED)使本行光标消失,通过函数m_mState(IndexInFieldList-1, flag1, flag1)使光标显示在下一行的位置,strName=m_mText(IndexInFieldList-1,3)获得光标所在行第四栏的信息,在这里也就是列表中媒体文件的路径,然后再调用相关函数来进行播放。
四种播放模式(顺序播放、随机播放、全曲循环、单曲循环)
首先我们需要定义一个Botton Group,在四个Radio Button选择一个单击右键,在弹出菜单中选择“ClassWizard”,翻到“Member Variables”,找到刚刚选择的Radio Button的ID,“Add Bariables”添加一个int型的成员m_order,如图所示:
然后再在中填写各个Radio Button函数中的内容:
void CMediaPlayerDlg::OnOrder()
{
//顺序播放
m_order=0;
}
11 / 24
void CMediaPlayerDlg::OnRandom()
{
// 随机播放
m_order=1;
}
void CMediaPlayerDlg::OnAllcircle()
{
//全曲循环
m_order=2;
}
void CMediaPlayerDlg::OnSinglecircle()
{
// 单曲循环
m_order=3;
}
之前在ClassWizard中已经添加好了OnTimer成员。这时我们要把它联合起来使用:
(关键代码)
CMediaPlayerDlg::OnTimer(UINT nIDEvent)
{//计时器的选择代码省略
switch(m_order)
{
ca 0:
{
KillTimer(0);
POSITION pos;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
m_us();
if(IndexInFieldList == m_mCount()-1)
{
m_mState(IndexInFieldList, LVIS_SELECTED,LVIS_SELECTED);
m_();
return;
}
m_mState(IndexInFieldList,~LVIS_SELECTED,
LVIS_SELECTED);
m_mState(IndexInFieldList+1, LVIS_SELECTED,LVIS_SELECTED);
CString strName=m_mText(IndexInFieldList+1,3);
m_(strName);
CMediaPlayerDlg::OnPlay();
break;
12 / 24
}
ca 1:
{
KillTimer(0);
m_us();
POSITION pos;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
m_mState(IndexInFieldList,~LVIS_SELECTED,
LVIS_SELECTED);
int i;
srand((unsigned)time( NULL ));
i=rand()%m_mCount();
m_mState(i, LVIS_SELECTED,LVIS_SELECTED);
CString strName=m_mText(i,3);
m_(strName);
CMediaPlayerDlg::OnPlay();
break;
}
ca 2:
{
KillTimer(0);
POSITION pos;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
m_us();
if(IndexInFieldList == m_mCount()-1)
{
IndexInFieldList=-1;
}
m_mState(IndexInFieldList,~LVIS_SELECTED,
LVIS_SELECTED);
m_mState(IndexInFieldList+1, LVIS_SELECTED,
LVIS_SELECTED);
CString strName=m_mText(IndexInFieldList+1,3);
m_(strName);
CMediaPlayerDlg::OnPlay();
break;
ca 3:
{
KillTimer(0);
m_rentPosition(0.01);
m_();
13 / 24
}
SetTimer(0,20,NULL);
break;
}
default:
{
KillTimer(0);
break;
}
}
各种播放模式的实现都是通过对播放列表进行操作来实现的。具体来讲就是通过控制列表中的光标的移动来获取光标所在行的相关信息。
(4)播放进度
滚动条和数值显示
因为他们共同构成一个整体,又在同一个OnTimer()函数中,所以就合起来说明。
首先为IDC_SLIDER1添加CSliderCtrl类型的成员m_schedule;
然后是滚动条的初始化,在的OnInitDialog()中添加语句:
m_ge(0,200,fal);
m_Freq(1);
m_eSize(1);
m_(0);
最后在对OnTimer()进行编辑:
void CMediaPlayerDlg::OnTimer(UINT nIDEvent)
{
switch(nIDEvent)
{
ca 0:
{
If(m_rentPosition()==0);
SetDlgItemText(IDC_CURRENT,"00:00");
SetDlgItemText(IDC_DURATION,"00:00");
//下面是有关播放模式的代码,在这里省略
//……
POSITION pos;double td;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
td=_ttol(m_mText(IndexInFieldList,4));
m_(
(int)((m_rentPosition()/td)*200));
SetDlgItemText(IDC_DURATION,
m_mText(IndexInFieldList,2));
SetDlgItemText(IDC_CURRENT,
14 / 24
}
}
CDialog::OnTimer(nIDEvent);
m_rentPositionString());
break;
}
此处的nIDEvent就是你前面设置的Timer的编号,当编号为0的时候就执行下面的语句。SetDlgItemText()让媒体的当前播放的位置和总长度以数值的形式显示出来。
5)列表选项
选中项上下移动
void CMediaPlayerDlg::OnListchooup()
{
// TODO: Add your control notification handler code here
m_us();
POSITION pos;
int num;
CString name;
CString form;
CString length;
CString strName;
CString duration;
UINT flag1 = LVIS_SELECTED|LVIS_FOCUSED;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
if(IndexInFieldList == -1)
{
AfxMessageBox("列表内没有被选中项!");
return;
}
if(IndexInFieldList == 0)
{
AfxMessageBox("已经到了列表头部!");
m_mState(IndexInFieldList, flag1, flag1);
return;
}
num=IndexInFieldList;
name=m_mText(num,0);
form=m_mText(num,1);
length=m_mText(num,2);
strName=m_mText(num,3);
duration=m_mText(num,4);
m_Item(num);
15 / 24
(
m_Item(num-1,_T(name));
m_mText(num-1,1,_T(form));
m_mText(num-1,2,_T(length));
m_mText(num-1,3,_T(strName));
m_mText(num-1,4,_T(duration));
m_mState(IndexInFieldList-1, flag1, flag1);
}
void CMediaPlayerDlg::OnListchoodown()
{
// TODO: Add your control notification handler code here
m_us();
POSITION pos;
int num;
CString name;
CString form;
CString length;
CString strName;
CString duration;
UINT flag1 = LVIS_SELECTED|LVIS_FOCUSED;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
if(IndexInFieldList == -1)
{
AfxMessageBox("列表内没有被选中项!");
return;
}
if(IndexInFieldList == m_mCount()-1)
{
AfxMessageBox("已经到了列表末尾!");
m_mState(IndexInFieldList, flag1, flag1);
return;
}
num=IndexInFieldList;
name=m_mText(num,0);
form=m_mText(num,1);
length=m_mText(num,2);
strName=m_mText(num,3);
duration=m_mText(num,4);
m_Item(num);
m_Item(num+1,_T(name));
m_mText(num+1,1,_T(form));
m_mText(num+1,2,_T(length));
m_mText(num+1,3,_T(strName));
16 / 24
m_mText(num+1,4,_T(duration));
m_mState(IndexInFieldList+1, flag1, flag1);
}
该功能和上下一曲的实现方法很相似。将光标所在行的信息通过临时变量记录下来,之后将该行删除,再通过m_Item()、m_mText()将临时变量记录下来的信息插入到前一行或后一行中去并让光标停留在该行。
删除选中项
void CMediaPlayerDlg::OnListchoodelete()
{
// TODO: Add your control notification handler code here
m_us();
POSITION pos;CString strname;
int num;
UINT flag1 = LVIS_SELECTED|LVIS_FOCUSED;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
if(IndexInFieldList == -1)
{
AfxMessageBox("列表内没有被选中项!");
return;
}
num=IndexInFieldList;
m_Item(num);
CMediaPlayerDlg::OnStop();
if(m_mCount()!=0)
{
if(IndexInFieldList == m_mCount())
{
m_mState(IndexInFieldList-1, flag1, flag1);
strname=m_mText(IndexInFieldList-1,3);
m_(strname);
}
el
{
m_mState(IndexInFieldList, flag1, flag1);
strname=m_mText(IndexInFieldList,3);
m_(strname);
}
CMediaPlayerDlg::OnPlay();
}
}
删除选中项通过m_Item(),不过要注意删完该行后光标应停留在哪里的问题。如果删除行不是最后一行,那么光标可以停留在下一行,如何是删除的最后一行,那么光标应该停留在上一行。
17 / 24
删除所有项
void CMediaPlayerDlg::OnListdeleteall()
{
// TODO: Add your control notification handler code here
CMediaPlayerDlg::OnStop();
int m=m_mCount(),i;
for(i=m-1;i>=0;i--)
{
m_Item(i);
}
}
获得列表中的行数,从最后一行往上逐行删除。
导入列表
void CMediaPlayerDlg::OnListimport()
{
// TODO: Add your control notification handler code here
m_us();
TCHAR szFilter[] = _T("列表文件(*.list)|*.list||");
// 构造打开文件对话框
CFileDialog fileDlg(TRUE, _T("list"), NULL, 0, szFilter, this);
CString strFilePath;
// 显示打开文件对话框
if (IDOK == l())
{
// 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里
strFilePath = hName();
}
CStdioFile file;int i;
if((strFilePath,CFile::modeRead|CFile::typeText))
{
POSITION pos;
pos = m_stSelectedItemPosition();
IndexInFieldList = m_tSelectedItem(pos);
m_mState(IndexInFieldList,
~LVIS_SELECTED,LVIS_SELECTED);
Begin();
CString str;CString strtemp[6];
while(ring(str))
{
for(i=0;i<5;i++)
AfxExtractSubString(strtemp[i],str,i,'|');
m_Item(0,strtemp[0]);
m_mText(0,1,_T(strtemp[1]));
18 / 24
}
m_(m_mText(0,3));
m_mState(0,LVIS_SELECTED,LVIS_SELECTED);
}
el
{
TRACE("Can't open list file!");
}
();
m_mText(0,2,_T(strtemp[2]));
m_mText(0,3,_T(strtemp[3]));
m_mText(0,4,_T(strtemp[4]));
m_(strtemp[3]);
m_media=static_cast
m_ia(strFilePath));
m_Item(m_media);
}
找到要导入的列表文件,虽然把文件的后缀名改为了list,实际上还是文本文件,只是为了与其他的非列表文本文件区分开来。
通过ring(str)逐行读取list文件内容。
通过AfxExtractSubString(strtemp[i],str,i,'|'),将字符串流切分成几个小的字符串,记录在数组中,再导入列表里面。
如图是以文本文件格式打开的list文件:
导出列表
void CMediaPlayerDlg::OnListexport()
{
// TODO: Add your control notification handler code here
TCHAR szFilter[]=_T("列表文件(*.list)|*.list||");
CFileDialog fileDlg(FALSE,_T("list"),NULL,OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT, szFilter, this);
CString strFilePath;
if (IDOK == l())
{
// 如果点击了文件对话框上的“保存”按钮,则将选择的文件路径显示到编辑框里
strFilePath = hName();
}
CStdioFile file;int i,j;
if((strFilePath,CFile::modeCreate|CFile::modeReadWrite|
CFile::typeText))
{
19 / 24
for(i=m_mCount()-1;i>=0;i--)
{
CString temp;
for(j=0;j<4;j++)
{
temp=m_mText(i,j);
temp+="|";
tring(temp);
}
temp=m_mText(i,j);
temp+="n";
tring(temp);
}
();
}
el
{
TRACE("Can't Open File");
}
}
前半部分获取保存路径,后半部分将列表信息写入到该路径下的文件中。
(6)获取按键信息
在”ClassWizard”中的”Message Maps”页面为”CMediaPlayerDlg”添加”Member
functions”:PreTranslateMessage如图所示。
20 / 24
再添加如下代码:
BOOL CMediaPlayerDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the ba class
if(pMsg->message==WM_KEYDOWN)
{
switch(pMsg->wParam)
{
ca VK_ESCAPE:
{//ESC键
if(m_rentPosition()!=0&&
m_lScreen())
m_lScreen(fal);
el
SendMessage(WM_CLOSE);
return TRUE;
break;
}
ca VK_SPACE:
{//空格键
if(spaceflag==0)
CMediaPlayerDlg::OnPau();
el
if(spaceflag==1)
CMediaPlayerDlg::OnPlay();
return TRUE;
break;
}
ca VK_LEFT:
{//←键
CMediaPlayerDlg::OnRever();
return TRUE;
break;
}
ca VK_RIGHT:
{//→键
CMediaPlayerDlg::OnForward();
return TRUE;
break;
}
ca VK_PRIOR:
{//PageUp键
CMediaPlayerDlg::OnLast();
return TRUE;
21 / 24
}
break;
}
ca VK_NEXT:
{//PageDown键
CMediaPlayerDlg::OnNext();
return TRUE;
break;
}
ca VK_UP:
{//↑键
CMediaPlayerDlg::OnAdd();
return TRUE;
break;
}
ca VK_DOWN:
{//↓键
CMediaPlayerDlg::OnSub();
return TRUE;
break;
}
default:
return TRUE;
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
到此,全屏不能碰键盘的bug被修复了,并且也可以通过键盘进行媒体播放操作,特别是在全屏播放视频文件的时候。
22 / 24
实验结果
(1) 音乐播放测试
(2) 视频播放测试
(3) 列表导入测试
23 / 24
(4) 列表导出测试
相关代码及工程文件下载:
/share/link?shareid=1420842405&uk=3425060786
2013-6-30
24 / 24
本文发布于:2024-02-25 13:37:05,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/zhishi/a/1708839425273112.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:基于MFC运用windows media player控件实现的多媒体播放器.doc
本文 PDF 下载地址:基于MFC运用windows media player控件实现的多媒体播放器.pdf
留言与评论(共有 0 条评论) |