实验 1 动态链接库的使用

  1. 实验说明

Libzplay 是遵循 GPL 协议的开源库,它集成了 mp3、flac、ac3、aac、wav等多种音频格式的解码器和编码器,提供了面向 C/C++、C#、Delphi 等多种编程语言的接口,仅需 3 行代码(创建播放资源,打开文件,开始播放)便可实现音乐播放功能。

  1. 实验目的

本实验通过 Libzplay 提供的 C 语言接口,实现简单的音乐播放器,以此学习 DLL 的隐式和显式加载方式。

  1. 实验原理

课程第 2 讲基础知识

  1. 实验环境

Windows 桌面系统,Visual Studio 6.0 及以上版本,libzplay SDK

  1. 实验内容
    • 隐式链接
    • 显示链接

练习 1 隐式链接 DLL

1、基础步骤

  1. 创建控制台应用程序项目

打开 VS,选择空项目模板创建控制台应用程序,命名为 zplay-im,然后向新建项目中添加源程序文件impilicit.cpp,编写 main 函数。

  1. 拷贝头文件和库文件

将 libzplay 库中的头文件 libzplay.h 和库文件 libzplay.lib 到 zplay-im 项目所在的目录。

  1. 编写播放程序

使用 libzplay C 语言接口播放音乐文件,至少需要依次调用如下 3 个函数:

ZPLAY_HANDLE player = zplay_CreateZPlay();
zplay_OpenFile(player, "homeland.mp3", sfAutodetect);
zplay_Play(player);

完整的程序如下

#include <stdio.h>
#include <stdlib.h>
#include "libzplay.h"
// 导入lib,否则报错 LNK2019 无法解析的外部符号
#pragma comment(lib,"libzplay")int main()
{ZPLAY_HANDLE player = zplay_CreateZPlay(); // 创建实例,libzplay申请各类资源zplay_OpenFile(player, "homeland.mp3", sfAutodetect); //打开音乐文件zplay_Play(player); // 播放音乐文件system("pause");return 0;
}
  1. 编译程序

编译成功后,将 libzplay 库中的 DLL 文件 libzplay.dll 拷贝到编译生成的可执行文件所在目录,或者将该 DLL 文件放入 DLL 加载过程要搜索的目录。歌曲文件以impilicit.cpp位置为原点,相对目录寻址。

  1. 运行程序仅编写上述代码,程序运行后将立即退出,应如何添加代码使其正常播放音乐?

    • 可以在底部加入一条scanf语句,getchar()语句等,使其处于一直等待输入的状态,程序自然也不会停止;
    • 使用while(true)也可以实现程序一直进行;
    • system("pause");也可以使得程序暂停。

2、拓展设计

  1. 根据 libzplay 提供的数据类型定义和函数接口原型,设计较为完整的音乐播放器,实现播放过程的暂停、播放、停止等控制功能。

  2. 播放完毕后,需要做清理工作:

    • zplay_OpenFile 和 zplay_Close 是相对应的 2 个函数,打开的音乐文件不再播放时需要关闭;
    • zplay_CreateZPlay 和 zplay_DestroyZPlay 是相对应的 2 个函数,libzplay 申请的各类资源,不再需要时需要销毁。

附带播放、暂停、停止的完整程序

#include <stdio.h>
#include <stdlib.h>
#include "libzplay.h"
// 导入lib,否则报错 LNK2019 无法解析的外部符号
#pragma comment(lib,"libzplay")void prompt(int statevalue)
{printf("当前播放状态:");switch (statevalue){case 1:printf("播放");break;case 2:printf("暂停");break;case 3:printf("停止");break;default:break;}printf("\n请输入操作代号:\n播放:1,暂停:2,停止:3:");
}int main()
{ZPLAY_HANDLE player = zplay_CreateZPlay(); // 创建实例,libzplay申请各类资源zplay_OpenFile(player, "homeland.mp3", sfAutodetect); //打开音乐文件zplay_Play(player); // 播放音乐文件int statevalue = 1; //定义音乐播放状态,初始时播放prompt(1);while (scanf_s("%d",&statevalue)){switch (statevalue){case 1:zplay_Play(player);prompt(1);break;case 2:zplay_Pause(player); // 暂停音乐播放prompt(2);break;case 3:zplay_Stop(player); // 停止音乐播放goto outexit; // 输入停止命令后跳到关闭文件、资源部分prompt(3);break;default:break;}}outexit:zplay_Close(player); //关闭不再播放的音乐文件zplay_DestroyZPlay(player);//销毁libzplay申请的各类资源system("pause");return 0;
}

运行效果如图所示,此时无声胜有声。相信大家已经听到大弦嘈嘈如急雨,小弦切切如私语。嘈嘈切切错杂弹,大珠小珠落玉盘。间关莺语花底滑,幽咽泉流冰下难。冰泉冷涩弦凝绝,凝绝不通声暂歇。别有幽愁暗恨生,此时无声胜有声。银瓶乍破水浆迸,铁骑突出刀枪鸣。曲终收拨当心画,四弦一声如裂帛。

  1. libzplay 部分枚举、结构体和宏定义
#define ZPLAY_HANDLE char*
enum TStreamFormat
{sfUnknown = 0,sfMp3 = 1,sfOgg = 2,sfWav = 3,sfPCM = 4,sfFLAC = 5,sfFLACOgg = 6,sfAC3 = 7,sfAacADTS = 8,sfWaveIn = 9,sfAutodetect = 1000
};typedef struct
{int fPlay;int fPause;int fEcho;int fEqualizer;int fVocalCut;int fSideCut;int fChannelMix;int fSlideVolume;int nLoop;int fReverse;int nSongIndex;int nSongsInQueue;
} TStreamStatus;typedef struct
{unsigned int hour;unsigned int minute;unsigned int second;unsigned int millisecond;
} TStreamHMSTime;typedef struct
{unsigned int sec;unsigned int ms;unsigned int samples;TStreamHMSTime hms;
} TStreamTime;
  1. libzplay 提供的部分 C 语言接口声明
ZPLAY_HANDLE __stdcall zplay_CreateZPlay();
int __stdcall zplay_OpenFile(ZPLAY_HANDLE handle, const char* sFileName,
TStreamFormat nFormat);
int __stdcall zplay_Play(ZPLAY_HANDLE handle);
int __stdcall zplay_Pause(ZPLAY_HANDLE handle);
int __stdcall zplay_Stop(ZPLAY_HANDLE handle);
void __stdcall zplay_GetPosition(ZPLAY_HANDLE handle, TStreamTime* pTime);
void __stdcall zplay_GetStatus(ZPLAY_HANDLE handle, TStreamStatus* pStatus);
int __stdcall zplay_Close(ZPLAY_HANDLE handle);
int __stdcall zplay_DestroyZPlay(ZPLAY_HANDLE handle);

3、思考与总结

  • 为什么最开始程序运行后便立即退出?

有可能是没有使进程阻塞的语句。在return 0前加上system("pause");语句可以使得程序暂停

练习 2 显式链接 DLL

1、基础步骤

  1. 创建控制台应用程序项目

打开 VS,选择空项目模板创建控制台应用程序,命名为 zplay-ex,然后向新建项目中添加源程序文件 expilicit.cpp,编写 main 函数。

  1. 编写播放程序
  • 定义三个函数指针

zplay_Play 的函数指针:typedef int(_stdcall* pfnPlay)(ZPLAY_HANDLE handle);

zplay_CreateZPlay和zplay_OpenFile的函数指针:

typedef ZPLAY_HANDLE (_stdcall* pfnCreateZPlay)();
typedef int (_stdcall* pfnOpenFile)(ZPLAY_HANDLE handle, const char* sFileName, TStreamFormat nFormat);
  • 动态加载 libzplay.dll 到应用程序的进程空间中
HMODULE hModule = LoadLibrary("libzplay.dll");
  • 动态获取三个函数的入口地址
pfnCreateZPlay CreateZPlay = (pfnCreateZPlay)*GetProcAddress*(hModule, "zplay_CreateZPlay");
pfnOpenFile OpenFile = (pfnOpenFile)GetProcAddress(hModule, "zplay_OpenFile");
pfnPlay Play = (pfnPlay)GetProcAddress(hModule, "zplay_Play");
  • 依次调用获取的三个函数
ZPLAY_HANDLE player = CreateZPlay();
OpenFile(player, "homeland.mp3", sfAutodetect);
Play(player);
  • 播放完毕,释放加载的 libzplay.dll

FreeLibrary(hModule);

完整程序

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "libzplay.h"
// 导入lib,否则报错 LNK2019 无法解析的外部符号typedef ZPLAY_HANDLE (_stdcall* pfnCreateZPlay)();
typedef int (_stdcall* pfnOpenFile)(ZPLAY_HANDLE handle, const char* sFileName, TStreamFormat nFormat);
typedef int(_stdcall* pfnPlay)(ZPLAY_HANDLE handle);int main()
{HMODULE hModule = LoadLibrary("libzplay.dll");pfnCreateZPlay CreateZPlay = (pfnCreateZPlay)GetProcAddress(hModule, "zplay_CreateZPlay");pfnOpenFile OpenFile = (pfnOpenFile)GetProcAddress(hModule, "zplay_OpenFile");pfnPlay Play = (pfnPlay)GetProcAddress(hModule, "zplay_Play");ZPLAY_HANDLE player = CreateZPlay(); OpenFile(player, "homeland.mp3", sfAutodetect); Play(player); system("pause");return 0;
}
  1. 编译程序

编译成功后,将 libzplay 库中的 DLL 文件 libzplay.dll 拷贝到编译生成的可执行文件所在目录,或者将该 DLL 文件放入 DLL 加载过程要搜索的目录。

  1. 运行程序

2、思考与总结

  1. 比较显式链接和隐式链接的区别;

    • 显式链接只用到了dll,但需要自己重写函数入口
    • 隐式链接通过.lib文件.h文件(dll允许应用程序导出的所有函数的符号名和可选的标识号,但无代码实现)加载dll,能够直接使用
  2. 试分析二者所适用的场景。
    • 我们一般不会用显示链接,想秀的除外。显示链接不需要用到lib库,全部独立自主进行dll调用,在执行过程中随时可以加载或卸载DLL文件,这是依赖于lib的隐式链接做不到的,因此显式链接具有更好的灵活性,同时也能更有效的使用内存,这在编写大型项目程序时大有作为。
    • 隐式链接则可以在我们写小代码时发挥作用,轻便快捷,非常适合自己使用。

软件安全之动态链接库的使用 Libzplay 播放音乐相关推荐

  1. android在线播放音乐

    2019独角兽企业重金招聘Python工程师标准>>> android在线音乐 一种方法是调用android自带的播放器 //调用系统自带播放器Intent intent = new ...

  2. 解决 后台播放音乐时,设置手机铃声,后台音乐不会暂停

    2019独角兽企业重金招聘Python工程师标准>>> 手机后台播放音乐时,设置手机铃声,后台音乐不会暂停,此现象的为设置手机铃声界面,并没要加入播放的foucs机制, 此修改在pa ...

  3. python定时器 循环_Python3 循环定时休息并播放音乐 (Windows)

    功能:工作一段时间(40 min),自动定时休息(10 min)并播放音乐,如此循环 文件名:EyeTimer.pyw,直接双击运行 关闭方法:在任务管理器里结束 Python 进程 #coding= ...

  4. 原来SMPlayer可以直接播放音乐CD碟

    在LINUX系统里想听音乐CD光碟,以前总不知道有什么软件可以直接听而不用MOUNT光驱的,今天突然发现原来SMPlayer可以直接播放音乐CD碟,而其它Amarok等等播放器却不可以,根本不用Mou ...

  5. [J2ME]Nokia播放音乐时发生MediaException的解决办法

     [J2ME]Nokia播放音乐时发生MediaException的解决办法 现象 在Nokia7610上播放音乐时遇到了-18的MediaException错误! J2ME中播放音乐资源的代码很容易 ...

  6. Python 技术篇-使用pygame库播放音乐没有声音问题解决办法

    用 pygame 库做音乐播放器的时候,调用歌曲发现没有声音. 这是源代码: import pygamepygame.init() pygame.mixer.init() pygame.mixer.m ...

  7. 【arduino】ESP8266播放音乐,ESP8266Audio初试体验测试报告

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 这个库之前见到过,也测试过,然后就忘了一点印象都没有了.最近纠结如何用arduino播 ...

  8. 【DIY】arduino播放音乐方案——TMRpcm

    值此魔兽世界怀旧服开服之际,作为一个怀旧的.休闲的.想玩但又没时间玩的老玩家,在爱与责任面前做出了正确的抉择!"爱与家庭"作为魔兽世界60级时候的经典任务一直被玩家们津津乐道,故事 ...

  9. AudioManager播放音乐

    AudioManager控制手机音频 主要是:声音类型,音量大小或者静音 AudioManager的常用方法:  adjustStreamVolume(int streamType,int direc ...

  10. QT中播放音乐遇到的问题合集

    1,使用 QSound播放 wav格式的文件 1.在.pro 文件中加 QT += multimedia,才可以使用Qsound 2.在使用的文件中添加头文件:#include 3.在main函数中可 ...

最新文章

  1. 查看oracle当前消耗,查找Oracle高消耗语句的方法
  2. BZOJ1003: [ZJOI2006]物流运输
  3. Python的运算符和编码
  4. Nutanix 将社区版代码带入云中
  5. 从RCNN到Fast RCNN,再到Faster RCNN的奥秘
  6. GDCM:gdcm::EncapsulatedDocument的测试程序
  7. 基准对象object中的基础类型----列表 (四)
  8. 树莓派4b ros镜像 网盘_树莓派4B的入手操作
  9. c语言列车调度 栈,这个用栈调度火车的程序到底是怎么回事?
  10. 2019上半年系统集成项目管理工程师上午真题及答案解析
  11. 移动通信原理B-------课后作业2
  12. PHP调用MYSQL存储过程实例
  13. python无头浏览器兼容问题_docker+python无头浏览器爬虫
  14. 电工模拟接线软件 app_电气工程师手机必备APP
  15. 【好玩的应用】QQ连连看辅助工具
  16. python正则匹配日期2019-03-11_正则表达式验证日期(多种日期格式)——转载
  17. java高级软件架构师实战培训阶段一
  18. Visual Studio 2017美化(修改字体,图片背景)
  19. 怎么开启MIUI8系统的ROOT超级权限
  20. java用date加一个小时_在Java中添加n个小时到一个date?

热门文章

  1. VS在新建或者导入项目时出现“不支持此接口(Exception from HRESULT:0x80004002 (E_NONINTERFACE))”的解决办法
  2. iPhone游戏开发纵谈
  3. git 出现 Author identity unknown 问题一
  4. SQL学习笔记(05)_JOIN的类型与用法
  5. 【数字电路抢救】2 逻辑函数的代数化简 3逻辑表达式的化简
  6. 多场景业务实战 --用户评论文本挖掘(写的有点细有点多)
  7. CCproxy 设置代理服务器。
  8. 工程伦理计算机论文,工程实践中的伦理问题研究
  9. 逆向学习第二天如何手动脱UPX、Aspack壳
  10. 【Java开源项目】消息推送平台发送一条短信