SQL Server查询速度慢的原因有很,常见的有以下几种:
1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
2、I/O吞吐量小,形成了瓶颈效应。
3、没有创建计算列导致查询不优化。
4、内存不足
5、网络速度慢
6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
9、返回了不必要的行和列
10、查询语句不好,没有优化
●可以通过以下方法来优化查询 :
1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.
2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)
3、升级硬件
4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段
5、提高网速;
6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。
配 置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。
7、增加服务器CPU个数;但是必须 明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询 的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT, DELETE还不能并行处理。
8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like ''a%'' 使用索引 like ''%a'' 不使用索引用 like ''%a%'' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。
9、DB Server 和APPLication Server 分离;OLTP和OLAP分离
10、分布式分区视图可用于实现数据库服务器联合体。
联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件''分区视图'')
a、在实现分区视图之前,必须先水平分区表
b、 在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上 运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。
11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。
在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:
1、 查询语句的词法、语法检查
2、 将语句提交给DBMS的查询优化器
3、 优化器做代数优化和存取路径的优化
4、 由预编译模块生成查询规划
5、 然后在合适的时间提交给系统处理执行
6、 最后将执行结果返回给用户。
软件开发网 www.mscto.com
其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。
12、 Commit和rollback的区别 Rollback:回滚所有的事物。 Commit:提交当前的事物. 没有必要在动态SQL里写事物,如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过程。
13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。
14、SQL的注释申明对执行没有任何影响
15、 尽可能不使用光标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查 询,用Case语句等等。游标可以按照它所支持的提取选项进行分类: 只进 必须按照从第一行到最后一行的顺序提取行。FETCH NEXT 是唯一允许的提取操作,也是默认方式。可滚动性 可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项 READ_ONLY:不允许通过游标定位更新(Update),且在组成结果集的行中没有锁。 OPTIMISTIC WITH valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新 某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获 取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。 选择这个并发选项OPTIMISTIC WITH ROW VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有 所更改。在 SQL Server 中,这个性能由 timestamp 数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有 timestamp 列的行时,SQL Server 先在时间戳列中存储当前的 @@DBTS 值,然后增加 @@DBTS 的值。如果某 个表具有 timestamp 列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需 比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。 SCROLL LOCKS 这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其 上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下 一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更 新该行。然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的 SELECT 语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器 为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则 COMMIT 语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标 SELECT 语句中的锁提示。锁提示 只读 乐观数值 乐观行版本控制 锁定无提示 未锁定 未锁定 未锁定 更新 NOLOCK 未锁定 未锁定 未锁定 未锁定 HOLDLOCK 共享 共享 共享 更新 UPDLOCK 错误 更新 更新 更新 TABLOCKX 错误 未锁定 未锁定 更新其它 未锁定 未锁定 未锁定 更新 *指定 NOLOCK 提示将使指定了该提示的表在游标内是只读的。
16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引
17、注意UNion和UNion all 的区别。UNION all好
18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的
19、查询时不要返回不需要的行、列
20、 用sp_configure ''query governor cost limit''或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。 SET LOCKTIME设置锁的时间
21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行
22、 在SQL2000以前,一般不要用如下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE ''%500''",因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时 候,创建计算列再创建索引来替代.还可以变通写法:WHERE SUBSTRING(firstname,1,1) = ''m''改为WHERE firstname like ''m%''(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同 的是IS NULL,“NOT", "NOT EXISTS", "NOT IN"能优化她,而”<>”等还是不能优化,用不到索引。
23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。
24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引: SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN (‘男’,‘女’)
25、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。
26、MIN() 和 MAX()能使用到合适的索引。
27、 数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程 序质量高,并且执行的速度快。
28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA 是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。
posted @
2009-01-05 10:47 石正 阅读(427) |
评论 (0) |
编辑 收藏
Gartner Group公布了IT业界未来四年来的十大新技术.
多核心处理器,虚拟化和社交网络排行前三,同时一些用户端技术也占了榜单的数个位置. Gartner建议CIO建立一个正式的机制,建立虚拟团队,选拔优秀的工作人员来研究新的思路和创新,特别是消费者所关注的新Web2.0技术,并运用这些技术来解决问题.
1. 多核心和混合处理器
2. 虚拟化和构造计算
3. 社交网络和社交软件
4. 云计算/Web平台
5. Web Mashups
6. 用户界面(加速计,无线界面,全息和三维成像等)
7. 普及计算
8. 上下文计算
9. 增强实体
10. 语义学
posted @
2008-06-17 08:52 石正 阅读(265) |
评论 (0) |
编辑 收藏
posted @
2008-06-13 14:00 石正 阅读(864) |
评论 (0) |
编辑 收藏
十大最不可思议的
计算机
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2189094
据英国《新科学家》杂志报道,现今的计算机利用
电子脉冲和磁体实现
信息操作和
数据储存,但是随着技术发展,还有其它更为怪异的方法处理这些信息。《新科学家》杂志列出了10大最不可思议的计算方式,包括光计算、量子计算、神经计算以及水波计算等。
1、光计算
光线传输编码信息不足为奇,因为全球通信全部依靠光缆来传输,但用光信号来处理数据和进行计算还是不切实际的。研究光计算机还是值得做的,因为使用光能加快计算机的速度和处理的数据量。
但对光的诱捕、储存以及操作依然非常困难。美国伊利诺伊州立大学保罗·布劳恩等人的研究让我们更接近这一目标。他们已经研制成一款三维光学波导光子晶体,可以诱捕光,使其降低速度,并在锐角转角处让光弯曲,而不必担心光逃逸。同时,美国哈佛大学的米哈伊尔·卢金已经开发出一种光晶体管,可以让单个光子从一个光
信号转换成另外一个光信号。
2、量子计算
如果你不再想打破所有传
统计算的规则,那么量子计算方式将最为现实。量子计算不使用传统计算的基本信息比特,而利用量子
机械效应创造量子比特,可实现平行计算,并且随着量子比特数的增加,它们处理数据的速度将呈指数形式上升,将能完成现代计算机所不能完成的事情,比如快速分解大质数、破解密码锁等。
3、DNA计算
DNA可能是完成计算的最完美材料。DNA计算的创始人是美国南加州大学的莱昂那多·阿德莱曼教授,他于1994年利用DNA计算方法解决了一个著名的数学难题“七顶点哈密尔顿路径”。最近,科学家们开始利用DNA计算来创造生物计算机,放在人体或生物体工作,其计算结果可通过荧光蛋白的活动来读取。
4、可逆计算
可逆计算通过恢复和重新利用丢失数据的这些能量来减少计算机的能耗。美国佛罗里达大学的迈克尔·弗兰克正在开发这样一种方法:每个输入操作都会产生一个输出信号,将输出信号的能量保存起来,为新的信号输入提供能量,从而创造出一种零热量产生的计算方式。
5、撞球计算
传统的计算涉及电子通过电路内部每一个分子时产生的连锁反应,而科学家们正在尝试其它种类的连锁反应,包括多米诺或者大理石的连锁反应。
但是
制作这样的微处理器可能需要很大空间,除非多米诺或者大理石也很小。美国IBM公司的研究人员已经开始实验这种逻辑电路,通过使用原子互相撞击传递信息。尽管这种逻辑门只能使用一次,但是它却比现有的最小晶体管还要小很多。
6、神经元计算
研究神经元通常是指模仿人类大脑的计算方式。芝加哥
西北大学的费尔迪南多·莫沙-伊万迪正在研究如何利用七鳃鳗的大脑细胞控制机器人。
从机器人身上的光敏感元件上输出信号传递给神经元,神经元的反馈再反过来用来控制机器人的动作。此外,英国纽卡斯尔大学的神经学家克莱尔·林德受《星球大战》电影中记录的一只蝗虫的启发,开发出一种非常精确的故障排除系统。美国国防部高级研究计划署近来正在利用飞蛾大脑的电子元件,尝试遥控半机械昆虫间谍——飞蛾。
7、核磁共振计算
如果你知道如何操作水,那么每杯水就是一台电脑。英国约克大学的苏珊·斯特尼及其同事使用核磁共振强磁场来控制和观察分子的交互作用,这种方法能够以三维方式表现信息。如果研究能够成功,只要一杯水就可以模拟大气。然而这个研究小组只完成了原理证明。
8、Glooper计算机
这可能是最不可思议的计算机之一,它使用gloopware当硬件。英国西英格兰大学的安德鲁·亚当马特兹可以在一种化学制剂中制造干涉离子波,其行为要充当逻辑门。此波通过脉冲循环化学反应产生。亚当马特兹安德鲁已经展示其化学逻辑门可用于制造机器手臂。当机器人手指进行模仿动作时,化学反应就被触发,进而控制这只手。
9、霉变计算机
即使像粘菌这样的原始生物也可以被用来解决现今计算机难以处理的问题。日本北海道大学的Toshiyuki Nakagaki及其同事首次证实粘菌能够找到最短路线通过迷宫。
在他们的试验中,一种阿米巴门原生质粘菌起初也是尝试所有路径走出迷宫的。当这种生物找出最短路径通往有食物的出口时就不再找别的路线了,直奔食物去了。对于计算机专家们来说,这是一个非常有趣的实验,可解决行销推销员的问题。
10、水波计算
最不可思议的就是计算机竟然以水波为动力。英国苏塞克斯大学的克莉森西娅·费尔南多等利用波动箱和高架
摄像机,用水波动模式制造出一种逻辑门,称作“异或门(exclusive OR gate)”。人造神经网络感知器可以模仿一些逻辑门,但不能模仿异或门。费尔南多实现了这种逻辑门的工作原理。
posted @
2008-04-17 17:49 石正 阅读(276) |
评论 (0) |
编辑 收藏
驻伊美军机器人叛乱平息 20年内不再考虑机器人
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2189098
去年刚刚进驻伊拉克的美军重装遥控机器人系统SWORDS,也许永远都不会有机会上阵杀敌,摧城拔寨了。事实上,这种配备有M249轻机枪的机器人从未放过一枪一弹,便被迅速撤出战场——因为它们做了更可怕的事情:将枪口对向它们的人类指挥官。
在
新闻发布会上,美国陆军地面作战指挥官凯文·法赫称,这些机器人“将它们的机枪指向未受批准的方向”。显然,这意味着这些机器人遭遇了严重
事故,将枪口指向友军部队。而这也导致了这支机器人部队在正式部署前线仅11小时便被召回。
法赫证实机器人并未开火,也没有任何人类在此次事变中伤亡。但这并不意味着零损失,所有关于机器人作战的研究项目都可能因之取消。法赫称:“一旦遇到如此严重的事变,我们必须花上10年乃至20年,推倒重来。”因此,我们必须再等上很久才能看到机器人和人类并肩作战了。
这不仅仅对于机器人工业是重大损失,而且也树立了一个重要先例:关于机器人的任何试验都必须以无人类伤亡为绝对前提。也许对一切事故的极度厌恶,终将导致此领域的任何创新都步履维艰。
posted @
2008-04-17 17:32 石正 阅读(216) |
评论 (0) |
编辑 收藏
http://www.ugia.cn/?p=121
http://hi.baidu.com/momoca/blog/item/422a73f023999fafa40f52b1.html
posted @
2008-04-02 11:03 石正 阅读(591) |
评论 (0) |
编辑 收藏
软件企业技术路线:项目\产品\队伍
项目为企业近期发展发展积累资金;
产品为企业中期发展创造条件;
队伍是企业长期发展的根本;
企业可以根据自身的长期发展计划,制定近期的发展计划;
在有条件时,应该首先建设队伍,其次设计产品,再次做项目;
实际执行时,往往是3者混合进行的;
作为企业的主要负责人,管理人员,乃至对企业发展关心的员工,都应该明确企业发展的基本规律.来指导日常的工作实践。
posted @
2008-03-03 08:30 石正 阅读(1157) |
评论 (1) |
编辑 收藏
杀人的历史评传——刘邦杀韩信
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=2&id=2067439
posted @
2008-02-27 11:18 石正 阅读(356) |
评论 (0) |
编辑 收藏
匈牙利科学家发明狗吠
翻译机 准确率达43%
http://club2.cat898.com/newbbs/dispbbs.asp?boardid=41&id=2040695
为了让狗的主人能够更好的了解自己
爱犬发出的叫声所表达的含义,匈牙利科学家日前发明了一种
计算机程序,该程序可以将狗的叫声“翻译”成人类能够听懂的语言。该程序
分析的准确率高达43%。
据报道,这个特殊的计算机程序是在分析了来自匈牙利14个地区6000多只牧羊犬在6种不同场合的叫声后设计而成的。这六种场合分别为:当只有一条狗时的叫声、狗之间发生冲突时候的叫声、当狗看到球后的叫声、当狗玩耍时发出的叫声和当狗看到陌生者时的叫声以及散步时发出的叫声。
据了解,来自匈牙利埃托沃罗兰德
大学的生态学
研究者查巴和他率领的科研团队通过研究发现,安装这种特殊软件程序的计算机能够准确分析出狗发出叫声所表达的含义。他们将狗的叫声被用录音设备记录下来,随后在计算机上进行数字化处理,然后研究人员使用计算机软件研究这些狗的叫声,以便分析这些叫声的不同之处。
研究人员发现,计算机基本能正确的辨别狗的叫声,计算机在分析狗所表达“打架”和“陌生者”这两种叫声时非常准确,高达43%。尽管辨别的
成功率不是很高,但是这个数字是人类无法比拟的。科学家表示,这个分析软件的识别率还有待于改善。
通过研究发现,狗之所以发出的叫声不同是由于它们的情绪受到相应影响的原因所致。查巴说:“如果这种设备研制成功投入使用的话,将有助于人类了解他们的爱犬日常都在‘说’些什么。”查巴表示,通过研究狗儿们发出不同的叫声可以分析出它们传递着相应的信息,这使得没有养狗经验的人也能知道狗为什么而叫。
据悉,该科研团队还计划研究其它动物的叫声,以便发现它们之间的共同之处。有关该研究的结果已经刊登在近日出版的《动物识别能力》杂志上。
posted @
2008-01-22 13:55 石正 阅读(276) |
评论 (0) |
编辑 收藏
设定access自增字段起始值.
下边修改picInfo表的
自增字段sn的起始值和步进值都为1
ALTER TABLE picInfo
ALTER COLUMN sn COUNTER (1, 1)
posted @
2008-01-10 11:38 石正 阅读(1168) |
评论 (0) |
编辑 收藏
http://www.cat898.com/Infolook.asp?bclass=1&id=57162
posted @
2007-12-14 09:10 石正 阅读(181) |
评论 (0) |
编辑 收藏
// 画实心椭圆形
CBrush Brush;
Brush.CreateSolidBrush( RGB( 111, 134, 233 ) );
pDC->BeginPath();
pDC->Ellipse( m_rcMark );
pDC->EndPath();
pDC->SelectObject(&Brush);
pDC->FillPath();
posted @
2007-11-28 11:34 石正 阅读(297) |
评论 (0) |
编辑 收藏
有三种方法:
1 在CApp类内设定其颜色值
2 OnCtrlColor
2 OnEraserBkColor
BOOL CMy444Dlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CBrush brush (RGB (128 , 0 , 128) );
CBrush* pOldBrush = pDC->SelectObject (&brush);
CRect reClip ;
pDC->GetClipBox (&reClip);
pDC-> PatBlt (reClip.left , reClip.top ,
reClip.Width ( ) , reClip.Height ( ) , PATCOPY );
pDC->SelectObject (pOldBrush );
return TRUE;
}
posted @
2007-11-27 14:37 石正 阅读(847) |
评论 (0) |
编辑 收藏
需要用到以下API:GetWindowRect和GetClientRect。
首先用GetWindowRect得到窗体的宽度与高度,再用GetClientRect得到窗体客户区的宽度与高度,用(窗体宽度-窗体客户区宽度)/2得到窗体边框宽度,之后用窗体高度-窗体客户区高度-窗体边框宽度得到*2得到标题栏的高度。
Windows XP 下窗口标题条高度 = 26
posted @
2007-11-27 14:13 石正 阅读(1120) |
评论 (1) |
编辑 收藏
为了对avi进行读写,微软提供了一套API,总共50个函数,他们的用途主要有两类,一个是avi文件的操作,一类是数据流streams的操作。
1、打开和关闭文件
AVIFileOpen ,AVIFileAddRef, AVIFileRelease
2、从文件中读取文件信息
通过AVIFileInfo可以获取avi文件的一些信息,这个函数返回一个AVIFILEINFO结构,通过AVIFileReadData可以用来获取AVIFileInfo函数得不到的信息。这些信息也许不包含在文件的头部,比如拥有file的公司和个人的名称。
3、写入文件信息
可以通过AVIFileWriteData函数来写入文件的一些额外信息。
4、打开和关闭一个流
打开一个数据流就跟打开文件一样,你可以通过 AVIFileGetStream函数来打开一个数据流,这个函数创建了一个流的接口,然后在该接口中保存了一个句柄。
如果你想操作文件的某一个单独的流,你可以采用AVIStreamOpenFromFile函数,这个函数综合了AVIFileOpen和AVIFileGetStream函数。
如果你想操作文件中的多个数据流,你就要首先AVIFileOpen,然后AVIFileGetStream。
可以通过AVIStreamAddRef来增加stream接口的引用。
通过AVIStreamRelease函数来关闭数据流。这个函数用来减少streams的引用计数,当计数减少为0时,删除。
5、从流中读取数据和信息
AVIStreamInfo函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
如果数据流还有一些其它的额外的信息,你可以通过AVIStreamReadData函数来获取。应用程序分配一个
内存,传递给这个函数,然后这个函数会通过这个内存返回数据流的信息,额外的信息可能包括数据流的压缩和解压缩的方法,你可以通过AVIStreamDataSize宏来回去需要申请内存块的大小。
可以通过AVIStreamReadFormat函数获取数据流的格式信息。这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。你可以通过给AVIStreamReadFormat传递一个空buffer就可以获取buffer的大小。也可以通过AVIStreamFormatSize宏。
可以通过AVIStreamRead函数来返回多媒体的数据。这个函数将数据复制到应用程序提供的内存中,对于视频流,这个函数返回图像祯,对于音频流,这个函数返回音频的sample数据。可以通过给AVIStreamRead传递一个NULL的buffer来获取需要的buffer的大小。也可以通过AVIStreamSampleSize宏来获取buffer的大小。
有些AVI数据流句柄可能需要在启动数据流的前要做一下准备工作,此时,我们可以调用AVIStreamBeginStreaming函数来告知AVI数据流handle来申请分配它需要的一些资源。在完毕后,调用AVIStreamEndStreamming函数来释放资源。
6、操作压缩的视频数据
如果你要演示一祯或者几祯压缩视频图像时,你可以调用AVIStreamRead函数,将获取的数据传递给DrawDib函数来显示图像。这些函数可以显示压缩和未压缩的图像。
AVIFile也提供了一个函数AVIStreamGetFrameOpen,来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据。也可以通过AVIStreamGetFrame函数来解压缩一个单独的视频祯。这个函数可以解压缩某一祯图像,然后将数据以一个BIMAPINFOHEADER结构返回。当你调用完AVIStreamGetFrame函数后,要调用AVIStreamGetFrameClose函数释放上一个函数申请的资源。
7、根据已存在的数据流创建文件
创建一个包含多个数据流的文件的方法就是整合多个数据流,将其写入一个新文件。这些数据流可以是内存中的数据,也可以是存在于另一个文件中。
我们可以用AVISave这个函数来build一个文件。这个函数可以创建一个文件,并且将指定的多个数据流按照指定的顺序写入文件,你也可以通过AVISaveV函数来创建一个新的文件,这个函数的功能和AVISave的功能一样,主要区别是AVISaveV采用的数据流数组,而AVISave是单个的数据流,多次保存。
我们可以调用AVISaveOptions函数来显示一个对话框,可以让用户来选择压缩方式。
我们可以在调用AVISave和AVISaveV函数时指定一个回调函数,用来显示avi文件的生成进度,可以让用户随时地取消生成avi文件。
我们可以调用GetSaveFileNamePreview函数来显示保存的对话框让用户选择保存的文件名。
通过AVIMakeFileFromStreams函数我们可以创建一个虚拟的文件句柄,其他的avi函数可以通过这个虚拟的文件句柄来操作文件中的数据流,操作完毕要记得调用AVIFileRelease释放。
8、向文件写入一个数据流
我们可以通过AVIFileCreateStream函数来在一个新文件或者已经存在的文件中创建一个数据流。这个函数根据AVISTREAMINFO结构定义了新的数据流,并为新的数据流创建一个接口,返回接口的指针。
在写入新的数据前,一定要指定流的格式信息,通过AVIStreamSetFormat函数,当设置一个视频流的时候,一定要使用BIMAPINFO结构来设置,音频就用WAVEFORMAT。
然后我们就可以通过AVIStreamWrite函数将我们的多媒体数据写入数据流了。这个函数将应用程序提供的内存数据复制到指定的流。缺省的avi handler将数据写入流的最后。
如果你有其他额外的信息需要写入流,你可以调用AVIFileWriteData或者AVIStreamWriteData,最后记得在完成数据写入后,要调用AVIStreamRelease。
9、数据流中的祯的位置
寻找起始祯:
可以通过AVIStreamStart函数来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,这个函数的AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
可以通过AVIStreamLength函数来获取流的长度。这个函数返回流中的sample的数目。也可以通过AVIStreamInfo函数来获取这些信息,可以通过AVIStreamLengthTime宏来获取流的长度,毫秒。
在视频流中,一个sample对应着一祯图像,所以,有时这些sample中没有视频数据,如果你调用AVIStreamRead函数来数据,可能返回NULL,也可以通过AVIStreamFindSample通过指定FIND_ANY标志来查找指定的sample。
查找关键祯
通过AVIStreamFindSample函数查找符合要寻找的sample,然后可以通过下面的宏判断是否关键祯。
在time和sample间互相切换。
AVIStreamSampleToTime这个函数可以将smaple转换成毫秒。对于视频,这个值代表的是这个祯开始播放的时间。
在了解了上面的知识后,我们对avi的文件结构以及如何操作avi文件心里就明白了,下面我们可以开始我们的编程了。我们要做两件事情:
1、如何将一组静态的bmp位图合成一个avi的视频文件;
2、如何将一个未压缩的avi文件解析成一幅幅位图。
示例程序界面如下:
下面的函数演示了如何将一个文件夹下面的所有bmp文件都保存为一个avi文件,函数的第一个参数是要生成的AVI的文件名,第二个参数是存放bmp文件的文件夹名,这个函数会枚举该文件夹下的所有bmp文件,合成一个AVI文件。
void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString strBmpDir)
{
// TODO: 在此添加控件通知处理程序代码
AVIFileInit();
PAVIFILE avi;
int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
int n = GetLastError();
if (res!=AVIERR_OK)
{
//an error occures
if (avi!=NULL)
AVIFileRelease(avi);
return ;
}
AVIFILEINFO avi_info;
AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
PAVISTREAM pStream;
res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
0 /*first stream*/);
if (res!=AVIERR_OK)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//do some task with the stream
int iNumFrames;
int iFirstFrame;
iFirstFrame=AVIStreamStart(pStream);
if (iFirstFrame==-1)
{
//Error getteing the frame inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
iNumFrames=AVIStreamLength(pStream);
if (iNumFrames==-1)
{
//Error getteing the number of frames inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//getting bitmap from frame
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
bih.biBitCount=24; //24 bit per pixel
bih.biClrImportant=0;
bih.biClrUsed = 0;
bih.biCompression = BI_RGB;
bih.biPlanes = 1;
bih.biSize = 40;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
//calculate total size of RGBQUAD scanlines (DWORD aligned)
bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
PGETFRAME pFrame;
pFrame=AVIStreamGetFrameOpen(pStream, NULL );
AVISTREAMINFO streaminfo;
AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
//Get the first frame
BITMAPINFOHEADER bih2;
long lsize = sizeof(bih2);
int index=0;
for (int i=iFirstFrame; i<iNumFrames; i++)
{
index= i-iFirstFrame;
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //
AVIStreamReadFormat(pStream,index,&bih2,&lsize);
BITMAPFILEHEADER stFileHdr;
BYTE* Bits=new BYTE[bih2.biSizeImage];
AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
//RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
bih2.biClrUsed =0;
stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
stFileHdr.bfType=0x4d42;
CString FileName;
FileName.Format("Frame-%05d.bmp", index);
CString strtemp = strBmpDir;
strtemp += "\\";
strtemp += FileName;
FILE* fp=_tfopen(strtemp ,_T("wb"));
fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
int e = GetLastError();
fclose(fp);
/////
delete Bits;
//CreateFromPackedDIBPointer(pDIB, index);
}
AVIStreamGetFrameClose(pFrame);
//close the stream after finishing the task
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
} |
下面的这个函数演示了如何将AVI文件中的每一桢图像单独取出来,保存为bmp文件。函数的头一个参数是avi文件名,第二个参数是存放bmp文件的文件夹。
//生成avi
void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
{
CFileFind finder;
strBmpDir += _T("\\*.*");
AVIFileInit();
AVISTREAMINFO strhdr;
PAVIFILE pfile;
PAVISTREAM ps;
int nFrames =0;
HRESULT hr;
BOOL bFind = finder.FindFile(strBmpDir);
while(bFind)
{
bFind = finder.FindNextFile();
if(!finder.IsDots() && !finder.IsDirectory())
{
CString str = finder.GetFilePath();
FILE *fp = fopen(str,"rb");
BITMAPFILEHEADER bmpFileHdr;
BITMAPINFOHEADER bmpInfoHdr;
fseek( fp,0,SEEK_SET);
fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
BYTE *tmp_buf = NULL;
if(nFrames ==0 )
{
AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;// stream type
strhdr.fccHandler = 0;
strhdr.dwScale = 1;
strhdr.dwRate = 15; // 15 fps
strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
// And create the stream;
hr = AVIFileCreateStream(pfile,&ps,&strhdr);
// hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
}
tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
hr = AVIStreamWrite(ps, // stream pointer
nFrames , // time of this frame
1, // number to write
(LPBYTE) tmp_buf,
bmpInfoHdr.biSizeImage , // size of this frame
AVIIF_KEYFRAME, // flags....
NULL,
NULL);
nFrames ++;
fclose(fp);
}
}
AVIStreamClose(ps);
if(pfile != NULL)
AVIFileRelease(pfile);
AVIFileExit();
} |
结束语:
以上代码在 vc 6.0 和windows xp平台调试通过。这两个函数你可以直接在你的程序中使用,更详细的代码可以参见随着本文附上的示例源码。这里我要指出的是,这个AVI文件和bmp互相转换过程中,avi中的视频数据都是存放的是没有压缩的数据,如果你要分解AVI文件是经过压缩编码,比如,DVSD,MPEG4编码,首先你要采用相应的解码器对视频数据解码,然后将解码过的数据保存为bmp文件。好了,关于avi文件的介绍就到这里结束了。
posted @
2007-10-24 10:30 石正 阅读(7495) |
评论 (16) |
编辑 收藏
摘要:本文详细的解析了AVI文件的存储结构,介绍了微软提供的用来操作AVI文件的一组API使用方法,并通过例子代码,演示了如何将一组静态Bmp图片合成一个avi视频文件以及如何将一个avi视频文件解析保存为一系列的bmp图像文件。
关键词:avi文件 bmp图像 vc
AVI是音频视频交错(Audio Video Interleaved)的英文缩写,它是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先用于Microsoft Video for Windows (简称VFW)环境,现在已被Windows 95/98、OS/2等多数操作系统直接支持。AVI格式允许视频和音频交错在一起同步播放,支持256色和RLE压缩,但AVI文件并未限定压缩标准,因此,AVI文件格式只是作为控制界面上的标准,不具有兼容性,用不同压缩算法生成的AVI文件,必须使用相应的解压缩算法才能播放出来。常用的AVI播放驱动程序,主要是Microsoft Video for Windows或Windows 95/98中的Video 1,以及Intel公司的Indeo Video。
在介绍AVI文件前,我们要先来看看RIFF文件结构。AVI文件采用的是RIFF文件结构方式,RIFF(Resource Interchange File Format,资源互换文件格式)是微软公司定义的一种用于管理windows环境中多媒体数据的文件格式,波形音频wave,MIDI和数字视频AVI都采用这种格式存储。构造RIFF文件的基本单元叫做数据块(Chunk),每个数据块包含3个部分,
1 4字节的数据块标记(或者叫做数据块的ID)
2 数据块的大小
3 数据
整个RIFF文件可以看成一个数据块,其数据块ID为RIFF,称为RIFF块。一个RIFF文件中只允许存在一个RIFF块。RIFF块中包含一系列的子块,其中有一种字块的ID为“LIST”,称为LIST,LIST块中可以再包含一系列的子块,但除了LIST块外的其他所有的子块都不能再包含子块。
RIFF和LIST块分别比普通的数据块多一个被称为形式类型(Form Type)和列表类型(List Type)的数据域,其组成如下:
1 4字节的数据块标记(Chunk ID)
2 数据块的大小
3 4字节的形式类型或者列表类型
4 数据
下面我们看看AVI文件的结构。AVI文件是目前使用的最复杂的RIFF文件,它能同时存储同步表现的音频视频数据。AVI的RIFF块的形式类型是AVI,它包含3个子块,如下所述:
1信息块,一个ID为”hdrl”的LIST块,定义AVI文件的数据格式。
2数据块,一个ID为 “movi”的LIST块,包含AVI的音视频序列数据
3索引块,ID为 “idxl”的子块,定义 “movi”LIST块的索引数据,是可选块。
AVI文件的结构如下图所示,下面将具体介绍AVI文件的各子块构造。
1信息块,信息块包含两个子块,即一个ID为 avih 的子块和一个ID 为 strl 的LIST块。
“avih”子块的内容可由如下的结构定义:
typedef struct
{
DWORD dwMicroSecPerFrame ; //显示每桢所需的时间ns,定义avi的显示速率
DWORD dwMaxBytesPerSec; // 最大的数据传输率
DWORD dwPaddingGranularity; //记录块的长度需为此值的倍数,通常是2048
DWORD dwFlages; //AVI文件的特殊属性,如是否包含索引块,音视频数据是否交叉存储
DWORD dwTotalFrame; //文件中的总桢数
DWORD dwInitialFrames; //说明在开始播放前需要多少桢
DWORD dwStreams; //文件中包含的数据流种类
DWORD dwSuggestedBufferSize; //建议使用的缓冲区的大小,
//通常为存储一桢图像以及同步声音所需要的数据之和
DWORD dwWidth; //图像宽
DWORD dwHeight; //图像高
DWORD dwReserved[4]; //保留值
}MainAVIHeader;
“strl” LIST块用于记录AVI数据流,每一种数据流都在该LIST块中占有3个子块,他们的ID分别是”strh”,”strf”, “strd”;
“strh”子块由如下结构定义,
typedef struct
{
FOURCC fccType; //4字节,表示数据流的种类 vids 表示视频数据流
//auds 音频数据流
FOURCC fccHandler;//4字节 ,表示数据流解压缩的驱动程序代号
DWORD dwFlags; //数据流属性
WORD wPriority; //此数据流的播放优先级
WORD wLanguage; //音频的语言代号
DWORD dwInitalFrames;//说明在开始播放前需要多少桢
DWORD dwScale; //数据量,视频每桢的大小或者音频的采样大小
DWORD dwRate; //dwScale /dwRate = 每秒的采样数
DWORD dwStart; //数据流开始播放的位置,以dwScale为单位
DWORD dwLength; //数据流的数据量,以dwScale为单位
DWORD dwSuggestedBufferSize; //建议缓冲区的大小
DWORD dwQuality; //解压缩质量参数,值越大,质量越好
DWORD dwSampleSize; //音频的采样大小
RECT rcFrame; //视频图像所占的矩形
}AVIStreamHeader;
“strf”子块紧跟在”strh”子块之后,其结构视”strh”子块的类型而定,如下所述;
如果 strh子块是视频数据流,则 strf子块的内容是一个与windows设备无关位图的BIMAPINFO结构,如下
typedef struct tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1]; //颜色表
}BITMAPINFO;
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPINFOHEADER;
如果 strh子块是音频数据流,则strf子块的内容是一个WAVEFORMAT结构,如下
typedef struct
{
WORD wFormatTag;
WORD nChannels; //声道数
DWORD nSamplesPerSec; //采样率
DWORD nAvgBytesPerSec; //WAVE声音中每秒的数据量
WORD nBlockAlign; //数据块的对齐标志
WORD biSize; //此结构的大小
}WAVEFORMAT
“strd”子块紧跟在strf子块后,存储供压缩驱动程序使用的参数,不一定存在,也没有固定的结构。
“strl” LIST块定义的AVI数据流依次将 “hdrl “ LIST 块中的数据流头结构与”movi” LIST块中的数据联系在一起,第一个数据流头结构用于数据流0,第二个用于数据流1,依次类推。
数据块中存储视频和音频数据流,数据可直接存于 “movi” LIST块中。数据块中音视频数据按不同的字块存放,其结构如下所述,
音频字块
“##wb”
Wave 数据流
视频子块中存储DIB数据,又分为压缩或者未压缩DIB,
“##db”
RGB数据流
“##dc”
压缩的图像数据流
看到了吧,avi文件的图像数据可以是压缩的,和非压缩格式的。对于压缩格式来说,也可采用不同的编码,也许你曾经遇到有些avi没法识别,就是因为编码方式不一样,如果没有相应的解码,你就没法识别视频数据。AVI的编码方式有很多种,比较常见的有 mpeg2,mpeg4, divx等。
索引块,索引快包含数据块在文件中的位置索引,能提高avi文件的读写速度,其中存放着一组AVIINDEXENTRY结构数据。如下,这个块并不是必需的,也许不存在。
typedef struct
{
DWORD ckid; //记录数据块中子块的标记
DWORD dwFlags; //表示chid所指子块的属性
DWORD dwChunkOffset; //子块的相对位置
DWORD dwChunkLength; //子块长度
};
Ok,现在我相信你肯定会对AVI的文件结构已经很清楚了,在介绍完了AVI文件结构后,我们就来看看如何对avi文件进行读写了,为了对avi进行读写,微软提供了一套API,总共50个函数,他们的用途主要有两类,一个是avi文件的操作,一类是数据流streams的操作。
1 打开和关闭文件
AVIFileOpen ,AVIFileAddRef, AVIFileRelease
2从文件中读取文件信息
通过AVIFileInfo可以获取avi文件的一些信息,这个函数返回一个AVIFILEINFO结构
通过AVIFileReadData可以用来获取AVIFileInfo函数得不到的信息。这些信息也许不包含在文件的头部,比如拥有file的公司和个人的名称。
3写入文件信息
可以通过AVIFileWriteData函数来写入文件的一些额外信息。
4打开和关闭一个流
打开一个数据流就跟打开文件一样,你可以通过 AVIFileGetStream函数来打开一个数据流,这个函数创建了一个流的接口,然后在该接口中保存了一个句柄。
如果你想操作文件的某一个单独的流,你可以采用AVIStreamOpenFromFile函数,这个函数综合了AVIFileOpen和AVIFileGetStream函数。
如果你想操作文件中的多个数据流,你就要首先AVIFileOpen,然后AVIFileGetStream。
可以通过AVIStreamAddRef来增加stream接口的引用。
通过AVIStreamRelease函数来关闭数据流。这个函数用来减少streams的引用计数,当计数减少为0时,删除。
5从流中读取数据和信息
AVIStreamInfo函数可以获取数据的一些信息,该函数返回一个AVISTREAMINFO结构,该结构包含了数据的类型压缩方法,建议的buffersize,回放的rate,以及一些description。
如果数据流还有一些其它的额外的信息,你可以通过AVIStreamReadData函数来获取。应用程序分配一个内存,传递给这个函数,然后这个函数会通过这个内存返回数据流的信息,额外的信息可能包括数据流的压缩和解压缩的方法,你可以通过AVIStreamDataSize宏来回去需要申请内存块的大小。
可以通过AVIStreamReadFormat函数获取数据流的格式信息。这个函数通过指定的内存返回数据流的格式信息,比如对于视频流,这个buffer包含了一个BIMAPINFO结构,对于音频流,内存块包含了WAVEFORMATEX或者PCMAVEFORMAT结构。你可以通过给AVIStreamReadFormat传递一个空buffer就可以获取buffer的大小。也可以通过AVIStreamFormatSize宏。
可以通过AVIStreamRead函数来返回多媒体的数据。这个函数将数据复制到应用程序提供的内存中,对于视频流,这个函数返回图像祯,对于音频流,这个函数返回音频的sample数据。可以通过给AVIStreamRead传递一个NULL的buffer来获取需要的buffer的大小。也可以通过AVIStreamSampleSize宏来获取buffer的大小。
有些AVI数据流句柄可能需要在启动数据流的前要做一下准备工作,此时,我们可以调用AVIStreamBeginStreaming函数来告知AVI数据流handle来申请分配它需要的一些资源。在完毕后,调用AVIStreamEndStreamming函数来释放资源。
6操作压缩的视频数据
如果你要演示一祯或者几祯压缩视频图像时,你可以调用AVIStreamRead函数,将获取的数据传递给DrawDib函数来显示图像。这些函数可以显示压缩和未压缩的图像。
AVIFile也提供了一个函数AVIStreamGetFrameOpen,来获取未压缩的视频祯,这个函数创建了内存来获取未压缩的数据。也可以通过AVIStreamGetFrame函数来解压缩一个单独的视频祯。这个函数可以解压缩某一祯图像,然后将数据以一个BIMAPINFOHEADER结构返回。当你调用完AVIStreamGetFrame函数后,要调用AVIStreamGetFrameClose函数释放上一个函数申请的资源。
7根据已存在的数据流创建文件
创建一个包含多个数据流的文件的方法就是整合多个数据流,将其写入一个新文件。这些数据流可以是内存中的数据,也可以是存在于另一个文件中。
我们可以用AVISave这个函数来build一个文件。这个函数可以创建一个文件,并且将指定的多个数据流按照指定的顺序写入文件,你也可以通过AVISaveV函数来创建一个新的文件,这个函数的功能和AVISave的功能一样,主要区别是AVISaveV采用的数据流数组,而AVISave是单个的数据流,多次保存。
我们可以调用AVISaveOptions函数来显示一个对话框,可以让用户来选择压缩方式。
我们可以在调用AVISave和AVISaveV函数时指定一个回调函数,用来显示avi文件的生成进度,可以让用户随时地取消生成avi文件。
我们可以调用GetSaveFileNamePreview函数来显示保存的对话框让用户选择保存的文件名。
通过AVIMakeFileFromStreams函数我们可以创建一个虚拟的文件句柄,其他的avi函数可以通过这个虚拟的文件句柄来操作文件中的数据流,操作完毕要记得调用AVIFileRelease释放。
8向文件写入一个数据流
我们可以通过AVIFileCreateStream函数来在一个新文件或者已经存在的文件中创建一个数据流。这个函数根据AVISTREAMINFO结构定义了新的数据流,并为新的数据流创建一个接口,返回接口的指针。
在写入新的数据前,一定要指定流的格式信息,通过AVIStreamSetFormat函数,当设置一个视频流的时候,一定要使用BIMAPINFO结构来设置,音频就用WAVEFORMAT。
然后我们就可以通过AVIStreamWrite函数将我们的多媒体数据写入数据流了。这个函数将应用程序提供的内存数据复制到指定的流。缺省的avi handler将数据写入流的最后。
如果你有其他额外的信息需要写入流,你可以调用AVIFileWriteData或者AVIStreamWriteData,
最后记得在完成数据写入后,要调用AVIStreamRelease。
9数据流中的祯的位置
寻找起始祯:
可以通过AVIStreamStart函数来获取第一祯包含的sample number。也可以通过AVIStreamInfo函数来获取这个信息,这个函数的AVISTREAMINFO结构中包含了dwStart,可以通过AVIStreamStartTime宏来获取第一个sample。
可以通过AVIStreamLength函数来获取流的长度。这个函数返回流中的sample的数目。也可以通过AVIStreamInfo函数来获取这些信息,可以通过AVIStreamLengthTime宏来获取流的长度,毫秒。
在视频流中,一个sample对应着一祯图像,所以,有时这些sample中没有视频数据,如果你调用AVIStreamRead函数来数据,可能返回NULL,也可以通过AVIStreamFindSample通过指定FIND_ANY标志来查找指定的sample。
查找关键祯
通过AVIStreamFindSample函数查找符合要寻找的sample,然后可以通过下面的宏判断是否关键祯。
在time和sample间互相切换
AVIStreamSampleToTime这个函数可以将smaple转换成毫秒。对于视频,这个值代表的是这个祯开始播放的时间。
在了解了上面的知识后,我们对avi的文件结构以及如何操作avi文件心里就明白了,下面我们可以开始我们的编程了。我们要做两件事情,1如何将一组静态的bmp位图合成一个avi的视频文件,2 如何将一个未压缩的avi文件解析成一幅幅位图。
示例程序界面如下:
下面的函数演示了如何将一个文件夹下面的所有bmp文件都保存为一个avi文件,函数的第一个参数是要生成的AVI的文件名,第二个参数是存放bmp文件的文件夹名,这个函数会枚举该文件夹下的所有bmp文件,合成一个AVI文件。
void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString strBmpDir)
{
// TODO: 在此添加控件通知处理程序代码
AVIFileInit();
PAVIFILE avi;
int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
int n = GetLastError();
if (res!=AVIERR_OK)
{
//an error occures
if (avi!=NULL)
AVIFileRelease(avi);
return ;
}
AVIFILEINFO avi_info;
AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
PAVISTREAM pStream;
res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
0 /*first stream*/);
if (res!=AVIERR_OK)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//do some task with the stream
int iNumFrames;
int iFirstFrame;
iFirstFrame=AVIStreamStart(pStream);
if (iFirstFrame==-1)
{
//Error getteing the frame inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
iNumFrames=AVIStreamLength(pStream);
if (iNumFrames==-1)
{
//Error getteing the number of frames inside the stream
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return ;
}
//getting bitmap from frame
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
bih.biBitCount=24; //24 bit per pixel
bih.biClrImportant=0;
bih.biClrUsed = 0;
bih.biCompression = BI_RGB;
bih.biPlanes = 1;
bih.biSize = 40;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
//calculate total size of RGBQUAD scanlines (DWORD aligned)
bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
PGETFRAME pFrame;
pFrame=AVIStreamGetFrameOpen(pStream, NULL );
AVISTREAMINFO streaminfo;
AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
//Get the first frame
BITMAPINFOHEADER bih2;
long lsize = sizeof(bih2);
int index=0;
for (int i=iFirstFrame; i<iNumFrames; i++)
{
index= i-iFirstFrame;
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index); //
AVIStreamReadFormat(pStream,index,&bih2,&lsize);
BITMAPFILEHEADER stFileHdr;
BYTE* Bits=new BYTE[bih2.biSizeImage];
AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
//RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
bih2.biClrUsed =0;
stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
stFileHdr.bfType=0x4d42;
CString FileName;
FileName.Format("Frame-%05d.bmp", index);
CString strtemp = strBmpDir;
strtemp += "\\";
strtemp += FileName;
FILE* fp=_tfopen(strtemp ,_T("wb"));
fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
int e = GetLastError();
fclose(fp);
/////
delete Bits;
//CreateFromPackedDIBPointer(pDIB, index);
}
AVIStreamGetFrameClose(pFrame);
//close the stream after finishing the task
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
}
下面的这个函数演示了如何将AVI文件中的每一桢图像单独取出来,保存为bmp文件。函数的头一个参数是avi文件名,第二个参数是存放bmp文件的文件夹。
//生成avi
void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
{
CFileFind finder;
strBmpDir += _T("\\*.*");
AVIFileInit();
AVISTREAMINFO strhdr;
PAVIFILE pfile;
PAVISTREAM ps;
int nFrames =0;
HRESULT hr;
BOOL bFind = finder.FindFile(strBmpDir);
while(bFind)
{
bFind = finder.FindNextFile();
if(!finder.IsDots() && !finder.IsDirectory())
{
CString str = finder.GetFilePath();
FILE *fp = fopen(str,"rb");
BITMAPFILEHEADER bmpFileHdr;
BITMAPINFOHEADER bmpInfoHdr;
fseek( fp,0,SEEK_SET);
fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
BYTE *tmp_buf = NULL;
if(nFrames ==0 )
{
AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;// stream type
strhdr.fccHandler = 0;
strhdr.dwScale = 1;
strhdr.dwRate = 15; // 15 fps
strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
// And create the stream;
hr = AVIFileCreateStream(pfile,&ps,&strhdr);
// hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
}
tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
hr = AVIStreamWrite(ps, // stream pointer
nFrames , // time of this frame
1, // number to write
(LPBYTE) tmp_buf,
bmpInfoHdr.biSizeImage , // size of this frame
AVIIF_KEYFRAME, // flags....
NULL,
NULL);
nFrames ++;
fclose(fp);
}
}
AVIStreamClose(ps);
if(pfile != NULL)
AVIFileRelease(pfile);
AVIFileExit();
}
结束语:
以上代码在 vc 6.0 和windows xp平台调试通过。这两个函数你可以直接在你的程序中使用,更详细的代码可以参见随着本文附上的示例源码。这里我要指出的是,这个AVI文件和bmp互相转换过程中,avi中的视频数据都是存放的是没有压缩的数据,如果你要分解AVI文件是经过压缩编码,比如,DVSD,MPEG4编码,首先你要采用相应的解码器对视频数据解码,然后将解码过的数据保存为bmp文件。好了,关于avi文件的介绍就到这里结束了
posted @
2007-10-24 10:22 石正 阅读(685) |
评论 (0) |
编辑 收藏
VC++ 6.0
方法1:
CPen Pen( PS_SOLID, 1, RGB( 255, 0, 0 ) );
CPen* pOldPen;
pOldPen = pDC->SelectObject( &Pen );
方法2:
CPen Pen( PS_SOLID, 1, RGB( 255, 0, 255 ) );
CPen* pOldPen;
pOldPen = (CPen*)SelectObject( hDC, Pen.GetSafeHandle() );
posted @
2007-09-17 09:48 石正 阅读(3781) |
评论 (2) |
编辑 收藏
正确的格式应该是:Copyright [dates] by [author/owner]
通常可以代替Copyright, 但是不可以用(c)。 All Rights Reserved 在某些国家曾经是必须的,但是现在在大多数国家,都不是法律上必须有的字样。
参见下面几个正确的格式:
1995-2004 Macromedia, Inc. All rights reserved.
2004 Microsoft Corporation. All rights reserved.
Copyright 2004 Adobe Systems Incorporated. All rights reserved.
1995-2004 Eric A. and Kathryn S. Meyer. All Rights Reserved.
请注意标点符号和大小写的用法,这也是专业精神的一种体现。
现在流行some rights reserved:creativecommons.org
some rights reserved 和copyright 本身并不矛盾,但是其中的界限更多是一个道德问题,真正的保留一部分权力,是指给浏览者fair use 的权利,fair use的界定也决不是随便乱用,或者抄袭。
posted @
2007-09-10 16:15 石正 阅读(846) |
评论 (0) |
编辑 收藏
测试视频时的笑话:
HJ对LCY说:
你能不能把头发“竖(束)”起来。
posted @
2007-09-05 11:09 石正 阅读(267) |
评论 (0) |
编辑 收藏
使计算机具有感知到人是否存在的视觉功能,这项技术就是人脸检测
posted @
2007-08-28 10:16 石正 阅读(164) |
评论 (0) |
编辑 收藏
3-D histogram 三维直方图
posted @
2007-08-27 15:33 石正 阅读(277) |
评论 (0) |
编辑 收藏
使用电子邮件e_mail的礼节
1、使用电子邮件e_mail最容易被忽略的礼节是:回复邮件时,请把原邮件的内容带上,以方便别人查看;但,原邮件所携带的附件是不需要带上的;
2、写正文时,不需要段落首行缩进;
posted @
2007-07-30 16:34 石正 阅读(243) |
评论 (0) |
编辑 收藏
软件工程第零原则,简称第0原则:所有的软件工作项都有唯一的最佳解决方案。
这里,先定义几个名词:
1、最佳解决方案。比如:在需要使用“懒惰策略”的时候(优化后置),使用“懒惰策略”就是最佳方案;不使用就不是最佳方案。2种方案的区别,关键在于,开发的进度和风险,2种方案的成本是不一样的。
2、团队最佳解决方案:团队最佳解决方案有时也是最佳解决方案,是团队能够提出的最佳方案,虽然不是该问题的最佳方案,但该方案如果可以满足问题的需求,团队又有能力实施,同时,相比向外部请求协助的代价要低的多,此时,基本可以认定该方案是团队最佳方案。这种情况下,采用“团队最佳方案”是最好的解决方法,理由是:满足需求,开发成本低,风险小。
在长期的软件工作实践中,软件小组成员之间经常出现意见不一致的时候。
具体出现的情况分类如下:
1、所有人的意见都是最佳方案的;
2、有一个或者几个人的意见是最佳方案的,有几个或者一个人的意见是不太合适的,或者是错误的;
3、所有人的意见都不是最佳方案,但有一个或者几个人的意见是“团队最佳解决方案”;
4、所有人的意见都不合适,或者都没有解决方案;
出现意见不一致时,容易造成团队内部争执,或者使团队进入到其他低效状态;为了提高团队工作效率,降低开发成本,需要减少这类低效状态的出现,采用合理的方式,是可以减少这类状态的;正面解决问题是其中的一种较好的方式;
在实际工作中发现,正面解决问题是较好的方法;即找到当前的最佳方案,并且让团队成员经过谈论,形成一致意见,或者,在找不到最佳方案时,把该问题记录下来,后续解决,不影响当前工作;
采用这种工作方法的前提条件就是团队所有成员对“问题具有唯一正确的解决方案”的原则形成一致的认识:所有的软件工作项都有唯一正确的解决方案。由于该原则是软件工作的基本原则,所以,命名该原则为:“软件工程第零原则”,简称“第0原则”。
以下对该原则做说明:
1、软件工作中所有工作项都有最佳解决方案:由于,计算机理论基于数学,软件工作中的所有工作项的根本基础是数学,可以说软件工作中的所有问题都可以转化为相应的数学问题,而数学问题是有最佳解决方案的,所以,软件工作中的所有工作项都有最佳解决方案;(这里,还有,成本进度等的量化计算与管理);
2、不同解决方案合适程度是不一样的:有时,几个方案都可行,但,其中是有区别的,区别在于,风险和成本,具体来说在于工作量、工期、运行性能、团队技术熟练程度等;
3、该原则的用途:学习软件知识时,明白所有问题有最佳解决方案,避免一叶障目;团队工作时,减少无效讨论;
该随笔的观点不太成熟,多提宝贵意见。谢谢!
posted @
2007-07-22 19:34 石正 阅读(325) |
评论 (0) |
编辑 收藏
感知 = 感觉 + 知觉
上边的公式今天的收获,很高兴啊。
上边公式说的是:人类获得信息的方式。起关键在于把过程分为2步,可以在人工智能工作中参考。
具体是如下说的:
感觉获得外界的信息,知觉把感觉获得的信息加工成为“概念”,后续的思维,进一步处理。
posted @
2007-07-22 18:52 石正 阅读(208) |
评论 (0) |
编辑 收藏
我希望有个安静祥和的家庭,家和万事兴啊。
posted @
2007-07-20 09:17 石正 阅读(175) |
评论 (0) |
编辑 收藏
软件工作中的加减法“软件加减法”
做软件时,有时会遇到很难解决的问题,仔细分析过后,觉得无法下手,超苦恼。
此时,不妨实验一下“软件加减法”,往往可以出奇制胜。
具体做法如下:
首先使用“减法算法”,“减法算法”不合适时,使用“加法算法”。以下具体描述“减法算法”和“加法算法”。
“减法算法”:
1、把怀疑有问题的模块去掉,不参与运行测试,看问题是否仍然会出现;
2、如果问题不出现了,则断定问题与该模块相关;
3、进一步在该模块内使用“软件加减法”或者其他方法,逐渐定位问题的具体位置;具体做法是:把该模块加入到系统中,把该模块内的部分子模块屏蔽掉,运行测试,看问题是否仍然会出现;
4、重复使用3,定位问题;
5、如果第1步的结果是,问题仍然出现,可以采取2种方法:一种是:进一步做减法,另一种是加法后减法,即把原来屏蔽掉的模块加上,然后,屏蔽其他模块测试;
6、减法算法对软件人员的能力要求比较高,优点是,人员能力交好、对项目和代码熟练程度较高时,解决问题速度快。
7、减法算法不行时,可以考虑采用加法算法。
“加法算法”:
1、新建一个项目;
2、把原来有问题的项目的模块,添加到新项目中一个;
3、测试;
4、测试通过后,添加下一个模块;
5、测试不通过,断定问题与该模块相关;对该模块,采用“减法算法”或者其他方法进一步定位问题位置;
6、“加法算法”,虽然速度稍微慢点,但使用简单方便,容易定位问题;相比其他方法,也许是最快的了,7、该算法最大的优点是,算法时间复杂度稳定,即解决问题使用的时间不会过长。
实际工作中,使用“软件加减法”时,往往是“加法算法”和“减法算法”混合使用,同时,最好甚至是必须结合其他的软件方法,才可以达到更好的效果。
希望大家对该问题,多提宝贵意见和建议。
posted @
2007-07-18 09:44 石正 阅读(350) |
评论 (0) |
编辑 收藏
称呼窗口为“窗户”
在软件工作中,有很多名词存在同义词,增加了工作的复杂程度,造成工作效率 和 质量的下降。
今天,我真是开眼界了,HJ竟然给我说了个“窗户”。
把屏幕上的 窗口 称做 “窗户”,我还是第一次听到,第一次见到。
真诚地希望这种开眼界的事情,逐渐少遇到点,这种事情少了,行业发展了,公司发展了,自己也发展了。
所有的提高,都是一点一点积累的,积少成多吧。
posted @
2007-07-13 10:56 石正 阅读(205) |
评论 (0) |
编辑 收藏
http://blog.csdn.net/llmmysun/archive/2004/11/05/168753.aspx
数据库移动,在项目实施过程中,经常会发生,对于有经验的DBA来说,数据库移动是十分容易。但对于一些只了解系统,对数据库不是十分懂的人员来说,oracle的移动就不是那么容量了。
例
如当系统安装完成以后,存储空间扩容了,需要对原对ORACLE进行移动,需要进行数据移动。以前在ORACLE8I FOR WINDOWS
2000中是可以进行移动的。现在ORACLE9I FOR AIX 5L 的移动如何做呢???其实经对各种不同的平台,操作过程完全一样。
一、复制移动数据文件
1)、获取数据库相关信息
首先要查看一下数据库的文件内容:
sqlplus “ / as sysdba”
? select * from v$datafile;
? select * from v$controlfile
? select * from v$logfile;
2)、移动应用数据文件
shutdown immediate关闭数据库,拷贝数据文件到另外一个目录下。需要copy 的文件有:
system01.dbf
indx01.dbf
temp01.dbf
users01.dbf
应用数据文件
3)、 修改数据库文件的位置
启动MOUNT模式
? startup mount;
? alter database rename file ‘\oracle\oradata\ocp\system01.dbf' to ‘\oradata\ocp\system01.dbf”
? alter database rename file ‘\oracle\oradata\ocp\indx01.dbf' to ‘\oradata\ocp\indx01.dbf';
注意只能进行更改移动的数据库文件,不包括控制文件与日志文件,TEMP文件。
二. 移动控制文件
(1)备份SPFILE中的内容:
再重新启动数据库:
? startup;
? create pfile='c:\init.ora' from spfile;
(2)修改init.ora文件中的内容:
*.control_files='\oradata\ocp\control01.ctl','\oradata\ocp\control02.ctl','\oradata\ocp\control03.ctl'
进行更改已经复制的目录位置。
Shutdown 数据库
(3)将控制文件复制过去,
将三个控制文件移动到上面所修改的的目录下。
(4) 倒入参数文件
以init.ora参数方式启动:
? startup pfile='\oracle\init.ora';
? create spfile from pfile='\oracle\init.ora';
? shutdown immediate;
? startup; 从spfile中进行启动。
这样控制文件移动工作就结束了。
三、 重建或重定位LOG文件
解决日志文件的移动方式有两种,一种是移动(RENAME方式)另一种是重新创建。
(1)移动LOG文件
重新移动数据库文件与移动系统数据库文件是一样的方法,只是要求数据库必须是“startup mount”方式进行。
? startup mount
? alter database rename file ‘\oracle\oradata\oralog1.ora' to ‘\oradata\ocp\oralog1.dbf'
样
(2)重建LOG文件
先重建一个组GROUP ,并添加一个LOG文件作为交换文件。
ALTER DATABASE ADD LOGFILE GROUP 4 ('$HOME/ORADATA/u01/log4.rdo','$HOME/ORADATA/u02/log4.rdo') SIZE 1M;
然后再删除其它的LOG组,要求必须保持有两个LOG文件组在系统中。
Alter database drop logfile member ‘\oracle\oradata\ocp\oralog01.ora'
四、重建系统监时(temp)文件系统
在移动数据表空间时,TEMP文件是不能被移动的,这里要求可以进行重建一个TEMP表空间,并设置为系统默认的TEMP文件系统。然后再册除原来的TEMP表空间,达到移动表空间的过程。
>
create temporary tablespace “temp02” tempfile ‘\oradata\ocp\temp02.dbf'
size 500m extent management local uniform size 10m;
然后再删除原来的TEMP文件就可以了。
五、测试数据库
进行重启动ORACLE数据
$ sqlplus “/as sysdba”
? startup;
? show sga;
?
posted @
2007-07-11 17:08 石正 阅读(201) |
评论 (0) |
编辑 收藏
xp下安装SQL server 2000企业版2006年12月17日 星期日 01:20xp下安装SQL2000 server企业版
网络搜索内容:
SQL2000企业版本适用于WIN 2000系统,2003系统和XP一般装不了需要选用个人版,当然如果你在不清楚的前提下辛辛苦苦下载了企业版本却不能安装,是不是很失望呢?这里介绍一个XP下安装装SQL2000企业版本方法以供参考
办法如下:
一.在SQL服务器的安装盘中找到MSDE这个目录,并且点击setup.exe安装它,过程简单直接下一步就OK了。
二. 重启系统WINDOWSXP,这下就可以看到SQL服务的图标出现了。
三. 再拿出SQL服务器版的安装光盘,直接安装客户端工具(这个不要多说吧?最简单的方法就是直接点击光盘根目录下的autorun.exe)根据提示安装,自检过程中知道系统不是SERVER版,会提示只安装客户端工具。
四. 打开企业管理器,试用SA用户连一下看看,是不是发现SA用户登陆失败?因为你还没有与信任SQL SERVER连接相关联。还好这个只要对系统注册表稍加修改就可以啦:在运行中输入regedit打开注册表编辑器,找到[HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\MSSQLSERVER\MSSQLSERVER],这个项里面有一个键值LoginMode,默认下,值是1,现在将值改为2,重启电脑。
五. 再打开企业管理,再连接试试,OK了!
posted @
2007-06-22 10:36 石正 阅读(1179) |
评论 (0) |
编辑 收藏
Visual C++中的异常处理浅析
http://dev.yesky.com/115/2158115_1.shtml
posted @
2007-06-20 10:07 石正 阅读(182) |
评论 (0) |
编辑 收藏
软件工作的基本方法之一:5分钟工作制
在软件工作中,遇到小难题(如:语法、简单算法等)时,如果自己5分钟内没有解决,则直接询问本小组的其他人员。
相关的一个基本工作方法:15分钟工作制
如果一个问题,本小组15分钟内无法解决时,则把本问题记录下来,留待明天(后续)解决。
以上2个方法,可以有效的保证工作的进度,保证工作的愉快程度。
相关的方法是:
软件开发工作中的1030原则2种方法的区别在2点:
1、时间长短不一,5分钟短,1030原则长一倍;
2、适用地方不一,5分钟适用于简单问题,1030适用于其他问题。
posted @
2007-05-11 09:02 石正 阅读(194) |
评论 (0) |
编辑 收藏
我工作的计算机编号是51,现在才明白,是每天都是劳动节的意思! 劳动节快乐!天天快乐!
最近真忙啊!
posted @
2007-04-29 16:15 石正 阅读(223) |
评论 (0) |
编辑 收藏
C++中,CTime 与 CString转换
CTime m_StartTime1 = CTime::GetCurrentTime();
CString csStartTime = m_StartTime1.Format( "%Y%m%d%H%M%S" );
一.将CString转为CTime的几种方法
CString timestr = "2000年04月05日";
int a,b,c ;
sscanf(timestr.GetBuffer(timestr.GetLength()),"%d年%d月%d日",&a,&b,&c);
CTime time(a,b,c,0,0,0);
--------or - ---------------------
CString s("2001-8-29 19:06:23");
int nYear, nMonth, nDate, nHour, nMin, nSec;
sscanf(s, "%d-%d-%d %d:%d:%d", &nYear, &nMonth, &nDate, &nHour, &nMin, &nSec);
CTime t(nYear, nMonth, nDate, nHour, nMin, nSec);
---- or ------------------------
CString timestr = "2000年04月05日";
int year,month,day;
BYTE tt[5];
//get year
memset(tt, 0, sizeof(tt));
tt[0] = timestr[0];
tt[1] = timestr[1];
tt[2] = timestr[2];
tt[3] = timestr[3];
year= atoi((char *)tt);
//get month
memset(tt, 0, sizeof(tt));
tt[0] = timestr[6];
tt[1] = timestr[7];
month = atoi((char *)tt);
//get day
memset(tt, 0, sizeof(tt));
tt[0] = timestr[10];
tt[1] = timestr[11];
CTime time(year,month,day,0,0,0);
从上面来看,很明显使用sscanf()函数的优势.
二.将CTIme转换为CString的方法:
CTime tmSCan = CTime::GetCurrentTime();
CString szTime = tmScan.Format("'%Y-%m-%d %H:%M:%S'");
这样得到的日期时间字符串就是以"2006-11-27 23:30:59"的格式.这是不是很方便呢?
//取得CTime中的日期
CString cstrDate = tmScan.Format("%Y-%m-%d");
//取得CTime中的时间
CString cstrTime = tmScan.Format("%H:%M-%S");
sprintf还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为了在出现问题时可以推卸责任吧。这里举个例子:
更多更好的sprintf()函数说明参考:《spirntf,你知道多少?》
http://blog.csdn.net/steedhorse/archive/2005/03/25/330206.aspx
time_t t = time(0);
//产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
char s[32];
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
sprintf在MFC中也能找到他的知音:CString::Format,strftime在MFC中自然也有她的同道:CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1417748
posted @
2007-04-18 09:47 石正 阅读(12939) |
评论 (5) |
编辑 收藏
源代码格式不对,也是软件错误。
源代码是软件的一个重要的组成部分,源代码格式不对,换句话说,源代码错误,肯定是软件错误。所以,把源代码编译没有错误等价为源代码正确,是错误的,把运行没有错误,等价为源代码正确,是错误的。
源代码格式的几点应该注意的事项(常识):
1、源代码的书写格式,应该体现源代码内部的逻辑关系;
2、源代码内部最重要的代码行是:空行;空行把代码分成各个相对独立的组成部分;
3、源代码内部第二重要的代码行是:注释行;
4、源代码行内最重要的字符是:空格;空格把代码行分成各个相对独立的组成部分;
5、源代码行内第二重要的字符是:制表符(Tab),制表符是行首缩进使用的字符;
6、注释合格的标准是:同等级技术能力的技术人员,可用比较方便的看懂代码;或者,应该从事该工作的技术人员可以容易的看懂;
7、注释行数应该占到总代码行数的三分之一;
8、每个源代码文件头部都必须有说明本文件功能的注释;
9、自己定义的每个函数都必须有说明函数功能的注释;
10、自己定义的每个变量都必须有说明变量功能的注释;
11、注释应该使用自己的母语(汉语);
12、一句注释合格的标准是,表达了需要表达的内容,语法正确;
13、清楚的注释能够方便代码阅读者理解代码的逻辑关系和各个函数的功能;
14、键盘上最重要的键是空格键,空格占英文文章的比例是20%左右;写软件源代码时,空格的比例也很大;所以,键盘上面积最大的键是空格;
posted @
2007-04-18 09:08 石正 阅读(499) |
评论 (1) |
编辑 收藏
引用自
http://www.91php.cn/trackback.php?tbID=88&extra=964ce999cp
SQL Server 2000中,当新建一个空的数据库后,在查询分析器中执行
sp_database 或select * from sysobjects where xtype = 'U'
都会得到dtproperties表,并显示为用户表, 可这个表我从没有创建过。但是在企业管理器中,dtproperties却显示为系统表。难道是bug ? 查看dtproperties,里面没有数据,是个空表。在SQL Server联机丛书里没有关于这个表的描述。
dtproperties表在企业管理器中无法删除,但在查询分析器中 执行drop table dtproperties却可以删除。
手工建立一个表,表名就叫dtproperties,然后再查看,在企业管理器中赫然显示为系统表!! 看来Bug是一定的了。
重新创建一个数据库, 写个触发器,让其在dtproperties表执行insert时执行,然后在SQL Server里随意操作,hehe,终于发现,在新建关系图时,dtproperties表被插入数据,看来它是用来存储数据关系图的。呵呵,想点办法,数据关系图以后也可以备份下来了。
再用drop table dtproperties删除表dtproperties,[手工创建dtproperties ,结构任意,] 然后在企业管理器中新建数据关系图,呵呵,SQL Server报错!再次证实前面的猜想!
SQL Server 2005中dtproperties 表不存在了,取而代之的是sysdiagram (记不住具体的名称了) ,这个bug也不存在了,SQL Server 2005中的系统表和SQL Server 2000有了很大的不同。
posted @
2007-04-05 10:21 石正 阅读(811) |
评论 (0) |
编辑 收藏
posted @
2007-03-30 09:51 石正 阅读(173) |
评论 (0) |
编辑 收藏
VC++6 写完的程序程编译链接通过后,点运行的时候出现one or more files out of date or not exist,解决办法。
把所有日期大于当前系统日期的文件,打开后,添加一个空格,然后保存,即可。
可以对照文件的目录,按时间倒序排列后,逐个处理。
posted @
2007-03-22 17:10 石正 阅读(1260) |
评论 (2) |
编辑 收藏
sysobjects
在数据库内创建的每个对象(约束、默认值、日志、规则、存储过程等)在表中占一行。只有在 tempdb 内,每个临时对象才在该表中占一行。
列名 |
数据类型 |
描述 |
name
|
sysname
|
对象名。 |
Id
|
int
|
对象标识号。 |
xtype
|
char(2)
|
对象类型。可以是下列对象类型中的一种:
C = CHECK 约束 D = 默认值或 DEFAULT 约束 F = FOREIGN KEY 约束 L = 日志 FN = 标量函数 IF = 内嵌表函数 P = 存储过程 PK = PRIMARY KEY 约束(类型是 K) RF = 复制筛选存储过程 S = 系统表 TF = 表函数 TR = 触发器 U = 用户表 UQ = UNIQUE 约束(类型是 K) V = 视图 X = 扩展存储过程 |
uid
|
smallint
|
所有者对象的用户 ID。 |
info
|
smallint
|
保留。仅限内部使用。 |
status
|
int
|
保留。仅限内部使用。 |
base_schema_ ver
|
int
|
保留。仅限内部使用。 |
replinfo
|
int
|
保留。供复制使用。 |
parent_obj
|
int
|
父对象的对象标识号(例如,对于触发器或约束,该标识号为表 ID)。 |
crdate
|
datetime
|
对象的创建日期。 |
ftcatid
|
smallint
|
为全文索引注册的所有用户表的全文目录标识符,对于没有注册的所有用户表则为 0。 |
schema_ver
|
int
|
版本号,该版本号在每次表的架构更改时都增加。 |
stats_schema_ ver
|
int
|
保留。仅限内部使用。 |
type
|
char(2)
|
对象类型。可以是下列值之一:
C = CHECK 约束 D = 默认值或 DEFAULT 约束 F = FOREIGN KEY 约束 FN = 标量函数 IF = 内嵌表函数 K = PRIMARY KEY 或 UNIQUE 约束 L = 日志 P = 存储过程 R = 规则 RF = 复制筛选存储过程 S = 系统表 TF = 表函数 TR = 触发器 U = 用户表 V = 视图 X = 扩展存储过程 |
userstat
|
smallint
|
保留。 |
sysstat
|
smallint
|
内部状态信息。 |
indexdel
|
smallint
|
保留。 |
refdate
|
datetime
|
留作以后使用。 |
version
|
int
|
留作以后使用。 |
deltrig
|
int
|
保留。 |
instrig
|
int
|
保留。 |
updtrig
|
int
|
保留。 |
seltrig
|
int
|
保留。 |
category
|
int
|
用于发布、约束和标识。 |
cache
|
smallint
|
保留。 |
posted @
2007-03-21 10:16 石正 阅读(2416) |
评论 (0) |
编辑 收藏
系统表的应用
系统表用的不多,写几个常用的系统表
各个数据库中的系统表:
1.SysObject:
存放数据库中的各个对象!
最重字段
1.1 Name,Id,Crdate,Xtype (U :用户表,V:视图,Tr:触发器,P:存储过程,S,系统表)
1.2 常用函数 Object_ID('对象名'),Object_Name
此外我们如果想生成1,2,3,4,5这样的等差数列,可以这样做
Select Identity(int,1,1) As Id Into # From SysObjects,SysColumns
2.SysColumns
存放各列的信息
最重字段
2.1 Name,Id,Colid--字段在表中存放的顺序
设一个表中字段有很多,想列出某表除某字段外的所有字段,可以这样写
Declare @Fields Varchar(5000)
Select @Fields=@Fields+Name
From SysColumns
Where Id=Object_ID('Test') and Name Not In ('字段1','字段2')
又如如何用列序号选择特定列
Create Function F_ColumnOrder(@TableName Varchar(10),@Colid Int)
Returns Table
As
Return
Select Name From SysColumns
Where ID=Object_ID(@TableName) And Colid=@Colid
--Try
Select * From Dbo.F_ColumnOrder('Users',2)
3.SysFiles
如果我们想知道SQL的数据文件存放目录的话,可以用
Select FileName From SysFiles
4.SysComments
保存视图或存储过程的语句!
5.Sysforeignkeys
fkeyid:外键表对象ID,rKeyid:主键表对象ID
有时我们想把数据库中的所有表都去掉,但是有约束的存在,有些表必须在主表前删除,否则就会报错,
这种情况下我们就通过该表来实现
**********************************************************
Master表中的系统表
SysDataBases
重要字段
1.1 Name,Dbid,Crdate
常用函数 DB_ID('数据库名')
sysaltfiles
保存各个数据库的MDF文件的物理地址
SysProcesses
保存进程信息
Kill spid 杀掉某进程
5.注意sp_MsForEachTable函数
删除某数据库中表
Exec Sp_MsForEachTable 'Truncate Table ?'
注意:有约束的不能删除,会出错的
6.如果想改系统表怎么办?
2种方法
1.通过查询分析器改
用如下语句:
Exec Sp_Configure 'allow updates',1--允计修改
Reconfigure with override
Exec Sp_Configure 'allow updates',0--不允计修改
Reconfigure with override
2.通过企业管理器改
在SQL实例上右键-属性-允计修改系统目录直接进行修改
posted @
2007-03-21 10:12 石正 阅读(720) |
评论 (0) |
编辑 收藏
master--->数据库的核心库,存储很多系统的信息和登录的信息等
tempd--->存储了诸如临时表等信息
Northwind--->北风数据库,是个例子数据库,可以模拟很多操作,并且提供例子供参考
名称 地址 说明
sysaltfiles 主数据库 保存数据库的文件
syscharsets 主数据库 字符集与排序顺序
sysconfigures 主数据库 配置选项
syscurconfigs 主数据库 当前配置选项
sysdatabases 主数据库 服务器中的数据库
syslanguages 主数据库 语言
syslogins 主数据库 登陆帐号信息
sysoledbusers 主数据库 链接服务器登陆信息
sysprocesses 主数据库 进程
sysremotelogins主数据库 远程登录帐号
syscolumns 每个数据库 列
sysconstrains 每个数据库 限制
sysfilegroups 每个数据库 文件组
sysfiles 每个数据库 文件
sysforeignkeys 每个数据库 外部关键字
sysindexs 每个数据库 索引
sysmenbers 每个数据库 角色成员
sysobjects 每个数据库 所有数据库对象
syspermissions 每个数据库 权限
systypes 每个数据库 用户定义数据类型
sysusers 每个数据库 用户
posted @
2007-03-21 10:07 石正 阅读(401) |
评论 (0) |
编辑 收藏
Access 密码破解原理- -
今天风雨无阻告知偶ACCESS数据库的密码极容易被破,使用网上流行的工具,不管你的密码多少位很快就能被破出来。偶原以为ACCESS与WORD EXCEL一样,作为MS OFFICE的套件,一样采用DES加密,非破暴力破解不可。密码倍数越长越难破。结果,一试,果然很快被破出来。
经查询,原来ACCESS加密过于简单。如下:
在库文件的地址00000042处开始的40个字节是Access 2000库的密码位。如果一个未加密的库,这40个字节原始数据依次为:29 77 EC 37 F2 C8 9C FA 69 D2 28 E6 BC 3A 8A 60 FB 18 7B 36 5A FE DF B1 D8 78 13 43 60 23 B1 33 9B ED 79 5B 3D 33 3D 39 7C 2A 。当你给数据库设置了密码后,这40个字节就变成了密钥。因此,要破解密码而不需保持原库的密码,只要将00000042 处开始的40个字节还原成原始数据就行了,从而跳过口令。
另外,当你设置了密码后,Access 2000 就将你的密码的ASCII码与以上的40个字节数据进行异或操作,因此,从库文件的地址00000042开始的40个字节就变成了密钥了。例如,如果你设置的密码为12345678901234567890(注意:最多只能设20个ASCII字符),经过异或操作后,则从00000042处开始的40个字节的数据就变成了 18 77 DE 37 C1 C8 A8 FA 5C D2 1E E6 8B 3A B2 60 C2 18 4B 36 6B FE ED B1 EB 78 27 43 55 23 87 33 AC ED 41 5B 04 39 4C 2A 。大家都知道,一个数据经过一次异或操作后,再一次经过同样的异或操作就可还原了。因此,对已经设置了密码的Access 2000库,只要将40个密钥数据与原始的40个数据进行一次异或操作就可得到密码了。
最后,要注意的是,由于ACCESS 2000对每个密码字符采用双字节表示,故40个字节原始数据可依次分为20组,每组代表一个密码字符,进行异或操作的是每组的第一个字节,第二个字节不变。
posted @
2007-01-09 14:24 石正 阅读(2235) |
评论 (1) |
编辑 收藏
将ANSI转换到Unicode
(1)通过L这个宏来实现,例如: CLSIDFromProgID( L"MAPI.Folder",&clsid);
(2)通过MultiByteToWideChar函数实现转换,例如:
char *szProgID = "MAPI.Folder";
WCHAR szWideProgID[128];
CLSID clsid;
long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
szWideProgID[lLen] = '\0';
(3)通过A2W宏来实现,例如:
USES_CONVERSION;
CLSIDFromProgID( A2W(szProgID),&clsid);
将Unicode转换到ANSI
(1)使用WideCharToMultiByte,例如:
// 假设已经有了一个Unicode 串 wszSomeString...
char szANSIString [MAX_PATH];
WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
(2)使用W2A宏来实现,例如:
USES_CONVERSION;
pTemp=W2A(wszSomeString);
posted @
2006-12-26 09:01 石正 阅读(13357) |
评论 (6) |
编辑 收藏
应用程序和应用软件的区别是什么?
这2个名词实际上区别很微妙!
一般该说是应用软件,或者说计算机应用系统。
这里的应用程序实际上是应用软件的一个通俗化的称呼,其实质含义应该是应用软件。真正好的软件系统,不会只有代码的,会有相应的文档的。
posted @
2006-11-04 08:04 石正 阅读(1410) |
评论 (0) |
编辑 收藏
热烈庆祝我的第一个工业机器人,胜利完成!
计划6个月,实际使用也是6个月。
春节后开始做,中间出差2个月,10月低前完成。
幸福啊!
希望这是一个好的开始。
posted @
2006-10-30 18:42 石正 阅读(351) |
评论 (2) |
编辑 收藏
我自己研制的第一台工业机器人就要出生了。
全称为:基于人工视觉的单自由度,双并行性固体颗粒分拣机器人。
共喜!
本周内一定可以完工!
posted @
2006-10-17 08:02 石正 阅读(359) |
评论 (2) |
编辑 收藏
我国机器人发展状况
内容比较新,基本反映了我国现在机器人产业的情况。
http://bbs.tiexue.net/post_1506225_1.html
posted @
2006-09-12 08:54 石正 阅读(847) |
评论 (0) |
编辑 收藏
昨天又把计算机的操作系统重新安装了下,开发环境重新安装了下。
业余做开发做科研,难度太大啊。
posted @
2006-09-11 16:59 石正 阅读(198) |
评论 (0) |
编辑 收藏
我的古老的计算机修好了,搬回家了。
做机器人开发是够用的。
可以开始进行软件开发部分的工作了。
posted @
2006-09-06 08:59 石正 阅读(196) |
评论 (0) |
编辑 收藏
分布式异地开发:GDD生命周期中的一天
http://51cmm.csai.cn/SPI/No039.htm
posted @
2006-09-02 16:10 石正 阅读(216) |
评论 (0) |
编辑 收藏
实施全过程监理的一般工作内容
http://scm.csai.cn/schedule/200607281609251391.htm
posted @
2006-09-01 15:06 石正 阅读(205) |
评论 (0) |
编辑 收藏
工业生产的几个阶段:机械化、自动化、信息化、智能化
posted @
2006-08-30 17:48 石正 阅读(943) |
评论 (0) |
编辑 收藏
连续出差37天,终于可以回来休息几天了
好累啊.体力劳动 + 脑力劳动,很锻炼人啊!
posted @
2006-08-19 16:26 石正 阅读(231) |
评论 (0) |
编辑 收藏
看到了红月亮,昨天晚上在白沙水库
第一次看到红月亮,特记录下来与大家分享。
昨天晚上在白沙水库,晚饭后去大坝东部,距离大坝西边大概1000米处,焊接电缆。
8点多的时候,抬头看天,东边偏南的天空中,一轮红月亮挂在那里;颜色象咸鸭蛋黄的红色,大概是空气中有水气的原因吧,边缘也不是很清晰,模模糊糊的。
9点多的时候,离开了大坝,天空中的红月亮仍然是红色的。
自东向西走,夏夜晚风习习,和王工一起边走边聊,有时也看看坝下树林里手电一闪一闪的,那是在照爬(cha)(知了的蛹)的人的。
posted @
2006-08-11 14:15 石正 阅读(344) |
评论 (3) |
编辑 收藏
本团队第100个发表的文章的机会,不让给别人了。本来计划,请发表第100个文章的人,一起吃饭的。
出差的地方,是当年 关羽过五关斩六将的 第一个关。
在那里竖了个介绍性质的石碑,上边写到: 当年关羽着急去冀洲找刘备,没有讨到过关文书,就要过关,忠于职守的守将 孔秀 上马迎战,只一个回合,人头就被斩落在地。
人们多从关羽角度考虑该事件,站在 孔秀 的角度考虑,一个忠于职守的人,呵呵呵呵
这也许是人类进步必然的历程吧。
希望人间更美好。
昨天早上,在水库附近,和着小鸟的鸣唱读书,感觉很幸福!
希望在哪里出差的日子里,有更多的机会可以读书。
posted @
2006-07-20 15:42 石正 阅读(226) |
评论 (0) |
编辑 收藏
确定明天要出差走了,估计会持续3周。
posted @
2006-07-12 16:57 石正 阅读(223) |
评论 (1) |
编辑 收藏
软件过程是指软件工程过程、软件管理过程和软件组织过程三者的有机结合。
软件工程过程是我们理解的常规的软件的需求分析、设计、编码、测试等过程;
软件管理过程是指为使软件工程过程顺利进行而实施的管理活动的集合。
上述两个过程是以软件工程组为主的活动。
软件组织的过程是企业级的对软件的组织活动,是以企业为主的活动;它是从软件过程的角度考虑问题,而并非关注软件开发工具,与框架软件生存周期无关,也与所采用的开发技术无关。
posted @
2006-07-12 14:11 石正 阅读(350) |
评论 (0) |
编辑 收藏