软件安全之动态链接库的使用 Libzplay 播放音乐「建议收藏」

实验1动态链接库的使用实验说明Libzplay是遵循GPL协议的开源库,它集成了mp3、flac、ac3、aac、wav等多种音频格式的解码器和编码器,提供了面向C/C++、C#、Delphi等多种编程语言的接口,仅需3行代码(创建播放资源,打开文件,开始播放)便可实现音乐播放功能。实验目的本实验通过Libzplay提供的C语言接口,实现简单的音乐播放器,以此学习DLL的隐式和显式加载方式。实验原理课程第2讲基础知识实验环境Windows

大家好,又见面了,我是你们的朋友全栈君。

实验 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的隐式链接做不到的,因此显式链接具有更好的灵活性,同时也能更有效的使用内存,这在编写大型项目程序时大有作为。
    • 隐式链接则可以在我们写小代码时发挥作用,轻便快捷,非常适合自己使用。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126537.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 过程控制系统与仪表pdf_计算机控制技术课程设计之炉温

    过程控制系统与仪表pdf_计算机控制技术课程设计之炉温目录一课程设计内容任务,,,,,,,,,,,,,,,,,,,,,,3二对课设任务的解读,,,,,,,,,,,,,,,,,,,,,,3三系统结构模型框图,,,,,,,,,,,,,,,,,,,,,,3四各部分程序流程图,,,,,,,,,,,,,,,,,,,,,,4五数字控制器设计,,,,,,,,,,,,,,,,,,,,,,5六系统仿真,,,,,,,,,,,,,,,,,,,…

    2022年9月29日
    0
  • javascript 如何添加元素

    javascript 如何添加元素//利用concatfunctionprepend(arr,item){return[item].concat(arr);}//使用push.applyfunctionprepend(arr,item){varnewArr=[item];[].push.apply(newArr,arr);returnnewArr;}//利用slice+unshift/splicefunctionprepend(arr,item){v.

    2022年6月22日
    25
  • java面试题高级_Java高级面试题整理(附答案)[通俗易懂]

    java面试题高级_Java高级面试题整理(附答案)[通俗易懂]javajava8java开发Java高级面试题整理(附答案)这是我收集的10道高级Java面试问题列表。这些问题主要来自Java核心部分,不涉及JavaEE相关问题。你可能知道这些棘手的Java问题的答案,或者觉得这些不足以挑战你的Java知识,但这些问题都是容易在各种Java面试中被问到的,而且包括我的朋友和同事在内的许多程序员都觉得很难回答。1…

    2022年7月18日
    12
  • 中国电信广东DNS服务器[通俗易懂]

    中国电信广东DNS服务器[通俗易懂]1中国电信广州用户(包括番禺、增城、从化等区电信用户)“首选DNS服务器”为:61.144.56.100“备用DNS服务器”为:61.144.56.101这个经过测试确实是目前最快最有效的DNS服务器。2中国电信深圳用户“首选DNS服务器”为:202.96.128.86“备用DNS服务器”设置为:202.96.128.1663中国电信广东省其他地区用户(包括佛山、中山、江…

    2022年7月11日
    76
  • 一张图讲解对象锁和关键字synchronized修饰方法(代码块)

    每个对象在出生的时候就有一把钥匙(监视器Monitor),那么被synchronized 修饰的方法相当于给方法加了一个锁,这个方法就可以进行同步,在多线程的时候,不会出现线程安全问题。注:Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 Monitor。

    2022年2月26日
    41
  • 刷屏专用超长复制_跪求超超长刷屏字符剑。。

    该楼层疑似违规已被系统折叠隐藏此楼查看此楼….飘过…..飘过……飘过…….飘过……..飘过……..飘过……..飘过……..飘过…….飘过……飘过…..飘过….飘过…飘过..飘过.飘过.飘过.飘过..飘过…飘过….飘过…..飘过……飘过…….飘过……..飘过……..飘过….

    2022年4月8日
    94

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号