博文
COM组件设计与应用(二)(2006-02-19 12:01:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1485
GUID 和 接口
作者:杨老师
一、前言
书接上回,话说在 doc(Word) 复合文件中,已经解决了保存 xls(Excel) 数据的问题了。那么,接下来又要解决另一个问题:当 WORD 程序读取复合文件,遇到了 xls 数据的时候,它该如何启动 Excel 呢?启动后,又如何让 Excel 自己去读入、解析、显示 xls 数据呢?
二、CLSID 概念
有一个非常简单的解决方案,那就是在对象数据的前面,保存有处理这个数据的程序名。(见下图左上)
图一、CLSID 的概念
这的确是一个简单的方法,但同时问题也很严重。在“张三”的计算机上,Excel 的路径是:"c:\office\Excel.exe",如果把这个 doc 文件复制到“李四”的计算机上使用,而“李四”的 Excel 的路径是:
"d:\Program files\Microsoft Office\Office\Excel.exe",完蛋了:-(
于是,微软想出了一个解决方案,那就是不使用直接的路径表示方法,而使用一个叫 CLSID(注1)的方式间接描述这些对象数据的处理程序路径。CLSID 其实就是一个号码,或者说是一个16字节的数。观察注册表(上图),在HKCR\CLSID\{......}主键下,LocalServer32(DLL组件使用InprocServer32) 中保存着程序路径名称。CLSID 的结构定义如下:typedef struct _GUID {
DWORD Data1; // 随机数
WORD Data2; // 和时间相关
WORD Data3; // 和时间相关
BYTE Data4[8]; // 和网卡MAC相关
} GUID;
typedef GUID CLSID; // 组件ID
typedef GUID IID; // 接口ID
#define REFCLSID const CLSID &
// 常见的声明和赋值方法
CLSID CLSID_Excel = {0x00024500,0x0000......
COM 组件设计与应用(一)(2006-02-18 18:46:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1483
起源及复合文件
作者:杨老师
一、前言
公元一九九五年某个夜黑风高的晚上,我的一位老师跟我说:“小杨呀,以后写程序就和搭积木一样啦。你赶快学习一些OLE的技术吧......”,当时我心里就寻思 :“开什么玩笑?搭积木方式写程序?再过100年吧......”,但作为一名听话的好学生,我开始在书店里“踅摸”(注1)有关OLE的书籍(注2)。功夫不负有心人,终于买到了我的第一本COM书《OLE2 高级编程技术》,这本800多页的大布头花费了我1/5的月工资呀......于是开始日夜耕读.....
功夫不负有心人,我坚持读完了全部著作,感想是:这本书,在说什么呐?
功夫不负有心人,我又读完了一遍大布头,感想是:咳~~~,没懂!
功夫不负有心人,我再,我再,我再读 ... 感想是:哦~~~,读懂了一点点啦,哈哈哈。
...... ......
功夫不负有心人,我终于,我终于懂了。
800页的书对现在的我来说,其实也就10几页有用。到这时候才体会出什么叫“书越读越薄”的道理了。到后来,能买到的书也多了,上网也更方便更便宜了......
为了让VCKBASE上的朋友,不再经历我曾经的痛苦、不再重蹈我“无头苍蝇”般探索的艰辛、为了VCKBASE的蓬勃发展、为了中国软件事业的腾飞(糟糕,吹的太也高了)......我打算节约一些在 BBS 上赚分的时间,写个系列论文,就叫“COM组件设计与应用”吧。今天是第一部分——起源。
二、文件的存储
传说350年前,牛顿被苹果砸到了头,于是发现了万有引力。但到了二十一世纪的现在,任何一个技术的发明和发展,已经不再依靠圣人灵光的一闪。技术的进步转而是被社会的需求、商业的利益、竞争的压力、行业的渗透等推动的。微软在Windows平台上的组件技术也不例外,它的发明,有其必然因素。什么是这个因素那?答案是——文件的存储。
打开记事本程序,输入了一篇文章后,保存。——这样的文件叫“非结构化文件”;
打开电子表格程序,输入一个班的学生姓名和考试成绩,保存。——这样的文件叫“标准结构化文件”;
在我们写的......
几种压缩算法原理介绍2(2006-02-04 13:55:00)
摘要:来自:http://www.cppblog.com/windcsn/archive/2006/01/06/2476.html || http://blog.csdn.net/windcsn/archive/2006/01/06/572389.aspx
3. Rice
对于由大word(例如:16或32位)组成的数据和教低的数据值,Rice编码能够获得较好的压缩比。音频和高动态变化的图像都是这种类型的数据,它们被某种预言预处理过(例如delta相邻的采样)。
尽管哈夫曼编码处理这种数据是最优的,却由于几个原因而不适合处理这种数据(例如:32位大小要求16GB的柱状图缓冲区来进行哈夫曼树编码)。因此一个比较动态的方式更适合由大word组成的数据。
3.1. 原理
Rice编码背后的基本思想是尽可能的用较少的位来存储多个字(正像使用哈夫曼编码一样)。实际上,有人可能想到Rice是静态的哈夫曼编码(例如,编码不是由实际数据内容的统计信息决定,而是由小的值比高的值常见的假定决定)。
编码非常简单:将值X用X个‘1’位之后跟一个0位来表示。
3.2. 实现
在基本压缩库针对Rice做了许多优化:
1. 每个字最没有意义的位被存储为k和最有意义的N-k位用Rice编码。K作为先前流中少许采样的位平均数。这是通常最好使用Rice编码的方法,隐藏噪音且对于动态变化的范围并不导致非常长的Rice编码。
2. 如果rice编码比固定的开端长,T,一个可选的编码:输出T个‘1’位,紧跟(log2(X-T))个‘1’和一个‘0’位,接着是X-T(最没有意义的(log2(X-T))-1位)。这对于大值来说都是比较高效的代码并且阻止可笑的长Rice编码(最坏的情况,对于一个32位word单个Rice编码可能变成232位或512MB)。
如果开端是4,下面是结果编码表:
X
bin
Rice
Thresholded
Rice
0
00000
0
0
1
00001
10
1......
几种压缩算法原理介绍1(2006-02-04 13:54:00)
摘要:来自:http://www.cppblog.com/windcsn/archive/2006/01/06/2476.html || http://blog.csdn.net/windcsn/archive/2006/01/06/572389.aspx
RLE
RLE又叫Run Length Encoding,是一个针对无损压缩的非常简单的算法。它用重复字节和重复的次数来简单描述来代替重复的字节。尽管简单并且对于通常的压缩非常低效,但它有的时候却非常有用(例如,JPEG就使用它)。
1.1. 原理
图2.1显示了一个如何使用RLE算法来对一个数据流编码的例子,其中出现六次的符号‘93’已经用3个字节来代替:一个标记字节(‘0’在本例中)重复的次数(‘6’)和符号本身(‘93’)。
RLE解码器遇到符号‘0’的时候,它表明后面的两个字节决定了需要输出哪个符号以及输出多少次。
1.2. 实现
RLE可以使用很多不同的方法。基本压缩库中详细实现的方式是非常有效的一个。一个特殊的标记字节用来指示重复节的开始,而不是对于重复非重复节都coding run。
因此非重复节可以有任意长度而不被控制字节打断,除非指定的标记字节出现在非重复节(顶多以两个字节来编码)的稀有情况下。为了最优化效率,标记字节应该是输入流中最少出现的符号(或许就不存在)。
重复runs能够在32768字节的时候运转。少于129字节的要求3个字节编码(标记+次数+符号),而大雨128字节要求四个字节(标记+次数的高4位|0x80+次数的低4位)。这是通常所有采用的压缩的做法,并且也是相比较三个字节固定编码(允许使用3个字节来编码256个字节)而言非常少见的有损压缩率的方法。
在这种模式下,最坏的压缩结果是:
输出大小=257/256*输入大小+1
2. 哈夫曼
哈夫曼编码是无损压缩当中最好的方法。它使用预先二进制描述来替换每个符号,长度由特殊符号出现的频率决定。常见的符号需要很少的位来表示,而不常见的符号需要很多为来表示。
哈夫曼算法在改变任何符号二进制编码引起少量密集表现方面是最佳的。然而,它并不处理符号的顺序和重复或序号的序列。
2.1. 原理
我不打算探究哈夫曼编码的所有实际的细节,但基本的原理是为每个符号找到新的二进制表示,从......
游戏编程起源(初学者)ⅩⅢ(2006-02-03 12:04:00)
摘要:★ 第七章 游戏的结构
☆ 简介
上一章的结尾,我曾经承诺将在本章教你写一个小的游戏引擎。但在结束上一章,然后我在考虑怎样更好的完成这个系列教程时,我感到这并不是一个好主意。(别,别,先把臭鸡蛋放下)让我告诉你我的真实想法。
有几次,我曾经间接或直接的提到你不能象在DOS模式下一样编写你的Windows程序,你必须组织好每一件事。因为全部的主循环在每一帧至少要被执行一次,所以你千万不能漏掉对Windows发给你的每一个请求的跟踪。
现在,你可能对我以前给你的Demo代码了解了一些,但你可能还没有把它扩充为一个大个儿的游戏程序,或者你还没有开始根据本教程系列编写自己的游戏,太好了!?现在,在我教你具体怎样编程前,我得出了一个结论,就是——现在,我们最后拿出一点儿时间来学习一下游戏的结构,看看一个游戏最终是怎样由各个部分组成的。
本章不同于以往,你将很少看到程序代码,所以,哈哈,如果你对前几章关于DirectDraw的部分还不是很明白,或者还有一些其它的事不是很清楚,不要紧,因为我们要先暂停一下前面的部分,开始一个新的话题。你只需要了解一些Windows编程的知识,所以看过或了解第一、二章的内容就可以了。
还有一件事情我要说一下:对于本章以及后面的一些章节,我都将以Terran(地球人)这个小游戏为蓝本讲解,所以你最好下载这个Demo(http://www.aeon-software.com/downloads.html)来更好的理解我所要讲的。目前的版本一,可能不能满足所有的显示卡,版本二出来后可能会好些(已经完成)。废话不说了,让我们开始吧!
☆ 概览
总之,在你开始编程前,你应该对你的游戏逻辑和具体操作方式有一个详细的方案。这将保证你在实施代码编辑时,不会出大的过错。否则,随着代码的编写,你本来清晰的思路会被意料外的问题和更新的创意搞得乱七八糟,最后使你自己都迷失在自己的代码中了。相信我,我是有惨痛教训的。^_^
你应该从WinMain()函数着手,它是程序开始的地方。这一点你可能认为理所应当,但是很多人从编写退出程序,或者从编写冲突检测函数,或者一些其它的细节处入手,而不是从WinMain()处开始,这是一种倒序的编程方法。对于初学者,理想的顺序还是从头到尾的正序方式。你首先应......
游戏编程起源(初学者)ⅩⅡ(2006-02-03 11:55:00)
摘要:☆ 颜色键
颜色键使一个位图被拷贝到另一个位图上时,不使所有的象素都显现。例如:当你把一个精灵(游戏中会动的对象一般都称作精灵)拷贝到地图上(背景上)时,这个精灵位图一般不会是一个精灵形状的位图,它通常都是一个矩形位图,位图里包含你所需要的精灵(除非你的精灵就是一个矩形机器人^_^),不使用颜色键拷贝的结果如图一:
【图一】
这并不是我们想在游戏中得到的效果。游戏中,这个精灵是不会有那个黑色的底框的。地图是先于精灵显示的,那么精灵走到树后时,还应有相应被遮挡的部分,这个先不讨论,下一节再说。现在,对我们更重要的是,如果不应用颜色键,这个精灵将永远带着这个黑色底框,这是绝对不能容忍的。
为了解决这个问题,我们使用源颜色键。这个源颜色键告诉你精灵矩形的哪些颜色将不被拷贝(当然我们是让黑色不被拷了^_^)。一个颜色键由两个值组成:一个低位颜色值,一个高位颜色值。当一个颜色键被申请使用时,在两个值之间的颜色,包括这两个值的颜色都将不会被拷贝。在DirectX中有一个结构用来处理它,叫作DDCOLORKEY,看看吧:
typedef struct _DDCOLORKEY{
DWORD dwColorSpaceLowValue;
DWORD dwColorSpaceHighValue;
} DDCOLORKEY, FAR* LPDDCOLORKEY;
很简单的结构,我就不解释了。我将展示给你使用了颜色键之后的效果。我使用颜色键的高位和低位两个值仅仅把黑色包括在它们之间。因此,黑色是唯一不会被拷贝的颜色。图二就是使用颜色键的结果:
【图二】
好多了,是不是?这就是我们想得到的结果!现在,在我告诉你怎样建立和使用颜色键之前,我还有说一说目标颜色键,尽管我们的确我们不常用到它(我们常用的是源颜色键)。鉴于源颜色键定义了哪些颜色键不能被拷贝,目标颜色键定义了哪些颜色不能被写入(覆盖)。听起来很怪异,是不是?我也有同感。^_^ 举个实例你就明白了。当你要把A位图拷贝到B位图的下面,意思就是把A位图作为背景,例如由于某种理由,需要把一个文本框拷贝到空的后缓冲区,然后再把背景画面拷贝到这个后缓冲区,但你又......
游戏编程起源(初学者)ⅩⅠ(2006-02-03 11:53:00)
摘要:★ DirectDraw的位图化图形
☆ 简介
终于,你已经掌握了制作一个完整游戏的基础知识了,只不过你现在还只能使用GDI。今天,我们就学习使用DirectX来执行每一件你以前用GDI完成的工作,以及一些关于DirectX其它的东东。具体内容是:装载(调用)位图,使用位块传输,填充表面,使用剪裁板、颜色键等拷贝位图。
你可以在不了解前一章内容的基础上学习本章,但象素格式是很重要的,我将经常直接或间接的提到它,所以你至少应该看看上一章关于象素格式的部分!^_^ 另外,我假设你已经本系列的第一、二、三、四章,并且拥有一个DirectX SDK游戏开发平台。准备好了吗?发动引擎吧,女士们、先生们!
☆ 装载位图
不管你信不信,你的确已经知道了把位图装载到DirectDraw表面的大部分知识。怎么会这样呢?Well,在Windows GDI下装载位图同在DirectDraw下极其相似,只是有一点点不同。轻轻的回忆一下,我们曾经使用LoadImage()函数得到位图的句柄,然后把位图选入到内存设备上下文中,最后利用BitBlt()函数把图形从内存设备上下文中拷贝到显示设备上下文中,设备上下文可以用GetDC()函数得到。如果这个承担显示任务的就是DirectDraw表面(现在我们就是要用它),我们就可以针对性的得到DirectDraw表面的设备上下文!感谢上帝,IDirectDrawSurface7接口提供了一个极其简单的函数来得到这个设备上下文:
HRESULT GetDC(HDC FAR *lphDC);
该函数的返回类型同所有DirectDraw函数的返回类型相同。如果函数调用成功,参数就是一个HDC类型的设备上下文的指针,很简单吧!本章就是从把一个位图装载到DirectDraw表面讲起的。千万要记住使用完了表面设备上下文后,你一定要释放它哦!你可能已经想到了,用表面接口函数ReleaseDC()完成:
HRESULT ReleaseDC(HDC hDC);
你不用回头去看关于GDI部分的位图调用,我将把适合于DirectDraw的位图调用展现给你。唯一不同的是:不是直接把设备上下文作为一个参数,而是用一个DirectDraw表面指针取代了它,然后函数从表面得到设备上......
游戏编程起源(初学者)Ⅹ(2006-02-02 12:23:00)
摘要:☆ 锁定表面
没什么令人意外的东东,我们将使用的函数是IDirectDrawSurface7::Lock()。让我们仔细看看它:
HRESULT Lock(
LPRECT lpDestRect,
LPDDSURFACEDESC lpDDSurfaceDesc,
DWORD dwFlags,
HANDLE hEvent
);
一定要检测函数的调用是否成功,否则可能会有大麻烦的:如果锁定失败,而返回的指针指向了一个不正确的内存区域,你若操控该区域,很有可能导致系统的混乱。函数的参数有以下这些组成:
※ LPRECT lpDestRect:是一个指向RECT结构的指针,它描述了将要被直接访问的表面上的矩形区。该参数被设置为NULL,以锁定整个表面。
※ LPDDSURFACEDESC2 lpDDSurfaceDesc:是DDSURFACEDESC2类型的结构变量的地址,它由直接访问表面内存所必需的全部信息进行填充。在该结构中返回的信息表面的基地址、间距和象素格式。
※ DWORD dwFlags:好像没有几个DirectX函数没有这个东东的。下面列出几个最有用的标志常量:
◎ DDLOCK_READONLY:被锁定的表面为只读。(当然就不能写入了)
◎ DDLOCK_SURFACEMEMORYPTR:表面返回一个指向锁定矩形左上角坐标的有效指针;如果没有指定矩形,那么返回表面左上角的坐标。此项为默认且无需显式的输入。
◎ DDLOCK_WAIT:如果其它线程或进程正在进行位转换操作,不能锁定表面内存,则一直等到该表面可以锁定为止或返回错误信息。
◎ DDLOCK_WRITEONLY:被锁定表面为可写。(当然就不能读取了)
由于我们使用锁定去操控象素,你将总会用到DDLOCK_SURFACEMEMORYPTR。即使我们目前还没有学习位块操作,但使用DDLOCK_WAIT总是一个好主意。
※ HANDLE hEvent:没用的东东,设置为NULL好了。
一旦我们锁定了表面,我们需要查看一下......
游戏编程起源(初学者)Ⅸ(2006-02-02 12:22:00)
摘要:★ 第五章 DirectDraw的调色板和象素
☆ 简介
今天我们将分别使用调色板和RGB模式来熟悉DirectDraw的基本图形。它们有什么不同呢?如果你曾经在DOS下编程,你可能使用过调色板映射模式。调色板是个颜色查询表,为了绘制象素,你将一个单独的字节写入视频内存,通过这个字节你可以索引到一个拥有各种颜色的链表,这个颜色的链表,或查询表就叫作调色板。而RGB模式是不同的,因为它不需要颜色查询表。在RGB模式下绘制一个象素,你可以直接把红色、绿色和蓝色的值写入视频内存。任何色彩深度高于8位的调色板都可以用RGB模式取代。
编写本章时,我假设你已经读过了前面几章,知道了怎样设置DirectDraw和创建表面。我们将使用DirectX7,它包含了最新的DirectDraw接口。实际上,DirectX 7 中的DirectDraw接口可能是最后的升级版本了!不用担心,未来的版本一定会兼容它的,但是未来可能是一个DirectDraw和Direct3D综合的产品,管它那,我们学的不会没有用的。^_^
在开始前我还有最后一件事要提醒你:在我的后续文章中关于调色板的部分可能再也用不到了,所以,如果你对于调色板模式不是很感兴趣,你可以跳过文章的前一部分,从象素格式开始看起。调色板的开发和使用是PC中使用的原始视频系统的内存限制带来的直接后果。现在由于充足的显存使调色板模式几乎废弃不用了。值得保留调色板模式的一个原因是,执行调色板操作可以实现一些有趣的动画效果。不罗嗦了,让我们开始吧!
☆ 创建DirectDraw的调色板
当你在色彩深度为8位或低于8位的模式下显示图形时,你必须创建调色板,也就是颜色查询表。更明确的讲,对于DirectX,调色板就是PALETTEENTRY结构。要建立一个调色板,我们要做如下三步:
1、 创建颜色查询链表。
2、 得到指向IDirectDrawPalette接口的指针。
3、 把调色板链接到DirectDraw表面。
我假设我们使用的是8位色彩深度。如果你要用16位或更高位的色彩深度编写游戏,你就不用继续看以下这段疯狂的Windows素材了。总之,8位色彩深度,我们可以有一个256个条目的调色板。所以,创建颜色查询链表,有256个条目在其中:
游戏编程起源(初学者)Ⅷ(2006-01-30 11:54:00)
摘要:☆ 设置协作等级和显示模式
我不需要说太多。Windows编程设置协作级别你只需要调用IDirectDraw7::SetCooperativeLevel()函数;设置显示模式你就调用IDirectDraw7::SetDisplayMode()函数。就这么简单!先来看看协作级别。这就是函数原形:
HRESULT SetCooperativeLevel(
HWND hWnd,
DWORD dwFlags
);
返回的类型是HRESULT,你应该已经熟悉它了。对于所有的DirectX函数调用,你都可以用SUCCEEDED()和FAILED()宏检测调用的结果。以下是函数SetCooperativeLevel()的参数:
※ HWND hWnd:很熟悉吧!传递主窗口的句柄给它,使Windows知道谁将使用它的资源。
※ DWORD dwFlags:这个也很眼熟吧!每次我们看到dwFlags参数,几乎都有一个大的标志常量列表供我们选择,并且可以用“|”组合。这次也不会让你失望的哦!
◎ DDSCL_ALLOWMODEX:启用Mode X 显示模式(如320×200,320×240或者320×400)。该标志只能用于DDSCL_EXCLUSIVE和DDSCL_FULLSCREEN模式。
◎ DDSCL_ALLOWREBOOT:在独占模式中启用Ctrl+Alt+Del组合键功能。
◎ DDSCL_EXCLUSIVE:请求独占模式,必须与DDSCL_FULLSCREEN同时使用。
◎ DDSCL_FULLSCREEN:独占模式的拥有者负责整个主表面,GDI被忽略,必须与DDSCL_EXCLUSIVE同时使用。
◎ DDSCL_NORMAL:表示常规的Windows应用程序,不能与DDSCL_ALLOWMODEX、DDSCL_EXCLUSEIVE或DDSCL_FULLSCREEN标志同时使用,在该模式下运行的应用程序不能进行页交换或者更改主调色板。
◎ DDSCL_NOWINDOWCHANGES:防止DirectDraw最小化或恢复应用程序窗口。
还有几个标志常量我们暂时用不到,就不说了。由于我们要建立一个全屏的640×480×16的显示模式,所以......