【rementine 源码 分析】【百度网盘目录管理系统源码】【百度网盘目录源码系统解析】简图源码_绘制源码

时间:2024-12-26 01:37:52 编辑:手游运营平台源码 来源:php弹窗源码

1.[Mybatis]-[基础支持层]-插件-plugin标签解析
2.pdms怎么读取(设备数据表简图)excel中的简图数据?
3.为什么我并未重写equals方法,equals和==的源码源码结果就已经不一样了?
4.爱上Arduino目录
5.webpack5loader和plugin原理解析
6.VC+++++是什么?

简图源码_绘制源码

[Mybatis]-[基础支持层]-插件-plugin标签解析

       该系列文章针对 Mybatis 3.5.1 版本

       一、Mybatis 绘制插件的作用

       Mybatis 针对SQL映射语句执行过程进行拦截处理,而对应的简图拦截器 Mybaits 又称之为插件(这些插件就是Mybatis的扩展点)。

       在 Mybaits 中允许用插件来拦截的源码源码方法包括:

       通过插件的方式可以实现SQL打印、分页等插件功能实现。绘制rementine 源码 分析

       二、简图Mybatis 源码源码插件配置

       插件代码逻辑实现后还需要加载到 Mybatis 中才能生效,Mybatis 绘制提供了配置标签,用来声明。简图

       插件在mybatis-config.xml 中的源码源码配置案例,如下:

       通过 标签,绘制然后在指定的简图属性 interceptor 配置插件实现类的全路径即可。

       三、源码源码`plugin` 标签解析

       再来回顾一下,绘制XMLConfigBuilder解析时序简图,如下:

       在时序图中加载解析在XMLConfigBuilder#pluginElement中完成,相关解析代码如下:

       如上述代码,`plugin` 的解析流程很简单

       1、加载 `plugin` 下的子标签

       2、获取 `plugin` 中 interceptor 属性中的 class 全路径名

       3、class 必须实现了 Interceptor接口,如果满足,通过反射实例化类

       4、把类加载到存放拦截器的拦截器容器,拦截器链 InterceptorChain

       简单来看一下InterceptorChain 代码定义

       如上述代码所示,InterceptorChain 本身就是一个容器,用来存放所有从 `plugin` 读取到的拦截器对象。

       而这里的拦截器列表,在使用过程中,会通过代理的方式,对目标对象层层代理,百度网盘目录管理系统源码通过责任链的方式实现代码执行前后的层层过滤,相关逻辑图如下:

       四、interceptor 过滤链代理处理代码

       上面提到了 interceptor 过滤链的实现是通过代理的方式层层包裹实现的,下面来简单阅读代理流程源码

       Mybatis 中只针对 Executor、ParameterHandler、ResultSetHandler、StatementHandler,这四种情况追加了过滤连的处理。

       相关的处理方法入口为InterceptorChain#pluginAll,如下图

       如上述代码,遍历所有的插件,调用插件本身的 plugin 方法来处理,也就是 Interceptor#plugin,来看通用实现

       (也可以自定义实现逻辑),代码如下

       如上述代码,通用的代理逻辑交由工具类 Plugin 来实现,

       接着来看一下 Plugin#wrap 方法源码

       如上述源码所示,通过配对当前 interceptor 是否符合目标对象 target ,如果配对,构建相应的代理对象。

       以此类推,随后实现如下图的效果:

       五、总结

       通过上述源码解析能够知道一个插件,也就是一个 Interceptor 的定义需要满足两个条件

       1、该插件实现类实现了 Interceptor 接口

       2、该插件实现类通过注解 @Intercepts 指定了该插件需要拦截的对象,也就是 Executor、ParameterHandler、ResultSetHandler、StatementHandler 中的一种或者多种

       `plugin` 标签中配置的是一系列拦截器,这些拦截器通过代理的百度网盘目录源码系统解析方式组合起来实现了过滤器链。

       而这些过滤器数据存储在 InterceptorChain 中,最终数据仍然会存在 Configuration 中,相关的 Configuration 逻辑图如下:

pdms怎么读取(设备数据表简图)excel中的数据?

       要把Excel数据(格式如下图)读取出来并动态创建类,并利用数据去实例化,然后在... 在其他设备上没问题,于是研究其他软件是怎么做的,淘宝的裁剪是自己做的,当... 然后放到工程Resources文件夹中。 里面内容是 它将我们excel表中的数据转成右图格

为什么我并未重写equals方法,equals和==的结果就已经不一样了?

       一张简图讲解

       当使用==为false就是因为创建的是两个对象,在堆内存中是两个不一样的地址,对于引用类型,==只是比较引用地址是否一样,这里显然0x不等于0x

       但通过equals的方法,因为String重写过了。代码如下:

           public boolean equals(Object anObject) {

               if (this == anObject) {

                   return true;

               }

               if (anObject instanceof String) {

                   String anotherString = (String)anObject;

                   int n = value.length;

                   if (n == anotherString.value.length) {

                       char v1[] = value;

                       char v2[] = anotherString.value;

                       int i = 0;

                       while (n-- != 0) {

                           if (v1[i] != v2[i])

                               return false;

                           i++;

                       }

                       return true;

                   }

               }

               return false;

           }

       比较的是内容是否一样。一样所以是ture

       但如果String s1 = "abc";

                   String s2 = "abc";

       使用==结果就是true,因为引用的地址是一样的,内容必然一样。

爱上Arduino目录

       目录

       前言

       1. 介绍

       目标读者

       什么是Physical Computing?

       2. Arduino理念

       原型

       Tinkering

       Patching

       改装电路

       改装键盘

       我们爱垃圾!

       改装玩具

       合作

       3. Arduino工作平台

       Arduino硬件

       Arduino集成开发环境(IDE)

       安装驱动程序:Macintosh操作系统下的方法

       安装驱动程序:Windows操作系统下的方法

       识别通信端口:Macintosh操作系统的情况

       识别通信端口:Windows操作系统的情况

       4. Arduino入门

       解析互动装置

       传感器与驱动器

       LED闪烁

       编写程序

       给我个奶酪(Parmesan)

       Arduino从不停止

       真正的Tinker都写注释

       代码,一步一步来

       我们将会做什么?

       什么是电?

       使用按钮控制LED灯

       它是如何工作的?

       一个电路,一千种用法

       5. 高级的输入输出控制方法

       尝试其他开关类型传感器

       使用PWM方式控制灯光亮度

       使用光线传感器取代按钮

       模拟输入

       尝试其他模拟传感器

       串行通信

       驱动较大功率负载设备(直流电机、灯泡等)

       复杂传感器

       6. 互动云

       制订计划

       编写程序源代码

       组装电路

       下面介绍如何安装

       7. 排疑解惑

       测试板子

       用面包板测试电路

       将问题独立出来

       开发环境(IDE)常见问题

       利用网络资源解决问题

       附录A:面包板

       附录B:认识电阻和电容

       附录C:Arduino语法参考

       附录D:阅读电路简图

webpack5loader和plugin原理解析

       大家好,今天为大家解析下loader和plugin

一、区别

       loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的微信公众号第三方源码文件中

       plugin赋予了Webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决loader无法实现的其他事从整个运行时机上来看,如下图所示:

       可以看到,两者在运行时机上的区别:

       loader运行在打包文件之前plugins在整个编译周期都起作用在Webpack运行的生命周期中会广播出许多事件,Plugin可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果

       对于loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将A.scss或A.less转变为B.css,单纯的文件转换过程

       下面我们来看看loader和plugin实现的原理

Loader原理loader概念

       帮助webpack将不同类型的文件转换为webpack可识别的模块。

loader执行顺序

       分类

       pre:前置loader

       normal:普通loader

       inline:内联loader

       post:后置loader

       执行顺序

       4类loader的执行优级为:pre>normal>inline>post。

       相同优先级的loader执行顺序为:从右到左,从下到上。

       例如:

//此时loader执行顺序:loader3-loader2-loader1module:{ rules:[{ test:/\.js$/,loader:"loader1",},{ test:/\.js$/,loader:"loader2",},{ test:/\.js$/,loader:"loader3",},],},//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},

       使用loader的方式

       配置方式:在webpack.config.js文件中指定loader。(pre、normal、postloader)

       内联方式:在每个import语句中显式指定loader。(inlineloader)

开发一个loader1.最简单的loader//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};

       它接受要处理的源码作为参数,输出转换后的js代码。

2.loader接受的参数

       content源文件的内容

       mapSourceMap数据

       meta数据,可以是任何内容

loader分类1.同步loadermodule.exports=function(content,map,meta){ returncontent;};

       this.callback方法则更灵活,因为它允许传递多个参数,而不仅仅是content。

module.exports=function(content,map,meta){ //传递map,让source-map不中断//传递meta,让下一个loader接收到其他参数this.callback(null,content,map,meta);return;//当调用callback()函数时,总是返回undefined};2.异步loadermodule.exports=function(content,map,meta){ constcallback=this.async();//进行异步操作setTimeout(()=>{ callback(null,result,map,meta);},);};

       由于同步计算过于耗时,在Node.js这样的彩色二维码生成器源码单线程环境下进行此操作并不是好的方案,我们建议尽可能地使你的loader异步化。但如果计算量很小,同步loader也是可以的。

3.RawLoader

       默认情况下,资源文件会被转化为UTF-8字符串,然后传给loader。通过设置raw为true,loader可以接收原始的Buffer。

module.exports=function(content){ //content是一个Buffer数据returncontent;};module.exports.raw=true;//开启RawLoader4.PitchingLoadermodule.exports=function(content){ returncontent;};module.exports.pitch=function(remainingRequest,precedingRequest,data){ console.log("dosomethings");};

       webpack会先从左到右执行loader链中的每个loader上的pitch方法(如果有),然后再从右到左执行loader链中的每个loader上的普通loader方法。

       在这个过程中如果任何pitch有返回值,则loader链被阻断。webpack会跳过后面所有的的pitch和loader,直接进入上一个loader。

loaderAPI方法名含义用法this.async异步回调loader。返回this.callbackconstcallback=this.async()this.callback可以同步或者异步调用的并返回多个结果的函数this.callback(err,content,sourceMap?,meta?)this.getOptions(schema)获取loader的optionsthis.getOptions(schema)this.emitFile产生一个文件this.emitFile(name,content,sourceMap)this.utils.contextify返回一个相对路径this.utils.contextify(context,request)this.utils.absolutify返回一个绝对路径this.utils.absolutify(context,request)

       更多文档,请查阅webpack官方loaderapi文档

手写clean-log-loader

       作用:用来清理js代码中的console.log

//loaders/clean-log-loader.jsmodule.exports=functioncleanLogLoader(content){ //将console.log替换为空returncontent.replace(/console\.log\(.*\);?/g,"");};手写banner-loader

       作用:给js代码添加文本注释

       loaders/banner-loader/index.js

constschema=require("./schema.json");module.exports=function(content){ //获取loader的options,同时对options内容进行校验//schema是options的校验规则(符合JSONschema规则)constoptions=this.getOptions(schema);constprefix=`/**Author:${ options.author}*/`;return`${ prefix}\n${ content}`;};

       loaders/banner-loader/schema.json

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},0手写babel-loader

       作用:编译js代码,将ES6+语法编译成ES5-语法。

       下载依赖

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},1

       loaders/babel-loader/index.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},2

       loaders/banner-loader/schema.json

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},3手写file-loader

       作用:将文件原封不动输出出去

       下载包

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},4

       loaders/file-loader.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},5

       loader配置

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},6手写style-loader

       作用:动态创建style标签,插入js中的样式代码,使样式生效。

       loaders/style-loader.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},7Plugin原理Plugin的作用

       通过插件我们可以扩展webpack,加入自定义的构建行为,使webpack可以执行更广泛的任务,拥有更强的构建能力。

Plugin工作原理

       webpack就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack通过Tapable来组织这条复杂的生产线。webpack在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。webpack的事件流机制保证了插件的有序性,使得整个系统扩展性很好。——「深入浅出Webpack」

       站在代码逻辑的角度就是:webpack在编译代码过程中,会触发一系列Tapable钩子事件,插件所做的,就是找到相应的钩子,往上面挂上自己的任务,也就是注册事件,这样,当webpack构建的时候,插件注册的事件就会随着钩子的触发而执行了。

Webpack内部的钩子什么是钩子

       钩子的本质就是:事件。为了方便我们直接介入和控制编译过程,webpack把编译过程中触发的各类关键事件封装成事件接口暴露了出来。这些接口被很形象地称做:hooks(钩子)。开发插件,离不开这些钩子。

Tapable

       Tapable为webpack提供了统一的插件接口(钩子)类型定义,它是webpack的核心功能库。webpack中目前有十种hooks,在Tapable源码中可以看到,他们是:

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},8

       Tapable还统一暴露了三个方法给插件,用于注入不同类型的自定义构建行为:

       tap:可以注册同步钩子和异步钩子。

       tapAsync:回调方式注册异步钩子。

       tapPromise:Promise方式注册异步钩子。

Plugin构建对象Compiler

       compiler对象中保存着完整的Webpack环境配置,每次启动webpack构建时它都是一个独一无二,仅仅会创建一次的对象。

       这个对象会在首次启动Webpack时创建,我们可以通过compiler对象上访问到Webapck的主环境配置,比如loader、plugin等等配置信息。

       它有以下主要属性:

       compiler.options可以访问本次启动webpack时候所有的配置文件,包括但不限于loaders、entry、output、plugin等等完整配置信息。

       compiler.inputFileSystem和compiler.outputFileSystem可以进行文件操作,相当于Nodejs中fs。

       compiler.hooks可以注册tapable的不同种类Hook,从而可以在compiler生命周期中植入不同的逻辑。

       compilerhooks文档

Compilation

       compilation对象代表一次资源的构建,compilation实例能够访问所有的模块和它们的依赖。

       一个compilation对象会对构建依赖图中所有模块,进行编译。在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、分块(chunk)、哈希(hash)和重新创建(restore)。

       它有以下主要属性:

       compilation.modules可以访问所有模块,打包的每一个文件都是一个模块。

       compilation.chunkschunk即是多个modules组成而来的一个代码块。入口文件引入的资源组成一个chunk,通过代码分割的模块又是另外的chunk。

       compilation.assets可以访问本次打包生成所有文件的结果。

       compilation.hooks可以注册tapable的不同种类Hook,用于在compilation编译模块阶段进行逻辑添加以及修改。

       compilationhooks文档

生命周期简图开发一个插件最简单的插件

       plugins/test-plugin.js

//此时loader执行顺序:loader1-loader2-loader3module:{ rules:[{ enforce:"pre",test:/\.js$/,loader:"loader1",},{ //没有enforce就是normaltest:/\.js$/,loader:"loader2",},{ enforce:"post",test:/\.js$/,loader:"loader3",},],},9注册hook//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};0启动调试

       通过调试查看compiler和compilation对象数据情况。

       package.json配置指令

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};1

       运行指令

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2

       此时控制台输出以下内容:

PSC:\Users\\Desktop\source>//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};2>source@1.0.0debug>node--inspect-brk./node_modules/webpack-cli/bin/cli.jsDebuggerlisteningonws://.0.0.1:/ea-7b--a7-fccForhelp,see:/post/

       开发思路:

       我们需要借助html-webpack-plugin来实现

       在html-webpack-plugin输出index.html前将内联runtime注入进去

       删除多余的runtime文件

       如何操作html-webpack-plugin?官方文档

       实现:

//loaders/loader1.jsmodule.exports=functionloader1(content){ console.log("hellofirstloader");returncontent;};7

VC+++++是什么?

       VC++是微软公司开发的一个IDE(集成开发环境),换句话说,就是使用c++的一个开发平台.有些软件就是这个编出来的...另外还有VB,VF.只是使用不同语言...但是,

        vc++是Windows平台上的C++编程环境,学习VC要了解很多Windows平台的特性并且还要掌握MFC、ATL、COM等的知识,难度比较大。Windows下编程需要了解Windows的消息机制以及回调(callback)函数的原理;MFC是WinAPI的包装类,需要理解文档视图类的结构,窗口类的结构,消息流向等等;COM是代码共享的二进制标准,需要掌握其基本原理等等。

        VC作为一个主流的开发平台一直深受编程爱好者的喜爱,但是很多人却对它的入门感到难于上青天,究其原因主要是大家对他错误的认识造成的,严格的来说VC++不是门语言,虽然它和C++之间有密切的关系,如果形象点比喻的话,可以把C++看作为一种“工业标准”,而VC++则是某种操作系统平台下的“厂商标准”,而“厂商标准”是在遵循“工业标准”的前提下扩展而来的。

        VC++应用程序的开发主要有两种模式,一种是WIN API方式,另一种则是MFC方式,传统的WIN API开发方式比较繁琐,而MFC则是对WIN API再次封装,所以MFC相对于WIN API开发更具备效率优势,但为了对WINDOWS开发有一个较为全面细致的认识,笔者在这里还是以讲解WIN API的相关内容为主线。

        话说到这里可能更多人关心的是学习VC++需要具备什么条件,为什么对于这扇门屡攻不破呢?

        要想学习好VC必须具备良好的C/C++的基础,必要的英语阅读能力也是必不可少的,因为大量的技术文档多以英文形式发布。

       [编辑本段]VC++中播放声音的方法

        声音是多媒体的一个重要组成部分,在应用程序中加入声音可以使界面更友好。在VC++中可以根据不同的应用要求,用不同的方法实现声音的播放。

        一.播放声音文件的简单方法

        在VC++ 中的多媒体动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播放声音。最简单的播放声音方法就是直接调用VC++中提供的声音播放函数BOOL sndPlaySound ( LPCSTR lpszSound,UINT fuSound ); 或BOOL PlaySound( LPCSTR lpszSound, HMODULE hmod, DWORD fuSound );其中参数lpszSound是需要播放声音的.WAV文件的路径和文件名, hmod在这里为NULL,fuSound是播放声音的标志,详细说明请参考VC++中的帮助。 例如播放C:soundmusic.wav可以用sndPlaySound ("c:\sound\music.wav",SND_ASYNC);或PlaySound("c:\sound\music.wav",NULL, SND_ASYNC|SND_NODEFAULT );如果没有找到music.wav文件,第一种格式将播放系统默认的声音,第二种格式不会播放系统默认的声音。

        二.将声音文件加入到程序中

        在VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也允许用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。

        要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件(实现过程并不复杂,这里不在叙述)。假设生成的声音文件资源标识符为IDR_WAVE1。在播放时只需要调用下面的语句:

        PlaySound(MAKEINTRESOURCE(IDR_WAVE1),AfxGetResourceHandle(), SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP);

        其中MAKEINTRESOURCE()宏将整数资源标识符转变为字符串,AfxGetResourceHandle()函数返回包含资源的模块句柄,

        SND_RESOURCE是必须的标志。

        作为资源的声音文件的第二种播放方法是把资源读入内存后作为内存数据播放。具体步骤入下:

        1.获得包含资源的模块句柄:

        HMODULE hmod=AfxGetResourceHandle();

        2.检索资源块信息:

        HRSRC hSndResource=FindResource(hmod,MAKEINTRESOURCE(IDR_WAVE1),_T("WAVE"));

        3. 装载资源数据并加锁:

        HGLOBAL hGlobalMem=LoadResource(hmod,hSndResource);

        LPCTSTR lpMemSound=(LPCSTR)LockResource(hGlobalMem);

        4.播放声音文件:

        sndPlaySound(lpMemSound,SND_MEMORY));

        5.释放资源句柄:

        FreeResource(hGlobalMem);

        三.播放声音文件的高级方法

        在VC++中提供了一组对音频设备及多媒体文件直接进行操作的函数。利用这些函数可以灵活地对声音文件进行各种处理。

        首先介绍几个要用到的数据结构。WAVEFORMATEX结构定义了WAVE音频数据文件的格式。WAVEHDR结构定义了波形音频缓冲区。读出的数据首先要填充此缓冲区才能送音频设备播放。WAVEOUTCAPS结构描述了音频设备的性能。MMCKINFO结构包含了RIFF文件中一个块的信息。详细的说明请参考VC++中的帮助。

        下面给出程序流程简图及程序源代码清单,在VC++环境下可直接使用:

        源程序清单如下:

        LPSTR szFileName;//声音文件名

        MMCKINFO mmckinfoParent;

        MMCKINFO mmckinfoSubChunk;

        DWORD dwFmtSize;

        HMMIO m_hmmio;//音频文件句柄

        DWORD m_WaveLong;

        HPSTR lpData;//音频数据

        HANDLE m_hData;

        HANDLE m_hFormat;

        WAVEFORMATEX * lpFormat;

        DWORD m_dwDataOffset;

        DWORD m_dwDataSize;

        WAVEHDR pWaveOutHdr;

        WAVEOUTCAPS pwoc;

        HWAVEOUT hWaveOut;

        //打开波形文件

        if(!(m_hmmio=mmioOpen(szFileName,NULL,MMIO_READ|MMIO_ALLOCBUF)))

        {

        //File open Error

        Error("Failed to open the file.");//错误处理函数

        return false;

        }

        //检查打开文件是否是声音文件

        mmckinfoParent.fccType =mmioFOURCC(’W’,’A’,’V’,’E’);

        if(mmioDescend(m_hmmio,(LPMMCKINFO)&mmckinfoParent,NULL,MMIO_FINDRIFF))

        {

        //NOT WAVE FILE AND QUIT

        }

        //寻找 ’fmt’ 块

        mmckinfoSubChunk.ckid =mmioFOURCC(’f’,’m’,’t’,’ ’);

        if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK))

        {

        //Can’t find ’fmt’ chunk

        }

        //获得 ’fmt ’块的大小,申请内存

        dwFmtSize=mmckinfoSubChunk.cksize ;

        m_hFormat=LocalAlloc(LMEM_MOVEABLE,LOWORD(dwFmtSize));

        if(!m_hFormat)

        {

        //failed alloc memory

        }

        lpFormat=(WAVEFORMATEX*)LocalLock(m_hFormat);

        if(!lpFormat)

        {

        //failed to lock the memory

        }

        if((unsigned long)mmioRead(m_hmmio,(HPSTR)lpFormat,dwFmtSize)!=dwFmtSize)

        {

        //failed to read format chunk

        }

        //离开 fmt 块

        mmioAscend(m_hmmio,&mmckinfoSubChunk,0);

        //寻找 ’data’ 块

        mmckinfoSubChunk.ckid=mmioFOURCC(’d’,’a’,’t’,’a’);

        if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK))

        {

        //Can’t find ’data’ chunk

        }

        //获得 ’data’块的大小

        m_dwDataSize=mmckinfoSubChunk.cksize ;

        m_dwDataOffset =mmckinfoSubChunk.dwDataOffset ;

        if(m_dwDataSize==0L)

        {

        //no data in the ’data’ chunk

        }

        //为音频数据分配内存

        lpData=new char[m_dwDataSize];

        if(!lpData)

        {

        //faile

        }

        if(mmioSeek(m_hmmio,SoundOffset,SEEK_SET)<0)

        {

        //Failed to read the data chunk

        }

        m_WaveLong=mmioRead(m_hmmio,lpData,SoundLong);

        if(m_WaveLong<0)

        {

        //Failed to read the data chunk

        }

        //检查音频设备,返回音频输出设备的性能

        if(waveOutGetDeVCaps(WAVE_MAPPER,&pwoc,sizeof(WAVEOUTCAPS))!=0)

        {

        //Unable to allocate or lock memory

        }

        //检查音频输出设备是否能播放指定的音频文件

        if(waveOutOpen(&hWaveOut,DevsNum,lpFormat,NULL,NULL,CALLBACK_NULL)!=0)

        {

        //Failed to OPEN the wave out devices

        }

        //准备待播放的数据

        pWaveOutHdr.lpData =(HPSTR)lpData;

        pWaveOutHdr.dwBufferLength =m_WaveLong;

        pWaveOutHdr.dwFlags =0;

        if(waveOutPrepareHeader(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0)

        {

        //Failed to prepare the wave data buffer

        }

        //播放音频数据文件

        if(waveOutWrite(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0)

        {

        //Failed to write the wave data buffer

        }

        //关闭音频输出设备,释放内存

        waveOutReset(hWaveOut);

        waveOutClose(hWaveOut);

        LocalUnlock(m_hFormat);

        LocalFree(m_hFormat);

        delete [] lpData;

        说明:1)以上使用的音频设备和声音文件操作函数的声明包含在mmsystem.h头文件中,因此在程序中必须用#include "mmsystem.h"语句加入头文件。同时在编译时要加入动态连接导入库winmm.lib,具体实现方法是从Developer Studio的Project菜单中选择Settings,然后在Link选项卡上的Object/Library Modules控制中加入winmm.lib。2)在pWaveOutHdr.lpData中指定不同的数据,可以播放音频数据文件中任意指定位置的声音。3) 以上程序均在VC++6.0中调试通过,在文中省略了对错误及异常情况的处理,在实际应用中必须加入。

        四.结论

        在VC++中可以根据应用需要采用不同的方法播放声音文件。简单应用可以直接调用声音播放函数。第二种方法可以把声音作为资源加入可执行文件中。如果在播放之前要对声音数据进行处理,可用第三种方法。