Jiang,Sheng ,a Microsoft Most Valuable Professional of Visual C++, became a Visual C++ programmer at 1999. He actives on several online Visual C++ forums ,blogs and newsgroups
10/23/2003
写篇文章真累
最近一篇文章长长短短,写了两个礼拜吧。
写这篇文章的主要原因是想把网页分析做得更加灵活。这篇文章的基础是我以前为一个EBS游戏写的外挂,可以自动修改网页内容(主要是表单)和定时submit表单(有的网站的submit有时间限制)。以前的代码是用VC来写的,和网页的修改同步很不方便。很多功能依赖于网页结构,网页结构一变的话,就需要重新编译代码。所以在重写的时候,想考虑做成类似于outlook的规则。但是在编写的时候,发现规则编写起来实在是太繁琐了。其实这些规则用VB来写脚本的话,可能就几句话。所以做得更加通用一点,用vbs算了。这样的话,修改的时候改的少些,虽然使用门槛要高些。
主要碰见的技术问题是 脚本中的浏览器的事件处理代码不能执行(CHtmlView捕获了事件,所以要在CHtmlView里面转发事件) 移植MFCIE的代码到MDI的时候的菜单出现很多问题,主要是MDI的菜单替换,以及插入MDI系统菜单之后收藏夹的位置变化
移植部分MFC7的代码到MFC6,中间还结合了KB的文章的代码,真是faint 无法直接创建支持事件的CCmdTaget类(ActiveX好像可以……) MFC的类向导不支持自动化中的默认参数 上面两个问题使得我不得不手动改ODL文件,中间出现无数问题…… 关闭窗口时出现非法操作(最后捕获了WindowClosing事件,Cancel掉了系统的处理之后自己关闭) 表单的自动完成没有资料,没做成功。好像和隐藏方法IShellUIHelper::AutoCompleteAttatch有关。 编辑网页源代码时文档结构的刷新有问题,IHtmlDomNote的Child在SetOutHTML之后全部不见了,最后是刷新整个文档结构树才解决。
9/26/2003
ASF学习笔记
使用OnStatus回调 在Windows Media Format SDK中,IWMStatusCallback::OnStatus 被很多对象调用。OnStatus接收SDK操作状态的变化。每种对象可能有不同的方式连接到IWMStatusCallback。
使用事件进行同步调用 1 使用Platform SDK的API CreateEvent创建一个事件对象 2 实现回调函数,,捕获事件,并且调用SetEvent函数标记事件对象 3 在应用程序中调用WaitForSingleObject 、监视事件对象。如果你是在为Windows程序编写代码,你必须创建一个消息循环对用户操作做出相应。
使用上下文参数 Windows Media Format SDK的一些回调函数具有pvContext参数,这个值是你在异步操作启动时传递给对象的。 通常,多个对象使用同一个回调时传递对象指针作为这个参数。
使用设置 设置的主要目的是描述其中的对象,以及对象之间的关系。不管是否使用编码/解码器,某些流需要配置才可以工作。流的配置信息可以用IWMCodecInfo3 接口的方法获得,但是不要手动配置一个使用了Windows Media编码/解码器的流。 创建/编辑设置的步骤 1 创建空设置,或者打开旧设置 2 配置每个流,如果需要的话,使用从编码/解码器获得的数据 3 配置互斥(可选) 4 配置带宽共享(可选) 5 配置优先级(可选)
设计设置
选择编码方式 1-pass Constant Bit Rate (CBR) 直播的唯一选择。以预定的码流率编码,并且质量最低。 2-pass CBR 文件形式的流媒体,长度固定,质量比1-pass Constant Bit Rate (CBR)好 1-pass Variable Bit Rate (VBR) 需要指定质量时使用,本地播放或者下载后播放 2-pass VBR – unconstrained 需要指定带宽时使用,但是真实带宽占用可以偏离指定带宽,本地播放或者下载后播放 2-pass VBR – constrained 需要指定带宽时使用,但是真实带宽占用不能大于指定带宽,本地播放或者下载后播放
码流率 除了数据之外,分包也要占用一定的带宽。如果流包含数据单位扩展,那么这将大大增加流的码流率。 同时,除了应用程序之外的任何连接都和应用程序共享网络带宽,所以不能认为应用程序可以完全使用客户的网络带宽。
配置流 如果流是视频/音频,使用Windows Media编码/解码器,那么你必须使用IWMCodecInfo3的方法从编码/解码器获得流配置对象。 如果流是其他类型,使用IWMProfile::CreateNewStream.创建一个新的流配置对象。 每个流配置都必须设置名字、连接名和流序号(从1到63)。 可能会修改使用Windows Media编码/解码器的two-pass VBR 音频流的VBR设置。视频流无需修改配置。 根据类型配置其他类型流。所有的这种流需要设置比特率和缓冲窗口。 使用IWMProfile::AddStream. 将流添加到媒体。 大部分设置可以通过IWMMediaProps访问。这些设置保存在WM_MEDIA_TYPE 结构中。对于音频和视频,WM_MEDIA_TYPE结构指针指向媒体特定的更多信息,通常是WAVEFORMATEX 或者WMVIDEOINFOHEADER结构。视频有第三个结构BITMAPINFOHEADER描述了视频的桢。
从编码/解码器获得流配置信息 使用Windows Media编码/解码器的视频/音频流需要从编码/解码器获得流配置信息。尽管你可以自行设置这些配置,从编码/解码器获得流配置信息使得数据是准确的。除非文档推荐,否则不要修改获得的流配置信息。 可以从设置管理器的IWMCodecInfo, IWMCodecInfo2, 和IWMCodecInfo3接口获得信息。
枚举安装的编码/解码器 编码/解码器的编号从0开始,音频和视频的编码/解码器有独立的编号。
枚举编码/解码器支持的格式
配置音频流 不要手动修改获得的配置的质量设置,而应该用IWMPropertyVault接口修改。 音频流的缓冲窗口不应该设置得比视频流的缓冲窗口大,否则会造成播放不同步。通常,音频流的缓冲窗口是1.5-3秒,视频流的缓冲窗口是3-5秒。
配置视频流 除非是RGB24数据,否则大小应该是4的倍数,否则会有非法格式/非法配置等错误。
配置屏幕流 和视频流一样,但是如果复杂度设置为0,那么IWMVideoMediaProps::SetQuality设置的质量会被忽略。
图像流 包含JPEG形式的图像数据。
视频流的定位性能 可以使用IWMVideoMediaProps::SetMaxKeyFrameSpacing设置关键桢间隔。增加关键桢数目会降低视频质量。
未压缩的音视频格式 不能用于流,必须手动设置带宽,缓冲窗口应该设为0
配置其他流 通常,这种流只需要比特率和缓冲窗口和WM_MEDIA_TYPE 中的媒体主类型设置。但是某些类型的流还需要其他设置
脚本流 WM_MEDIA_TYPE的成员formattype 要设置为WMFORMAT_Script,指明pbFormat成员指向一个WMSCRIPTFORMAT 结构。 只有一种脚本媒体类型,WMSCRIPTTYPE_TwoStrings。
文件传输流 每个采样需要一个数据单位扩展,你需要实现一个数据单位扩展系统。 调用IWMStreamConfig2::AddDataUnitExtension添加数据单位扩展到流。 hr = pStreamConfig2->AddDataUnitExtension(CLSID_WMTPropertyFileName, -1, NULL, 0);
网页流 WM_MEDIA_TYPE.majortype WMMEDIATYPE_Filetransfer. WM_MEDIA_TYPE.subtype WMMEDIASUBTYPE_WebStream. WM_MEDIA_TYPE.bFixedSizeSamples False. WM_MEDIA_TYPE.bTemporalCompression True. WM_MEDIA_TYPE.lSampleSize 0. WM_MEDIA_TYPE.formattype WMFORMAT_WebStream. WM_MEDIA_TYPE.pUnk NULL. WM_MEDIA_TYPE.cbFormat sizeof(WMT_WEBSTREAM_FORMAT). WM_MEDIA_TYPE.pbFormat 一个配置好的WMT_WEBSTREAM_FORMAT结构的指针. WMT_WEBSTREAM_FORMAT.cbSampleHeaderFixedData sizeof(WMT_WEBSTREAM_SAMPLE_HEADER). WMT_WEBSTREAM_FORMAT.wVersion 1. WMT_WEBSTREAM_FORMAT.wreserved 0.
文本流 媒体类型WMMEDIATYPE_TEXT
计算比特率和缓冲窗口 简单的办法是设置为数据长度/时间.但是图像和文件流可能突发数据很多,但是有很多空闲时间.缓冲窗口必须设置得足够大.需要的时候,可以适当增加这些值.
变码流率流
数据单位扩展
保存/重新使用配置 不要手动更改PRX文件。看起来很小的改变会使得配置无效。
互斥
流优先级
带宽共享
包大小
写ASF文件 使用IWMWriter::SetProfile对写入对象进行设置。但是,设置了之后,对设置对象的修改不会自动反映到写入对象,除非再次调用IWMWriter::SetProfile。 设置写入对象会复位全部头属性,所以必须在设置之后再修改这些属性。
输入
设置对象中的每个连接有一个输入号。除非配置中有互斥流,否则每个流有一个连接。互斥流共享连接。 写入流时需要用输入号来区别每个流,所以必须用连接名字来判断每个流的输入号。
枚举输入格式 SDK可以对输入进行预处理来判断输入的格式是否支持。
设置输入格式 找到符合数据的输入格式之后,可以调用IWMWriter::SetInputProps让它可以被写入对象使用。对于视频流,必须设置桢的大小。
其他类型的流和预压缩流 其他类型的流无需设置。 预压缩流需要设置输入格式为NULL。这个设置必须在BeginWriting之前完成。同时需要调用IWMHeaderInfo3::AddCodecInfo设置预压缩流的格式。
BeginWriting之前,还可以用IWMWriterAdvanced2::SetInputSetting设置和流无关的设置。
元数据 使用写入对象的IWMHeaderInfo 或者IWMHeaderInfo2接口访问元数据。必须在IWMWriter::BeginWriting之前完成元数据的写入。 注意,如果创建了写入对象而没有释放,然后再创建写入对象,一些元数据会被复制到新的对象中。
写入采样 写入采样之前要调用IWMWriter::BeginWriting. 1 用IWMWriter::AllocateSample分配缓冲区,并且获得其INSSBuffer接口 2 用INSSBuffer::GetBuffer获得缓冲区地址 3 复制数据到缓冲区中 4 用INSSBuffer::SetLength设置复制的数据长度 5 把缓冲区、输入编号和媒体时间传递给IWMWriter::WriteSample方法。音频数据持续时间是一样的,所以可以简单地在现有时间上加上一个常数。对于视频,需要根据桢率计算媒体时间。 WriteSample是异步调用,在下一次WriteSample调用之前可能没有结束。所以要在每次写入采样之前调用AllocateSample获取缓冲区对象。 所有采样写完之后,调用IWMWriter::EndWriting完成写入操作。 流数据应该几乎同时结束,否则某些流数据可能丢失。
写入压缩采样 使用IWMWriterAdvanced::WriteStreamSample 替代IWMWriter::WriteSample。
写入图像采样 必须用IWMWriterAdvanced2::SetInputSetting设置图像质量g_wszJPEGCompressionQuality,范围从1到100。图像采样压缩比通常很大,所以要使用尝试的方法设置缓冲窗口大小。
强制关键桢 使用INSSBuffer3::SetProperty设置缓冲区对象的WM_SampleExtensionGUID_OutputCleanPoint为TRUE。
9/25/2003
ASF学习笔记
一个设置是一个ASF的配置(configuration)的描述数据集合。一个设置必须至少包含一个流的配置设置。
流信息
设置中的流信息包含流的比特率(bit rate),缓冲窗口和媒体属性的设置。视频和音频的流信息准确描述了文件中的媒体配置,包括压缩数据使用的编码和解码器(如果有的话)。
一个设置也包含很多创建ASF文件时使用的ASF的特性,这包括互斥、媒体优先级、带宽共享和数据单位扩展。
每次写文件是必须提供设置。你可以调用IWMWriter::SetProfile指定一个设置。
设置有三种形式,应用程序中设置对象包含的数据,XML文件,或者ASF文件头。
设置对象
可以用设置管理器创建空设置对象,然后从现有数据载入设置
XML文件
具有PRX扩展名.注意Windows Media 9 Series 中没有原来的系统设置(system profiles)也不再使用,而作为这种形式存在。保存自定义设置时必须保存成这种文件。
ASF文件头
ASF读者创建一个设置对象,然后从ASF文件头载入格式信息。但是修改文件头不会影响文件的内容。可以重新对文件编码来完成格式的修改。
使用设置编辑器
除了用Windows Media Format SDK之外,还可以用Windows Media Encoder 9 Series中包含的设置编辑器创建设置。在应用程序中使用IWMProfileManager::LoadProfileByData载入预定义的设置。但是,启用“视频大小:和输入相同”这个选项将设置视频的大小为0;Windows Media Encoder 9 Series可以识别并且处理这种情况,但是Windows Media Format SDK的写入对象不会自动处理,所以应用程序必须并且处理这种情况.
下面是一个XML格式的配置
// 73647561-0000-0010-8000-00AA00389B71 ''auds'' == WMMEDIATYPE_Audio
// 00000001-0000-0010-8000-00AA00389B71 WMMEDIASUBTYPE_PCM
// 73647561-0000-0010-8000-00AA00389B71 ''auds'' == WMMEDIATYPE_Audio
// 56555949-0000-0010-8000-00AA00389B71 ''YV12'' == MEDIASUBTYPE_IYUV
// 73636d64-0000-0010-8000-00AA00389B71 ''scmd'' == WMMEDIATYPE_Script
// 82f38a70-c29f-11d1-97ad-00a0c95ea850 WMSCRIPTTYPE_TwoStrings
媒体采样(Media Sample)
媒体采样,或者采样,是一块数字媒体数据。采样是Windows Media Format SDK可以读写的数据的最小单位。采样内容由采样相关的媒体类型指出。对于视频,每个采样表示一个桢,每个单独采样中包含的数据量由创建ASF时指定的设置设置。
采样可以包含未压缩的数据,或者压缩过的数据,这时被称为流采样。创建ASF时,采样被传递给写入对象,写入对象使用相关的编码器压缩数据,并且写入ASF文件的数据段。播放时,读出对象读出压缩的数据,解压数据,并且提供未压缩格式的数据。
采样被封装在Windows Media Format SDK的自动分配的缓冲区对象中。需要的时候,你也可以自己分配缓冲区对象,使用它的读写特性。
这里的采样并非音频采样。通常,音频采样质量用每秒录制的采样数据数量表示,例如CD质量是44,100采样/秒,或者44.1 kHz。
输入,流和输出
输入对象是你用于写入文件的任何数字媒体流,必须是可以支持的格式。支持很多标准RGB和YUV作为视频输入格式,PCM作为音频输入格式。如果编码器不支持某种输入格式,那么写入对象会初始化一个辅助对象,转换输入流到可以支持的格式,例如调整色深转换、缩放,调整声音质量、采样率和频道数目。某些情况下,压缩格式的食品和音频可用于输入。输入也可以是其他格式,例如文字,脚本命令,图像,或者任意文件数据。
输出是读取对象传递给应用程序,提供用户体验的数据。一个输出等同于一个流。如果你使用互斥属性,那么所有互斥数据共享一个输出。
一个流是一个ASF文件中包含的数据。一个流的生命期中只有一种压缩设置。一个简单的ASF具有两种流:视频和音频。更加复杂的ASF文件可以包含两路音频和多路视频。音频可以有同样的压缩设置,但是内容不同,例如不同语言的讲解;视频可以有同样的内容,但是具有不同的压缩比例。格式是在设置对象中指定的。
某些输入可以是压缩过的,这时读取对象必须以流编号依次访问数据,而不是按输出顺序访问数据。
编号
流具有从1开始的编号,这是在设置中指定的。同时,流具有一个索引以在设置中枚举流。这两个数字并不相关,例如输入1并不一定是编号为1的流,编号为1的流并不一定是输入1,等等。
格式
每种媒体类型的全部信息。每个格式有一个主类型,例如音频或视频,并且可能有一个子类型。格式包含依赖于主类型的不同信息。视频和音频格式比其他格式需要更多信息。
输入格式
描述你传递给写入对象的数字媒体类型。如果ASF文件中的流是用编码器压缩,那么编码器只支持某些输入格式。使用Windows Media 音频和视频编码器时,可以使用写入对象枚举支持的输入格式。写到文件时,你有责任选择一个匹配输入媒体的输入格式。
某些格式不必匹配编码器指明的输入格式,编码器可以自行转换数据到需要的格式。
流格式
ASF文件中的数据保存形式。在设置中描述,可以符合或不符合输入、输出格式(例如使用了某种编码/解码器)。可能必须获得编码/解码器信息之后,才可以设置流格式
输出格式。
描述你传递给读出对象的数字媒体类型。如果ASF文件中的流是用编码器压缩,那么编码器只支持某些输出格式。使用Windows Media 音频和视频编码器时,可以使用读出对象枚举支持的输出格式。读出文件时,你有责任选择一个匹配输出媒体的输出格式。
某些格式不必匹配编码器指明的输出格式,编码器可以自行转换数据到需要的格式。
比特率(Bit Rate)
每秒传递给ASF的数据的数量,以位/秒(bps)或者千位/秒(kbps)为单位。经常与带宽混淆,带宽也以bps或者kbps为单位。
如果用户的带宽小于ASF的比特率,那么播放可能中断。通常,带宽不足会导致跳过某些采样,或者更多的数据缓冲时间。
每个ASF文件创建时被指定一个比特率,它基于文件中流的数量。不同的流可以有不同的比特率。比特率可以是常数(压缩的数据可以以基本同样的速度被传输)或者可变(保留压缩的数据质量,即使可能造成突发数据溢出)。
同一个内容可以被压缩成多个比特率不同的流,然后你可以配置他们为互斥的。这个属性叫多比特率(multiple bit rate), 或者MBR.
元数据
描述ASF文件或者文件内容的信息,位于文件头。元数据的项称为属性。每一个属性由名字和值组成。全局常数用于标识属性,例如ASF文件的标题被保存在 g_wszWMTitle 属性中。在Windows Media Format SDK 中定义了最常用的内建属性,但是你也可以定义自己的属性。由于其他开发者可能和你是用同样的名字,所以可能造成冲突。
一些全局属性可以被修改,例如g_wszWMSeekable属性(文档是否可以从任意点被读取)
一些属性纯粹用于信息用途,并且必须被设置,例如g_wszWMAuthor属性(作者)
属性可以被应用到整个文件或者单独的流。
你可以用Windows Media Format SDK编辑MP3文件的元数据,但是必须使用ID3-compliant属性保留与其他MP3应用程序的兼容性。
媒体时间
自第一个采样开始的时间计量方式,单位和SDK其他时间的单位一样,是100纳秒。它使得文件中不同的流可以被同步。你写入的每一个采样都必须有媒体时间。ASF文件数据段中每一个数据对象都有媒体时间。每一个输出的数据也都有媒体时间。
缓冲
读取对象打开流文件时从文件头的信息决定缓冲区大小。实际比特率是变化的,但是平均值应该是设置中指定的值。
缓冲窗口是以可以缓冲的数据时间长度来衡量的。例如,32Kbps的流,3秒的缓冲窗口,意味着缓冲区大小为 12,000字节(32000*3/8)。解码器限制了这个数值,所以缓冲窗口的平均比特率不大于流的比特率。
通常在设置中指定这个值,写入对象处理剩下的部分。写入压缩数据到流时,必须自己确定写入的速度不会超出这个值
ASF文件中的段
一个ASF文件中的段以对象的方式组织起来。一共有三种顶层对象,必须有的头对象(Head),数据对象(Data),以及可选的索引对象(Index)。
每个对象都以全球唯一标志(GUID)和大小开始。这些数字使得文件读者可以解析这些信息,并且载入到相应的对象。因为这些GUID,底层的对象可以以任何顺序排列,并且仍然可以被识别。这使得一个不完整的ASF文件仍然可被正确读取,只要有一个完整的文件头和至少一个数据对象。某些对象,例如流属性对象,可能有多个示例。
头对象包含文件的描述信息,同时是唯一的顶层对象容器。
数据对象以包的格式存储流数据。数据对象还具有文件ID和包总个数属性,但是对于流格式,包总个数属性没有意义。
每一个数据包包含发送时间和持续时间。这使得读者可以发现流传输的中断。
数据包的数据被封装到载荷(payloads)中。一个载荷可以包含一个或者多个媒体对象(media objects),媒体对象的一个例子是视频流的一个桢。大的媒体对象,例如视频流的一个关键桢,可能被扩展到多个载荷,甚至多个包。为了跟踪对象的片断,每个对象的段具有从0到255的编号。
除了数据之外,载荷也具有以毫秒为单位的时间戳。
所有的包具有头对象中指定的统一的大小。当一个包包含的数据少于指定大小时,用数据("padding" data )填充不足部分。
索引对象包含时间《-》关键桢的配对,以更有效地在文件中定位。因为它处于文件末尾,实时媒体不能访问这个对象。
3/18/2002
使用IPicture的OLE实现读取和显示BMP,GIF,JPG,ICO,EMF,WMF图像
作者的话:GDI+看起来是更好的解决方案,但是IPicture的OLE实现更简单。
问题的起源
很久以来,我都被一个问题困扰。关于程序中显示图像的问题,我在网络上搜索了很长时间,找到了无数的解决方案,比如分析文件格式,直接读取文件的;用控件的(柯达的ImgEdit控件);以及不知道内部实现方法的库(ImageLoad)。而我找到的方法大都不容易使用,特别是那些直接按位读取图像的。很多时候我不得不为每种文件格式写一段代码。
目前我的解决方案
在使用了多种图形显示方案之后,我决定使用微软提供的IPicture的OLE实现来显示图像。由于MFC的CPictureHolder类是这个实现的一个封装,而且自带了从图像句柄读入图形的功能,所以我的工作以CPictureHolder类为基础。要是你不喜欢MFC的话,你可以把CPictureHolder类的代码从MFC的源代码里面分离出来单独使用。
CPictureHolder类的设计目的是实现ActiveX控件的Picture属性,允许用户在控件中显示图像。作为默认的Picture属性,开发者可以指定一个位图(Bitmap),图标(Icon),或者图元文件(Metafile)来显示。虽然微软的文档中并未说明支持JPEG格式的图像,但是经过我的测试,在Windows98以上,以及Windows2000中,它实际上是支持JPEG格式的,根据微软的文档,支持的图像格式有BMP,DIB,WMF,ICO。笔者并未测试其他图像格式是否被支持,例如PNG、GIF、PCX等等,有兴趣的读者可以去自己测试一下。(据111222的文档http://www.csdn.net/develop/read_article.asp?id=10632,也支持GIF格式)
由于CPictureHolder类并未实现从文件/资源/内存读入图像,使用起来很不方便,所以我扩展了这个类以支持上述功能。部分工作基于Dr. Yovav Gad, (Sources@SuperMain.com ,www.SuperMain.com)和http://www.thecodeproject.com/bitmap/cpicture.asp的代码。
要使用CPictureHolder类,必须先包含afxctl.h
源代码
#include <afxctl.h>
class CPicture:public CPictureHolder
{
public:
BOOL Load(CString sFilePathName);//从文件读取图像
BOOL Load(HINSTANCE hInstance,LPCTSTR lpszResourceName, LPCSTR ResourceType);//从资源读取图像
BOOL LoadPictureData(BYTE* pBuffer, int nSize);//从内存读取图像
BOOL SaveAsBitmap(CString sFilePathName);//写入到BMP文件
void Render(CDC* pDC, LPRECT pDrawRect/*目标矩形,单位是逻辑坐标单位*/, LPRECT
pSrcRect=NULL/*来源矩形,单位是0.01毫米,如果为空,则拉伸整个图像到目标矩形*/,LPCRECT prcWBounds=NULL/*图元文件专用,绑定矩形*/);//在给定的DC上画图,
CPicture();
virtual ~CPicture();
void UnloadPicture();//释放图像,作用同CPictureHolder::~CPictureHolder()
public:
LONG get_Height(); // 以0.01毫米为单位的图像高度
LONG get_Width(); // 以0.01毫米为单位的图像宽度
};
//-----------------------------------------------------------------------------
// Does: Free The Allocated Memory That Holdes The IPicture Interface Data
// ~~~~ And Clear Picture Information
//
// Note: This Might Also Be Useful If U Only Need To Show The Picture Once
// ~~~~~ Or If U Copy The Picture To The Device Context, So It Can Still
// Remain On Screen - But IPicture Data Is Not Needed No More
//
//-----------------------------------------------------------------------------
void CPicture::UnloadPicture()
//=============================================================================
{
if(m_pPict != NULL){
m_pPict->Release();
m_pPict = NULL;
}
}
//-----------------------------------------------------------------------------
// Does: Open a Resource And Load It Into IPicture (Interface)
// ~~~~ (.BMP .DIB .EMF .GIF .ICO .JPG .WMF)
//
// Note: When Adding a Bitmap Resource It Would Automatically Show On "Bitmap"
// ~~~~ This NOT Good Coz We Need To Load It From a Custom Resource "BMP"
// To Add a Custom Rresource: Import Resource -> Open As -> Custom
// (Both .BMP And .DIB Should Be Found Under "BMP")
//
// InPut: ResourceName - As a UINT Defined (Example: IDR_PICTURE_RESOURCE)
// ~~~~~ ResourceType - Type Name (Example: "JPG")
//
// OutPut: TRUE If Succeeded...
// ~~~~~~
//-----------------------------------------------------------------------------
BOOL CPicture::Load(HINSTANCE hInstance,LPCTSTR lpszResourceName, LPCSTR ResourceType)
//=============================================================================
{
HGLOBAL hGlobal = NULL;
HRSRC hSource = NULL;
LPVOID lpVoid = NULL;
int nSize = 0;
BOOL bResult=FALSE;
if(m_pPict != NULL) UnloadPicture(); // Important - Avoid Leaks...
hSource = FindResource(hInstance, lpszResourceName, ResourceType);
if(hSource == NULL)
{
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "FindResource() Failed\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
return(FALSE);
}
hGlobal = LoadResource(hInstance, hSource);
if(hGlobal == NULL)
{
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "LoadResource() Failed\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
return(FALSE);
}
lpVoid = LockResource(hGlobal);
if(lpVoid == NULL)
{
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "LockResource() Failed\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
return(FALSE);
}
nSize = (UINT)SizeofResource(hInstance, hSource);
if(LoadPictureData((BYTE*)hGlobal, nSize)) bResult = TRUE;
UnlockResource(hGlobal); // 16Bit Windows Needs This
FreeResource(hGlobal); // 16Bit Windows Needs This (32Bit - Automatic Release)
return(bResult);
}
//-----------------------------------------------------------------------------
// Does: Open a File And Load It Into IPicture (Interface)
// ~~~~ (.BMP .DIB .EMF .GIF .ICO .JPG .WMF)
//
// InPut: sFilePathName - Path And FileName Target To Save
// ~~~~~
//
// OutPut: TRUE If Succeeded...
// ~~~~~~
//-----------------------------------------------------------------------------
BOOL CPicture::Load(CString sFilePathName)
//=============================================================================
{
if(!PathFileExists(sFilePathName))return FALSE;
BOOL bResult = FALSE;
CFile PictureFile;
CFileException e;
int nSize = 0;
if(m_pPict != NULL) UnloadPicture(); // Important - Avoid Leaks...
if(PictureFile.Open(sFilePathName, CFile::modeRead CFile::typeBinary, &e))
{
nSize = PictureFile.GetLength();
BYTE* pBuffer = new BYTE[nSize];
if(PictureFile.Read(pBuffer, nSize) > 0)
{
if(LoadPictureData(pBuffer, nSize)) bResult = TRUE;
}
PictureFile.Close();
delete [] pBuffer;
}
else // Open Failed...
{
TCHAR szCause[255];
e.GetErrorMessage(szCause, 255, NULL);
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, szCause, ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
bResult = FALSE;
}
return(bResult);
}
//-----------------------------------------------------------------------------
// Does: Read The Picture Data From a Source (File / Resource)
// ~~~~ And Load It Into The Current IPicture Object In Use
//
// InPut: Buffer Of Data Source (File / Resource) And Its Size
// ~~~~~
//
// OutPut: Feed The IPicture Object With The Picture Data
// ~~~~~~ (Use Draw Functions To Show It On a Device Context)
// TRUE If Succeeded...
//-----------------------------------------------------------------------------
BOOL CPicture::LoadPictureData(BYTE *pBuffer, int nSize)
//=============================================================================
{
BOOL bResult = FALSE;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
if(hGlobal == NULL)
{
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "Can not allocate enough memory\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
return(FALSE);
}
void* pData = GlobalLock(hGlobal);
memcpy(pData, pBuffer, nSize);
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
if(CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) == S_OK)
{
HRESULT hr;
if((hr = OleLoadPicture(pStream, nSize, FALSE, IID_IPicture, (LPVOID *)&m_pPict)) == E_NOINTERFACE)
{
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "IPicture interface is not supported\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
return(FALSE);
}
else // S_OK
{
pStream->Release();
pStream = NULL;
bResult = TRUE;
}
}
FreeResource(hGlobal); // 16Bit Windows Needs This (32Bit - Automatic Release)
return(bResult);
}
//-----------------------------------------------------------------------------
// Does: Draw The Loaded Picture Direct To The Client DC
// ~~~~
//
// Note: Bigger OR Smaller Dimentions Than The Original Picture Size
// ~~~~ Will Draw The Picture Streached To Its New Given NEW Dimentions...
//
// InPut: pDC - Given DC To Draw On
// ~~~~~ pSrcRect- Dimentions Of The Picture To Draw From(As a Rectangle)
// DrawRect - Dimentions Of The Picture To Draw To(As a Rectangle)
// OutPut: TRUE If Succeeded...
// ~~~~~~
//-----------------------------------------------------------------------------
//=============================================================================
void CPicture::Render(CDC* pDC, LPRECT pDrawRect, LPRECT pSrcRect/*=NULL*/,LPCRECT prcWBounds/*=NULL*/)
{
if (pDC == NULL m_pPict == NULL) return ;
CRect recrDest(pDrawRect);
long Width = 0;
long Height = 0;
m_pPict->get_Width(&Width);
m_pPict->get_Height(&Height);
CRect SrcRect(0,0,Width,Height);
if(pSrcRect){
SrcRect=*pSrcRect;
}
CRect DrawRect(pDrawRect);
HRESULT hrP = NULL;
hrP = m_pPict->Render(pDC->m_hDC,
DrawRect.left, // Left
DrawRect.top, // Top
DrawRect.Width(), // Right
DrawRect.Height(), // Bottom
SrcRect.left,
SrcRect.top,
SrcRect.Width(),
SrcRect.Height(),
prcWBounds);
if (SUCCEEDED(hrP)) return;
AfxThrowMemoryException();
return;
}//-----------------------------------------------------------------------------
// Does: Saves The Picture That Is Stored In The IPicture Object As a Bitmap
// ~~~~ (Converts From Any Known Picture Type To a Bitmap / Icon File)
//
// InPut: sFilePathName - Path And FileName Target To Save
// ~~~~~
//
// OutPut: TRUE If Succeeded...
// ~~~~~~
//-----------------------------------------------------------------------------
BOOL CPicture::SaveAsBitmap(CString sFilePathName)
//=============================================================================
{
BOOL bResult = FALSE;
ILockBytes *Buffer = 0;
IStorage *pStorage = 0;
IStream *FileStream = 0;
BYTE *BufferBytes;
STATSTG BytesStatistics;
DWORD OutData;
long OutStream;
CFile BitmapFile; CFileException e;
double SkipFloat = 0;
DWORD ByteSkip = 0;
_ULARGE_INTEGER RealData;
CreateILockBytesOnHGlobal(NULL, TRUE, &Buffer); // Create ILockBytes Buffer
HRESULT hr = ::StgCreateDocfileOnILockBytes(Buffer,
STGM_SHARE_EXCLUSIVE STGM_CREATE STGM_READWRITE, 0, &pStorage);
hr = pStorage->CreateStream(L"PICTURE",
STGM_SHARE_EXCLUSIVE STGM_CREATE STGM_READWRITE, 0, 0, &FileStream);
m_pPict->SaveAsFile(FileStream, TRUE, &OutStream); // Copy Data Stream
FileStream->Release();
pStorage->Release();
Buffer->Flush();
// Get Statistics For Final Size Of Byte Array
Buffer->Stat(&BytesStatistics, STATFLAG_NONAME);
// Cut UnNeeded Data Coming From SaveAsFile() (Leave Only "Pure" Picture Data)
SkipFloat = (double(OutStream) / 512); // Must Be In a 512 Blocks...
if(SkipFloat > DWORD(SkipFloat)) ByteSkip = (DWORD)SkipFloat + 1;
else ByteSkip = (DWORD)SkipFloat;
ByteSkip = ByteSkip * 512; // Must Be In a 512 Blocks...
// Find Difference Between The Two Values
ByteSkip = (DWORD)(BytesStatistics.cbSize.QuadPart - ByteSkip);
// Allocate Only The "Pure" Picture Data
RealData.LowPart = 0;
RealData.HighPart = 0;
RealData.QuadPart = ByteSkip;
BufferBytes = (BYTE*)malloc(OutStream);
if(BufferBytes == NULL)
{
Buffer->Release();
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, "Can not allocate enough memory\t", ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
}
Buffer->ReadAt(RealData, BufferBytes, OutStream, &OutData);
if(BitmapFile.Open(sFilePathName, CFile::typeBinary CFile::modeCreate CFile::modeWrite, &e))
{
BitmapFile.Write(BufferBytes, OutData);
BitmapFile.Close();
bResult = TRUE;
}
else // Write File Failed...
{
TCHAR szCause[255];
e.GetErrorMessage(szCause, 255, NULL);
HWND hWnd = AfxGetApp()->GetMainWnd()->m_hWnd;
MessageBoxEx(hWnd, szCause, ERROR_TITLE, MB_OK MB_ICONSTOP, LANG_ENGLISH);
bResult = FALSE;
}
Buffer->Release();
free(BufferBytes);
return(bResult);
}
LONG CPicture::get_Height()
{
LONG nHeight = 0;
if (m_pPict != NULL)
{
m_pPict->get_Height(&nHeight);
}
return nHeight;
}
LONG CPicture::get_Width()
{
LONG nWidth = 0;
if (m_pPict != NULL)
{
m_pPict->get_Width(&nWidth);
}
return nWidth;
}