﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Welcome to tiger's blog!</title><link>http://www.cppblog.com/tiger/</link><description>What lead to success, what we are seeking...</description><language>zh-cn</language><lastBuildDate>Tue, 07 Apr 2026 12:48:47 GMT</lastBuildDate><pubDate>Tue, 07 Apr 2026 12:48:47 GMT</pubDate><ttl>60</ttl><item><title>const用法</title><link>http://www.cppblog.com/tiger/archive/2014/09/12/208286.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Fri, 12 Sep 2014 11:41:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2014/09/12/208286.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/208286.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2014/09/12/208286.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/208286.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/208286.html</trackback:ping><description><![CDATA[#include <iostream>
using namespace std;

/*
 *	const由编译器决定
 *	指针运算符*，是从右到左，那么如：char const * pContent，
	可以理解为char const (* pContent)，即* pContent为const，而pContent则是可变的。
*/
int main(int argc, char *argv[])
{
	int nData = 1;

	//情形1: const修饰a1/a2, 以下两种情形等同
	const int a1 = 1;
//	a1 = 2;		//a1为常量，编译报错

	int const a2 = 1;
//	a2 = 2;		//a2为常量，编译报错
	/////////////////////////////////////

	//情形2: const修饰(*p1)/(*p2), 以下两种情形等同
	const int *p1 = &nData;
//	*p1 = 2;	//*p1为常量，编译报错
	p1++;		//p1指针为非常量，野指针，但编译通过
	cout<<"*p1 == "<<*p1<<endl;

	int const *p2 = &nData;
//	*p2 = 2;	//*p2为常量，编译报错
	p2++;		//p2指针为非常量，野指针，但编译通过
	cout<<"*p2 == "<<*p2<<endl;
	/////////////////////////////////////

	//情形3: const修饰p3
	int * const p3 = &nData;
//	p3++;		//p3为常量，编译报错
	*p3 = 2;	//*p3为非常量，可改变，编译通过
	cout<<"*p3 == "<<*p3<<endl;
	/////////////////////////////////////

	//情形4: 前两个const修饰*p4、最后的const修饰p4
	const int const * const p4 = &nData;
//	p4++;		//p4为常量，编译报错
//	*p4 = 2;	//*p4为常量，编译报错
	/////////////////////////////////////

	//情形5: const修饰(*p5)/(*p6)与a3/a4, 以下两种情形等同
	const int *p5 = &nData, a3 = 1;
//	*p5 = 2;	//*p5为常量，编译报错
//	a3 = 2;		//a3为常量，编译报错
	p5++;		//p5为非常量，可改变，编译通过
	cout<<"*p5 == "<<*p5<<endl;

	int const *p6 = &nData, a4 = 1;
//	*p6 = 2;	//*p6为常量，编译报错
//	a4 = 2;		//a4为常量，编译报错
	*p6++;		//*p6为非常量，可改变，编译通过
	cout<<"*p6 == "<<*p6<<endl;
	/////////////////////////////////////

	//情形6: const仅修饰p7, 因为(* const p7是整体)
	int * const p7 = &nData, a5 = 1;
//	p7++;		//p7为常量，编译报错
	*p7 = 2;	//*p7为非常量，可改变，编译通过
	a5 = 2;		//a5为非常量，可改变，编译通过
	cout<<"*p7 == "<<*p7<<" a5 == "<<a5<<endl;
	/////////////////////////////////////

	//情形7: 前两个const修饰*p8与a6, 最后的const修饰p8
	const int const * const p8 = &nData, a6 = 1;
//	p8++;		//p8为常量，编译报错
//	*p8 = 2;	//*p8为常量，编译报错
//	a6 = 2;		//a6为常量，编译报错
	/////////////////////////////////////

	//情形8: const修饰b1/b2数组，数组的值不可改变, 以下两种情形等同
	const int b1[] = {0, 1};
//	b1[0] = 2;	//b1数组为常量，编译报错

	int const b2[] = {0, 1};
//	b2[0] = 2;	//b2数组为常量，编译报错
	/////////////////////////////////////

	return 0;
} <img src ="http://www.cppblog.com/tiger/aggbug/208286.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2014-09-12 19:41 <a href="http://www.cppblog.com/tiger/archive/2014/09/12/208286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>无名非阻塞管道</title><link>http://www.cppblog.com/tiger/archive/2010/09/04/125858.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Sat, 04 Sep 2010 03:14:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2010/09/04/125858.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/125858.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2010/09/04/125858.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/125858.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/125858.html</trackback:ping><description><![CDATA[int fd[2];<br>int nPipeReadFlag = 0;<br>fd[0] = -1;<br>fd[2] = -1;<br><br>if ((-1 == pipe(fd)) || (-1 == fd[0]) || (-1 == fd[1]))<br>&nbsp;{<br>&nbsp;&nbsp;SK_ERROR(("Create pipe failed!"));<br>&nbsp;&nbsp;return;<br>&nbsp;}<br><br>//以下代码设置读管道为非阻塞。设置写管道等类似，其他读写代码等一样<br>nPipeReadFlag = fcntl(fd[0], F_GETFL, 0);<br>&nbsp;nPipeReadFlag |= O_NONBLOCK;<br>&nbsp;if (fcntl(fd[0], F_SETFL, nPipeReadFlag) &lt; 0)<br>&nbsp;{<br>&nbsp;&nbsp;SK_ERROR(("set read pipe flaf failed!"));<br>&nbsp;&nbsp;return;<br>&nbsp;}
<img src ="http://www.cppblog.com/tiger/aggbug/125858.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2010-09-04 11:14 <a href="http://www.cppblog.com/tiger/archive/2010/09/04/125858.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NAND Flash--嵌入式NAND Flash读写技术</title><link>http://www.cppblog.com/tiger/archive/2010/05/17/115610.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Mon, 17 May 2010 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2010/05/17/115610.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/115610.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2010/05/17/115610.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/115610.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/115610.html</trackback:ping><description><![CDATA[<p>NAND Flash--嵌入式NAND Flash读写技术</p>
<p>NAND Flash控制器</p>
<p>S3C2410板的Nand Flash支持由两部分组成:Nand Flash控制器(集成在S3C2410 CPU)和Nand Flash存储芯片(K9F1208U0B)两部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命令才能完成。所以Nand Flash相当于S3C2410的一个外设,而不位于它的内存地址区.</p>
<p>&nbsp;&nbsp; 为了支持NAND Flash的启动装载，S3C2410A配置了一个叫Steppingstone的内部SRAM缓冲器。当系统启动时，NAND Flash存储器的前4KB将被自动加载到Steppingstone中，然后系统自动执行这些载入的启动代码。</p>
<p>&nbsp;&nbsp; 一般情况下，这4KB的启动代码需要将NAND Flash中的内容复制到SDRAM中。使用S3C2410A内部硬件ECC功能可以对NAND Flash的数据进行有效性的检查。复制完成后，将在SDRAM中执行主程序。</p>
<p>NAND Flash控制其具有以下特性：</p>
<p>&nbsp;&nbsp;&nbsp; * NAND Flash模式：支持读/擦除/编程NAND Flash存储器。</p>
<p>&nbsp;&nbsp;&nbsp; * 自动启动模式：复位后，启动代码被传送到Steppingstone中。传送完毕后，启动代码在Steppingstone中执行。</p>
<p>&nbsp;&nbsp;&nbsp; * 具备硬件ECC（校验码：Error Correction Code）生成模块（硬件生成校验码，通过软件校验）</p>
<p>&nbsp;&nbsp;&nbsp; * NAND Flash启动以后，4KB的内部SRAM缓冲器Steppingstone可以作为其他用途使用。</p>
<p>&nbsp;&nbsp;&nbsp; * NAND Flash控制器不能通过DMA访问，可以使用LDM/STM指令来代替DMA操作。</p>
<p>自启动模式的执行步骤如下：</p>
<p>（1）完成复位</p>
<p>（2）如果自动启动模式使能，NAND Flash存储器的前4KB自动复制到Steppingstone内部缓冲器；</p>
<p>（3）Steppingstone映射到nGCS0；</p>
<p>（4）CPU在Steppingstone的4KB内部缓冲器中开始执行启动代码。</p>
<p>注意：在自动启动模式下，不进行ECC检测。因此，应确保NAND Flash的前4KB不能有位错误（一般NAND Flash厂家都能确保）。</p>
<p><br>NAND Flash模式需要进行以下配置：</p>
<p>（1）通过NFCONF寄存器设置NAND Flash配置；</p>
<p>（2）将NAND Flash命令写入NFCONF寄存器；</p>
<p>（3）将NAND Flash地址写入NFADDR寄存器；</p>
<p>（4）通过NFSTAT寄存器检查NAND Flash状态，并读/写数据。在读操作之前或者编程操作之后应该检查R/nB信号。</p>
<p>引脚配置</p>
<p>D[7：0]&nbsp; 数据/命令/地址的输入/输出口（与数据总线共享）</p>
<p>CLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 命令锁存使能（输出）</p>
<p>ALE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 地址锁存使能（输出）</p>
<p>nFCE&nbsp;&nbsp;&nbsp;&nbsp; NAND Flash片选使能（输出）</p>
<p>nFRE&nbsp;&nbsp;&nbsp;&nbsp; NAND Flash读使能（输出）</p>
<p>nFWE&nbsp;&nbsp;&nbsp;&nbsp; NAND Flash写使能（输出）</p>
<p>R/nB&nbsp;&nbsp;&nbsp;&nbsp; NAND Flash就绪/忙（输入）</p>
<p>系统启动和NAND Flash所需的配置如下：</p>
<p>（1）OM[1：0]=00b：使能NAND Flash控制器为自动启动模式；</p>
<p>（2）NAND Flash存储器的页面大小应该为512字节；</p>
<p>（3）NCON：NAND Flash存储器寻址步数选择。0为3步；1为4步寻址。</p>
<p>相关寄存器</p>
<p>NAND Flash配置寄存器</p>
<p>NFCONF&nbsp;&nbsp;&nbsp; 地址0x4E000000</p>
<p>NAND Flash命令设置寄存器</p>
<p>NFCMD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 地址0x4E000004</p>
<p>NAND Flash地址设置寄存器</p>
<p>NFADDR&nbsp;&nbsp;&nbsp;&nbsp; 地址0x4E000008</p>
<p>NAND Flash数据寄存器</p>
<p>NFDATA&nbsp;&nbsp;&nbsp;&nbsp; 地址0x4E00000C</p>
<p>NAND Flash操作状态寄存器</p>
<p>NFSTAT&nbsp;&nbsp;&nbsp;&nbsp; 地址0x4E000010</p>
<p>NAND Flash ECC寄存器</p>
<p>NFECC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 地址0x4E000014</p>
<p><br>下面针对三星的K9F1208U0M为例说明nand flash的读写。</p>
<p>NAND Flash物理组成</p>
<p>正如硬盘的盘片被分为磁道，每个磁道又分为若干扇区，一块nand flash也分为若干block，每个block分为如干page。一般而言，block、page之间的关系随着芯片的不同而不同，典型的分配是这样的：</p>
<p>1block = 32page</p>
<p>1page = 512bytes(datafield) + 16bytes(oob)</p>
<p><br>需要注意的是，对于flash的读写都是以一个page开始的，但是在读写之前必须进行flash的擦写，而擦写则是以一个block为单位的。按照这种组织方式形成三类地址</p>
<p>Column Address：列地址，地址的低8位</p>
<p>Page Address：页地址</p>
<p>Block Address：块地址</p>
<p>8个I/O引脚充当地址、数据、命令的复用端口，所以每次传地址只能传8位，而nand falsh的地址位位26位，因此读写一次nand flash需要传送4次（A[7:0] A[16:9] A[24:17] </p>
<p>A[25]</p>
<p>一页有528B，在每一页中，最后16个字节（OOB)用于nand flash执行完命令后设置状态用的，剩余512B又分为前半部（1st half Page Register）和后半部（2nd half Page Register）。可以通过nand flash命令对1st half和2nd half</p>
<p>以及OOB进行定位通过nand flash内置的指针指向各自的首地址</p>
<p>存储操作特点：</p>
<p>1.擦除操作的最小单位是块</p>
<p>2.Nand Flash芯片每一位只能从1变为0，而不能从0变为1，所以在对其进行写入操作之前一定要将相应块擦除（擦除就是将相应块的位全部变为1</p>
<p>3 OOB部分的第六字节（即517字节）标志是否坏块，如果不是坏块该值为FF，否则为坏块</p>
<p>4 除OOB第六字节外，通常至少把OOB前3字节存放Nand Flash硬件ECC码</p>
<p>NAND Flash寻址方式</p>
<p>512byte需要9bit来表示，对于528byte系列的NAND，这512byte被分成1st half Page Register和2nd half Page Register，各自的访问由地址指针命令来选择，A[7:0]就是所谓的column address（列地址），在进行擦除操作时不需要列地址，为什么？因为以块为单位擦除。32个page需要5bit来表示，占用A[13:9]，即该page在块内的相对地址。A8这一位地址被用来设置512byte的1st half page还是2nd half page，0表示1st，1表示2nd。Block的地址是由A14以上的bit来表示。</p>
<p>例如64MB（512Mb）的NAND flash（实际中由于存在spare area,故都大于这个值），共4096block，因此，需要12个bit来表示，即A[25:14]，如果是128MB(1Gbit) 的528byte/page的NAND Flash，则block address用A[26:14]表示。由于地址只能在I/O[7:0]上传递，因此，必须采用移位的方式进行。以NAND_ADDR 为例：</p>
<p>第1 步是传递column address，就是NAND_ADDR[7:0]，不需移位即可传递到I/O[7:0]上，而halfpage pointer即A8 是由操作指令决定的，即指令决定在哪个halfpage 上进行读写，而真正的A8 的值是不需程序员关心的。</p>
<p>第2 步就是将NAND_ADDR 右移9位，将NAND_ADDR[16:9]传到I/O[7:0]上；</p>
<p>第3 步将NAND_ADDR[24:17]放到I/O上；</p>
<p>第4步需要将NAND_ADDR[25]放到I/O上；</p>
<p>因此，整个地址传递过程需要4 步才能完成，即4-step addressing。 如果NAND Flash 的容量是32MB（256Mbit）以下，那么，block adress最高位只到bit24，因此寻址只需要3步。 </p>
<p><br>Nand flash主要的内设命令</p>
<p>Nand flash命令执行是通过将命令字送到Nand flash控制寄存器的命令寄存器中来执行的，其命令是分周期执行的，每条命令都有一个或多个执行周期，每个执行周期都有相应的代码表示将要执行的动作。</p>
<p>功能</p>
<p>第一时钟周期</p>
<p>第二时钟周期</p>
<p>读取数据寄存器</p>
<p>Read1</p>
<p>00h/01h</p>
<p>&nbsp; </p>
<p>读取数据寄存器下半区（OOB）</p>
<p>Read2</p>
<p>50h</p>
<p>&nbsp; </p>
<p>读取芯片ID</p>
<p>90h</p>
<p>&nbsp; </p>
<p>RESET</p>
<p>FFh</p>
<p>&nbsp; </p>
<p>写页面（page program）</p>
<p>（首先写入00h（A区）/01h(B区）/05h(C区)表示写入区；再写入80h开始编程模式（写入模式），接下来写入地址和数据，最后写入10h表示编程结束。</p>
<p>80h</p>
<p>10h</p>
<p>块擦除（block erase）</p>
<p>60h</p>
<p>D0h</p>
<p>读取状态（read status）</p>
<p>70h</p>
<p>&nbsp; </p>
<p>&nbsp;</p>
<p>Nand Flash地址的计算</p>
<p>Column Address: 列地址。Column Address其实就是指定Page上的某个Byte，指定这个Byte其实也就是指定此页的读写起始地址。</p>
<p>Paage Address：页地址。由于页地址总是以512Bytes对齐的，所以它的低9位总是0。确定读写操作是在Flash上的哪个页进行的。</p>
<p>当我们得到一个Nand Flash地址srcaddr时候，我们可以这样分解出Column Address和Page Address</p>
<p>columnaddr=srcaddr%512&nbsp; //column address</p>
<p>pageaddr=srcaddr&gt;&gt;9&nbsp;&nbsp;&nbsp;&nbsp; //page address</p>
<p>也可以这么认为，一个Nand Flash地址的A0~A7是它的column_addr，A9~A25是它的Page Address。(注意地址位A8并没有出现，也就是A8被忽略，在下面你将了解到这是什么原因)</p>
<p>以read1命令为例：</p>
<p>Read1 命令的操作分为4个Cycle，发送完读命令00h或01h（00h与01h的区别请见下文描述）之后将分4个Cycle发送参数，1st.Cycle是发送Column Address。2nd.Cycle ,3rd.Cycle和4th.Cycle则是指定Page Address（每次向地址寄存器发送的数据只能是8位，所以17位的Page Address必须分成3次进行发送</p>
<p>Read1的命令里面出现了两个命令选项，分别是00h和01h。这里出现了两个读命是否令你意识到什么呢？是的，00h是用于读写1st half的命令，而01h是用于读取2nd half的命令。现在我可以结合上图给你说明为什么K9F1208U0B的DataField被分为2个half了。</p>
<p>如上文所提及的，Read1的1st.Cycle是发送Column Address，假设我现在指定的Column Address是0，那么读操作将从此页的第0号Byte开始一直读取到此页的最后一个Byte(包括Spare Field)，如果我指定的Column Address是127，情况也与前面一样，但不知道你发现没有，用于传递Column Address的数据线有8条（I/O0~I/O7，对应A0~A7，这也是A8为什么不出现在我们传递的地址位中），也就是说我们能够指定的 Column Address范围为0~255，但不要忘了，1个Page的DataField是由512个Byte组成的，假设现在我要指定读命令从第256个字节处开始读取此页，那将会发生什么情景？我必须把Column Address设置为256，但Column Address最大只能是255，这就造成数据溢出。。。正是因为这个原因我们才把Data Field分为两个半区，当要读取的起始地址（Column Address）在0~255内时我们用00h命令，当读取的起始地址是在256~511时，则使用01h命令.假设现在我要指定从第256个byte开始读取此页，那么我将这样发送命令串</p>
<p>column_addr=256;</p>
<p>NF_CMD=0x01; &#223;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从2nd half开始读取</p>
<p>NF_ADDR=column_addr&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1st Cycle</p>
<p>NF_ADDR=page_address&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2nd.Cycle</p>
<p>NF_ADDR=(page_address&gt;&gt;8)&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3rd.Cycle</p>
<p>NF_ADDR=(page_address&gt;&gt;16)&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4th.Cycle</p>
<p>其中NF_CMD和NF_ADDR分别是NandFlash的命令寄存器和地址寄存器的地址解引用，我一般这样定义它们，</p>
<p>#define rNFCMD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile unsigned char *)0x4e000004)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //NADD Flash command</p>
<p>#define rNFADDR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile unsigned char *)0x4e000008)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //NAND Flash address</p>
<p>事实上，当NF_CMD=0x01时，地址寄存器中的第8位（A8）将被设置为1（如上文分析，A8位不在我们传递的地址中，这个位其实就是硬件电路根据 01h或是00h这两个命令来置高位或是置低位），这样我们传递column_addr的值256随然由于数据溢出变为1，但A8位已经由于NF_CMD =0x01的关系被置为1了，所以我们传到地址寄存器里的值变成了</p>
<p>A0&nbsp; A1&nbsp; A2&nbsp; A3&nbsp; A4&nbsp; A5&nbsp; A6&nbsp; A7&nbsp; A8</p>
<p>0&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp; &amp; 0xff = 0000 0000</p>
<p>这8个位所表示的正好是256，这样读操作将从此页的第256号byte（2nd half的第0号byte）开始读取数据。</p>
<p>现在举一个例子，假设我要从Nand Flash中的第5000字节处开始读取1024个字节到内存的0x30000000处，我们这样调用read函数</p>
<p>nf_read(5000, 0x30000000,1024);</p>
<p>我们来分析5000这个src_addr.</p>
<p>根据&nbsp;&nbsp;&nbsp; </p>
<p>column_addr=src_addr%512;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>page_address=(src_addr&gt;&gt;9);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>我们可得出column_addr=5000%512=392</p>
<p>page_address=(5000&gt;&gt;9)=9</p>
<p>于是我们可以知道5000这个地址是在第9页的第392个字节处，于是我们的nf_read函数将这样发送命令和参数</p>
<p>column_addr=5000%512;</p>
<p>&gt;page_address=(5000&gt;&gt;9);</p>
<p>NF_CMD=0x01;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从2nd half开始读取</p>
<p>NF_ADDR= column_addr &amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1st Cycle A[7:0]</p>
<p>NF_ADDR=page_address&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2nd.Cycle A[16:9]</p>
<p>NF_ADDR=(page_address&gt;&gt;8)&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3rd.Cycle&nbsp;&nbsp; A[24:17]</p>
<p>NF_ADDR=(page_address&gt;&gt;16)&amp;0xff;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4th.Cycle&nbsp;&nbsp; A[25]</p>
<p>向NandFlash的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从rNFDATA寄存器(NandFlash数据寄存器)读取数据了.</p>
<p>我用下面的代码进行数据的读取.</p>
<p>for(i=column_addr;i&lt;512;i++)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *buf++=NF_RDDATA();</p>
<p>}</p>
<p>每当读取完一个Page之后,数据指针会落在下一个Page的0号Column(0号Byte).</p>
<p>例如实现一个从某字节处开始读取size大小的数据</p>
<p>static int NF_read(unsigned int src_addr,unsigned char *desc_addr,int size)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int column_addr = src_addr % 512;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int page_address =(src_addr &gt;&gt; 9);</p>
<p>&nbsp;&nbsp;&nbsp; unsigned char * buf = desc_addr;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; while((unsigned int)buf &lt; (unsigned int)(desc_addr)+size)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_L();&nbsp;&nbsp;&nbsp; //enable chip</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(column_addr &gt; 255)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_CMD(0x01);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_CMD(0x00);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR(cloumn_addr &amp; 0xff);&nbsp;&nbsp;&nbsp; //column address A[7:0];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR(page_address &amp; 0xff);&nbsp;&nbsp; //page address A[16:9]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR((page_address &gt;&gt; 8) &amp; 0xff);&nbsp; //A[24:17]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR((page_address &gt;&gt;16) &amp; 0xff);&nbsp; //A[25];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WAITRB();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(i=column_addr;i&lt;512;i++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *buf++=NF_RDDATA();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; column_addr = 0;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; page_address ++;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; return ;</p>
<p>}</p>
<p><br>打开s3c2410 的datasheet page 230：我们定义如下寄存器</p>
<p>#define rNFCONF&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile unsigned *)0x4e000000)&nbsp;&nbsp; //nand flash configuration</p>
<p>#define rNFCMD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile char *)0x4e000004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nand flash command</p>
<p>#define rNFADDR (*(volatile char *)0x4e000008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nand flash address</p>
<p>#define rNFDATA&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile char *)0x4e00000c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nand flash data</p>
<p>#define rNFSTAT&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile unsigned *)0x4e000010&nbsp; //nand flash opreation status</p>
<p>#define rNFECC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile int *)0x4e000014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //nand flash ecc</p>
<p>#define rNFECC0&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile char *)0x4e000014</p>
<p>#define rNFECC1&nbsp;&nbsp;&nbsp;&nbsp; (*(volatile char *)0x4e000015</p>
<p>#define rNFECC2&nbsp;&nbsp;&nbsp;&nbsp; (*volatile char *)0x4e000016</p>
<p>#define NF_CMD(cmd) {rNFCMD=cmd;}</p>
<p>#define NF_ADDR(addr)&nbsp;&nbsp; {rNFADDR=addr;}</p>
<p>#define NF_nFCEL_L()&nbsp;&nbsp;&nbsp; {rNFCONF &amp;= ~(1&lt;&lt;11);}</p>
<p>#define NF_nFCLE_H()&nbsp;&nbsp;&nbsp; {rNFCONF |= (1&lt;&lt;11);}</p>
<p>#define NF_RSTECC()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {rNFCONF |= (1&lt;&lt;12);}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Initialize ECC</p>
<p>#define NF_RDDATA() (rNFDATA)</p>
<p>#define NF_WRDATA(data) {rNFDATA=data;}</p>
<p>#define NF_WATRB()&nbsp; {while(!(rNFSTAT&amp;(1&lt;&lt;0)));}</p>
<p>//读一页数据的程序。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>static int NF_RreadPage(int block,int page,char *buffer)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int blockpage;</p>
<p>&nbsp;&nbsp;&nbsp; char *pbuf=buffer;</p>
<p>&nbsp;&nbsp;&nbsp; char *oob[16];</p>
<p>&nbsp;&nbsp;&nbsp; unsigned char ecc[3];</p>
<p>&nbsp;&nbsp;&nbsp; page=page&amp;0x1f;</p>
<p>&nbsp;&nbsp;&nbsp; blockpage=(block &lt;&lt; 5)+page;</p>
<p>&nbsp;&nbsp;&nbsp; NF_RSTECC();&nbsp;&nbsp;&nbsp; //Initialize ECC;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x00);&nbsp;&nbsp; //read command;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(0);&nbsp;&nbsp;&nbsp;&nbsp; //A[7:0]&nbsp; column=0 从第0字节开始读一直读完512B</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(blockpage&amp;0xff);&nbsp;&nbsp; //A[16:9];</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockpage&gt;&gt;8)&amp;0xff);&nbsp;&nbsp; //A[24:17]</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockpage&gt;&gt;16)&amp;0xff); //A[25];</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++);&nbsp; //wait tWB(100ns)</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //wait tR(max 12us)</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;512;i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *pbuf++=NF_RDDATA();</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; ecc[0]=rNFECC0;</p>
<p>&nbsp;&nbsp;&nbsp; ecc[1]=rNFECC1;</p>
<p>&nbsp;&nbsp;&nbsp; ecc[2]=rNFECC2;</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;16;i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oob=NF_RDDATA(); //read oob;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; if(ecc[0]==oob[0] &amp;&amp; ecc[1] == oob[1] &amp;&amp; ecc[2] == oob[2])&nbsp; //Ecc校验；</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print("ECC OK:%x,%x,%x\n",oob[0],oob[1],oob[2]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;</p>
<p>&nbsp;&nbsp;&nbsp; }else{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("ECC ERROR: read:%x,%x,%x, ECC reg:%x,%x,%x\n",oob[0],oob[1],oob[2],ecc[0],ecc[1],ecc[2]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>static int NF_WritePage(unsigned int block,unsigned int page,char *buffer)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int blockpage=(block&lt;&lt;5)+page;</p>
<p>&nbsp;&nbsp;&nbsp; char *pbuf=buffer;</p>
<p>&nbsp;&nbsp;&nbsp; oobbuf[16]={0xff};</p>
<p>&nbsp;&nbsp;&nbsp; NF_RSRECC();</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x00);</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(blockpage&amp;0xff);</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockpage&gt;&gt;8)&amp;0xff);</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockpage&gt;&gt;16)&amp;0xff);</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;512;i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WRDATA(*pbuf++);</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; oobbuf[0]=rNFECC0;</p>
<p>&nbsp;&nbsp;&nbsp; oobbuf[1]=rNFECC1;</p>
<p>&nbsp;&nbsp;&nbsp; oobbuf[2]=rNFECC2;</p>
<p>&nbsp;&nbsp;&nbsp; oobbuf[5]=0xff;</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;16;i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WRDATA(oobbuf);</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x10);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Write 2nd command;</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++);&nbsp; //tWB=100ns;</p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x70);&nbsp;&nbsp;&nbsp; //read status command;</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;3;i++);</p>
<p>&nbsp;&nbsp;&nbsp; if(NF_RDDATA()&amp;0x1)&nbsp;&nbsp;&nbsp;&nbsp; //write error</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_MarkBadBlock(block);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</p>
<p>&nbsp;&nbsp;&nbsp; }else{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p><br>static int NF_EraseBlock(U32 block)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; U32 blockPage=(block&lt;&lt;5);</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>#if BAD_CHECK//坏块校验</p>
<p>&nbsp;&nbsp;&nbsp; if(NF_IsBadBlock(block))</p>
<p>&nbsp;&nbsp;&nbsp; return 0;</p>
<p>#endif</p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();//NF的CE（片选）拉低</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x60);&nbsp;&nbsp; // Erase one block 1st command</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(blockPage&amp;0xff);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 块擦除只针对页</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;8)&amp;0xff);&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;16)&amp;0xff);</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0xd0);&nbsp;&nbsp; // Erase one blcok 2nd command</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++); //wait tWB(100ns)//??????</p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();&nbsp;&nbsp;&nbsp; // Wait tBERS max 3ms.</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x70);&nbsp;&nbsp; // Read status command</p>
<p>&nbsp;&nbsp;&nbsp; if (NF_RDDATA()&amp;0x1) // Erase error</p>
<p>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp; Uart_Printf("[ERASE_ERROR:block#=%d]\n",block);</p>
<p>&nbsp;&nbsp;&nbsp; NF_MarkBadBlock(block);</p>
<p>&nbsp;&nbsp;&nbsp; return 0;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; else </p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();////NF的CE（片选）拉高</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p><br>NAND设备存在坏块，为和上层文件系统接口，NAND设备的驱动程序必须给文件系统提供一个可靠的存储空间，这就需要ECC（Error Corection Code）校验，坏块标注、地址映射等一系列的技术手段来达到可靠存储目的。</p>
<p>&nbsp;&nbsp;&nbsp; SSFDC软件规范中，详细定义了如何利用NAND设备每个页中的冗余信息来实现上述功能。这个软件规范中，很重要的一个概念就是块的逻辑地址，它将在物理上可能不连续、不可靠的空间分配编号，为他们在逻辑空间上给系统文件提供一个连续可靠的存储空间。</p>
<p>表3给出了SSFDC规范中逻辑地址的标注方法。在系统初始化的时候，驱动程序先将所有的块扫描一遍，读出他们所对应的逻辑地址，并把逻辑地址和虚拟地址的映射表建好。系统运行时，驱动程序通过查询映射表，找到需要访问的逻辑地址所对应的物理地址然后进行数据读写。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表3 冗余字节定义</p>
<p>字节序号</p>
<p>内容</p>
<p>字节序号</p>
<p>内容</p>
<p>512</p>
<p>用户定义数据</p>
<p>520</p>
<p>后256BECC校验和</p>
<p>513</p>
<p>521</p>
<p>514</p>
<p>522</p>
<p>515</p>
<p>523</p>
<p>块逻辑地址</p>
<p>516</p>
<p>数据状态</p>
<p>524</p>
<p>517</p>
<p>块状态</p>
<p>525</p>
<p>前256BECC校验和</p>
<p>518</p>
<p>块逻辑地址1</p>
<p>526</p>
<p>519</p>
<p>527</p>
<p><br>表4给出了块逻辑地址的存放格式，LA表示逻辑地址，P代表偶校验位。逻辑地址只有10bit，代表只有1024bit的寻址空间。而SSFDC规范将NAND设备分成了多个zone，每个zone 内有1024块，但这物理上的1024块映射到逻辑空间只有1000块，其他的24块就作为备份使用，当有坏块存在时，就可以以备份块将其替换。</p>
<p>表4&nbsp; 逻辑地址格式</p>
<p>D7</p>
<p>D6</p>
<p>D5</p>
<p>D4</p>
<p>D3</p>
<p>D2</p>
<p>D1</p>
<p>D0</p>
<p>&nbsp; </p>
<p>&nbsp; </p>
<p>0</p>
<p>0</p>
<p>0</p>
<p>1</p>
<p>0</p>
<p>LA9</p>
<p>LA8</p>
<p>LA7</p>
<p>第518&nbsp;&nbsp; 523字节</p>
<p>&nbsp; </p>
<p>LA6</p>
<p>LA5</p>
<p>LA4</p>
<p>LA3</p>
<p>LA2</p>
<p>LA1</p>
<p>LA0</p>
<p>P</p>
<p>第519&nbsp;&nbsp; 524字节</p>
<p>&lt;!--[if !supportMisalignedColumns]--&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&lt;!--[endif]--&gt;&nbsp; </p>
<p>有了以上的软件规范，就可以对NAND设备写出较标准的ECC校验，并可以编写检测坏块、标记坏块、建立物理地址和逻辑地址的映射表的程序了。</p>
<p><br>static int NF_IsBadBlock(unsigned int&nbsp; block)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int blockPage;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned char data;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; blockPage=(block&lt;&lt;5);&nbsp;&nbsp; // For 2'nd cycle I/O[7:5] </p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x50);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Spare array read command</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(517&amp;0xf);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Read the mark of bad block in spare array(M addr=5) </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(blockPage&amp;0xff);&nbsp;&nbsp;&nbsp; // The mark of bad block is in 0 page</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;8)&amp;0xff);&nbsp;&nbsp; // For block number A[24:17]</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;16)&amp;0xff);&nbsp; // For block number A[25]</p>
<p>&nbsp;&nbsp; for(i=0;i&lt;10;i++);&nbsp;&nbsp; // wait tWB(100ns) //?????</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();&nbsp;&nbsp;&nbsp; // Wait tR(max 12us)</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; data=NF_RDDATA();</p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_H();&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; if(data!=0xff)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("[block %d has been marked as a bad block(%x)]\n",block,data);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; else</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>static int NF_MarkBadBlock(U32 block)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int blockPage=(block&lt;&lt;5);</p>
<p>&nbsp;&nbsp;&nbsp; seBuf[0]=0xff;</p>
<p>&nbsp;&nbsp;&nbsp; seBuf[1]=0xff;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; seBuf[2]=0xff;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; seBuf[5]=0x44;&nbsp;&nbsp; // Bad blcok mark=0</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L(); </p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x50);&nbsp;&nbsp; //read OOB</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x80);&nbsp;&nbsp; // Write 1st command</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(0x0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // The mark of bad block is </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(blockPage&amp;0xff);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // marked 5th spare array </p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;8)&amp;0xff);&nbsp;&nbsp; // in the 1st page.</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;16)&amp;0xff);&nbsp; //</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;16;i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp; NF_WRDATA(seBuf);&nbsp;&nbsp;&nbsp; // Write spare array</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x10);&nbsp;&nbsp; // Write 2nd command</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++);&nbsp; //tWB = 100ns. </p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Wait tPROG(200~500us)</p>
<p>&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x70);</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;3;i++);&nbsp; //twhr=60ns</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; if (NF_RDDATA()&amp;0x1) // Spare arrray write error</p>
<p>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("[Program error is occurred but ignored]\n");</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; else </p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; printf("[block #%d is marked as a bad block]\n",block);</p>
<p>&nbsp;&nbsp;&nbsp; return 1;</p>
<p>}</p>
<p>int search_logic_block(void)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立物理地址到逻辑地址的映射表</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; unsigned int block,i,blockPage,logic_no,zone,zone_i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned char oob[16];</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;BLOCK_NR;i++)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //初始化全局变量</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lg2ph=space_block=0xffff;</p>
<p>&nbsp;&nbsp;&nbsp; logic_number=0;</p>
<p>&nbsp;&nbsp;&nbsp; space_nr=0;</p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();</p>
<p>&nbsp;&nbsp;&nbsp; zone=BLOCK_NR/1024;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //确定NAND设备中zone的个数</p>
<p>&nbsp;&nbsp;&nbsp; for(zone_i=0;zone_i&lt;zone;zone_i++)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //搜索每个zone 内逻辑地址和物理地址的映射关系</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(block=0;block&lt;1024;block++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blockPage=((block+zone_i*1024)&lt;&lt;BLOCK_ADDRERSS_SHIFT);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WATIRB();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //等待R/B#信号有效</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_CMD(0x50);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 读取每个block内部第0个Page内冗余的16个字节</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR(0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Column 0</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR(blockPage&amp;0xff);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;8)&amp;0xff);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Block &amp; page num.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_ADDR((blockPage&gt;&gt;16)&amp;0xff);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WATIRB();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //等待R/B#信号有效</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(i=0;i&lt;16;i++)&nbsp; se=NF_RDDATA();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Write spare array</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NF_WATIRB();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(oob[5]!=0xff)[q8]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //检测是否存在坏块</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk("\n\rphysic block %d is bad block\n\r",block);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(oob[7]!=se[12][q9] )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk("block address1:%d!=block address2 %d\n\r",oob[7],oob[12]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if((oob[6][q10] &amp;0xf8)==0x10)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //计算该block对应的逻辑地址</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logic_no=((0x7&amp;oob[6])&lt;&lt;7)+(se[7]&gt;&gt;1)+zone_i*1000;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(lg2ph[logic_no]!=0xffff)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //说明有2个block拥有相同的逻辑地址</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk("physical block %d and block %d have the same logic number %d\n",lg2ph[logic_no],block,logic_no);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else lg2ph[logic_no]=block;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //将该block的逻辑地址关系记入lg2ph表</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logic_number++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(oob[7]==0xff)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //说明该block尚未编号</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {space_block[space_nr]=block;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; space_nr++;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; printk("there are totally %d logic blocks\n\r",logic_number);</p>
<p>&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp; return logic_number;</p>
<p>}</p>
<p>这段代码的主要作用就是产生数组lg2ph[],这个数组的含义就是&#8220;块物理地址=lg2ph[逻辑地址]&#8221;。</p>
<p><br>static unsigned short NF_CheckId(void)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned short id;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0x90);</p>
<p>&nbsp;&nbsp;&nbsp; NF_ADDR(0x0);</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++); //wait tWB(100ns)////?????</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; id=NF_RDDATA()&lt;&lt;8;&nbsp; // Maker code(K9S1208V:0xec)</p>
<p>&nbsp;&nbsp;&nbsp; id|=NF_RDDATA();&nbsp;&nbsp;&nbsp; // Devide code(K9S1208V:0x76)</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; return id;</p>
<p>}</p>
<p>static void NF_Reset(void)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int i;</p>
<p>&nbsp;&nbsp;&nbsp; unsigned short id;</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_L();</p>
<p>&nbsp;&nbsp;&nbsp; NF_CMD(0xFF);&nbsp;&nbsp; //reset command</p>
<p>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;10;i++);&nbsp; //tWB = 100ns. </p>
<p>&nbsp;&nbsp;&nbsp; NF_WAITRB();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //wait 200~500us;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_nFCE_H();</p>
<p>}</p>
<p><br>static void NF_Init(void)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; rNFCONF=(1&lt;&lt;15)|(1&lt;&lt;14)|(1&lt;&lt;13)|(1&lt;&lt;12)|(1&lt;&lt;11)|(TACLS&lt;&lt;8)|(TWRPH0&lt;&lt;4)|(TWRPH1&lt;&lt;0); </p>
<p>&nbsp;&nbsp;&nbsp; // 1&nbsp; 1&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; 1,&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xxx,&nbsp; r xxx,&nbsp;&nbsp; r xxx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; // En 512B 4step ECCR nFCE=H tACLS&nbsp;&nbsp; tWRPH0&nbsp;&nbsp; tWRPH1</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; NF_Reset();</p>
<p>}<br></p>
<img src ="http://www.cppblog.com/tiger/aggbug/115610.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2010-05-17 17:37 <a href="http://www.cppblog.com/tiger/archive/2010/05/17/115610.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux MTD 源代码</title><link>http://www.cppblog.com/tiger/archive/2010/05/11/115076.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Tue, 11 May 2010 02:16:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2010/05/11/115076.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/115076.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2010/05/11/115076.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/115076.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/115076.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;Linux MTD 源代码目录专有名词：.... 7Linux MTD介绍：... 8设备层和原始设备层的函数调用关系（红色部分需要我们实现）：... 9NOR型Flash芯片驱动与MTD原始设备... 10NAND和NOR的比较... 11源码分析... 14头文件分析... 14mtd.h. 14MTD_CHAR_MAJOR. 14...&nbsp;&nbsp;<a href='http://www.cppblog.com/tiger/archive/2010/05/11/115076.html'>阅读全文</a><img src ="http://www.cppblog.com/tiger/aggbug/115076.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2010-05-11 10:16 <a href="http://www.cppblog.com/tiger/archive/2010/05/11/115076.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符编码方式介绍</title><link>http://www.cppblog.com/tiger/archive/2009/11/12/100806.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Thu, 12 Nov 2009 08:23:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2009/11/12/100806.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/100806.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2009/11/12/100806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/100806.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/100806.html</trackback:ping><description><![CDATA[<p><strong>1. ASCII码</strong></p>
<p>我们知道，在计算机内部，所有的信息最终都表示为一个二进制的字符串。每一个二进制位（bit）有0和1两种状态，因此八个二进制位就可以组合出256种状态，这被称为一个字节（byte）。也就是说，一个字节一共可以用来表示256种不同的状态，每一个状态对应一个符号，就是256个符号，从0000000到11111111。</p>
<p>上个世纪60年代，美国制定了一套字符编码，对英语字符与二进制位之间的关系，做了统一规定。这被称为ASCII码，一直沿用至今。</p>
<p>ASCII码一共规定了128个字符的编码，比如空格&#8220;SPACE&#8221;是32（二进制00100000），大写的字母A是65（二进制01000001）。这128个符号（包括32个不能打印出来的控制符号），只占用了一个字节的后面7位，最前面的1位统一规定为0。</p>
<p><strong>2、非ASCII编码</strong></p>
<p>英语用128个符号编码就够了，但是用来表示其他语言，128个符号是不够的。比如，在法语中，字母上方有注音符号，它就无法用ASCII码表示。于是，一些欧洲国家就决定，利用字节中闲置的最高位编入新的符号。比如，法语中的&#233;的编码为130（二进制10000010）。这样一来，这些欧洲国家使用的编码体系，可以表示最多256个符号。</p>
<p>但是，这里又出现了新的问题。不同的国家有不同的字母，因此，哪怕它们都使用256个符号的编码方式，代表的字母却不一样。比如，130在法语编码中代表了&#233;，在希伯来语编码中却代表了字母Gimel (ג)，在俄语编码中又会代表另一个符号。但是不管怎样，所有这些编码方式中，0—127表示的符号是一样的，不一样的只是128—255的这一段。</p>
<p>至于亚洲国家的文字，使用的符号就更多了，汉字就多达10万左右。一个字节只能表示256种符号，肯定是不够的，就必须使用多个字节表达一个符号。比如，简体中文常见的编码方式是GB2312，使用两个字节表示一个汉字，所以理论上最多可以表示256x256=65536个符号。</p>
<p>中文编码的问题需要专文讨论，这篇笔记不涉及。这里只指出，虽然都是用多个字节表示一个符号，但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的。</p>
<p><strong>3.Unicode</strong></p>
<p>正如上一节所说，世界上存在着多种编码方式，同一个二进制数字可以被解释成不同的符号。因此，要想打开一个文本文件，就必须知道它的编码方式，否则用错误的编码方式解读，就会出现乱码。为什么电子邮件常常出现乱码？就是因为发信人和收信人使用的编码方式不一样。</p>
<p>可以想象，如果有一种编码，将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码，那么乱码问题就会消失。这就是Unicode，就像它的名字都表示的，这是一种所有符号的编码。</p>
<p>Unicode当然是一个很大的集合，现在的规模可以容纳100多万个符号。每个符号的编码都不一样，比如，U+0639表示阿拉伯字母Ain，U+0041表示英语的大写字母A，U+4E25表示汉字&#8220;严&#8221;。具体的符号对应表，可以查询<a href="http://www.unicode.org/" target=_blank><font color=#336699><u>unicode.org</u></font></a>，或者专门的<a href="http://www.chi2ko.com/tool/CJK.htm" target=_blank><font color=#336699><u>汉字对应表</u></font></a>。</p>
<p><strong>4. Unicode的问题</strong></p>
<p>需要注意的是，Unicode只是一个符号集，它只规定了符号的二进制代码，却没有规定这个二进制代码应该如何存储。</p>
<p>比如，汉字&#8220;严&#8221;的unicode是十六进制数4E25，转换成二进制数足足有15位（100111000100101），也就是说这个符号的表示至少需要2个字节。表示其他更大的符号，可能需要3个字节或者4个字节，甚至更多。</p>
<p>这里就有两个严重的问题，第一个问题是，如何才能区别unicode和ascii？计算机怎么知道三个字节表示一个符号，而不是分别表示三个符号呢？第二个问题是，我们已经知道，英文字母只用一个字节表示就够了，如果unicode统一规定，每个符号用三个或四个字节表示，那么每个英文字母前都必然有二到三个字节是0，这对于存储来说是极大的浪费，文本文件的大小会因此大出二三倍，这是无法接受的。</p>
<p>它们造成的结果是：1）出现了unicode的多种存储方式，也就是说有许多种不同的二进制格式，可以用来表示unicode。2）unicode在很长一段时间内无法推广，直到互联网的出现。</p>
<p><strong>5.UTF-8</strong></p>
<p>互联网的普及，强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32，不过在互联网上基本不用。<strong>重复一遍，这里的关系是，UTF-8是Unicode的实现方式之一。</strong></p>
<p>UTF-8最大的一个特点，就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号，根据不同的符号而变化字节长度。</p>
<p>UTF-8的编码规则很简单，只有二条：</p>
<p>1）对于单字节的符号，字节的第一位设为0，后面7位为这个符号的unicode码。因此对于英语字母，UTF-8编码和ASCII码是相同的。</p>
<p>2）对于n字节的符号（n&gt;1），第一个字节的前n位都设为1，第n+1位设为0，后面字节的前两位一律设为10。剩下的没有提及的二进制位，全部为这个符号的unicode码。</p>
<p>下表总结了编码规则，字母x表示可用编码的位。</p>
<blockquote>
<div>
<p>Unicode符号范围 | UTF-8编码方式<br>(十六进制) | （二进制）<br>--------------------+---------------------------------------------<br>0000 0000-0000 007F | 0xxxxxxx<br>0000 0080-0000 07FF | 110xxxxx 10xxxxxx<br>0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx<br>0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</p>
</div>
</blockquote>
<p>下面，还是以汉字&#8220;严&#8221;为例，演示如何实现UTF-8编码。</p>
<p>已知&#8220;严&#8221;的unicode是4E25（100111000100101），根据上表，可以发现4E25处在第三行的范围内（0000 0800-0000 FFFF），因此&#8220;严&#8221;的UTF-8编码需要三个字节，即格式是&#8220;1110xxxx 10xxxxxx 10xxxxxx&#8221;。然后，从&#8220;严&#8221;的最后一个二进制位开始，依次从后向前填入格式中的x，多出的位补0。这样就得到了，&#8220;严&#8221;的UTF-8编码是&#8220;11100100 10111000 10100101&#8221;，转换成十六进制就是E4B8A5。</p>
<p><strong>6. Unicode与UTF-8之间的转换</strong></p>
<p>通过上一节的例子，可以看到&#8220;严&#8221;的Unicode码是4E25，UTF-8编码是E4B8A5，两者是不一样的。它们之间的转换可以通过程序实现。</p>
<p>在Windows平台下，有一个最简单的转化方法，就是使用内置的记事本小程序Notepad.exe。打开文件后，点击&#8220;文件&#8221;菜单中的&#8220;另存为&#8221;命令，会跳出一个对话框，在最底部有一个&#8220;编码&#8221;的下拉条。</p>
<p>&#160;</p>
<p>里面有四个选项：ANSI，Unicode，Unicode big endian 和 UTF-8。</p>
<p>1）ANSI是默认的编码方式。对于英文文件是ASCII编码，对于简体中文文件是GB2312编码（只针对Windows简体中文版，如果是繁体中文版会采用Big5码）。</p>
<p>2）Unicode编码指的是UCS-2编码方式，即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。</p>
<p>3）Unicode big endian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。</p>
<p>4）UTF-8编码，也就是上一节谈到的编码方法。</p>
<p>选择完&#8221;编码方式&#8220;后，点击&#8221;保存&#8220;按钮，文件的编码方式就立刻转换好了。</p>
<p><strong>7. Little endian和Big endian</strong></p>
<p>上一节已经提到，Unicode码可以采用UCS-2格式直接存储。以汉字&#8221;严&#8220;为例，Unicode码是4E25，需要用两个字节存储，一个字节是4E，另一个字节是25。存储的时候，4E在前，25在后，就是Big endian方式；25在前，4E在后，就是Little endian方式。</p>
<p>这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中，小人国里爆发了内战，战争起因是人们争论，吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情，前后爆发了六次战争，一个皇帝送了命，另一个皇帝丢了王位。</p>
<p>因此，第一个字节在前，就是&#8221;大头方式&#8220;（Big endian），第二个字节在前就是&#8221;小头方式&#8220;（Little endian）。</p>
<p>那么很自然的，就会出现一个问题：计算机怎么知道某一个文件到底采用哪一种方式编码？</p>
<p>Unicode规范中定义，每一个文件的最前面分别加入一个表示编码顺序的字符，这个字符的名字叫做&#8221;零宽度非换行空格&#8220;（ZERO WIDTH NO-BREAK SPACE），用FEFF表示。这正好是两个字节，而且FF比FE大1。</p>
<p>如果一个文本文件的头两个字节是FE FF，就表示该文件采用大头方式；如果头两个字节是FF FE，就表示该文件采用小头方式。</p>
<p><strong>8. 实例</strong></p>
<p>下面，举一个实例。</p>
<p>打开&#8221;记事本&#8220;程序Notepad.exe，新建一个文本文件，内容就是一个&#8221;严&#8220;字，依次采用ANSI，Unicode，Unicode big endian 和 UTF-8编码方式保存。</p>
<p>然后，用文本编辑软件<a href="http://www.google.cn/search?aq=t&amp;oq=UltraEdit&amp;complete=1&amp;hl=zh-CN&amp;newwindow=1&amp;rlz=1B3GGGL_zh-CNCN216CN216&amp;q=ultraedit+%E4%B8%8B%E8%BD%BD&amp;btnG=Google+%E6%90%9C%E7%B4%A2&amp;meta=" target=_blank><font color=#336699><u>UltraEdit中</u></font></a>的&#8221;十六进制功能&#8220;，观察该文件的内部编码方式。</p>
<p>1）ANSI：文件的编码就是两个字节&#8220;D1 CF&#8221;，这正是&#8220;严&#8221;的GB2312编码，这也暗示GB2312是采用大头方式存储的。</p>
<p>2）Unicode：编码是四个字节&#8220;FF FE 25 4E&#8221;，其中&#8220;FF FE&#8221;表明是小头方式存储，真正的编码是4E25。</p>
<p>3）Unicode big endian：编码是四个字节&#8220;FE FF 4E 25&#8221;，其中&#8220;FE FF&#8221;表明是大头方式存储。</p>
<p>4）UTF-8：编码是六个字节&#8220;EF BB BF E4 B8 A5&#8221;，前三个字节&#8220;EF BB BF&#8221;表示这是UTF-8编码，后三个&#8220;E4B8A5&#8221;就是&#8220;严&#8221;的具体编码，它的存储顺序与编码顺序是一致的。</p>
<img src ="http://www.cppblog.com/tiger/aggbug/100806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2009-11-12 16:23 <a href="http://www.cppblog.com/tiger/archive/2009/11/12/100806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Singleton)</title><link>http://www.cppblog.com/tiger/archive/2007/04/16/22003.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Mon, 16 Apr 2007 02:57:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/16/22003.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/22003.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/16/22003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/22003.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/22003.html</trackback:ping><description><![CDATA[<p>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CSingleton<br>{<br>private:<br>&nbsp;CSingleton();</p>
<p>public:<br>&nbsp;virtual ~CSingleton();</p>
<p>public:<br>&nbsp;static CSingleton *GetInstance();</p>
<p>private:<br>&nbsp;static CSingleton *s_pSingleton;</p>
<p>};</p>
<p>CSingleton *CSingleton::s_pSingleton = NULL;</p>
<p>CSingleton::CSingleton()<br>{<br>}</p>
<p>CSingleton::~CSingleton()<br>{<br>&nbsp;if(CSingleton::s_pSingleton != NULL)<br>&nbsp;{<br>&nbsp;&nbsp;delete CSingleton::s_pSingleton;<br>&nbsp;&nbsp;CSingleton::s_pSingleton = NULL;<br>&nbsp;}<br>}</p>
<p>CSingleton *CSingleton::GetInstance()<br>{<br>&nbsp;if(CSingleton::s_pSingleton == NULL)<br>&nbsp;{<br>&nbsp;&nbsp;CSingleton::s_pSingleton =&nbsp; new CSingleton();<br>&nbsp;}</p>
<p>&nbsp;return CSingleton::s_pSingleton;<br>}</p>
<p>void main()<br>{<br>&nbsp;for(int i = 0; i &lt; 10; i++)<br>&nbsp;{<br>&nbsp;&nbsp;CSingleton *pSingleton = CSingleton::GetInstance();<br>&nbsp;&nbsp;cout&lt;&lt;i&lt;&lt;"\t"&lt;&lt;pSingleton&lt;&lt;endl;<br>&nbsp;}<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/22003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-16 10:57 <a href="http://www.cppblog.com/tiger/archive/2007/04/16/22003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Decorator)</title><link>http://www.cppblog.com/tiger/archive/2007/04/14/21865.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Sat, 14 Apr 2007 07:06:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/14/21865.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21865.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/14/21865.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21865.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21865.html</trackback:ping><description><![CDATA[<p>// Component.h: interface for the CComponent class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_COMPONENT_H__9FC0172F_E325_422F_82D4_719D1E4DB8A6__INCLUDED_)<br>#define AFX_COMPONENT_H__9FC0172F_E325_422F_82D4_719D1E4DB8A6__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CComponent&nbsp; <br>{<br>public:<br>&nbsp;CComponent();<br>&nbsp;virtual ~CComponent();</p>
<p>public:<br>&nbsp;virtual void Func(void) = 0;</p>
<p>};</p>
<p>#endif // !defined(AFX_COMPONENT_H__9FC0172F_E325_422F_82D4_719D1E4DB8A6__INCLUDED_)<br><br>// Component.cpp: implementation of the CComponent class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Component.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CComponent::CComponent()<br>{</p>
<p>}</p>
<p>CComponent::~CComponent()<br>{</p>
<p>}<br><br><br>// Decorator.h: interface for the CDecorator class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_DECORATOR_H__D1305BFE_4F87_4D1F_A661_8496D9C28CFE__INCLUDED_)<br>#define AFX_DECORATOR_H__D1305BFE_4F87_4D1F_A661_8496D9C28CFE__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>#include "Component.h"</p>
<p>class CDecorator : public CComponent&nbsp; <br>{<br>public:<br>&nbsp;CDecorator(CComponent *pComponent);<br>&nbsp;virtual ~CDecorator();</p>
<p>protected:<br>&nbsp;CComponent *m_pComponent;</p>
<p>};</p>
<p>class CConcreteDecorator : public CDecorator<br>{<br>public:<br>&nbsp;CConcreteDecorator(CComponent *pComponent);<br>&nbsp;virtual ~CConcreteDecorator();</p>
<p>public:<br>&nbsp;void Func(void);</p>
<p>private:<br>&nbsp;void AdditionFunc(void);<br>};</p>
<p>#endif // !defined(AFX_DECORATOR_H__D1305BFE_4F87_4D1F_A661_8496D9C28CFE__INCLUDED_)<br><br>// Decorator.cpp: implementation of the CDecorator class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Decorator.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CDecorator::CDecorator(CComponent *pComponent)<br>{<br>&nbsp;m_pComponent = pComponent;<br>}</p>
<p>CDecorator::~CDecorator()<br>{</p>
<p>}</p>
<p>//////////////////////////////////////////////////////////////////////<br>CConcreteDecorator::CConcreteDecorator(CComponent *pComponent)<br>&nbsp;: CDecorator(pComponent)<br>{<br>}</p>
<p>CConcreteDecorator::~CConcreteDecorator()<br>{<br>}</p>
<p>void CConcreteDecorator::Func()<br>{<br>&nbsp;m_pComponent-&gt;Func();<br>&nbsp;AdditionFunc();<br>}</p>
<p>void CConcreteDecorator::AdditionFunc()<br>{<br>&nbsp;cout&lt;&lt;"CConcreteDecorator::AdditionFunc"&lt;&lt;endl;<br>}<br>//////////////////////////////////////////////////////////////////////<br><br>//main.cpp<br>#include "ConcreteComponent.h"<br>#include "Decorator.h"</p>
<p>void main()<br>{<br>&nbsp;CComponent *pComponent = new CConcreteComponent();<br>&nbsp;CComponent *pDecorator = new CConcreteDecorator(pComponent);<br>&nbsp;pDecorator-&gt;Func();<br>&nbsp;delete pDecorator;<br>&nbsp;delete pComponent;<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/21865.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-14 15:06 <a href="http://www.cppblog.com/tiger/archive/2007/04/14/21865.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Strategy)</title><link>http://www.cppblog.com/tiger/archive/2007/04/14/21864.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Sat, 14 Apr 2007 07:04:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/14/21864.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21864.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/14/21864.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21864.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21864.html</trackback:ping><description><![CDATA[<p>// Strategy.h: interface for the CStrategy class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_STRATEGY_H__174A97DA_5D7A_4183_8361_4B6471820E62__INCLUDED_)<br>#define AFX_STRATEGY_H__174A97DA_5D7A_4183_8361_4B6471820E62__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CStrategy&nbsp; <br>{<br>public:<br>&nbsp;CStrategy();<br>&nbsp;virtual ~CStrategy();</p>
<p>public:<br>&nbsp;virtual void AlgrithmInterface(void) = 0;</p>
<p>};</p>
<p>class CConcreteStrategyA : public CStrategy<br>{<br>public:<br>&nbsp;CConcreteStrategyA();<br>&nbsp;virtual ~CConcreteStrategyA();</p>
<p>public:<br>&nbsp;void AlgrithmInterface(void);<br>};</p>
<p>class CConcreteStrategyB : public CStrategy<br>{<br>public:<br>&nbsp;CConcreteStrategyB();<br>&nbsp;virtual ~CConcreteStrategyB();</p>
<p>public:<br>&nbsp;void AlgrithmInterface(void);<br>};</p>
<p>#endif // !defined(AFX_STRATEGY_H__174A97DA_5D7A_4183_8361_4B6471820E62__INCLUDED_)<br><br>// Strategy.cpp: implementation of the CStrategy class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Strategy.h"</p>
<p>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CStrategy::CStrategy()<br>{</p>
<p>}</p>
<p>CStrategy::~CStrategy()<br>{</p>
<p>}</p>
<p>//////////////////////////////////////////////////////////////////////<br>CConcreteStrategyA::CConcreteStrategyA()<br>{</p>
<p>}</p>
<p>CConcreteStrategyA::~CConcreteStrategyA()<br>{</p>
<p>}</p>
<p>void CConcreteStrategyA::AlgrithmInterface()<br>{<br>&nbsp;cout&lt;&lt;"CConcreteStrategyA::AlgrithmInterface"&lt;&lt;endl;<br>}<br>//////////////////////////////////////////////////////////////////////</p>
<p>//////////////////////////////////////////////////////////////////////<br>CConcreteStrategyB::CConcreteStrategyB()<br>{</p>
<p>}</p>
<p>CConcreteStrategyB::~CConcreteStrategyB()<br>{</p>
<p>}</p>
<p>void CConcreteStrategyB::AlgrithmInterface()<br>{<br>&nbsp;cout&lt;&lt;"CConcreteStrategyB::AlgrithmInterface"&lt;&lt;endl;<br>}<br>//////////////////////////////////////////////////////////////////////<br><br>// Context.h: interface for the CContext class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_CONTEXT_H__E299AB56_F88B_4134_83B5_DF6A5AFCADE4__INCLUDED_)<br>#define AFX_CONTEXT_H__E299AB56_F88B_4134_83B5_DF6A5AFCADE4__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CStrategy;</p>
<p>class CContext&nbsp; <br>{<br>public:<br>&nbsp;CContext(CStrategy *pStrategy);<br>&nbsp;virtual ~CContext();</p>
<p>public:<br>&nbsp;void DoAction(void);</p>
<p>private:<br>&nbsp;CStrategy *m_pStrategy;</p>
<p>};</p>
<p>#endif // !defined(AFX_CONTEXT_H__E299AB56_F88B_4134_83B5_DF6A5AFCADE4__INCLUDED_)<br><br>// Context.cpp: implementation of the CContext class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Context.h"<br>#include "Strategy.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CContext::CContext(CStrategy *pStrategy)<br>{<br>&nbsp;m_pStrategy = pStrategy;<br>}</p>
<p>CContext::~CContext()<br>{</p>
<p>}</p>
<p>void CContext::DoAction()<br>{<br>&nbsp;m_pStrategy-&gt;AlgrithmInterface();<br>}<br><br>//main.cpp<br>#include "Strategy.h"<br>#include "Context.h"</p>
<p>void main()<br>{<br>&nbsp;CStrategy *pStrategy = new CConcreteStrategyB();<br>&nbsp;CContext *pContext = new CContext(pStrategy);<br>&nbsp;pContext-&gt;DoAction();<br>&nbsp;delete pContext;<br>&nbsp;delete pStrategy;<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/21864.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-14 15:04 <a href="http://www.cppblog.com/tiger/archive/2007/04/14/21864.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Abstract Factory)</title><link>http://www.cppblog.com/tiger/archive/2007/04/13/21765.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Fri, 13 Apr 2007 01:52:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/13/21765.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21765.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/13/21765.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21765.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21765.html</trackback:ping><description><![CDATA[<p>// Factory.h: interface for the CFactory class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_FACTORY_H__EA4BC919_0C3C_445D_95D0_0B3BC1A79A46__INCLUDED_)<br>#define AFX_FACTORY_H__EA4BC919_0C3C_445D_95D0_0B3BC1A79A46__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CProduct;</p>
<p>class CFactory&nbsp; <br>{<br>public:<br>&nbsp;CFactory();<br>&nbsp;virtual ~CFactory();</p>
<p>public:<br>&nbsp;virtual CProduct *CreateA(void) = 0;<br>&nbsp;virtual CProduct *CreateB(void) = 0;</p>
<p>};</p>
<p>class CFactory1 : public CFactory<br>{<br>public:<br>&nbsp;CFactory1();<br>&nbsp;virtual ~CFactory1();</p>
<p>public:<br>&nbsp;CProduct *CreateA(void);<br>&nbsp;CProduct *CreateB(void);</p>
<p>};</p>
<p>class CFactory2 : public CFactory<br>{<br>public:<br>&nbsp;CFactory2();<br>&nbsp;virtual ~CFactory2();</p>
<p>public:<br>&nbsp;CProduct *CreateA(void);<br>&nbsp;CProduct *CreateB(void);</p>
<p>};</p>
<p>#endif // !defined(AFX_FACTORY_H__EA4BC919_0C3C_445D_95D0_0B3BC1A79A46__INCLUDED_)<br><br>// Factory.cpp: implementation of the CFactory class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Factory.h"</p>
<p>#include "Product.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CFactory::CFactory()<br>{</p>
<p>}</p>
<p>CFactory::~CFactory()<br>{</p>
<p>}</p>
<p>/////////////////////////////////////////////<br>CFactory1::CFactory1()<br>{</p>
<p>}</p>
<p>CFactory1::~CFactory1()<br>{</p>
<p>}</p>
<p>CProduct *CFactory1::CreateA()<br>{<br>&nbsp;return new CProductA1();<br>}</p>
<p>CProduct *CFactory1::CreateB()<br>{<br>&nbsp;return new CProductB1();<br>}<br>/////////////////////////////////////////////</p>
<p>/////////////////////////////////////////////<br>CFactory2::CFactory2()<br>{</p>
<p>}</p>
<p>CFactory2::~CFactory2()<br>{</p>
<p>}</p>
<p>CProduct *CFactory2::CreateA()<br>{<br>&nbsp;return new CProductA2();<br>}</p>
<p>CProduct *CFactory2::CreateB()<br>{<br>&nbsp;return new CProductB2();<br>}<br>/////////////////////////////////////////////<br><br>// Product.h: interface for the CProduct class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_PRODUCT_H__2A9147E1_2856_4F8A_9AD3_3BC7D3FD97E3__INCLUDED_)<br>#define AFX_PRODUCT_H__2A9147E1_2856_4F8A_9AD3_3BC7D3FD97E3__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CProduct&nbsp; <br>{<br>public:<br>&nbsp;CProduct();<br>&nbsp;virtual ~CProduct();</p>
<p>public:<br>&nbsp;virtual void Show(void) = 0;</p>
<p>};</p>
<p>class CProductA1 : public CProduct&nbsp; <br>{<br>public:<br>&nbsp;CProductA1();<br>&nbsp;virtual ~CProductA1();</p>
<p>public:<br>&nbsp;void Show(void);</p>
<p>};</p>
<p>class CProductB1 : public CProduct&nbsp; <br>{<br>public:<br>&nbsp;CProductB1();<br>&nbsp;virtual ~CProductB1();</p>
<p>public:<br>&nbsp;void Show(void);</p>
<p>};</p>
<p>class CProductA2 : public CProduct&nbsp; <br>{<br>public:<br>&nbsp;CProductA2();<br>&nbsp;virtual ~CProductA2();</p>
<p>public:<br>&nbsp;void Show(void);</p>
<p>};</p>
<p>class CProductB2 : public CProduct&nbsp; <br>{<br>public:<br>&nbsp;CProductB2();<br>&nbsp;virtual ~CProductB2();</p>
<p>public:<br>&nbsp;void Show(void);</p>
<p>};</p>
<p>#endif // !defined(AFX_PRODUCT_H__2A9147E1_2856_4F8A_9AD3_3BC7D3FD97E3__INCLUDED_)<br><br>// Product.cpp: implementation of the CProduct class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Product.h"<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CProduct::CProduct()<br>{</p>
<p>}</p>
<p>CProduct::~CProduct()<br>{</p>
<p>}</p>
<p>/////////////////////////////////////////////////////////<br>CProductA1::CProductA1()<br>{</p>
<p>}</p>
<p>CProductA1::~CProductA1()<br>{</p>
<p>}</p>
<p>void CProductA1::Show()<br>{<br>&nbsp;cout&lt;&lt;"CProductA1::Show"&lt;&lt;endl;<br>}<br>/////////////////////////////////////////////////////////</p>
<p>/////////////////////////////////////////////////////////<br>CProductB1::CProductB1()<br>{</p>
<p>}</p>
<p>CProductB1::~CProductB1()<br>{</p>
<p>}</p>
<p>void CProductB1::Show()<br>{<br>&nbsp;cout&lt;&lt;"CProductB1::Show"&lt;&lt;endl;<br>}<br>/////////////////////////////////////////////////////////</p>
<p>/////////////////////////////////////////////////////////<br>CProductA2::CProductA2()<br>{</p>
<p>}</p>
<p>CProductA2::~CProductA2()<br>{</p>
<p>}</p>
<p>void CProductA2::Show()<br>{<br>&nbsp;cout&lt;&lt;"CProductA2::Show"&lt;&lt;endl;<br>}<br>/////////////////////////////////////////////////////////</p>
<p>/////////////////////////////////////////////////////////<br>CProductB2::CProductB2()<br>{</p>
<p>}</p>
<p>CProductB2::~CProductB2()<br>{</p>
<p>}</p>
<p>void CProductB2::Show()<br>{<br>&nbsp;cout&lt;&lt;"CProductB2::Show"&lt;&lt;endl;<br>}<br>/////////////////////////////////////////////////////////<br><br>//main<br>#include "Factory.h"<br>#include "Product.h"</p>
<p>#define NULL 0</p>
<p>void main()<br>{<br>&nbsp;CFactory *pFactory = NULL;<br>&nbsp;CProduct *pProductA = NULL;<br>&nbsp;CProduct *pProductB = NULL;</p>
<p>&nbsp;for(int i = 0; i &lt; 2; i++)<br>&nbsp;{<br>&nbsp;&nbsp;if(i == 0)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;pFactory = new CFactory1();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;else<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;pFactory = new CFactory2();<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;pProductA = pFactory-&gt;CreateA();<br>&nbsp;&nbsp;pProductB = pFactory-&gt;CreateB();<br>&nbsp;&nbsp;pProductA-&gt;Show();<br>&nbsp;&nbsp;pProductB-&gt;Show();<br>&nbsp;&nbsp;delete pFactory;<br>&nbsp;&nbsp;delete pProductA;<br>&nbsp;&nbsp;delete pProductB;<br>&nbsp;}<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/21765.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-13 09:52 <a href="http://www.cppblog.com/tiger/archive/2007/04/13/21765.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Bridge)</title><link>http://www.cppblog.com/tiger/archive/2007/04/13/21764.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Fri, 13 Apr 2007 01:49:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/13/21764.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21764.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/13/21764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21764.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21764.html</trackback:ping><description><![CDATA[<p>// Abstraction.h: interface for the CAbstraction class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_ABSTRACTION_H__EEDB1B76_5760_4067_A2CB_95604AE082E4__INCLUDED_)<br>#define AFX_ABSTRACTION_H__EEDB1B76_5760_4067_A2CB_95604AE082E4__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CAbstractionImp;</p>
<p>class CAbstraction&nbsp; <br>{<br>public:<br>&nbsp;CAbstraction(CAbstractionImp *pAbstrationImp);<br>&nbsp;virtual ~CAbstraction();</p>
<p>protected:<br>&nbsp;CAbstraction();</p>
<p>public:<br>&nbsp;virtual void Func(void) = 0;</p>
<p>protected:<br>&nbsp;CAbstractionImp *m_pAbstractionImp;</p>
<p>};</p>
<p>class CRefindedAbstraction : public CAbstraction<br>{<br>public:<br>&nbsp;CRefindedAbstraction(CAbstractionImp *pAbstrationImp);<br>&nbsp;virtual ~CRefindedAbstraction();</p>
<p>public:<br>&nbsp;void Func(void);</p>
<p>};</p>
<p>#endif // !defined(AFX_ABSTRACTION_H__EEDB1B76_5760_4067_A2CB_95604AE082E4__INCLUDED_)<br><br>// Abstraction.cpp: implementation of the CAbstraction class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "Abstraction.h"</p>
<p>#include "AbstractionImp.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CAbstraction::CAbstraction()<br>{</p>
<p>}</p>
<p>CAbstraction::CAbstraction(CAbstractionImp *pAbstractionImp)<br>{<br>&nbsp;m_pAbstractionImp = pAbstractionImp;<br>}</p>
<p>CAbstraction::~CAbstraction()<br>{</p>
<p>}</p>
<p>/////////////////////////////////////////////////////////<br>CRefindedAbstraction::CRefindedAbstraction(CAbstractionImp *pAbstractionImp)<br>&nbsp;: CAbstraction(pAbstractionImp)<br>{<br>&nbsp;<br>}</p>
<p>CRefindedAbstraction::~CRefindedAbstraction()<br>{</p>
<p>}</p>
<p>void CRefindedAbstraction::Func()<br>{<br>&nbsp;m_pAbstractionImp-&gt;Func();<br>}<br><br>// AbstractionImp.h: interface for the CAbstractionImp class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_ABSTRACTIONIMP_H__29762645_3054_47C4_BF33_D8DACB518CD5__INCLUDED_)<br>#define AFX_ABSTRACTIONIMP_H__29762645_3054_47C4_BF33_D8DACB518CD5__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>class CAbstractionImp&nbsp; <br>{<br>public:<br>&nbsp;CAbstractionImp();<br>&nbsp;virtual ~CAbstractionImp();</p>
<p>public:<br>&nbsp;virtual void Func(void) = 0;</p>
<p>};</p>
<p>class CAbstractionImpA : public CAbstractionImp<br>{<br>public:<br>&nbsp;CAbstractionImpA();<br>&nbsp;virtual ~CAbstractionImpA();</p>
<p>public:<br>&nbsp;void Func(void);</p>
<p>};</p>
<p>class CAbstractionImpB : public CAbstractionImp<br>{<br>public:<br>&nbsp;CAbstractionImpB();<br>&nbsp;virtual ~CAbstractionImpB();</p>
<p>public:<br>&nbsp;void Func(void);</p>
<p>};</p>
<p>#endif // !defined(AFX_ABSTRACTIONIMP_H__29762645_3054_47C4_BF33_D8DACB518CD5__INCLUDED_)<br><br>// AbstractionImp.cpp: implementation of the CAbstractionImp class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "AbstractionImp.h"<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CAbstractionImp::CAbstractionImp()<br>{</p>
<p>}</p>
<p>CAbstractionImp::~CAbstractionImp()<br>{</p>
<p>}</p>
<p>/////////////////////////////////////////////<br>CAbstractionImpA::CAbstractionImpA()<br>{</p>
<p>}</p>
<p>CAbstractionImpA::~CAbstractionImpA()<br>{</p>
<p>}</p>
<p>void CAbstractionImpA::Func()<br>{<br>&nbsp;cout&lt;&lt;"CAbstractionImpA::Func"&lt;&lt;endl;<br>}</p>
<p>/////////////////////////////////////////////<br>CAbstractionImpB::CAbstractionImpB()<br>{</p>
<p>}</p>
<p>CAbstractionImpB::~CAbstractionImpB()<br>{</p>
<p>}</p>
<p>void CAbstractionImpB::Func()<br>{<br>&nbsp;cout&lt;&lt;"CAbstractionImpB::Func"&lt;&lt;endl;<br>}<br><br>//main<br>#include "Abstraction.h"<br>#include "AbstractionImp.h"</p>
<p>void main()<br>{<br>&nbsp;CAbstractionImp *pAbstractionImp = new CAbstractionImpB();<br>&nbsp;CAbstraction *pAbstraction = new CRefindedAbstraction(pAbstractionImp);<br>&nbsp;pAbstraction-&gt;Func();<br>&nbsp;delete pAbstraction;<br>&nbsp;delete pAbstractionImp;<br>}<br></p>
<img src ="http://www.cppblog.com/tiger/aggbug/21764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-13 09:49 <a href="http://www.cppblog.com/tiger/archive/2007/04/13/21764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Adapter)</title><link>http://www.cppblog.com/tiger/archive/2007/04/13/21763.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Fri, 13 Apr 2007 01:47:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/13/21763.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21763.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/13/21763.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21763.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21763.html</trackback:ping><description><![CDATA[<p>//类Adapter<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CAdaptee<br>{<br>public:<br>&nbsp;CAdaptee(){}<br>&nbsp;virtual ~CAdaptee(){}</p>
<p>public:<br>&nbsp;void Func1(){cout&lt;&lt;"CAdaptee"&lt;&lt;endl;}</p>
<p>};</p>
<p>class CTarget<br>{<br>public:<br>&nbsp;CTarget(){}<br>&nbsp;virtual ~CTarget(){}</p>
<p>public:<br>&nbsp;virtual void Func() = 0;</p>
<p>};</p>
<p>class CAdapter : public CTarget, private CAdaptee<br>{<br>public:<br>&nbsp;CAdapter(){}<br>&nbsp;virtual ~CAdapter(){}</p>
<p>public:<br>&nbsp;void Func(){Func1();}</p>
<p>};</p>
<p>void main()<br>{<br>&nbsp;CTarget *pTarget = new CAdapter();<br>&nbsp;pTarget-&gt;Func();<br>&nbsp;delete pTarget;<br>}</p>
<p><br>//对象Adapter(组合)<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CAdaptee<br>{<br>public:<br>&nbsp;CAdaptee(){}<br>&nbsp;virtual ~CAdaptee(){}</p>
<p>public:<br>&nbsp;void Func1(){cout&lt;&lt;"CAdaptee"&lt;&lt;endl;}</p>
<p>};</p>
<p>class CTarget<br>{<br>public:<br>&nbsp;CTarget(){}<br>&nbsp;virtual ~CTarget(){}</p>
<p>public:<br>&nbsp;virtual void Func() = 0;</p>
<p>};</p>
<p>class CAdapter : public CTarget<br>{<br>public:<br>&nbsp;CAdapter(){}<br>&nbsp;virtual ~CAdapter(){}</p>
<p>public:<br>&nbsp;void Func(){m_Adaptee.Func1();}</p>
<p>private:<br>&nbsp;CAdaptee m_Adaptee;</p>
<p>};</p>
<p>void main()<br>{<br>&nbsp;CTarget *pTarget = new CAdapter();<br>&nbsp;pTarget-&gt;Func();<br>&nbsp;delete pTarget;<br>}<br><br>//对象Adapter(参数)<br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CAdaptee<br>{<br>public:<br>&nbsp;CAdaptee(){}<br>&nbsp;virtual ~CAdaptee(){}</p>
<p>public:<br>&nbsp;void Func1(){cout&lt;&lt;"CAdaptee"&lt;&lt;endl;}</p>
<p>};</p>
<p>class CTarget<br>{<br>public:<br>&nbsp;CTarget(){}<br>&nbsp;virtual ~CTarget(){}</p>
<p>public:<br>&nbsp;virtual void Func() = 0;</p>
<p>};</p>
<p>class CAdapter : public CTarget<br>{<br>public:<br>&nbsp;CAdapter(CAdaptee *pAdaptee){m_pAdaptee = pAdaptee;}<br>&nbsp;virtual ~CAdapter(){}</p>
<p>public:<br>&nbsp;void Func(){m_pAdaptee-&gt;Func1();}</p>
<p>private:<br>&nbsp;CAdaptee *m_pAdaptee;</p>
<p>};</p>
<p>void main()<br>{<br>&nbsp;CAdaptee *pAdaptee = new CAdaptee();<br>&nbsp;CTarget *pTarget = new CAdapter(pAdaptee);<br>&nbsp;pTarget-&gt;Func();<br>&nbsp;delete pTarget;<br>&nbsp;delete pAdaptee;<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/21763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-13 09:47 <a href="http://www.cppblog.com/tiger/archive/2007/04/13/21763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式(Facade)</title><link>http://www.cppblog.com/tiger/archive/2007/04/13/21762.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Fri, 13 Apr 2007 01:44:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/04/13/21762.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/21762.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/04/13/21762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/21762.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/21762.html</trackback:ping><description><![CDATA[<p>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>class CSubSystem1<br>{<br>public:<br>&nbsp;CSubSystem1(){}<br>&nbsp;virtual ~CSubSystem1(){}</p>
<p>public:<br>&nbsp;void Func(){cout&lt;&lt;"SubSystem1!"&lt;&lt;endl;}</p>
<p>};</p>
<p>class CSubSystem2<br>{<br>public:<br>&nbsp;CSubSystem2(){}<br>&nbsp;virtual ~CSubSystem2(){}</p>
<p>public:<br>&nbsp;void Func(){cout&lt;&lt;"SubSystem2!"&lt;&lt;endl;}</p>
<p>};</p>
<p>class CFacade<br>{<br>public:<br>&nbsp;CFacade();<br>&nbsp;virtual ~CFacade();</p>
<p>public:<br>&nbsp;void Func();</p>
<p>private:<br>&nbsp;CSubSystem1 *m_pSubSystem1;<br>&nbsp;CSubSystem2 *m_pSubSystem2;</p>
<p>};</p>
<p>CFacade::CFacade()<br>{<br>&nbsp;m_pSubSystem1 = new CSubSystem1();<br>&nbsp;m_pSubSystem2 = new CSubSystem2();<br>}</p>
<p>CFacade::~CFacade()<br>{<br>&nbsp;delete m_pSubSystem1;<br>&nbsp;delete m_pSubSystem2;<br>}</p>
<p>void CFacade::Func()<br>{<br>&nbsp;m_pSubSystem1-&gt;Func();<br>&nbsp;m_pSubSystem2-&gt;Func();<br>}</p>
<p>void main()<br>{<br>&nbsp;CFacade *pFacade = new CFacade();<br>&nbsp;pFacade-&gt;Func();<br>&nbsp;delete pFacade;<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/21762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-04-13 09:44 <a href="http://www.cppblog.com/tiger/archive/2007/04/13/21762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用文件模拟实现C++输出流cout(函数指针)</title><link>http://www.cppblog.com/tiger/archive/2007/01/11/17526.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Thu, 11 Jan 2007 08:13:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/11/17526.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17526.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/11/17526.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17526.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17526.html</trackback:ping><description><![CDATA[<p>本例通过操作符重载、函数重载及函数指针等相关技术模拟实现C++输出流，具体代码如下：<br>//FileStream.h<br>// FileStream.h: interface for the CFileStream class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_FILESTREAM_H__38C31298_92F6_4273_B6CF_C9718B5B5D69__INCLUDED_)<br>#define AFX_FILESTREAM_H__38C31298_92F6_4273_B6CF_C9718B5B5D69__INCLUDED_</p>
<p>#if _MSC_VER &gt; 1000<br>#pragma once<br>#endif // _MSC_VER &gt; 1000</p>
<p>#include &lt;fstream.h&gt;</p>
<p>class CFileStream&nbsp; <br>{<br>public:<br>&nbsp;CFileStream();<br>&nbsp;virtual ~CFileStream();</p>
<p>public:<br>&nbsp;bool Open(char const* const pchFileName);&nbsp;&nbsp;//打开文件<br>&nbsp;bool Close(void);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//关闭文件</p>
<p>public:<br>&nbsp;CFileStream&amp; operator&lt;&lt;(int nData);<br>&nbsp;CFileStream&amp; operator&lt;&lt;(float fData);<br>&nbsp;CFileStream&amp; operator&lt;&lt;(char chData);</p>
<p>&nbsp;CFileStream&amp; operator&lt;&lt;(int (*Func)(const int &amp;nData));</p>
<p>private:<br>&nbsp;ofstream m_OutFile;</p>
<p>};</p>
<p>#endif // !defined(AFX_FILESTREAM_H__38C31298_92F6_4273_B6CF_C9718B5B5D69__INCLUDED_)<br><br><br>//FileStream.cpp<br>// FileStream.cpp: implementation of the CFileStream class.<br>//<br>//////////////////////////////////////////////////////////////////////</p>
<p>#include "FileStream.h"</p>
<p>//////////////////////////////////////////////////////////////////////<br>// Construction/Destruction<br>//////////////////////////////////////////////////////////////////////</p>
<p>CFileStream::CFileStream()<br>{</p>
<p>}</p>
<p>CFileStream::~CFileStream()<br>{</p>
<p>}</p>
<p>//打开文件<br>bool CFileStream::Open(char const* const pchFileName)<br>{<br>&nbsp;m_OutFile.open(pchFileName, ios::binary | ios::in, 0);<br>&nbsp;return true;<br>}</p>
<p>//关闭文件<br>bool CFileStream::Close()<br>{<br>&nbsp;m_OutFile.close();<br>&nbsp;return true;<br>}</p>
<p>CFileStream&amp; CFileStream::operator&lt;&lt;(int nData)<br>{<br>&nbsp;m_OutFile&lt;&lt;nData&lt;&lt;endl;<br>&nbsp;return *this;<br>}</p>
<p>CFileStream&amp; CFileStream::operator&lt;&lt;(float fData)<br>{<br>&nbsp;m_OutFile&lt;&lt;fData&lt;&lt;endl;<br>&nbsp;return *this;<br>}</p>
<p>CFileStream&amp; CFileStream::operator&lt;&lt;(char chData)<br>{<br>&nbsp;m_OutFile&lt;&lt;chData&lt;&lt;endl;<br>&nbsp;return *this;<br>}</p>
<p>CFileStream&amp; CFileStream::operator&lt;&lt;(int (*Func)(const int &amp;nData))<br>{<br>&nbsp;m_OutFile&lt;&lt;Func;<br>&nbsp;return *this;<br>}<br><br>//main<br>#include "FileStream.h"</p>
<p>int Func(const int &amp;nData)<br>{<br>&nbsp;return nData;<br>}</p>
<p>void main(void)<br>{<br>&nbsp;CFileStream file;<br>&nbsp;file.Open("C:\\tiger.txt");</p>
<p>&nbsp;file&lt;&lt;3&lt;&lt;4.5f&lt;&lt;'f';<br>&nbsp;file&lt;&lt;4&lt;&lt;6.78f&lt;&lt;'r';</p>
<p>&nbsp;file&lt;&lt;Func(100);</p>
<p>&nbsp;file.Close();</p>
<p>&nbsp;cout&lt;&lt;"文件写入完毕!"&lt;&lt;endl;<br>}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17526.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-11 16:13 <a href="http://www.cppblog.com/tiger/archive/2007/01/11/17526.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++技巧</title><link>http://www.cppblog.com/tiger/archive/2007/01/11/17514.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Thu, 11 Jan 2007 02:23:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/11/17514.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17514.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/11/17514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17514.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17514.html</trackback:ping><description><![CDATA[
		<p>1、获取数组大小：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>void main(void)<br />{<br />   float fArray[6] = {0.0f};<br />   cout&lt;&lt; sizeof(fArray) / sizeof(fArray[0])&lt;&lt;endl; <br />}<br />除系统内建数据类型可以按此方式外，自定义结构体也适用。<br /><br />2、结构体初始化：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>typedef struct tagTest<br />{<br /> int nData;<br /> float fData;<br /> char chData;<br />}TEST;</p>
		<p>void main(void)<br />{<br /> TEST test[3] = {{1, 1.1f, 'a'},<br />     {2, 2.2f, 'b'},<br />     {3, 3.3f, 'c'}};</p>
		<p> for(int i = 0; i &lt; sizeof(test) / sizeof(test[0]); i++)<br /> {<br />  cout&lt;&lt;i&lt;&lt;endl;<br />  cout&lt;&lt;"nData: "&lt;&lt;test[i].nData&lt;&lt;endl;<br />  cout&lt;&lt;"fData: "&lt;&lt;test[i].fData&lt;&lt;endl;<br />  cout&lt;&lt;"chData: "&lt;&lt;test[i].chData&lt;&lt;endl;<br />  cout&lt;&lt;endl;<br /> }<br />}<br /><br />3、调用构造函数初始化数组：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CTest<br />{<br />public:<br /> CTest(int nData, float fData);<br /> virtual ~CTest(void);</p>
		<p>public:<br /> void Display(void);</p>
		<p>private:<br /> int m_nData;<br /> float m_fData;<br />};</p>
		<p>CTest::CTest(int nData, float fData)<br /> :m_nData(nData), m_fData(fData)<br />{<br />}</p>
		<p>CTest::~CTest()<br />{<br />}</p>
		<p>void CTest::Display()<br />{<br /> cout&lt;&lt;"nData: "&lt;&lt;m_nData&lt;&lt;endl;<br /> cout&lt;&lt;"fData: "&lt;&lt;m_fData&lt;&lt;endl;<br />}</p>
		<p>void main(void)<br />{<br /> CTest test[3] = {CTest(1, 1.1f), CTest(2, 2.2f), CTest(3, 3.3f)};</p>
		<p> for(int i = 0; i &lt; sizeof(test) / sizeof(test[0]); i++)<br /> {<br />  cout&lt;&lt;i&lt;&lt;endl;<br />  test[i].Display();<br />  cout&lt;&lt;endl;<br /> }<br />}<br /><br />4、用atexit函数代替exit、abort函数<br />对象的析构函数在程序从main函数退出时，或者标准C库函数exit被调用时才会被调用，多数情况下main函数的结尾也是靠调用</p>
		<p>exit函数来结束程序的，这意味着在main函数结束前调用exit或abort函数时十分危险的，可以通过一下例子证明：<br />1)、未调用exit或abort函数：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CTest<br />{<br />public:<br /> CTest(void){cout&lt;&lt;"CTest()"&lt;&lt;endl;}<br /> virtual ~CTest(void){cout&lt;&lt;"~CTest()"&lt;&lt;endl;}</p>
		<p>};</p>
		<p>void main(void)<br />{<br /> CTest test;<br />}</p>
		<p>程序执行结果：<br />CTest()<br />~CTest()</p>
		<p>2)、调用exit或abort函数：<br />void main(void)<br />{<br /> CTest test;<br /> exit(0); //或abort();<br />}<br />程序执行结果：<br />CTest()</p>
		<p>从上面的例子可以明确的看到调用exit或abort函数以后对象的析构函数未被调用，这无意间就导致了内存泄漏，而且这种错误</p>
		<p>无法察觉。但是在实际情况中确实存在程序运行期间要求退出应用程序的需求，那么我们是否就束手无策呢？答案并非如此。我</p>
		<p>们可以调用标准C库函数atexit，atexit函数确保所有的析构函数被调用，其具体代码如下：<br />3)、调用atexit函数：<br />void main(void)<br />{<br /> CTest test;<br /> atexit(0);<br />}</p>
		<p>程序执行结果：<br />CTest()<br />~CTest()<br /><br />5、存取全局函数的函数指针<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>void Func(const int &amp;nData)<br />{<br /> cout&lt;&lt;nData&lt;&lt;endl;<br />}</p>
		<p>typedef void (*pFunc)(const int &amp;nData);<br />class CTest<br />{<br />public:<br /> CTest(void){}<br /> virtual ~CTest(void){}</p>
		<p>public:<br /> void SetFunc(pFunc p){m_pFunc = p;}<br /> void Run(int nData){m_pFunc(nData); }</p>
		<p>public:<br /> pFunc m_pFunc;</p>
		<p>};</p>
		<p>void main(void)<br />{<br /> CTest test;<br /> test.SetFunc(Func);<br /> test.Run(456);<br />}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-11 10:23 <a href="http://www.cppblog.com/tiger/archive/2007/01/11/17514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>友元</title><link>http://www.cppblog.com/tiger/archive/2007/01/10/17505.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Wed, 10 Jan 2007 09:36:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/10/17505.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17505.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/10/17505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17505.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17505.html</trackback:ping><description><![CDATA[
		<p>1、友元函数：<br />#include &lt;iostream&gt;<br />using namespace std;<br /><br />class CTest<br />{<br />public:<br /> CTest(void){}<br /> virtual ~CTest(void){}</p>
		<p>public:<br /> friend void Func(CTest t);</p>
		<p>private:<br /> void Display(void){cout&lt;&lt;m_nData&lt;&lt;endl;}</p>
		<p>private:<br /> int m_nData;<br />};</p>
		<p>void Func(CTest t)<br />{<br /> t.m_nData = 6;<br /> t.Display();<br />}</p>
		<p>void main(void)<br />{<br /> CTest t;<br /> Func(t);<br />}</p>
		<p>2、友元对象<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CStudent;<br />class CTeacher<br />{<br />public:<br /> void SetValue(const CStudent &amp;student);<br /> int GetValue(void);</p>
		<p>private:<br /> int m_nMemberData;<br />};</p>
		<p>class CStudent<br />{<br />public:<br /> CStudent(const int &amp;nMemberData){m_nMemberData = nMemberData;}</p>
		<p>public:<br /> friend class CTeacher;</p>
		<p>private:<br /> int m_nMemberData;<br />};</p>
		<p>void CTeacher::SetValue(const CStudent &amp;student)<br />{<br /> m_nMemberData = student.m_nMemberData;<br />}</p>
		<p>int CTeacher::GetValue()<br />{<br /> return m_nMemberData;<br />}</p>
		<p>void main(void)<br />{<br /> CTeacher teacher;<br /> CStudent student(11);</p>
		<p> teacher.SetValue(student);<br /> cout&lt;&lt;teacher.GetValue()&lt;&lt;endl;<br />}<br /><br />3、友元类<br />方法一：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CTest;<br />class CTest1<br />{<br />public:<br /> CTest1(int nData){m_nData = nData;}<br /> virtual ~CTest1(void){}</p>
		<p>public:<br /> friend CTest;</p>
		<p>private:<br /> int m_nData;<br />};</p>
		<p>class CTest<br />{<br />public:<br /> CTest(void){}<br /> virtual ~CTest(void){}</p>
		<p>public:<br /> void Func(CTest1 t){cout&lt;&lt;t.m_nData&lt;&lt;endl;}</p>
		<p>};</p>
		<p>void main(void)<br />{<br /> CTest test;<br /> CTest1 test1(27);<br /> test.Func(test1);<br />}<br /><br />方法二：<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CTest1;</p>
		<p>class CTest<br />{<br />public:<br /> CTest(void){}<br /> virtual ~CTest(void){}</p>
		<p>public:<br /> void Func(CTest1 *pTest1);</p>
		<p>};</p>
		<p>class CTest1<br />{<br />public:<br /> CTest1(int nData){m_nData = nData;}<br /> virtual ~CTest1(void){}</p>
		<p>public:<br /> friend CTest;</p>
		<p>private:<br /> int m_nData;<br />};</p>
		<p>void CTest::Func(CTest1 *pTest1)<br />{<br /> cout&lt;&lt;pTest1-&gt;m_nData&lt;&lt;endl;<br />}</p>
		<p>void main(void)<br />{<br /> CTest test;<br /> CTest1 test1(27);<br /> test.Func(&amp;test1);<br />}<br /><br />4、嵌套友元<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>class CLevel1<br />{<br />public:<br /> CLevel1(int nData);<br /> virtual ~CLevel1(void);</p>
		<p>public:<br /> class CLevel2<br /> {<br /> public:<br />  CLevel2(void);<br />  virtual ~CLevel2(void);</p>
		<p> public:<br />  void Func(CLevel1 level1);</p>
		<p> };</p>
		<p>public:<br /> friend CLevel1::CLevel2;</p>
		<p>private:<br /> int m_nData;</p>
		<p>};</p>
		<p>CLevel1::CLevel1(int nData) : m_nData(nData)<br />{<br />}</p>
		<p>CLevel1::~CLevel1()<br />{<br />}</p>
		<p>CLevel1::CLevel2::CLevel2()<br />{<br />}</p>
		<p>CLevel1::CLevel2::~CLevel2()<br />{<br />}</p>
		<p>void CLevel1::CLevel2::Func(CLevel1 level1)<br />{<br /> cout&lt;&lt;level1.m_nData&lt;&lt;endl;<br />}</p>
		<p>void main(void)<br />{<br /> CLevel1 level1(100);<br /> CLevel1::CLevel2 level2;<br /> level2.Func(level1);<br />}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-10 17:36 <a href="http://www.cppblog.com/tiger/archive/2007/01/10/17505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE下带编辑与下拉功能的ListCtrl</title><link>http://www.cppblog.com/tiger/archive/2007/01/10/17479.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Wed, 10 Jan 2007 02:47:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/10/17479.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17479.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/10/17479.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17479.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17479.html</trackback:ping><description><![CDATA[
		<p>CListCtrl带编辑功能与下拉功能的本质即在列表中嵌入CEdit和CComboBox控件，其具体代码如下所示：<br />//InPlaceEdit.h<br />#if !defined(AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_)<br />#define AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>class CInPlaceEdit : public CEdit<br />{</p>
		<p>public:<br /> <br />// Implementation<br /> <br /> // Returns the instance of the class<br /> static CInPlaceEdit* GetInstance(); </p>
		<p> // Deletes the instance of the class<br /> static void DeleteInstance(); </p>
		<p> // Creates the Windows edit control and attaches it to the object<br /> // Shows the edit ctrl<br /> BOOL ShowEditCtrl(DWORD dwStyle, const RECT&amp; rCellRect, CWnd* pParentWnd, <br />       UINT uiResourceID, int iRowIndex, int iColumnIndex,<br />       CString&amp; strValidChars, CString&amp; rstrCurSelection);</p>
		<p>// Overrides<br /> // ClassWizard generated virtual function overrides<br /> //{{AFX_VIRTUAL(CInPlaceEdit)<br /> public:<br /> virtual BOOL PreTranslateMessage(MSG* pMsg);<br /> //}}AFX_VIRTUAL</p>
		<p>
				<br />// Attributes<br /> // afx_msg void OnPaste(WPARAM wParam, LPARAM lParam);</p>
		<p>protected: <br /> // Generated message map functions<br /> //{{AFX_MSG(CInPlaceEdit)<br /> afx_msg void OnKillFocus(CWnd* pNewWnd);<br /> afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);<br /> afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);<br /> //}}AFX_MSG</p>
		<p> DECLARE_MESSAGE_MAP()</p>
		<p>private:</p>
		<p>// Implementation</p>
		<p> // Constructor<br /> CInPlaceEdit();</p>
		<p> // Hide the copy constructor and operator =<br /> CInPlaceEdit (CInPlaceEdit&amp;) {}</p>
		<p> operator = (CInPlaceEdit) {}<br /> <br /> // Destructor<br /> virtual ~CInPlaceEdit();</p>
		<p>// Attributes</p>
		<p> // Index of the item in the list control<br /> int m_iRowIndex;</p>
		<p> // Index of the subitem in the list control<br /> int m_iColumnIndex;</p>
		<p> // To indicate whether ESC key was pressed<br /> BOOL m_bESC;<br /> <br /> // Valid characters<br /> CString m_strValidChars;</p>
		<p> // Singleton instance<br /> static CInPlaceEdit* m_pInPlaceEdit;</p>
		<p> // Previous string value in the edit control<br /> CString m_strWindowText;<br />};</p>
		<p>/////////////////////////////////////////////////////////////////////////////</p>
		<p>//{{AFX_INSERT_LOCATION}}<br />// Microsoft Visual C++ will insert additional declarations immediately before the previous line.</p>
		<p>#endif // !defined(AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_)<br /><br />//InPlaceEdit.cpp<br />#include "stdafx.h"<br />#include "InPlaceEdit.h"</p>
		<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
		<p>#define CTRL_C 0x3<br />#define CTRL_V 0x16<br />#define CTRL_X 0x18</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceEdit</p>
		<p>CInPlaceEdit* CInPlaceEdit::m_pInPlaceEdit = NULL;  </p>
		<p>CInPlaceEdit::CInPlaceEdit()<br />{<br /> m_iRowIndex= -1;<br /> m_iColumnIndex = -1;<br /> m_bESC = FALSE;<br /> m_strValidChars.Empty();<br />}</p>
		<p>CInPlaceEdit::~CInPlaceEdit()<br />{<br />}</p>
		<p>BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)<br /> //{{AFX_MSG_MAP(CInPlaceEdit)<br /> ON_WM_KILLFOCUS()<br /> ON_WM_CHAR() <br /> ON_WM_CREATE()<br /> //}}AFX_MSG_MAP<br />// ON_MESSAGE(WM_PASTE, OnPaste)<br />END_MESSAGE_MAP()</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceEdit message handlers<br />/*<br />void CInPlaceEdit::OnPaste(WPARAM , LPARAM )<br />{<br /> if (m_strValidChars.IsEmpty())<br /> {<br />  return; <br /> }</p>
		<p>    CString strFromClipboard;</p>
		<p> // get the text from clipboard<br /> if(OpenClipboard()) {<br />  HANDLE l_hData = GetClipboardData(CF_TEXT);<br />  if(NULL == l_hData) {<br />   return;<br />  }<br />  <br />  char *l_pBuffer = (char*)GlobalLock(l_hData);<br />  if(NULL != l_pBuffer) {<br />   strFromClipboard = l_pBuffer;<br />  }</p>
		<p>  GlobalUnlock(l_hData);<br />  CloseClipboard();<br /> }</p>
		<p> // Validate the characters before pasting <br /> for(int iCounter_ = 0; iCounter_ &lt; strFromClipboard.GetLength(); iCounter_++)<br /> {<br />  if (-1 == m_strValidChars.Find(strFromClipboard.GetAt(iCounter_)))<br />  {<br />   return;<br />  }<br /> }<br />  <br /> //let the individual control handle other processing<br /> CEdit::Default(); <br />}<br />*/</p>
		<p>void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd) <br />{<br /> CEdit::OnKillFocus(pNewWnd);<br /> <br /> // TODO: Add your message handler code here</p>
		<p> // Get the text in the edit ctrl<br /> CString strEdit;<br /> GetWindowText(strEdit);</p>
		<p>// if(strEdit.GetLength() == 1)<br />//  strEdit = _T("0") + strEdit;</p>
		<p> // Send Notification to parent of edit ctrl<br /> LV_DISPINFO dispinfo;<br /> dispinfo.hdr.hwndFrom = GetParent()-&gt;m_hWnd;<br /> dispinfo.hdr.idFrom = GetDlgCtrlID();<br /> dispinfo.hdr.code = LVN_ENDLABELEDIT;</p>
		<p> dispinfo.item.mask = LVIF_TEXT;<br /> dispinfo.item.iItem = m_iRowIndex;<br /> dispinfo.item.iSubItem = m_iColumnIndex;<br /> dispinfo.item.pszText = m_bESC ? LPTSTR((LPCTSTR)m_strWindowText) : LPTSTR((LPCTSTR)strEdit);<br /> dispinfo.item.cchTextMax = m_bESC ? m_strWindowText.GetLength() : strEdit.GetLength();<br /> <br /> GetParent()-&gt;SendMessage(WM_NOTIFY, GetParent()-&gt;GetDlgCtrlID(), (LPARAM)&amp;dispinfo);</p>
		<p> PostMessage(WM_CLOSE);<br />}</p>
		<p>void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p>  if ((m_strValidChars.IsEmpty()) || ((-1 != m_strValidChars.Find(static_cast&lt;TCHAR&gt; (nChar))) || <br />  (nChar == VK_BACK) || (nChar == CTRL_C) || (nChar == CTRL_V) || (nChar == CTRL_X)))<br /> {<br />  CEdit::OnChar(nChar, nRepCnt, nFlags);<br /> }<br /> else<br /> {<br />  MessageBeep(MB_ICONEXCLAMATION);<br />  return;<br /> }<br />}</p>
		<p>BOOL CInPlaceEdit::PreTranslateMessage(MSG* pMsg) <br />{<br /> // TODO: Add your specialized code here and/or call the base class<br /> if (WM_KEYDOWN == pMsg-&gt;message &amp;&amp; (VK_ESCAPE == pMsg-&gt;wParam || VK_RETURN == pMsg-&gt;wParam))<br /> {<br />  if (VK_ESCAPE == pMsg-&gt;wParam)<br />  {<br />   m_bESC = TRUE;<br />  }</p>
		<p>  GetParent()-&gt;SetFocus();<br />  return TRUE;<br /> }</p>
		<p> return CEdit::PreTranslateMessage(pMsg);<br />}</p>
		<p>int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) <br />{<br /> if (CEdit::OnCreate(lpCreateStruct) == -1)<br />  return -1;<br /> <br /> // TODO: Add your specialized creation code here<br /> // Set the proper font<br /> CFont* pFont = GetParent()-&gt;GetFont();<br /> SetFont(pFont);</p>
		<p> ShowWindow(SW_SHOW);<br /> SetWindowText(m_strWindowText);<br /> SetSel(0, -1);<br /> SetFocus();<br /> <br />   <br /> return 0;<br />}</p>
		<p>CInPlaceEdit* CInPlaceEdit::GetInstance()<br />{<br /> if(m_pInPlaceEdit == NULL)<br /> {<br />  m_pInPlaceEdit = new CInPlaceEdit;<br /> }<br /> return m_pInPlaceEdit;<br />}</p>
		<p>void CInPlaceEdit::DeleteInstance()<br />{<br /> delete m_pInPlaceEdit;<br /> m_pInPlaceEdit = NULL;<br />}</p>
		<p>BOOL CInPlaceEdit::ShowEditCtrl(DWORD dwStyle, const RECT &amp;rCellRect, CWnd* pParentWnd, <br />        UINT uiResourceID, int iRowIndex, int iColumnIndex,<br />        CString&amp; strValidChars, CString&amp; rstrCurSelection)<br />{<br /> m_iRowIndex = iRowIndex;<br /> m_iColumnIndex = iColumnIndex;<br /> m_strValidChars = strValidChars;<br /> m_strWindowText = rstrCurSelection;<br /> m_bESC = FALSE;</p>
		<p> if (NULL == m_pInPlaceEdit-&gt;m_hWnd) <br /> {<br />  return m_pInPlaceEdit-&gt;Create(dwStyle, rCellRect, pParentWnd, uiResourceID); <br /> } </p>
		<p> return TRUE;<br />}<br /><br />//InPlaceCombo.h<br />#if !defined(AFX_INPLACECOMBO_H__2E04D8D9_827F_4FBD_9E87_30AF8C31639D__INCLUDED_)<br />#define AFX_INPLACECOMBO_H__2E04D8D9_827F_4FBD_9E87_30AF8C31639D__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>class CInPlaceCombo : public CComboBox<br />{<br />public:<br />  <br />// Implementation<br /> <br /> // Returns the instance of the class<br /> static CInPlaceCombo* GetInstance(); </p>
		<p> // Deletes the instance of the class<br /> static void DeleteInstance(); </p>
		<p> // Creates the Windows combo control and attaches it to the object, if needed and shows the combo ctrl<br /> BOOL ShowComboCtrl(DWORD dwStyle, const CRect&amp; rCellRect, CWnd* pParentWnd, UINT uiResourceID,<br />        int iRowIndex, int iColumnIndex, CStringList* pDropDownList, CString strCurSelecetion = "", int iCurSel = -1);</p>
		<p>// Overrides<br /> // ClassWizard generated virtual function overrides<br /> //{{AFX_VIRTUAL(CInPlaceCombo)<br /> public:<br /> virtual BOOL PreTranslateMessage(MSG* pMsg);<br /> //}}AFX_VIRTUAL</p>
		<p>protected:</p>
		<p> // Generated message map functions<br /> //{{AFX_MSG(CInPlaceCombo)<br /> afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);<br /> afx_msg void OnKillFocus(CWnd* pNewWnd);<br /> afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);<br /> afx_msg void OnCloseup();<br /> //}}AFX_MSG<br /> DECLARE_MESSAGE_MAP()</p>
		<p>private:</p>
		<p>// Implementation<br /> // Constructor<br /> CInPlaceCombo();</p>
		<p> // Hide the copy constructor and operator =<br /> CInPlaceCombo (CInPlaceCombo&amp;) {}</p>
		<p> operator = (CInPlaceCombo) {}</p>
		<p> // Destructor<br /> virtual ~CInPlaceCombo();</p>
		<p>// Attributes</p>
		<p> // Index of the item in the list control<br /> int m_iRowIndex;</p>
		<p> // Index of the subitem in the list control<br /> int m_iColumnIndex;</p>
		<p> // To indicate whether ESC key was pressed<br /> BOOL m_bESC;<br /> <br /> // Singleton instance<br /> static CInPlaceCombo* m_pInPlaceCombo;</p>
		<p> // Previous selected string value in the combo control<br /> CString m_strWindowText;</p>
		<p> // List of items to be shown in the drop down<br /> CStringList m_DropDownList;<br />};</p>
		<p>/////////////////////////////////////////////////////////////////////////////</p>
		<p>//{{AFX_INSERT_LOCATION}}<br />// Microsoft Visual C++ will insert additional declarations immediately before the previous line.</p>
		<p>#endif // !defined(AFX_INPLACECOMBO_H__2E04D8D9_827F_4FBD_9E87_30AF8C31639D__INCLUDED_)<br /><br />//InPlaceCombo.cpp<br />#include "stdafx.h"<br />#include "InPlaceCombo.h"</p>
		<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceCombo</p>
		<p>CInPlaceCombo* CInPlaceCombo::m_pInPlaceCombo = NULL; </p>
		<p>CInPlaceCombo::CInPlaceCombo()<br />{<br /> m_iRowIndex = -1;<br /> m_iColumnIndex = -1;<br /> m_bESC = FALSE;<br />}</p>
		<p>CInPlaceCombo::~CInPlaceCombo()<br />{<br />}</p>
		<p>BEGIN_MESSAGE_MAP(CInPlaceCombo, CComboBox)<br /> //{{AFX_MSG_MAP(CInPlaceCombo)<br /> ON_WM_CREATE()<br /> ON_WM_KILLFOCUS()<br /> ON_WM_CHAR()<br /> ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)<br /> //}}AFX_MSG_MAP<br />END_MESSAGE_MAP()</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceCombo message handlers</p>
		<p>int CInPlaceCombo::OnCreate(LPCREATESTRUCT lpCreateStruct) <br />{<br /> if (CComboBox::OnCreate(lpCreateStruct) == -1)<br /> {<br />  return -1;<br /> }<br /> <br /> // Set the proper font<br /> CFont* pFont = GetParent()-&gt;GetFont();<br /> SetFont(pFont);<br /> <br /> SetFocus();</p>
		<p> ResetContent(); <br /> for (POSITION Pos_ = m_DropDownList.GetHeadPosition(); Pos_ != NULL;)<br /> {<br />  AddString((LPCTSTR) (m_DropDownList.GetNext(Pos_)));<br /> }</p>
		<p> return 0;<br />}</p>
		<p>BOOL CInPlaceCombo::PreTranslateMessage(MSG* pMsg) <br />{<br /> // If the message if for "Enter" or "Esc"<br /> // Do not process<br /> if (pMsg-&gt;message == WM_KEYDOWN)<br /> {<br />  if(pMsg-&gt;wParam == VK_RETURN || pMsg-&gt;wParam == VK_ESCAPE)<br />  {<br />   ::TranslateMessage(pMsg);<br />   ::DispatchMessage(pMsg);<br />   // DO NOT process further<br />   return TRUE;    <br />  }<br /> }<br /> <br /> return CComboBox::PreTranslateMessage(pMsg);<br />}</p>
		<p>void CInPlaceCombo::OnKillFocus(CWnd* pNewWnd) <br />{<br /> CComboBox::OnKillFocus(pNewWnd);<br /> <br /> // Get the current selection text<br /> CString str;<br /> GetWindowText(str);</p>
		<p> // Send Notification to parent of ListView ctrl<br /> LV_DISPINFO dispinfo;<br /> dispinfo.hdr.hwndFrom = GetParent()-&gt;m_hWnd;<br /> dispinfo.hdr.idFrom = GetDlgCtrlID();<br /> dispinfo.hdr.code = LVN_ENDLABELEDIT;</p>
		<p> dispinfo.item.mask = LVIF_TEXT;<br /> dispinfo.item.iItem = m_iRowIndex;<br /> dispinfo.item.iSubItem = m_iColumnIndex;<br /> dispinfo.item.pszText = m_bESC ? LPTSTR((LPCTSTR)m_strWindowText) : LPTSTR((LPCTSTR)str);<br /> dispinfo.item.cchTextMax = m_bESC ? m_strWindowText.GetLength() : str.GetLength();<br /> <br /> GetParent()-&gt;SendMessage(WM_NOTIFY, GetParent()-&gt;GetDlgCtrlID(), (LPARAM)&amp;dispinfo);</p>
		<p> // Close the control<br /> PostMessage(WM_CLOSE);<br />}</p>
		<p>void CInPlaceCombo::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) <br />{<br /> // If the key is "Esc" set focus back to the list control<br /> if (nChar == VK_ESCAPE || nChar == VK_RETURN)<br /> {<br />  if (nChar == VK_ESCAPE)<br />  {<br />   m_bESC = TRUE;<br />  }</p>
		<p>  GetParent()-&gt;SetFocus();<br />  return;<br /> }<br /> <br /> CComboBox::OnChar(nChar, nRepCnt, nFlags);<br />}</p>
		<p>void CInPlaceCombo::OnCloseup() <br />{<br /> // Set the focus to the parent list control<br /> GetParent()-&gt;SetFocus();<br />}</p>
		<p>CInPlaceCombo* CInPlaceCombo::GetInstance()<br />{<br /> if(m_pInPlaceCombo == NULL)<br /> {<br />  m_pInPlaceCombo = new CInPlaceCombo;<br /> }<br /> return m_pInPlaceCombo;<br />}</p>
		<p>void CInPlaceCombo::DeleteInstance()<br />{<br /> delete m_pInPlaceCombo;<br /> m_pInPlaceCombo = NULL;<br />}</p>
		<p>BOOL CInPlaceCombo::ShowComboCtrl(DWORD dwStyle, const CRect &amp;rCellRect, CWnd* pParentWnd, UINT uiResourceID,<br />          int iRowIndex, int iColumnIndex, CStringList* pDropDownList, <br />          CString strCurSelecetion /*= ""*/, int iCurSel /*= -1*/)<br />{</p>
		<p> m_iRowIndex = iRowIndex;<br /> m_iColumnIndex = iColumnIndex;<br /> m_bESC = FALSE;<br /> <br /> m_DropDownList.RemoveAll(); <br /> m_DropDownList.AddTail(pDropDownList);</p>
		<p> BOOL bRetVal = TRUE;</p>
		<p> if (-1 != iCurSel)<br /> {<br />  GetLBText(iCurSel, m_strWindowText);<br /> }<br /> else if (!strCurSelecetion.IsEmpty()) <br /> {<br />  m_strWindowText = strCurSelecetion;<br /> }<br /> <br /> if (NULL == m_pInPlaceCombo-&gt;m_hWnd) <br /> {<br />  bRetVal = m_pInPlaceCombo-&gt;Create(dwStyle, rCellRect, pParentWnd, uiResourceID); <br /> }</p>
		<p> SetCurSel(iCurSel);</p>
		<p> return bRetVal;<br />}<br /><br />//MyComboListCtrl.h<br />#if !defined(AFX_MYCOMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_)<br />#define AFX_MYCOMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>//the max listCtrl columns<br />#define MAX_LISTCTRL_COLUMNS 100</p>
		<p>#include &lt;afxtempl.h&gt;<br />#include &lt;afxcmn.h&gt;</p>
		<p>class CInPlaceCombo;<br />class CInPlaceEdit;</p>
		<p>// User define message <br />// This message is posted to the parent<br />// The message can be handled to make the necessary validations, if any<br />#define WM_VALIDATE  WM_USER + 0x7FFD</p>
		<p>// User define message <br />// This message is posted to the parent<br />// The message should be handled to spcify the items to the added to the combo<br />#define WM_SET_ITEMS WM_USER + 0x7FFC</p>
		<p>class CMyComboListCtrl : public CListCtrl<br />{<br />public:<br /> <br />// Implementation<br /> typedef enum {MODE_READONLY,MODE_DIGITAL_EDIT,MODE_TEXT_EDIT,MODE_COMBO} COMBOLISTCTRL_COLUMN_MODE;</p>
		<p> // Constructor<br /> CMyComboListCtrl();</p>
		<p> // Destructor<br /> virtual ~CMyComboListCtrl();</p>
		<p> // Sets/Resets the column which support the in place combo box<br /> void SetComboColumns(int iColumnIndex, bool bSet = true);<br /> <br /> // Sets/Resets the column which support the in place edit control<br /> void SetReadOnlyColumns(int iColumnIndex, bool bSet = true);</p>
		<p> // Sets the valid characters for the edit ctrl<br /> void SetValidEditCtrlCharacters(CString&amp; rstrValidCharacters);</p>
		<p> // Sets the vertical scroll<br /> void EnableVScroll(bool bEnable = true);</p>
		<p> // Sets the horizontal scroll<br /> void EnableHScroll(bool bEnable = true);</p>
		<p> //insert column<br /> int CMyComboListCtrl::InsertColumn(int nCol,LPCTSTR lpszColumnHeading,int nFormat = LVCFMT_LEFT,int nWidth = -1,int nSubItem = -1);</p>
		<p> //Get column counts<br /> int GetColumnCounts();</p>
		<p> //delete all column<br /> void DeleteAllColumn();</p>
		<p> //set column Valid char string<br /> void SetColumnValidEditCtrlCharacters(CString &amp;rstrValidCharacters,int column = -1);</p>
		<p>// Overrides<br /> // ClassWizard generated virtual function overrides<br /> //{{AFX_VIRTUAL(CMyComboListCtrl)<br /> //}}AFX_VIRTUAL</p>
		<p>protected:</p>
		<p>// Methods<br /> // Generated message map functions<br /> //{{AFX_MSG(CMyComboListCtrl)<br /> afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);<br /> afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);<br /> afx_msg void OnLButtonDown(UINT nFlags, CPoint point);<br /> afx_msg void OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);<br /> afx_msg void OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);<br /> //}}AFX_MSG</p>
		<p> DECLARE_MESSAGE_MAP()</p>
		<p>private:<br />   <br />// Implementation</p>
		<p> // Returns the row &amp; column index of the column on which mouse click event has occured<br /> bool HitTestEx(CPoint&amp; rHitPoint, int* pRowIndex, int* pColumnIndex) const;</p>
		<p> // Creates and displays the in place combo box<br /> CInPlaceCombo* ShowInPlaceList(int iRowIndex, int iColumnIndex, CStringList&amp; rComboItemsList, <br />           CString strCurSelecetion = "", int iSel = -1);</p>
		<p> // Creates and displays the in place edit control<br /> CInPlaceEdit* ShowInPlaceEdit(int iRowIndex, int iColumnIndex, CString&amp; rstrCurSelection);</p>
		<p> // Calculates the cell rect<br /> void CalculateCellRect(int iColumnIndex, int iRowIndex, CRect&amp; robCellRect);</p>
		<p> // Checks whether column supports in place combo box<br /> bool IsCombo(int iColumnIndex);</p>
		<p> // Checks whether column is read only<br /> bool IsReadOnly(int iColumnIndex);</p>
		<p> // Scrolls the list ctrl to bring the in place ctrl to the view<br /> void ScrollToView(int iColumnIndex, /*int iOffSet, */CRect&amp; obCellRect);</p>
		<p>// Attributes<br /> <br /> // List of columns that support the in place combo box<br /> CList&lt;int, int&gt; m_ComboSupportColumnsList;</p>
		<p> // List of columns that are read only<br /> CList&lt;int, int&gt; m_ReadOnlyColumnsList;</p>
		<p> // Valid characters<br /> CString m_strValidEditCtrlChars;</p>
		<p> // The window style of the in place edit ctrl<br /> DWORD m_dwEditCtrlStyle;</p>
		<p> // The window style of the in place combo ctrl<br /> DWORD m_dwDropDownCtrlStyle;</p>
		<p> //columnCounts<br /> int m_iColumnCounts;</p>
		<p> //column types<br /> COMBOLISTCTRL_COLUMN_MODE m_modeColumn[MAX_LISTCTRL_COLUMNS];</p>
		<p> //column <br /> CString m_strValidChars[MAX_LISTCTRL_COLUMNS];<br /> //int m_<br />};</p>
		<p>/////////////////////////////////////////////////////////////////////////////</p>
		<p>//{{AFX_INSERT_LOCATION}}<br />// Microsoft Visual C++ will insert additional declarations immediately before the previous line.</p>
		<p>#endif // !defined(AFX_MYCOMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_)<br /><br /><br />//MyComboListCtrl.cpp<br />#include "stdafx.h"<br />#include "MyComboListCtrl.h"<br />#include "InPlaceCombo.h"<br />#include "InPlaceEdit.h"</p>
		<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
		<p>//#defines<br />#define FIRST_COLUMN    0<br />#define MIN_COLUMN_WIDTH   10<br />#define MAX_DROP_DOWN_ITEM_COUNT 10</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CMyComboListCtrl</p>
		<p>CMyComboListCtrl::CMyComboListCtrl()<br />{<br /> m_iColumnCounts = 0;<br /> m_ComboSupportColumnsList.RemoveAll();<br /> m_ReadOnlyColumnsList.RemoveAll();<br /> m_strValidEditCtrlChars.Empty();<br /> m_dwEditCtrlStyle = ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_NOHIDESEL;<br /> m_dwDropDownCtrlStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | <br />       CBS_DROPDOWNLIST | CBS_DISABLENOSCROLL;<br />}</p>
		<p>CMyComboListCtrl::~CMyComboListCtrl()<br />{<br /> CInPlaceCombo::DeleteInstance();<br /> CInPlaceEdit::DeleteInstance();  <br />}</p>
		<p>
				<br />BEGIN_MESSAGE_MAP(CMyComboListCtrl, CListCtrl)<br /> //{{AFX_MSG_MAP(CMyComboListCtrl)<br /> ON_WM_HSCROLL()<br /> ON_WM_VSCROLL()<br /> ON_WM_LBUTTONDOWN()<br /> ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)<br /> ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)<br /> //}}AFX_MSG_MAP<br />END_MESSAGE_MAP()</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CMyComboListCtrl message handlers</p>
		<p>CInPlaceCombo* CMyComboListCtrl::ShowInPlaceList(int iRowIndex, int iColumnIndex, CStringList&amp; rComboItemsList, <br />              CString strCurSelecetion /*= ""*/, int iSel /*= -1*/)<br />{<br /> // The returned obPointer should not be saved<br /> <br /> // Make sure that the item is visible<br /> if (!EnsureVisible(iRowIndex, TRUE))<br /> {<br />  return NULL;<br /> }</p>
		<p> // Make sure that iColumnIndex is valid <br /> CHeaderCtrl* pHeader = static_cast&lt;CHeaderCtrl*&gt; (GetDlgItem(FIRST_COLUMN));</p>
		<p> int iColumnCount = pHeader-&gt;GetItemCount();</p>
		<p> if (iColumnIndex &gt;= iColumnCount || GetColumnWidth(iColumnIndex) &lt; MIN_COLUMN_WIDTH) <br /> {<br />  return NULL;<br /> }</p>
		<p> // Calculate the rectangle specifications for the combo box<br /> CRect obCellRect(0, 0, 0, 0);<br /> CalculateCellRect(iColumnIndex, iRowIndex, obCellRect);</p>
		<p> int iHeight = obCellRect.Height();  <br /> int iCount = (int )rComboItemsList.GetCount();</p>
		<p> iCount = (iCount &lt; MAX_DROP_DOWN_ITEM_COUNT) ? <br />  iCount + MAX_DROP_DOWN_ITEM_COUNT : (MAX_DROP_DOWN_ITEM_COUNT + 1); </p>
		<p> obCellRect.bottom += iHeight * iCount; </p>
		<p> // Create the in place combobox<br /> CInPlaceCombo* pInPlaceCombo = CInPlaceCombo::GetInstance();<br /> pInPlaceCombo-&gt;ShowComboCtrl(m_dwDropDownCtrlStyle, obCellRect, this, 0, iRowIndex, iColumnIndex, &amp;rComboItemsList, <br />         strCurSelecetion, iSel);<br /> <br /> return pInPlaceCombo;<br />}</p>
		<p>CInPlaceEdit* CMyComboListCtrl::ShowInPlaceEdit(int iRowIndex, int iColumnIndex, CString&amp; rstrCurSelection)<br />{<br /> // Create an in-place edit control<br /> CInPlaceEdit* pInPlaceEdit = CInPlaceEdit::GetInstance();<br />  <br /> CRect obCellRect(0, 0, 0, 0);<br /> CalculateCellRect(iColumnIndex, iRowIndex, obCellRect);<br />   <br /> pInPlaceEdit-&gt;ShowEditCtrl(m_dwEditCtrlStyle, obCellRect, this, 0, <br />          iRowIndex, iColumnIndex,<br />          m_strValidChars[iColumnIndex], rstrCurSelection);</p>
		<p> return pInPlaceEdit;<br />}</p>
		<p>void CMyComboListCtrl::OnHScroll(UINT iSBCode, UINT iPos, CScrollBar* pScrollBar) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> if (GetFocus() != this)<br /> {<br />  SetFocus();<br /> }</p>
		<p> CListCtrl::OnHScroll(iSBCode, iPos, pScrollBar);<br />}</p>
		<p>void CMyComboListCtrl::OnVScroll(UINT iSBCode, UINT iPos, CScrollBar* pScrollBar) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> if (GetFocus() != this)<br /> {<br />  SetFocus();<br /> }</p>
		<p> CListCtrl::OnVScroll(iSBCode, iPos, pScrollBar);<br />}</p>
		<p>void CMyComboListCtrl::OnLButtonDown(UINT iFlags, CPoint obPoint) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> int iColumnIndex = -1;<br /> int iRowIndex = -1;</p>
		<p> // Get the current column and row<br /> if (!HitTestEx(obPoint, &amp;iRowIndex, &amp;iColumnIndex))<br /> {<br />  return;<br /> }</p>
		<p> CListCtrl::OnLButtonDown(iFlags, obPoint);<br /> <br /> // If column is not read only then<br /> // If the SHIFT or CTRL key is down call the base class<br /> // Check the high bit of GetKeyState to determine whether SHIFT or CTRL key is down<br /> if ((GetKeyState(VK_SHIFT) &amp; 0x80) || (GetKeyState(VK_CONTROL) &amp; 0x80))<br /> {<br />  return;<br /> }</p>
		<p> // Get the current selection before creating the in place combo box<br /> CString strCurSelection = GetItemText(iRowIndex, iColumnIndex);<br /> <br /> if (-1 != iRowIndex)<br /> {<br />  UINT flag = LVIS_FOCUSED;<br />  <br />  if ((GetItemState(iRowIndex, flag ) &amp; flag) == flag)<br />  {<br />   // Add check for LVS_EDITLABELS<br />   if (GetWindowLong(m_hWnd, GWL_STYLE) &amp; LVS_EDITLABELS)<br />   {<br />    // If combo box is supported<br />    // Create and show the in place combo box<br />    if (IsCombo(iColumnIndex))<br />    {<br />     CStringList obComboItemsList;<br />          <br />     GetParent()-&gt;SendMessage(WM_SET_ITEMS, (WPARAM)iColumnIndex, (LPARAM)&amp;obComboItemsList);  <br />     <br />     CInPlaceCombo* pInPlaceComboBox = ShowInPlaceList(iRowIndex, iColumnIndex, obComboItemsList, strCurSelection);<br />     ASSERT(pInPlaceComboBox); <br />     <br />     // Set the selection to previous selection<br />     pInPlaceComboBox-&gt;SelectString(-1, strCurSelection);<br />    }<br />    // If combo box is not read only<br />    // Create and show the in place edit control<br />    else if (!IsReadOnly(iColumnIndex))<br />    {<br />     CInPlaceEdit* pInPlaceEdit = ShowInPlaceEdit(iRowIndex, iColumnIndex, strCurSelection);<br />    }<br />   }<br />  }<br /> }  <br />}</p>
		<p>bool CMyComboListCtrl::HitTestEx(CPoint &amp;obPoint, int* pRowIndex, int* pColumnIndex) const<br />{<br /> if (!pRowIndex || !pColumnIndex)<br /> {<br />  return false;<br /> }</p>
		<p> // Get the row index<br /> *pRowIndex = HitTest(obPoint, NULL);</p>
		<p> if (pColumnIndex)<br /> {<br />  *pColumnIndex = 0;<br /> }</p>
		<p> // Make sure that the ListView is in LVS_REPORT<br /> if ((GetWindowLong(m_hWnd, GWL_STYLE) &amp; LVS_TYPEMASK) != LVS_REPORT)<br /> {<br />  return false;<br /> }</p>
		<p> // Get the number of columns<br /> CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);</p>
		<p> int iColumnCount = pHeader-&gt;GetItemCount();</p>
		<p> // Get bounding rect of item and check whether obPoint falls in it.<br /> CRect obCellRect;<br /> GetItemRect(*pRowIndex, &amp;obCellRect, LVIR_BOUNDS);<br /> <br /> if (obCellRect.PtInRect(obPoint))<br /> {<br />  // Now find the column<br />  for (*pColumnIndex = 0; *pColumnIndex &lt; iColumnCount; (*pColumnIndex)++)<br />  {<br />   int iColWidth = GetColumnWidth(*pColumnIndex);<br />   <br />   if (obPoint.x &gt;= obCellRect.left &amp;&amp; obPoint.x &lt;= (obCellRect.left + iColWidth))<br />   {<br />    return true;<br />   }<br />   obCellRect.left += iColWidth;<br />  }<br /> }<br /> return false;<br />}</p>
		<p>void CMyComboListCtrl::SetComboColumns(int iColumnIndex, bool bSet /*= true*/)<br />{<br /> // If the Column Index is not present &amp;&amp; Set flag is false<br /> // Then do nothing <br /> // If the Column Index is present &amp;&amp; Set flag is true<br /> // Then do nothing<br /> POSITION Pos = m_ComboSupportColumnsList.Find(iColumnIndex);</p>
		<p> // If the Column Index is not present &amp;&amp; Set flag is true<br /> // Then Add to list<br /> if ((NULL == Pos) &amp;&amp; bSet) <br /> {<br />  m_ComboSupportColumnsList.AddTail(iColumnIndex); <br /> }</p>
		<p> // If the Column Index is present &amp;&amp; Set flag is false<br /> // Then Remove from list<br /> if ((NULL != Pos) &amp;&amp; !bSet) <br /> {<br />  m_ComboSupportColumnsList.RemoveAt(Pos); <br /> }<br />}</p>
		<p>void CMyComboListCtrl::SetReadOnlyColumns(int iColumnIndex, bool bSet /*= true*/)<br />{<br /> // If the Column Index is not present &amp;&amp; Set flag is false<br /> // Then do nothing <br /> // If the Column Index is present &amp;&amp; Set flag is true<br /> // Then do nothing<br /> POSITION Pos = m_ReadOnlyColumnsList.Find(iColumnIndex);</p>
		<p> // If the Column Index is not present &amp;&amp; Set flag is true<br /> // Then Add to list<br /> if ((NULL == Pos) &amp;&amp; bSet) <br /> {<br />  m_ReadOnlyColumnsList.AddTail(iColumnIndex); <br /> }</p>
		<p> // If the Column Index is present &amp;&amp; Set flag is false<br /> // Then Remove from list<br /> if ((NULL != Pos) &amp;&amp; !bSet) <br /> {<br />  m_ReadOnlyColumnsList.RemoveAt(Pos); <br /> }<br />}</p>
		<p>bool CMyComboListCtrl::IsReadOnly(int iColumnIndex)<br />{<br /> if (m_ReadOnlyColumnsList.Find(iColumnIndex))<br /> {<br />  return true;<br /> }<br /> <br /> return false;<br />}</p>
		<p>bool CMyComboListCtrl::IsCombo(int iColumnIndex)<br />{<br /> if (m_ComboSupportColumnsList.Find(iColumnIndex))<br /> {<br />  return true;<br /> }</p>
		<p> return false;<br />}</p>
		<p>void CMyComboListCtrl::CalculateCellRect(int iColumnIndex, int iRowIndex, CRect&amp; robCellRect)<br />{<br /> GetItemRect(iRowIndex, &amp;robCellRect, LVIR_BOUNDS);<br /> <br /> CRect rcClient;<br /> GetClientRect(&amp;rcClient);</p>
		<p> if (robCellRect.right &gt; rcClient.right) <br /> {<br />  robCellRect.right = rcClient.right;<br /> }</p>
		<p> ScrollToView(iColumnIndex, robCellRect); <br />}</p>
		<p>void CMyComboListCtrl::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) <br />{<br /> LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;<br /> // TODO: Add your control notification handler code here<br /> <br /> // Update the item text with the new text<br /> SetItemText(pDispInfo-&gt;item.iItem, pDispInfo-&gt;item.iSubItem, pDispInfo-&gt;item.pszText);</p>
		<p> GetParent()-&gt;SendMessage(WM_VALIDATE, GetDlgCtrlID(), (LPARAM)pDispInfo); <br /> <br /> *pResult = 0;<br />}</p>
		<p>void CMyComboListCtrl::SetValidEditCtrlCharacters(CString &amp;rstrValidCharacters)<br />{<br /> m_strValidEditCtrlChars = rstrValidCharacters;<br />}</p>
		<p>void CMyComboListCtrl::SetColumnValidEditCtrlCharacters(CString &amp;rstrValidCharacters,int column)<br />{<br /> if(column&gt;MAX_LISTCTRL_COLUMNS-1)<br />  return;<br /> if(column == -1)<br /> {<br />  for(int i=0;i&lt;MAX_LISTCTRL_COLUMNS;i++)<br />  {<br />   m_strValidChars[i] = rstrValidCharacters;<br />  }<br /> }<br /> else<br />  m_strValidChars[column] = rstrValidCharacters;<br />}</p>
		<p>void CMyComboListCtrl::EnableHScroll(bool bEnable /*= true*/)<br />{<br /> if (bEnable)<br /> {<br />  m_dwDropDownCtrlStyle |= WS_HSCROLL;<br /> }<br /> else<br /> {<br />  m_dwDropDownCtrlStyle &amp;= ~WS_HSCROLL;<br /> } <br />}</p>
		<p>void CMyComboListCtrl::EnableVScroll(bool bEnable /*= true*/)<br />{<br /> if (bEnable)<br /> {<br />  m_dwDropDownCtrlStyle |= WS_VSCROLL;<br /> }<br /> else<br /> {<br />  m_dwDropDownCtrlStyle &amp;= ~WS_VSCROLL;<br /> }<br />}</p>
		<p>void CMyComboListCtrl::ScrollToView(int iColumnIndex, /*int iOffSet, */CRect&amp; robCellRect)<br />{<br /> // Now scroll if we need to expose the column<br /> CRect rcClient;<br /> GetClientRect(&amp;rcClient);</p>
		<p> int iColumnWidth = GetColumnWidth(iColumnIndex);</p>
		<p> // Get the column iOffset<br /> int iOffSet = 0;<br /> for (int iIndex_ = 0; iIndex_ &lt; iColumnIndex; iIndex_++)<br /> {<br />  iOffSet += GetColumnWidth(iIndex_);<br /> }</p>
		<p> // If x1 of cell rect is &lt; x1 of ctrl rect or<br /> // If x1 of cell rect is &gt; x1 of ctrl rect or **Should not ideally happen**<br /> // If the width of the cell extends beyond x2 of ctrl rect then<br /> // Scroll</p>
		<p> CSize obScrollSize(0, 0);</p>
		<p> if (((iOffSet + robCellRect.left) &lt; rcClient.left) || <br />  ((iOffSet + robCellRect.left) &gt; rcClient.right))<br /> {<br />  obScrollSize.cx = iOffSet + robCellRect.left;<br /> }<br /> else if ((iOffSet + robCellRect.left + iColumnWidth) &gt; rcClient.right)<br /> {<br />  obScrollSize.cx = iOffSet + robCellRect.left + iColumnWidth - rcClient.right;<br /> }</p>
		<p> Scroll(obScrollSize);<br /> robCellRect.left -= obScrollSize.cx;<br /> <br /> // Set the width to the column width<br /> robCellRect.left += iOffSet;<br /> robCellRect.right = robCellRect.left + iColumnWidth;<br />}</p>
		<p>void CMyComboListCtrl::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) <br />{<br /> LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;<br /> // TODO: Add your control notification handler code here<br /> if (IsReadOnly(pDispInfo-&gt;item.iSubItem))<br /> {<br />  *pResult = 1;<br />  return;<br /> }</p>
		<p> *pResult = 0;<br />}</p>
		<p>int CMyComboListCtrl::InsertColumn(int nCol,LPCTSTR lpszColumnHeading,int nFormat ,int nWidth,int nSubItem)<br />{<br /> m_iColumnCounts++;<br /> return CListCtrl::InsertColumn( nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);<br />}</p>
		<p>int CMyComboListCtrl::GetColumnCounts()<br />{<br /> return m_iColumnCounts;<br />}</p>
		<p>void CMyComboListCtrl::DeleteAllColumn()<br />{<br /> for(int i=0;i&lt;m_iColumnCounts;i++)<br /> {<br />  DeleteColumn(0);<br /> }<br />}<br /><br />//应用<br />LRESULT CRTUProtocolIndexInfoBaseDlg::PopulateComboList(WPARAM wParam, LPARAM lParam)<br />{<br /> // Get the Combobox window pointer<br /> CComboBox* pInPlaceCombo = static_cast&lt;CComboBox *&gt;(GetFocus());</p>
		<p> // Get the inplace combbox top left<br /> CRect obWindowRect;</p>
		<p> pInPlaceCombo-&gt;GetWindowRect(&amp;obWindowRect);<br /> <br /> CPoint obInPlaceComboTopLeft(obWindowRect.TopLeft());<br /> <br /> // Get the active list<br /> // Get the control window rect<br /> // If the inplace combobox top left is in the rect then<br /> // The control is the active control<br /> CWnd *pFocusWnd = GetFocus();<br /> pFocusWnd-&gt;GetWindowRect(&amp;obWindowRect); <br /> <br /> int iColIndex = (int)wParam;<br /> <br /> CStringList* pComboList = reinterpret_cast&lt;CStringList *&gt;(lParam);<br /> pComboList-&gt;RemoveAll(); <br /> <br /> if(obWindowRect.PtInRect(obInPlaceComboTopLeft))<br /> {    <br />  if((iColIndex == 1) || (iColIndex == 2))<br />  {<br />   pComboList-&gt;AddTail(_T("是"));<br />   pComboList-&gt;AddTail(_T("否")); <br />  }   <br /> }<br /> return true;<br />}<br /><br />//初始化列<br /> CFont font;<br /> VERIFY(font.CreateFont(<br />    18,                        // nHeight<br />    0,                         // nWidth<br />    0,                         // nEscapement<br />    0,                         // nOrientation<br />    FW_NORMAL,                 // nWeight<br />    FALSE,                     // bItalic<br />    FALSE,                     // bUnderline<br />    0,                         // cStrikeOut<br />    ANSI_CHARSET,              // nCharSet<br />    OUT_DEFAULT_PRECIS,        // nOutPrecision<br />    CLIP_DEFAULT_PRECIS,       // nClipPrecision<br />    DEFAULT_QUALITY,           // nQuality<br />    DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily<br />    _T("宋体")));<br /> m_CtrlListBaseInfo.SetFont(&amp;font);<br /> m_CtrlListBaseInfo.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);</p>
		<p> m_CtrlListBaseInfo.InsertColumn(0, _T("序号"), LVCFMT_CENTER, 40);<br /> m_CtrlListBaseInfo.InsertColumn(1, _T("有效标志"), LVCFMT_CENTER, 60);</p>
		<p> m_CtrlListBaseInfo.SetReadOnlyColumns(0);  //read only<br /> m_CtrlListBaseInfo.SetReadOnlyColumns(1);  //read only<br /> m_CtrlListBaseInfo.SetComboColumns(1, TRUE);<br /> m_CtrlListBaseInfo.EnableVScroll();</p>
		<p>m_CtrlListBaseInfo.InsertColumn(2, _T("功能码"), LVCFMT_CENTER, 80);<br /> <br /> CString strValidChars(_T("0123456789ABCDEFabcdef"));<br /> m_CtrlListBaseInfo.SetColumnValidEditCtrlCharacters(strValidChars, 2);//HEX only edit</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17479.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-10 10:47 <a href="http://www.cppblog.com/tiger/archive/2007/01/10/17479.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE下可编辑的ListCtrl</title><link>http://www.cppblog.com/tiger/archive/2007/01/09/17456.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Tue, 09 Jan 2007 04:29:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/09/17456.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17456.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/09/17456.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17456.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17456.html</trackback:ping><description><![CDATA[
		<p>和C++中一样，WinCE下可编辑的ListCtrl控件也是将Edit控件置于列表框中以达到可编辑的功能，在此我们需要重载CListCtrl和CEdit控件以完成其功能，具体代码如下：<br />//InPlaceEdit.h<br />#if !defined(AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_)<br />#define AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>class CInPlaceEdit : public CEdit<br />{</p>
		<p>public:<br /> <br />// Implementation<br /> <br /> // Returns the instance of the class<br /> static CInPlaceEdit* GetInstance(); </p>
		<p> // Deletes the instance of the class<br /> static void DeleteInstance(); </p>
		<p> // Creates the Windows edit control and attaches it to the object<br /> // Shows the edit ctrl<br /> BOOL ShowEditCtrl(DWORD dwStyle, const RECT&amp; rCellRect, CWnd* pParentWnd, <br />       UINT uiResourceID, int iRowIndex, int iColumnIndex,<br />       CString&amp; strValidChars, CString&amp; rstrCurSelection);</p>
		<p>// Overrides<br /> // ClassWizard generated virtual function overrides<br /> //{{AFX_VIRTUAL(CInPlaceEdit)<br /> public:<br /> virtual BOOL PreTranslateMessage(MSG* pMsg);<br /> //}}AFX_VIRTUAL</p>
		<p>
				<br />// Attributes<br /> // afx_msg void OnPaste(WPARAM wParam, LPARAM lParam);</p>
		<p>protected: <br /> // Generated message map functions<br /> //{{AFX_MSG(CInPlaceEdit)<br /> afx_msg void OnKillFocus(CWnd* pNewWnd);<br /> afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);<br /> afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);<br /> //}}AFX_MSG</p>
		<p> DECLARE_MESSAGE_MAP()</p>
		<p>private:</p>
		<p>// Implementation</p>
		<p> // Constructor<br /> CInPlaceEdit();</p>
		<p> // Hide the copy constructor and operator =<br /> CInPlaceEdit (CInPlaceEdit&amp;) {}</p>
		<p> operator = (CInPlaceEdit) {}<br /> <br /> // Destructor<br /> virtual ~CInPlaceEdit();</p>
		<p>// Attributes</p>
		<p> // Index of the item in the list control<br /> int m_iRowIndex;</p>
		<p> // Index of the subitem in the list control<br /> int m_iColumnIndex;</p>
		<p> // To indicate whether ESC key was pressed<br /> BOOL m_bESC;<br /> <br /> // Valid characters<br /> CString m_strValidChars;</p>
		<p> // Singleton instance<br /> static CInPlaceEdit* m_pInPlaceEdit;</p>
		<p> // Previous string value in the edit control<br /> CString m_strWindowText;<br />};</p>
		<p>/////////////////////////////////////////////////////////////////////////////</p>
		<p>//{{AFX_INSERT_LOCATION}}<br />// Microsoft Visual C++ will insert additional declarations immediately before the previous line.</p>
		<p>#endif // !defined(AFX_INPLACEEDIT_H__175AEDFF_731E_4721_8399_DE406A465861__INCLUDED_)<br /><br />//InPlaceEdit.cpp<br />#include "stdafx.h"<br />#include "InPlaceEdit.h"</p>
		<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
		<p>#define CTRL_C 0x3<br />#define CTRL_V 0x16<br />#define CTRL_X 0x18</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceEdit</p>
		<p>CInPlaceEdit* CInPlaceEdit::m_pInPlaceEdit = NULL;  </p>
		<p>CInPlaceEdit::CInPlaceEdit()<br />{<br /> m_iRowIndex= -1;<br /> m_iColumnIndex = -1;<br /> m_bESC = FALSE;<br /> m_strValidChars.Empty();<br />}</p>
		<p>CInPlaceEdit::~CInPlaceEdit()<br />{<br />}</p>
		<p>BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)<br /> //{{AFX_MSG_MAP(CInPlaceEdit)<br /> ON_WM_KILLFOCUS()<br /> ON_WM_CHAR() <br /> ON_WM_CREATE()<br /> //}}AFX_MSG_MAP<br />// ON_MESSAGE(WM_PASTE, OnPaste)<br />END_MESSAGE_MAP()</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CInPlaceEdit message handlers<br />/*<br />void CInPlaceEdit::OnPaste(WPARAM , LPARAM )<br />{<br /> if (m_strValidChars.IsEmpty())<br /> {<br />  return; <br /> }</p>
		<p>    CString strFromClipboard;</p>
		<p> // get the text from clipboard<br /> if(OpenClipboard()) {<br />  HANDLE l_hData = GetClipboardData(CF_TEXT);<br />  if(NULL == l_hData) {<br />   return;<br />  }<br />  <br />  char *l_pBuffer = (char*)GlobalLock(l_hData);<br />  if(NULL != l_pBuffer) {<br />   strFromClipboard = l_pBuffer;<br />  }</p>
		<p>  GlobalUnlock(l_hData);<br />  CloseClipboard();<br /> }</p>
		<p> // Validate the characters before pasting <br /> for(int iCounter_ = 0; iCounter_ &lt; strFromClipboard.GetLength(); iCounter_++)<br /> {<br />  if (-1 == m_strValidChars.Find(strFromClipboard.GetAt(iCounter_)))<br />  {<br />   return;<br />  }<br /> }<br />  <br /> //let the individual control handle other processing<br /> CEdit::Default(); <br />}<br />*/</p>
		<p>void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd) <br />{<br /> CEdit::OnKillFocus(pNewWnd);<br /> <br /> // TODO: Add your message handler code here</p>
		<p> // Get the text in the edit ctrl<br /> CString strEdit;<br /> GetWindowText(strEdit);</p>
		<p> // Send Notification to parent of edit ctrl<br /> LV_DISPINFO dispinfo;<br /> dispinfo.hdr.hwndFrom = GetParent()-&gt;m_hWnd;<br /> dispinfo.hdr.idFrom = GetDlgCtrlID();<br /> dispinfo.hdr.code = LVN_ENDLABELEDIT;</p>
		<p> dispinfo.item.mask = LVIF_TEXT;<br /> dispinfo.item.iItem = m_iRowIndex;<br /> dispinfo.item.iSubItem = m_iColumnIndex;<br /> dispinfo.item.pszText = m_bESC ? LPTSTR((LPCTSTR)m_strWindowText) : LPTSTR((LPCTSTR)strEdit);<br /> dispinfo.item.cchTextMax = m_bESC ? m_strWindowText.GetLength() : strEdit.GetLength();<br /> <br /> GetParent()-&gt;SendMessage(WM_NOTIFY, GetParent()-&gt;GetDlgCtrlID(), (LPARAM)&amp;dispinfo);</p>
		<p> PostMessage(WM_CLOSE);<br />}</p>
		<p>void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p>  if ((m_strValidChars.IsEmpty()) || ((-1 != m_strValidChars.Find(static_cast&lt;TCHAR&gt; (nChar))) || <br />  (nChar == VK_BACK) || (nChar == CTRL_C) || (nChar == CTRL_V) || (nChar == CTRL_X)))<br /> {<br />  CEdit::OnChar(nChar, nRepCnt, nFlags);<br /> }<br /> else<br /> {<br />  MessageBeep(MB_ICONEXCLAMATION);<br />  return;<br /> }<br />}</p>
		<p>BOOL CInPlaceEdit::PreTranslateMessage(MSG* pMsg) <br />{<br /> // TODO: Add your specialized code here and/or call the base class<br /> if (WM_KEYDOWN == pMsg-&gt;message &amp;&amp; (VK_ESCAPE == pMsg-&gt;wParam || VK_RETURN == pMsg-&gt;wParam))<br /> {<br />  if (VK_ESCAPE == pMsg-&gt;wParam)<br />  {<br />   m_bESC = TRUE;<br />  }</p>
		<p>  GetParent()-&gt;SetFocus();<br />  return TRUE;<br /> }</p>
		<p> return CEdit::PreTranslateMessage(pMsg);<br />}</p>
		<p>int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) <br />{<br /> if (CEdit::OnCreate(lpCreateStruct) == -1)<br />  return -1;<br /> <br /> // TODO: Add your specialized creation code here<br /> // Set the proper font<br /> CFont* pFont = GetParent()-&gt;GetFont();<br /> SetFont(pFont);</p>
		<p> ShowWindow(SW_SHOW);<br /> SetWindowText(m_strWindowText);<br /> SetSel(0, -1);<br /> SetFocus();<br /> <br />   <br /> return 0;<br />}</p>
		<p>CInPlaceEdit* CInPlaceEdit::GetInstance()<br />{<br /> if(m_pInPlaceEdit == NULL)<br /> {<br />  m_pInPlaceEdit = new CInPlaceEdit;<br /> }<br /> return m_pInPlaceEdit;<br />}</p>
		<p>void CInPlaceEdit::DeleteInstance()<br />{<br /> delete m_pInPlaceEdit;<br /> m_pInPlaceEdit = NULL;<br />}</p>
		<p>BOOL CInPlaceEdit::ShowEditCtrl(DWORD dwStyle, const RECT &amp;rCellRect, CWnd* pParentWnd, <br />        UINT uiResourceID, int iRowIndex, int iColumnIndex,<br />        CString&amp; strValidChars, CString&amp; rstrCurSelection)<br />{<br /> m_iRowIndex = iRowIndex;<br /> m_iColumnIndex = iColumnIndex;<br /> m_strValidChars = strValidChars;<br /> m_strWindowText = rstrCurSelection;<br /> m_bESC = FALSE;</p>
		<p> if (NULL == m_pInPlaceEdit-&gt;m_hWnd) <br /> {<br />  return m_pInPlaceEdit-&gt;Create(dwStyle, rCellRect, pParentWnd, uiResourceID); <br /> } </p>
		<p> return TRUE;<br />}<br /><br />//ComboListCtrl.h<br />#if !defined(AFX_COMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_)<br />#define AFX_COMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>//the max listCtrl columns<br />#define MAX_LISTCTRL_COLUMNS 100</p>
		<p>#include &lt;afxtempl.h&gt;</p>
		<p>class CInPlaceEdit;</p>
		<p>// User define message <br />// This message is posted to the parent<br />// The message can be handled to make the necessary validations, if any<br />#define WM_VALIDATE  WM_USER + 0x7FFD</p>
		<p>// User define message <br />// This message is posted to the parent<br />// The message should be handled to spcify the items to the added to the combo<br />#define WM_SET_ITEMS WM_USER + 0x7FFC</p>
		<p>class CComboListCtrl : public CListCtrl<br />{<br />public:<br /> <br />// Implementation<br /> typedef enum {MODE_READONLY,MODE_DIGITAL_EDIT,MODE_TEXT_EDIT,MODE_COMBO} COMBOLISTCTRL_COLUMN_MODE;</p>
		<p> // Constructor<br /> CComboListCtrl();</p>
		<p> // Destructor<br /> virtual ~CComboListCtrl();</p>
		<p> // Sets/Resets the column which support the in place combo box<br /> <br /> // Sets/Resets the column which support the in place edit control<br /> void SetReadOnlyColumns(int iColumnIndex, bool bSet = true);</p>
		<p> // Sets the valid characters for the edit ctrl<br /> void SetValidEditCtrlCharacters(CString&amp; rstrValidCharacters);</p>
		<p> // Sets the vertical scroll</p>
		<p> // Sets the horizontal scroll</p>
		<p> //insert column<br /> int CComboListCtrl::InsertColumn(int nCol,LPCTSTR lpszColumnHeading,int nFormat = LVCFMT_LEFT,int nWidth = -1,int nSubItem = -1);</p>
		<p> //Get column counts<br /> int GetColumnCounts();</p>
		<p> //delete all column<br /> void DeleteAllColumn();</p>
		<p> //set column Valid char string<br /> void SetColumnValidEditCtrlCharacters(CString &amp;rstrValidCharacters,int column = -1);</p>
		<p>// Overrides<br /> // ClassWizard generated virtual function overrides<br /> //{{AFX_VIRTUAL(CComboListCtrl)<br /> //}}AFX_VIRTUAL</p>
		<p>protected:</p>
		<p>// Methods<br /> // Generated message map functions<br /> //{{AFX_MSG(CComboListCtrl)<br /> afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);<br /> afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);<br /> afx_msg void OnLButtonDown(UINT nFlags, CPoint point);<br /> afx_msg void OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);<br /> afx_msg void OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);<br /> //}}AFX_MSG</p>
		<p> DECLARE_MESSAGE_MAP()</p>
		<p>private:<br />   <br />// Implementation</p>
		<p> // Returns the row &amp; column index of the column on which mouse click event has occured<br /> bool HitTestEx(CPoint&amp; rHitPoint, int* pRowIndex, int* pColumnIndex) const;</p>
		<p> // Creates and displays the in place combo box</p>
		<p> // Creates and displays the in place edit control<br /> CInPlaceEdit* ShowInPlaceEdit(int iRowIndex, int iColumnIndex, CString&amp; rstrCurSelection);</p>
		<p> // Calculates the cell rect<br /> void CalculateCellRect(int iColumnIndex, int iRowIndex, CRect&amp; robCellRect);</p>
		<p> // Checks whether column supports in place combo box</p>
		<p> // Checks whether column is read only<br /> bool IsReadOnly(int iColumnIndex);</p>
		<p> // Scrolls the list ctrl to bring the in place ctrl to the view<br /> void ScrollToView(int iColumnIndex, /*int iOffSet, */CRect&amp; obCellRect);</p>
		<p>// Attributes</p>
		<p> // List of columns that are read only<br /> CList&lt;int, int&gt; m_ReadOnlyColumnsList;</p>
		<p> // Valid characters<br /> CString m_strValidEditCtrlChars;</p>
		<p> // The window style of the in place edit ctrl<br /> DWORD m_dwEditCtrlStyle;</p>
		<p> //columnCounts<br /> int m_iColumnCounts;</p>
		<p> //column types<br /> COMBOLISTCTRL_COLUMN_MODE m_modeColumn[MAX_LISTCTRL_COLUMNS];</p>
		<p> //column <br /> CString m_strValidChars[MAX_LISTCTRL_COLUMNS];<br /> //int m_<br />};</p>
		<p>/////////////////////////////////////////////////////////////////////////////</p>
		<p>//{{AFX_INSERT_LOCATION}}<br />// Microsoft Visual C++ will insert additional declarations immediately before the previous line.</p>
		<p>#endif // !defined(AFX_COMBOLISTCTRL_H__9089600F_374F_4BFC_9482_DEAC0E7133E8__INCLUDED_)<br /><br />//ComboListCtrl.cpp<br />#include "stdafx.h"<br />#include "ComboListCtrl.h"<br />#include "InPlaceEdit.h"</p>
		<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
		<p>//#defines<br />#define FIRST_COLUMN    0<br />#define MIN_COLUMN_WIDTH   10<br />#define MAX_DROP_DOWN_ITEM_COUNT 10</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CComboListCtrl</p>
		<p>CComboListCtrl::CComboListCtrl()<br />{<br /> m_iColumnCounts = 0;<br /> m_ReadOnlyColumnsList.RemoveAll();<br /> m_strValidEditCtrlChars.Empty();<br /> m_dwEditCtrlStyle = ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_NOHIDESEL;<br />}</p>
		<p>CComboListCtrl::~CComboListCtrl()<br />{<br /> CInPlaceEdit::DeleteInstance();  <br />}</p>
		<p>
				<br />BEGIN_MESSAGE_MAP(CComboListCtrl, CListCtrl)<br /> //{{AFX_MSG_MAP(CComboListCtrl)<br /> ON_WM_HSCROLL()<br /> ON_WM_VSCROLL()<br /> ON_WM_LBUTTONDOWN()<br /> ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)<br /> ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)<br /> //}}AFX_MSG_MAP<br />END_MESSAGE_MAP()</p>
		<p>/////////////////////////////////////////////////////////////////////////////<br />// CComboListCtrl message handlers</p>
		<p>CInPlaceEdit* CComboListCtrl::ShowInPlaceEdit(int iRowIndex, int iColumnIndex, CString&amp; rstrCurSelection)<br />{<br /> // Create an in-place edit control<br /> CInPlaceEdit* pInPlaceEdit = CInPlaceEdit::GetInstance();<br />  <br /> CRect obCellRect(0, 0, 0, 0);<br /> CalculateCellRect(iColumnIndex, iRowIndex, obCellRect);<br />   <br /> pInPlaceEdit-&gt;ShowEditCtrl(m_dwEditCtrlStyle, obCellRect, this, 0, <br />          iRowIndex, iColumnIndex,<br />          m_strValidChars[iColumnIndex], rstrCurSelection);</p>
		<p> return pInPlaceEdit;<br />}</p>
		<p>void CComboListCtrl::OnHScroll(UINT iSBCode, UINT iPos, CScrollBar* pScrollBar) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> if (GetFocus() != this)<br /> {<br />  SetFocus();<br /> }</p>
		<p> CListCtrl::OnHScroll(iSBCode, iPos, pScrollBar);<br />}</p>
		<p>void CComboListCtrl::OnVScroll(UINT iSBCode, UINT iPos, CScrollBar* pScrollBar) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> if (GetFocus() != this)<br /> {<br />  SetFocus();<br /> }</p>
		<p> CListCtrl::OnVScroll(iSBCode, iPos, pScrollBar);<br />}</p>
		<p>void CComboListCtrl::OnLButtonDown(UINT iFlags, CPoint obPoint) <br />{<br /> // TODO: Add your message handler code here and/or call default</p>
		<p> int iColumnIndex = -1;<br /> int iRowIndex = -1;</p>
		<p> // Get the current column and row<br /> if (!HitTestEx(obPoint, &amp;iRowIndex, &amp;iColumnIndex))<br /> {<br />  return;<br /> }</p>
		<p> CListCtrl::OnLButtonDown(iFlags, obPoint);<br /> <br /> // If column is not read only then<br /> // If the SHIFT or CTRL key is down call the base class<br /> // Check the high bit of GetKeyState to determine whether SHIFT or CTRL key is down<br /> if ((GetKeyState(VK_SHIFT) &amp; 0x80) || (GetKeyState(VK_CONTROL) &amp; 0x80))<br /> {<br />  return;<br /> }</p>
		<p> // Get the current selection before creating the in place combo box<br /> CString strCurSelection = GetItemText(iRowIndex, iColumnIndex);<br /> <br /> if (-1 != iRowIndex)<br /> {<br />  UINT flag = LVIS_FOCUSED;<br />  <br />  if ((GetItemState(iRowIndex, flag ) &amp; flag) == flag)<br />  {<br />   // Add check for LVS_EDITLABELS<br />   if (GetWindowLong(m_hWnd, GWL_STYLE) &amp; LVS_EDITLABELS)<br />   {<br />    if (!IsReadOnly(iColumnIndex))<br />    {<br />     CInPlaceEdit* pInPlaceEdit = ShowInPlaceEdit(iRowIndex, iColumnIndex, strCurSelection);<br />    }<br />   }<br />  }<br /> }  <br />}</p>
		<p>bool CComboListCtrl::HitTestEx(CPoint &amp;obPoint, int* pRowIndex, int* pColumnIndex) const<br />{<br /> if (!pRowIndex || !pColumnIndex)<br /> {<br />  return false;<br /> }</p>
		<p> // Get the row index<br /> *pRowIndex = HitTest(obPoint, NULL);</p>
		<p> if (pColumnIndex)<br /> {<br />  *pColumnIndex = 0;<br /> }</p>
		<p> // Make sure that the ListView is in LVS_REPORT<br /> if ((GetWindowLong(m_hWnd, GWL_STYLE) &amp; LVS_TYPEMASK) != LVS_REPORT)<br /> {<br />  return false;<br /> }</p>
		<p> // Get the number of columns<br /> CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);</p>
		<p> int iColumnCount = pHeader-&gt;GetItemCount();</p>
		<p> // Get bounding rect of item and check whether obPoint falls in it.<br /> CRect obCellRect;<br /> GetItemRect(*pRowIndex, &amp;obCellRect, LVIR_BOUNDS);<br /> <br /> if (obCellRect.PtInRect(obPoint))<br /> {<br />  // Now find the column<br />  for (*pColumnIndex = 0; *pColumnIndex &lt; iColumnCount; (*pColumnIndex)++)<br />  {<br />   int iColWidth = GetColumnWidth(*pColumnIndex);<br />   <br />   if (obPoint.x &gt;= obCellRect.left &amp;&amp; obPoint.x &lt;= (obCellRect.left + iColWidth))<br />   {<br />    return true;<br />   }<br />   obCellRect.left += iColWidth;<br />  }<br /> }<br /> return false;<br />}</p>
		<p>void CComboListCtrl::SetReadOnlyColumns(int iColumnIndex, bool bSet /*= true*/)<br />{<br /> // If the Column Index is not present &amp;&amp; Set flag is false<br /> // Then do nothing <br /> // If the Column Index is present &amp;&amp; Set flag is true<br /> // Then do nothing<br /> POSITION Pos = m_ReadOnlyColumnsList.Find(iColumnIndex);</p>
		<p> // If the Column Index is not present &amp;&amp; Set flag is true<br /> // Then Add to list<br /> if ((NULL == Pos) &amp;&amp; bSet) <br /> {<br />  m_ReadOnlyColumnsList.AddTail(iColumnIndex); <br /> }</p>
		<p> // If the Column Index is present &amp;&amp; Set flag is false<br /> // Then Remove from list<br /> if ((NULL != Pos) &amp;&amp; !bSet) <br /> {<br />  m_ReadOnlyColumnsList.RemoveAt(Pos); <br /> }<br />}</p>
		<p>bool CComboListCtrl::IsReadOnly(int iColumnIndex)<br />{<br /> if (m_ReadOnlyColumnsList.Find(iColumnIndex))<br /> {<br />  return true;<br /> }<br /> <br /> return false;<br />}</p>
		<p>void CComboListCtrl::CalculateCellRect(int iColumnIndex, int iRowIndex, CRect&amp; robCellRect)<br />{<br /> GetItemRect(iRowIndex, &amp;robCellRect, LVIR_BOUNDS);<br /> <br /> CRect rcClient;<br /> GetClientRect(&amp;rcClient);</p>
		<p> if (robCellRect.right &gt; rcClient.right) <br /> {<br />  robCellRect.right = rcClient.right;<br /> }</p>
		<p> ScrollToView(iColumnIndex, robCellRect); <br />}</p>
		<p>void CComboListCtrl::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) <br />{<br /> LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;<br /> // TODO: Add your control notification handler code here<br /> <br /> // Update the item text with the new text<br /> SetItemText(pDispInfo-&gt;item.iItem, pDispInfo-&gt;item.iSubItem, pDispInfo-&gt;item.pszText);</p>
		<p> GetParent()-&gt;SendMessage(WM_VALIDATE, GetDlgCtrlID(), (LPARAM)pDispInfo); <br /> <br /> *pResult = 0;<br />}</p>
		<p>void CComboListCtrl::SetValidEditCtrlCharacters(CString &amp;rstrValidCharacters)<br />{<br /> m_strValidEditCtrlChars = rstrValidCharacters;<br />}</p>
		<p>void CComboListCtrl::SetColumnValidEditCtrlCharacters(CString &amp;rstrValidCharacters,int column)<br />{<br /> if(column&gt;MAX_LISTCTRL_COLUMNS-1)<br />  return;<br /> if(column == -1)<br /> {<br />  for(int i=0;i&lt;MAX_LISTCTRL_COLUMNS;i++)<br />  {<br />   m_strValidChars[i] = rstrValidCharacters;<br />  }<br /> }<br /> else<br />  m_strValidChars[column] = rstrValidCharacters;<br />}</p>
		<p>void CComboListCtrl::ScrollToView(int iColumnIndex, /*int iOffSet, */CRect&amp; robCellRect)<br />{<br /> // Now scroll if we need to expose the column<br /> CRect rcClient;<br /> GetClientRect(&amp;rcClient);</p>
		<p> int iColumnWidth = GetColumnWidth(iColumnIndex);</p>
		<p> // Get the column iOffset<br /> int iOffSet = 0;<br /> for (int iIndex_ = 0; iIndex_ &lt; iColumnIndex; iIndex_++)<br /> {<br />  iOffSet += GetColumnWidth(iIndex_);<br /> }</p>
		<p> // If x1 of cell rect is &lt; x1 of ctrl rect or<br /> // If x1 of cell rect is &gt; x1 of ctrl rect or **Should not ideally happen**<br /> // If the width of the cell extends beyond x2 of ctrl rect then<br /> // Scroll</p>
		<p> CSize obScrollSize(0, 0);</p>
		<p> if (((iOffSet + robCellRect.left) &lt; rcClient.left) || <br />  ((iOffSet + robCellRect.left) &gt; rcClient.right))<br /> {<br />  obScrollSize.cx = iOffSet + robCellRect.left;<br /> }<br /> else if ((iOffSet + robCellRect.left + iColumnWidth) &gt; rcClient.right)<br /> {<br />  obScrollSize.cx = iOffSet + robCellRect.left + iColumnWidth - rcClient.right;<br /> }</p>
		<p> Scroll(obScrollSize);<br /> robCellRect.left -= obScrollSize.cx;<br /> <br /> // Set the width to the column width<br /> robCellRect.left += iOffSet;<br /> robCellRect.right = robCellRect.left + iColumnWidth;<br />}</p>
		<p>void CComboListCtrl::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) <br />{<br /> LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;<br /> // TODO: Add your control notification handler code here<br /> if (IsReadOnly(pDispInfo-&gt;item.iSubItem))<br /> {<br />  *pResult = 1;<br />  return;<br /> }</p>
		<p> *pResult = 0;<br />}</p>
		<p>int CComboListCtrl::InsertColumn(int nCol,LPCTSTR lpszColumnHeading,int nFormat ,int nWidth,int nSubItem)<br />{<br /> m_iColumnCounts++;<br /> return CListCtrl::InsertColumn( nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);<br />}</p>
		<p>int CComboListCtrl::GetColumnCounts()<br />{<br /> return m_iColumnCounts;<br />}</p>
		<p>void CComboListCtrl::DeleteAllColumn()<br />{<br /> for(int i=0;i&lt;m_iColumnCounts;i++)<br /> {<br />  DeleteColumn(0);<br /> }<br />}<br /><br />//应用<br />CComboListCtrl m_CtrlList;<br /><br />m_CtrlList.InsertColumn(0, _T("编号"), LVCFMT_CENTER, 50);<br /> m_CtrlList.InsertColumn(1, _T("姓名"), LVCFMT_CENTER, 80);<br /> m_CtrlList.InsertColumn(2, _T("学号"), LVCFMT_CENTER, 80);<br /> m_CtrlList.InsertColumn(3, _T("性别"), LVCFMT_CENTER, 80);</p>
		<p> CString strValidChars;// <br /> m_CtrlList.SetReadOnlyColumns(0);//read only</p>
		<p> strValidChars = _T("");<br /> m_CtrlList.SetColumnValidEditCtrlCharacters(strValidChars,1);//none control edit <br /> <br /> strValidChars = _T("0123456789.");<br /> m_CtrlList.SetColumnValidEditCtrlCharacters(strValidChars,2);//digital only edit<br /> <br /> m_CtrlList.SetReadOnlyColumns(3);//read only<br /> <br /> CFont font;<br /> VERIFY(font.CreateFont(<br />    20,                        // nHeight<br />    0,                         // nWidth<br />    0,                         // nEscapement<br />    0,                         // nOrientation<br />    FW_NORMAL,                 // nWeight<br />    FALSE,                     // bItalic<br />    FALSE,                     // bUnderline<br />    0,                         // cStrikeOut<br />    ANSI_CHARSET,              // nCharSet<br />    OUT_DEFAULT_PRECIS,        // nOutPrecision<br />    CLIP_DEFAULT_PRECIS,       // nClipPrecision<br />    DEFAULT_QUALITY,           // nQuality<br />    DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily<br />    _T("宋体")));<br /> m_CtrlList.SetFont(&amp;font);<br /> m_CtrlList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);</p>
		<p> CString str;<br /> for(int i=0;i&lt;30;i++)<br /> {<br />  str.Format(_T("%d"), i+1);<br />  m_CtrlList.InsertItem(i, str);<br />  m_CtrlList.SetItemText(i, 1, _T("张三"));<br />  m_CtrlList.SetItemText(i, 2, _T("1234"));<br />  m_CtrlList.SetItemText(i, 3, _T("男"));<br /> }</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17456.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-09 12:29 <a href="http://www.cppblog.com/tiger/archive/2007/01/09/17456.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinCE数据库操作之CeCreateDatabaseEx</title><link>http://www.cppblog.com/tiger/archive/2007/01/09/17455.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Tue, 09 Jan 2007 04:17:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/09/17455.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17455.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/09/17455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17455.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17455.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 与CCeDBDatabase类数据库相比，CeCreateDatabaseEx具有独特的优势，其创建路径可以指定的特性决定了其数据可以永久保存的固有优势，但相比CCeDBDatabase类数据库而言，CeCreateDatabaseEx完全应用api函数操作，其操作也相对复杂一些。Records允许的9种数据类型：                 																	...&nbsp;&nbsp;<a href='http://www.cppblog.com/tiger/archive/2007/01/09/17455.html'>阅读全文</a><img src ="http://www.cppblog.com/tiger/aggbug/17455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-09 12:17 <a href="http://www.cppblog.com/tiger/archive/2007/01/09/17455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO数据库编程(WinCE)</title><link>http://www.cppblog.com/tiger/archive/2007/01/09/17454.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Tue, 09 Jan 2007 04:03:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/09/17454.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17454.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/09/17454.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17454.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17454.html</trackback:ping><description><![CDATA[
		<p>与C++中ADO数据库编程一样，WinCE中ADO数据库操作也需要_Connection和_Recordset指针，在此我们将其封装以求更方便，注：WinCE系统必须支持数据库的ADO操作。具体代码如下所示：<br />//VOConnection.h<br /><br />#if !defined(AFX_VOCONNECTION_H__EE2E886A_8A8A_4CC8_9A48_28681F64544B__INCLUDED_)<br />#define AFX_VOCONNECTION_H__EE2E886A_8A8A_4CC8_9A48_28681F64544B__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>#include &lt;COMDEF.H&gt;<br />#include &lt;ADOCE30.H&gt;<br />#include "VOString.h"</p>
		<p>class CVOConnection  <br />{<br />public: <br /> CVOConnection(LPCTSTR pcszProvider = NULL);<br /> virtual ~CVOConnection();</p>
		<p> BOOL Execute(LPCTSTR pcszSQL);<br /> BOOL Initialize();<br /> LPCTSTR GetProvider() { return m_strProvider; }</p>
		<p> operator _Connection*() { return m_Conn; }</p>
		<p> void SetConnectDB(LPCTSTR lpszDBName);<br /> void ClearConnect();<br /> <br />protected:<br /> CVOString m_strProvider;<br /> static TCHAR*  g_ProgID;<br /> static CLSID  g_ClsID;<br /> static BOOL   g_Init;</p>
		<p> _Connection*  m_Conn;<br />};</p>
		<p>#endif // !defined(AFX_VOCONNECTION_H__EE2E886A_8A8A_4CC8_9A48_28681F64544B__INCLUDED_)<br /><br />//VOConnection.cpp<br />#include "stdafx.h"<br />#include &lt;COMDEF.H&gt;<br />#include &lt;ADOCE30.H&gt;<br />#include "VOConnection.h"<br />#include "VORecordset.h"</p>
		<p>#ifdef _DEBUG<br />#undef THIS_FILE<br />static char THIS_FILE[]=__FILE__;<br />#define new DEBUG_NEW<br />#endif</p>
		<p>const IID IID__Connection = { 0x113033de, 0xf682, 0x11d2, { 0xbb, 0x62, 0x00, 0xc0, 0x4f, 0x68, 0x0a, 0xcc}};</p>
		<p>void __stdcall _com_issue_error(HRESULT m_hr)<br />{<br /> TCHAR pcszError[1024];</p>
		<p> _stprintf(pcszError, TEXT("_com_issue_error(%ld)\n"), m_hr);<br /> OutputDebugString(pcszError);<br />}</p>
		<p>BOOL CVOConnection::g_Init = FALSE;<br />CLSID CVOConnection::g_ClsID;<br />TCHAR* CVOConnection::g_ProgID = TEXT("ADOCE.Connection.3.0");</p>
		<p>//////////////////////////////////////////////////////////////////////<br />// Construction/Destruction<br />//////////////////////////////////////////////////////////////////////</p>
		<p>extern CVOConnection m_Conn;</p>
		<p>CVOConnection::CVOConnection(LPCTSTR pcszProvider) : m_Conn(NULL)<br />{<br /> <br /> if(!g_Init)<br />  Initialize();<br /> <br /> HRESULT hr;</p>
		<p> if(pcszProvider == NULL)<br /> {<br /> // pcszProvider = TEXT("cedb");</p>
		<p>  hr = CoCreateInstance(g_ClsID, NULL, CLSCTX_INPROC_SERVER, IID__Connection, (LPVOID*) &amp;m_Conn);<br />//  hr = m_Conn-&gt;put_Provider((LPTSTR)pcszProvider);<br />  hr = m_Conn-&gt;Open(TEXT("Disk\\tiger.mdb"),TEXT(""),TEXT(""),adOpenUnspecified);<br /> }<br /> else<br /> {<br />  m_strProvider = pcszProvider;<br />  m_Conn = NULL;<br /> }<br />}</p>
		<p>CVOConnection::~CVOConnection()<br />{<br /> if(m_Conn)<br /> {<br />  m_Conn-&gt;Close();<br />  m_Conn-&gt;Release();<br /> }<br />}</p>
		<p>BOOL CVOConnection::Initialize()<br />{<br /> if(FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))<br />  return FALSE;<br /> <br /> HRESULT hr;</p>
		<p> hr = CLSIDFromProgID(g_ProgID, &amp;g_ClsID);</p>
		<p> if(FAILED(hr))<br />  return FALSE;</p>
		<p> return TRUE;<br />}</p>
		<p>BOOL CVOConnection::Execute(LPCTSTR pcszSQL)<br />{<br /> CVORecordset rs(*this);</p>
		<p> return rs.Open(pcszSQL, adOpenForwardOnly, adLockPessimistic);<br />}</p>
		<p>void CVOConnection::SetConnectDB(LPCTSTR lpszDBName)<br />{<br /> if( m_Conn )<br /> {<br />  m_Conn-&gt;Close();<br />  m_Conn-&gt;put_Provider((LPTSTR)(TEXT("cedb")));<br />  m_Conn-&gt;Open((LPTSTR)lpszDBName,TEXT(""),TEXT(""),adOpenUnspecified); <br />  m_strProvider = lpszDBName;<br /> }<br />}</p>
		<p>void CVOConnection::ClearConnect()<br />{<br /> if( m_Conn )<br /> {<br />  m_Conn-&gt;Close();<br />  m_Conn-&gt;put_Provider((LPTSTR)(TEXT("cedb")));<br />  m_Conn-&gt;Open(TEXT(""),TEXT(""),TEXT(""),adOpenUnspecified); <br />  m_strProvider = TEXT("");<br /> }<br />}<br /><br />//VORecordset.h<br />#if !defined(AFX_VORECORDSET_H__B81BD14E_98F0_42A7_A64F_3FA21F5A3E5D__INCLUDED_)<br />#define AFX_VORECORDSET_H__B81BD14E_98F0_42A7_A64F_3FA21F5A3E5D__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>#include &lt;COMDEF.H&gt;<br />#include &lt;ADOCE30.H&gt;<br />#include "VOConnection.h"</p>
		<p>
				<br />class CVORecordset  <br />{</p>
		<p>public:<br /> CVORecordset(CVOConnection&amp; rConn);<br /> virtual ~CVORecordset();</p>
		<p>public:<br /> <br /> LPCTSTR GetFieldValueString(int iField);<br /> VARIANT GetFieldValue(int iField);<br /> LPCTSTR GetFieldName(int iField);<br /> Field* GetField(int iField);<br /> long GetFieldCount() { return m_FldCnt; }</p>
		<p> BOOL MoveNext();<br /> BOOL MoveFirst();<br /> BOOL IsEOF();<br /> BOOL IsBOF();<br /> BOOL Close();<br />// BOOL Open(LPCTSTR);<br /> BOOL Open(LPCTSTR pcszSource, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType);<br /> BOOL Initialize();<br /> BOOL IsOpen()   { return m_fIsOpen; }<br /> operator _Recordset*() { return m_rs; }</p>
		<p>protected:<br /> BOOL m_fIsOpen;<br /> CVOConnection&amp;  m_rConn;<br /> _Recordset*   m_rs;<br /> Fields*    m_Fields;<br /> Field*    m_Field;<br /> long    m_FldCnt;</p>
		<p> static TCHAR*  g_ProgID;<br /> static CLSID  g_ClsID;<br /> static BOOL   g_Init;<br />};<br />#endif // !defined(AFX_VORECORDSET_H__B81BD14E_98F0_42A7_A64F_3FA21F5A3E5D__INCLUDED_)<br /><br />//VORecordset.cpp<br />#include "stdafx.h"<br />#include "VORecordset.h"</p>
		<p>#ifdef _DEBUG<br />#undef THIS_FILE<br />static char THIS_FILE[]=__FILE__;<br />#define new DEBUG_NEW<br />#endif</p>
		<p>const IID IID__Recordset = { 0x113033f6, 0xf682, 0x11d2, { 0xbb, 0x62, 0x00, 0xc0, 0x4f, 0x68, 0x0a, 0xcc}};</p>
		<p>//////////////////////////////////////////////////////////////////////<br />// Construction/Destruction<br />//////////////////////////////////////////////////////////////////////</p>
		<p>BOOL CVORecordset::g_Init = FALSE;<br />CLSID CVORecordset::g_ClsID;<br />TCHAR* CVORecordset::g_ProgID = TEXT("ADOCE.Recordset.3.0");</p>
		<p>CVORecordset::CVORecordset(CVOConnection&amp; rConn) : m_rConn(rConn), m_rs(NULL)<br />{<br /> if(!g_Init)<br />  Initialize();</p>
		<p> HRESULT hr;</p>
		<p> hr = CoCreateInstance(g_ClsID, NULL, CLSCTX_INPROC_SERVER, IID__Recordset, (LPVOID*)&amp;m_rs);</p>
		<p> VARIANT varConn;</p>
		<p> varConn.pdispVal = (_Connection*)m_rConn;<br /> varConn.vt = VT_DISPATCH;<br /> hr = m_rs-&gt;put_ActiveConnection(varConn);<br />}</p>
		<p>CVORecordset::~CVORecordset()<br />{<br /> Close();<br /> if(m_rs)<br />  m_rs-&gt;Release();<br />}</p>
		<p>BOOL CVORecordset::Initialize()<br />{<br /> HRESULT hr;</p>
		<p> hr = CLSIDFromProgID( g_ProgID, &amp;g_ClsID);</p>
		<p> return(!FAILED(hr));<br />}</p>
		<p>BOOL CVORecordset::Open(LPCTSTR pcszSource, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType)<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::Open() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> HRESULT hr;<br /> m_rs-&gt;Close();<br /> hr = m_rs-&gt;Open(_variant_t(pcszSource), _variant_t( TEXT("Disk\\tiger.mdb") ), CursorType, LockType, adCmdUnknown);<br /> m_fIsOpen = (!FAILED(hr));</p>
		<p> m_rs-&gt;get_Fields(&amp;m_Fields);<br /> m_Fields-&gt;get_Count(&amp;m_FldCnt);</p>
		<p> return m_fIsOpen;<br />}</p>
		<p>BOOL CVORecordset::Close()<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::Close() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> m_rs-&gt;Close();</p>
		<p> m_fIsOpen = FALSE;<br /> return TRUE;<br />}</p>
		<p>BOOL CVORecordset::IsBOF()<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::BOF() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> VARIANT_BOOL fValue;<br /> HRESULT   hr = m_rs-&gt;get_BOF(&amp;fValue);</p>
		<p> return (fValue == VARIANT_TRUE);<br />}</p>
		<p>BOOL CVORecordset::IsEOF()<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::EOF() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> VARIANT_BOOL fValue;<br /> HRESULT   hr = m_rs-&gt;get_EOF(&amp;fValue);</p>
		<p> return (fValue == VARIANT_TRUE);<br />}</p>
		<p>BOOL CVORecordset::MoveFirst()<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::MoveFirst() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> HRESULT hr = m_rs-&gt;MoveFirst();</p>
		<p> return (!FAILED(hr));<br />}</p>
		<p>BOOL CVORecordset::MoveNext()<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::MoveNext() RecordSet COM Object not initialized\n"));<br />  return FALSE;<br /> }</p>
		<p> HRESULT hr = m_rs-&gt;MoveNext();</p>
		<p> return (!FAILED(hr));<br />}</p>
		<p>Field* CVORecordset::GetField(int iField)<br />{<br /> if(!m_rs)<br /> {<br />  TRACE(TEXT("CVORecordset::GetField() RecordSet COM Object not initialized\n"));<br />  return NULL;<br /> }</p>
		<p> HRESULT hr = m_Fields-&gt;get_Item(_variant_t((long)iField), &amp;m_Field);</p>
		<p> if(FAILED(hr))<br />  return NULL;</p>
		<p> return m_Field;<br />}</p>
		<p>LPCTSTR CVORecordset::GetFieldName(int iField)<br />{<br /> Field* pField = GetField(iField);</p>
		<p> if(!pField)<br /> {<br />  TRACE(TEXT("CVORecordset::GetFieldName() Invalid Field Index\n"));<br />  return NULL;<br /> }</p>
		<p> BSTR strFieldName;</p>
		<p> pField-&gt;get_Name(&amp;strFieldName);</p>
		<p> return strFieldName;<br />}</p>
		<p>VARIANT CVORecordset::GetFieldValue(int iField)<br />{<br /> VARIANT value;<br /> HRESULT hr;</p>
		<p> VariantInit(&amp;value);</p>
		<p> Field* pField = GetField(iField);</p>
		<p> if(!pField)<br /> {<br />  TRACE(TEXT("CVORecordset::GetFieldValue() Invalid Field Index\n"));<br />  return _variant_t((long)0);<br /> }</p>
		<p> pField = GetField(iField);<br /> hr = pField-&gt;get_Value(&amp;value);</p>
		<p> return value;<br />}</p>
		<p>LPCTSTR CVORecordset::GetFieldValueString(int iField)<br />{<br /> VARIANT value = GetFieldValue(iField);<br /> VARIANT valueString;</p>
		<p> VariantInit(&amp;valueString);</p>
		<p> if(value.vt == VT_BSTR)<br />  valueString = value;<br /> else<br />  VariantChangeType(&amp;valueString, &amp;value, 0, VT_BSTR);</p>
		<p> return valueString.bstrVal;<br />}<br /><br />//VOString.h<br />#if !defined(AFX_VOSTRING_H__91406803_1D87_4DA9_A5A0_499A88AC4E86__INCLUDED_)<br />#define AFX_VOSTRING_H__91406803_1D87_4DA9_A5A0_499A88AC4E86__INCLUDED_</p>
		<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
		<p>class CVOString  <br />{<br />public:<br /> CVOString(const CVOString&amp; rSrc);<br /> CVOString(LPCTSTR pcszValue = TEXT(""));<br /> virtual ~CVOString();<br /> operator LPCTSTR() { return (LPCTSTR)m_pBuffer; }<br /> DWORD GetLength() { return m_dwLength; }<br /> const CVOString&amp; operator =(LPCTSTR pcszValue);<br /> BOOL operator == (LPCTSTR pcszValue);<br /> const CVOString&amp; operator += (LPCTSTR pcszAppend);<br />protected:<br /> BOOL SetMinBufferSize(DWORD dwChars);<br /> DWORD m_dwLength;<br /> DWORD m_dwBufferSize;<br /> TCHAR* m_pBuffer;<br />};</p>
		<p>#endif // !defined(AFX_VOSTRING_H__91406803_1D87_4DA9_A5A0_499A88AC4E86__INCLUDED_)<br /><br />//VOString.cpp<br />//#include "stdafx.h"<br />#include "VOString.h"</p>
		<p>//////////////////////////////////////////////////////////////////////<br />// Construction/Destruction<br />//////////////////////////////////////////////////////////////////////</p>
		<p>CVOString::CVOString(LPCTSTR pcszValue)<br />{<br /> m_pBuffer = NULL;<br /> m_dwBufferSize = 0;</p>
		<p> *this = pcszValue;<br />}</p>
		<p>// Copy Constructor<br />CVOString::CVOString(const CVOString &amp;rSrc)<br />{<br /> m_pBuffer = NULL;<br /> m_dwBufferSize = 0;</p>
		<p> *this = rSrc.m_pBuffer;<br />}</p>
		<p>CVOString::~CVOString()<br />{<br /> if(m_pBuffer)<br />  delete m_pBuffer;<br />}</p>
		<p>const CVOString&amp; CVOString::operator = (LPCTSTR pcszValue)<br />{</p>
		<p> m_dwLength = _tcslen(pcszValue);</p>
		<p> SetMinBufferSize(m_dwLength);<br /> _tcscpy(m_pBuffer, pcszValue);<br /> return *this;<br />}</p>
		<p>BOOL CVOString::operator == (LPCTSTR pcszValue)<br />{<br /> return(_tcscmp(pcszValue, m_pBuffer) == 0);<br />}</p>
		<p>const CVOString&amp; CVOString::operator += (LPCTSTR pcszAppend)<br />{<br /> SetMinBufferSize(GetLength() + _tcslen(pcszAppend));</p>
		<p> _tcscat(m_pBuffer, pcszAppend);<br /> return *this;<br />}</p>
		<p>BOOL CVOString::SetMinBufferSize(DWORD dwChars)<br />{<br /> if(m_dwBufferSize &lt; dwChars + 1)<br /> {<br />  TCHAR* pNewBuffer;<br />  DWORD dwNewBufferSize = dwChars + 256;</p>
		<p>  pNewBuffer = new TCHAR[dwNewBufferSize];</p>
		<p>  if(m_pBuffer)<br />  {<br />   memmove(pNewBuffer, m_pBuffer, m_dwBufferSize);<br />   delete m_pBuffer;<br />  }</p>
		<p>  m_pBuffer = pNewBuffer;<br />  m_dwBufferSize = dwNewBufferSize;<br /> }</p>
		<p> return TRUE;<br />}</p>
<img src ="http://www.cppblog.com/tiger/aggbug/17454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-09 12:03 <a href="http://www.cppblog.com/tiger/archive/2007/01/09/17454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO数据库编程(C++)</title><link>http://www.cppblog.com/tiger/archive/2007/01/09/17453.html</link><dc:creator>tiger</dc:creator><author>tiger</author><pubDate>Tue, 09 Jan 2007 03:48:00 GMT</pubDate><guid>http://www.cppblog.com/tiger/archive/2007/01/09/17453.html</guid><wfw:comment>http://www.cppblog.com/tiger/comments/17453.html</wfw:comment><comments>http://www.cppblog.com/tiger/archive/2007/01/09/17453.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tiger/comments/commentRss/17453.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tiger/services/trackbacks/17453.html</trackback:ping><description><![CDATA[一、在VC++中使用ADO编程<br />ADO实际上就是由一组Automation对象构成的组件，因此可以象使用其它任何Automation对象一样使用ADO。ADO中最重要的对象有三个：Connection、Command和Recordset，它们分别表示连接对象、命令对象和记录集对象。如果您熟悉使用MFC中的ODBC类（CDatabase、CRecordset)编程，那么学习ADO编程就十分容易了。<br />使用ADO编程时可以采用以下三种方法之一：<br />1、使用预处理指令#import<br />#import "C:\Program Files\Common Files\System\ADO\msado15.dll"    no_namespace rename("EOF", "EndOfFile")<br />但要注意不能放在stdAfx.h文件的开头，而应该放在所有include指令的后面。否则在编译时会出错。<br />程序在编译过程中，VC++会读出msado15.dll中的类型库信息，自动产生两个该类型库的头文件和实现文件msado15.tlh和msado15.tli（在您的Debug或Release目录下）。在这两个文件里定义了ADO的所有对象和方法，以及一些枚举型的常量等。我们的程序只要直接调用这些方法就行了，与使用MFC中的COleDispatchDriver类调用Automation对象十分类似。<br /><br />2、使用MFC中的CIDispatchDriver<br />就是通过读取msado15.dll中的类型库信息，建立一个COleDispatchDriver类的派生类，然后通过它调用ADO对象。<br /><br />3、直接用COM提供的API<br />如使用如下代码：<br />CLSID clsid;<br />HRESULT hr = ::CLSIDFromProgID(L"ADODB.Connection", &amp;clsid);<br />if(FAILED(hr))<br />{...}<br />::CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)<br />      &amp;pDispatch);<br />if(FAILED(hr))<br />{...}<br />以上三种方法，第一和第二种类似，可能第一种好用一些，第三种编程可能最麻烦。但可能第三种方法也是效率最高的，程序的尺寸也最小，并且对ADO的控制能力也最强。<br />据微软资料介绍，第一种方法不支持方法调用中的默认参数，当然第二种方法也是这样，但第三种就不是这样了。采用第三种方法的水平也最高。当你需要绕过ADO而直接调用OLE DB底层的方法时，就一定要使用第三种方法了。<br />ADO编程的关键，就是熟练地运用ADO提供的各种对象(object)、方法(method)、属性(property)和容器（collection）。另外，如果是在MS SQL或Oracle等大型数据库上编程，还要能熟练使用SQL语言。<br /><br />二、使用#import方法的编程步骤<br />这里建议您使用#import的方法，因为它易学、易用，代码也比较简洁。<br />1、 添加#import指令<br />打开stdafx.h文件，将下列内容添加到所有的include指令之后：<br />#include &lt;icrsint.h&gt;  //Include support for VC++ Extensions<br />#import "C:\Program Files\Common Files\System\ADO\msado15.dll"    no_namespace rename("EOF", "adoEOF")<br />其中icrsint.h文件包含了VC++扩展的一些预处理指令、宏等的定义，用于COM编程时使用。<br /><br />2、定义_ConnectionPtr型变量，并建立数据库连接<br />建立了与数据库服务器的连接后，才能进行其他有关数据库的访问和操作。ADO使用Connection对象来建立与数据库服务器的连接，所以它相当于MFC中的CDatabase类。和CDatabase类一样，调用Connection对象的Open方法即可建立与服务器的连接。<br />数据类型 _ConnectionPtr实际上就是由类模板_com_ptr_t而得到的一个具体的实例类，其定义可以到msado15.tlh、comdef.h 和comip.h这三个文件中找到。在msado15.tlh中有：<br />_COM_SMARTPTR_TYPEDEF(_Collection, __uuidof(_Collection));<br />经宏扩展后就得到了_ConnectionPtr类。_ConnectionPtr类封装了Connection对象的Idispatch接口指针，及一些必要的操作。我们就是通过这个指针来操纵Connection对象。类似地，后面用到的_CommandPtr和_RecordsetPtr类型也是这样得到的，它们分别表示命令对象指针和记录集对象的指针。<br />（1）、连接到MS SQL Server<br />注意连接字符串的格式，提供正确的连接字符串是成功连接到数据库服务器的第一步，有关连接字符串的详细信息参见微软MSDN Library光盘。<br />本例连接字符串中的server_name，database_name，user_name和password在编程时都应该替换成实际的内容。<br />_ConnectionPtr pMyConnect=NULL;<br />HRESULT hr=pMyConnect.CreateInstance(__uuidof(Connection)));<br />if(FAILED(hr))return;<br /><br />_bstr_t strConnect="Provider=SQLOLEDB; Server=server_name;"<br />  "Database=database_name; uid=user_name; pwd=password;"; <br />//connecting to the database server now:<br />try{pMyConnect-&gt;Open(strConnect,"","",NULL);}<br />catch (_com_error &amp;e)<br />{<br />  ::MessageBox(NULL,e.Description(),"警告",MB_OK | MB_ICONWARNING);<br />}<br /><br />注意Connection对象的Open方法中的连接字符串参数必须是BSTR或_bstr_t类型。另外，本例是直接通过OLE DB Provider建立连接，所以无需建立数据源。<br /><br />（2）、通过ODBC Driver连接到Database Server<br />连接字符串格式与直接用ODBC编程时的差不多：<br />_bstr_t strConnect="DSN=datasource_name; Database=database_name; uid=user_name; pwd=password;";<br />此时与ODBC编程一样，必须先建立数据源。<br /><br />3、定义_RecordsetPtr型变量，并打开数据集<br />定义_RecordsetPtr型变量，然后通过它调用Recordset对象的Open方法，即可打开一个数据集。所以Recordset对象与MFC中的CRecordset类类似，它也有当前记录、当前记录指针的概念。如：<br />_RecordsetPtr m_pRecordset;<br />if(!FAILED(m_pRecordset.CreateInstance( __uuidof( Recordset )))<br />{<br />  m_pDoc-&gt;m_initialized=FALSE;<br />  return;<br />}<br /><br />try{<br />  m_pRecordset-&gt;Open(_variant_t("mytable"),<br />            _variant_t((IDispatch *)pMyConnect,true), adOpenKeyset,<br />            adLockOptimistic, adCmdTable);<br />}<br />catch (_com_error &amp;e)<br />{<br />  ::MessageBox(NULL,"无法打开mytable表。","提示",<br />MB_OK | MB_ICONWARNING);<br />}<br />Recordset对象的Open方法非常重要，它的第一个参数可以是一个SQL语句、一个表的名字或一个命令对象等等；第二个参数就是前面建立的连接对象的指针。此外，用Connection和Command对象的Execute方法也能得到记录集，但是只读的。<br /><br />4、读取当前记录的数据<br />我认为读取数据的最方便的方法如下：<br />try{<br />  m_pRecordset-&gt;MoveFirst();  <br />  while(m_pRecordset-&gt;adoEOF==VARIANT_FALSE) <br />  {<br />  //Retrieve column's value: <br />  CString sName=(char*)(_bstr_t)(m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("name"))-&gt;Value);<br />  short cAge=(short)(m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("age"))-&gt;Value);<br />  //Do something what you want to do:<br />  ......<br />  m_pRecordset-&gt;MoveNext();  <br />  }<br />}//try<br />catch (_com_error &amp;e)<br />{<br />  CString str=(char*)e.Description();<br />  ::MessageBox(NULL,str+"\n又出毛病了。","提示",<br />MB_OK | MB_ICONWARNING);<br />}<br /><br />本例中的name和age都是字段名，读取的字段值分别保存在sName和cAge变量内。例中的Fields是Recordset对象的容器，GetItem方法返回的是Field对象，而Value则是Field对象的一个属性（即该字段的值）。通过此例，应掌握操纵对象属性的方法。例如，要获得Field 对象的Value属性的值可以直接用属性名Value来引用它（如上例），但也可以调用Get方法，例如：<br />CString sName=(char*)(_bstr_t)(m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("name"))-&gt;GetValue());<br />从此例还可以看到，判断是否到达记录集的末尾，使用记录集的adoEOF属性，其值若为真即到了结尾，反之则未到。判断是否到达记录集开头，则可用BOF属性。<br />另外，读取数据还有一个方法，就是定义一个绑定的类，然后通过绑定的变量得到字段值（详见后面的介绍）。<br /><br />5、修改数据<br />方法一：<br />try{<br />  m_pRecordset-&gt;MoveFirst();  <br />  while(m_pRecordset-&gt;adoEOF==VARIANT_FALSE) <br />  {  <br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("姓名"))-&gt;Value=_bstr_t("赵薇");<br />  ......<br />  m_pRecordset-&gt;Update();<br /><br />  m_pRecordset-&gt;MoveNext();  <br />  }<br />}//try<br />改变了Value属性的值，即改变了字段的值。<br />方法二：<br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("姓名"))-&gt;PutValue(_bstr_t("赵薇"));<br />方法三：就是用定义绑定类的方法（详见后面的介绍）。<br /><br />6、添加记录<br />新记录添加成功后，即自动成为当前记录。AddNew方法有两种形式，一个含有参数，而另一个则不带参数。<br />方法一（不带参数）：<br />// Add new record into this table:<br />try{<br />  if(!m_pRecordset-&gt;Supports(adAddNew)) return;<br /><br />  m_pRecordset-&gt;AddNew(); <br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />  (_variant_t("姓名"))-&gt;Value=_bstr_t("赵薇");<br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />  (_variant_t("性别"))-&gt;Value=_bstr_t("女");<br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />  (_variant_t("age"))-&gt;Value=_variant_t((short)20);<br />  m_pRecordset-&gt;Fields-&gt;GetItem<br />  (_variant_t("marry"))-&gt;Value=_bstr_t("未婚");<br />  m_pRecordset-&gt;Update();  <br />}//try<br />catch (_com_error &amp;e)<br />{<br />  ::MessageBox(NULL, "又出毛病了。","提示",MB_OK | MB_ICONWARNING);<br />}<br />这种方法弄完了还要调用Update()。<br />方法二（带参数）：<br />  _variant_t varName[4],narValue[4];<br />  varName[0] = L"姓名";<br />  varName[1] = L"性别";<br />  varName[2] = L"age";<br />  varName[3] = L"marry";<br />  narValue[0]=_bstr_t("赵薇");<br />  narValue[1]=_bstr_t("女");<br />  narValue[2]=_variant_t((short)20);<br />  narValue[3]=_bstr_t("未婚");<br /><br />  const int nCrit = sizeof varName / sizeof varName[0];<br />  // Create SafeArray Bounds and initialize the array<br />  SAFEARRAYBOUND rgsaName[1],rgsaValue[1];<br />  rgsaName[0].lLbound = 0;  <br />  rgsaName[0].cElements = nCrit;<br />  SAFEARRAY *psaName = SafeArrayCreate( VT_VARIANT, 1, rgsaName );<br />  rgsaValue[0].lLbound = 0;<br />  rgsaValue[0].cElements = nCrit;<br />  SAFEARRAY *psaValue = SafeArrayCreate( VT_VARIANT, 1, rgsaValue );<br />  // Set the values for each element of the array<br />  HRESULT hr1=S_OK.hr2=S_OK;<br />  for( long i = 0 ; i &lt; nCrit &amp;&amp; SUCCEEDED( hr1 ) &amp;&amp; SUCCEEDED( hr2 );i++)  <br />  {      <br />  hr1=SafeArrayPutElement(psaName, &amp;i,&amp;varName[i]);<br />  hr2=SafeArrayPutElement(psaValue, &amp;i,&amp;narValue[i]);      }<br />  <br />  // Initialize and fill the SafeArray<br />  VARIANT vsaName,vsaValue;  <br />  vsaName.vt = VT_VARIANT | VT_ARRAY;<br />  vsaValue.vt = VT_VARIANT | VT_ARRAY;<br />  V_ARRAY(&amp;vsaName) = psaName;//&amp;vsaName-&gt;parray=psaName;<br />  //see definition in oleauto.h file.<br />  V_ARRAY(&amp;vsaValue) = psaValue;<br />  <br />  // Add a new record:<br />  m_pRecordset-&gt;AddNew(vsaName,vsaValue);<br />这种方法不需要调用Update，因为添加后，ADO会自动调用它。此方法主要是使用SafeArray挺麻烦。<br />方法三：就是用定义绑定类的方法（详见后面的介绍）。<br /><br />7、删除记录<br />调用Recordset的Delete方法就行了，删除的是当前记录。要了解Delete的其它用法请查阅参考文献。<br />try{<br />  m_pRecordset-&gt;MoveFirst();  <br />  while(m_pRecordset-&gt;adoEOF==VARIANT_FALSE) <br />  {<br />  CString sName=(char*)(_bstr_t)(m_pRecordset-&gt;Fields-&gt;GetItem<br />    (_variant_t("姓名"))-&gt;Value);<br />  if(::MessageBox(NULL,"姓名="+sName+"\n删除她吗？",<br />    "提示",MB_YESNO | MB_ICONWARNING)==IDYES)<br />  {<br />    m_pRecordset-&gt;Delete(adAffectCurrent);  <br />    m_pRecordset-&gt;Update();<br />  }<br />  m_pRecordset-&gt;MoveNext();  <br />  }<br />}//try<br />catch (_com_error &amp;e)<br />{<br />  ::MessageBox(NULL,"又出毛病了。","提示",MB_OK | MB_ICONWARNING);<br />}<br /><br />8、使用带参数的命令<br />Command对象所代表的就是一个Provider能够理解的命令，如SQL语句等。使用Command对象的关键就是把表示命令的语句设置到CommandText属性中，然后调用Command对象的Execute方法就行了。一般情况下在命令中无需使用参数，但有时使用参数，可以增加其灵活性和效率。<br />(1). 建立连接、命令对象和记录集对象<br />本例中表示命令的语句就是一个SQL语句（SELECT语句）。SELECT语句中的问号?就代表参数，如果要多个参数，就多放几个问号，每个问号代表一个参数。<br />_ConnectionPtr  Conn1;<br />_CommandPtr    Cmd1;<br />ParametersPtr  *Params1 = NULL;  // Not an instance of a smart pointer.<br />_ParameterPtr  Param1;<br />_RecordsetPtr  Rs1;<br /><br />try<br />{<br />// Create Connection Object (1.5 Version)<br />Conn1.CreateInstance( __uuidof( Connection ) );<br />Conn1-&gt;ConnectionString = bstrConnect;<br />    Conn1-&gt;Open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );<br />    // Create Command Object<br />    Cmd1.CreateInstance( __uuidof( Command ) );<br />    Cmd1-&gt;ActiveConnection = Conn1;<br />    Cmd1-&gt;CommandText  = _bstr_t("SELECT * FROM mytable WHERE age&lt; ?");<br />}//try<br />要注意命令对象必须与连接对象关联起来才能起作用，本例中将命令对象的ActiveConnection属性设置为连接对象的指针，即为此目的：<br />Cmd1-&gt;ActiveConnection = Conn1;<br /><br />(2). 创建参数对象，并给参数赋值<br />// Create Parameter Object<br />Param1 = Cmd1-&gt;CreateParameter( _bstr_t(bstrEmpty),<br />      adInteger,<br />      adParamInput,<br />      -1,<br />        _variant_t( (long) 5) );<br />Param1-&gt;Value = _variant_t( (long) 5 );<br />Cmd1-&gt;Parameters-&gt;Append( Param1 );<br />用命令对象的方法来创建一个参数对象，其中的长度参数（第三个）如果是固定长度的类型，就填-1，如果是字符串等可变长度的就填其实际长度。Parameters是命令对象的一个容器，它的Append方法就是把创建的参数对象追加到该容器里。Append进去的参数按先后顺序与SQL语句中的问号从左至右一一对应。<br /><br />(3). 执行命令打开记录集<br />// Open Recordset Object<br />Rs1 = Cmd1-&gt;Execute( &amp;vtEmpty, &amp;vtEmpty2, adCmdText );<br />但要注意，用Command和Connection对象的Execute方法得到的Recordset是只读的。因为在打开Recordset之前，我们无法设置它的LockType属性（其默认值为只读）。而在打开之后设置LockType不起作用。<br />我发现用上述方法得到记录集Rs1后，不但Rs1中的记录无法修改，即使直接用SQL语句修改同一表中任何记录都不行。<br />要想能修改数据，还是要用Recordset自己的Open方法才行，如：<br />try{<br />  m_pRecordset-&gt;Open((IDispatch *) Cmd1, vtMissing,<br />    adOpenStatic, adLockOptimistic, adCmdUnspecified);<br />  }<br />  catch (_com_error &amp;e)<br />  {<br />  ::MessageBox(NULL,"mytable表不存在。","提示",MB_OK | MB_ICONWARNING);<br />  }<br />Recordset对象的Open方法真是太好了，其第一个参数可以是SQL语句、表名字、命令对象指针等等。<br /><br />9、响应ADO的通知事件<br />通知事件就是当某个特定事件发生时，由Provider通知客户程序，换句话说，就是由Provider调用客户程序中的一个特定的方法（即事件的处理函数）。所以为了响应一个事件，最关键的就是要实现事件的处理函数。<br />(1). 从ConnectionEventsVt接口派生出一个类<br />为了响应_Connection的通知事件，应该从ConnectionEventsVt接口派生出一个类：<br />class CConnEvent : public ConnectionEventsVt<br />{<br />private:<br />      ULONG  m_cRef;<br />public:<br />      CConnEvent() { m_cRef = 0; };<br />      ~CConnEvent() {};<br /><br />      STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);<br />      STDMETHODIMP_(ULONG) AddRef(void);<br />      STDMETHODIMP_(ULONG) Release(void);<br />      STDMETHODIMP raw_InfoMessage( <br />        struct Error *pError,<br />        EventStatusEnum *adStatus,<br />        struct _Connection *pConnection);<br />      STDMETHODIMP raw_BeginTransComplete( <br />        LONG TransactionLevel,<br />        struct Error *pError,<br />        EventStatusEnum *adStatus,<br />        struct _Connection *pConnection);<br />  ......<br />};<br /><br />(2). 实现每一个事件的处理函数(凡是带raw_前缀的方法都把它实现了)：<br />STDMETHODIMP CConnEvent::raw_InfoMessage( <br />        struct Error *pError,<br />        EventStatusEnum *adStatus,<br />        struct _Connection *pConnection)<br />        {<br />        *adStatus = adStatusUnwantedEvent;<br />        return S_OK;<br />        };<br /><br />有些方法虽然你并不需要，但也必须实现它，只需简单地返回一个S_OK即可。但如果要避免经常被调用，还应在其中将adStatus参数设置为adStatusUnwantedEvent，则在本次调用后，以后就不会被调用了。<br />另外还必须实现QueryInterface, AddRef, 和Release三个方法: <br />STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv) <br />{<br />      *ppv = NULL;<br />      if (riid == __uuidof(IUnknown) || <br />          riid == __uuidof(ConnectionEventsVt)) *ppv = this;<br />      if (*ppv == NULL)<br />        return ResultFromScode(E_NOINTERFACE);<br />      AddRef();<br />      return NOERROR;<br />  }<br />  STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };<br />  STDMETHODIMP_(ULONG) CConnEvent::Release()<br />{ <br />if (0 != --m_cRef) return m_cRef;<br />delete this;<br />return 0;<br />}<br /><br />(3). 开始响应通知事件<br />// Start using the Connection events<br />IConnectionPointContainer  *pCPC = NULL;<br />IConnectionPoint        *pCP = NULL;<br /><br />hr = pConn.CreateInstance(__uuidof(Connection));<br />  if (FAILED(hr)) return;<br /><br />hr = pConn-&gt;QueryInterface(__uuidof(IConnectionPointContainer), <br />      (void **)&amp;pCPC);<br />if (FAILED(hr)) return;<br />hr = pCPC-&gt;FindConnectionPoint(__uuidof(ConnectionEvents), &amp;pCP);<br />pCPC-&gt;Release();<br />if (FAILED(hr)) return;<br /><br />pConnEvent = new CConnEvent();<br />  hr = pConnEvent-&gt;QueryInterface(__uuidof(IUnknown), (void **) &amp;pUnk);<br />  if (FAILED(hr)) return rc; <br />  hr = pCP-&gt;Advise(pUnk, &amp;dwConnEvt);<br />  pCP-&gt;Release();<br />  if (FAILED(hr)) return;<br /><br />pConn-&gt;Open("dsn=Pubs;", "sa", "", adConnectUnspecified); <br />也就是说在连接(Open)之前就做这些事。<br /><br />(4). 停止响应通知事件<br />pConn-&gt;Close();<br />// Stop using the Connection events<br />  hr = pConn-&gt;QueryInterface(__uuidof(IConnectionPointContainer), <br />      (void **) &amp;pCPC);<br />  if (FAILED(hr)) return;<br />  hr = pCPC-&gt;FindConnectionPoint(__uuidof(ConnectionEvents), &amp;pCP);<br />  pCPC-&gt;Release();<br />  if (FAILED(hr)) return rc;<br />  hr = pCP-&gt;Unadvise( dwConnEvt );<br />  pCP-&gt;Release();<br />  if (FAILED(hr)) return;<br />在连接关闭之后做这件事。 <br /><br />10、邦定数据<br />定义一个绑定类，将其成员变量绑定到一个指定的记录集，以方便于访问记录集的字段值。<br />(1). 从CADORecordBinding派生出一个类：<br />class CCustomRs : public CADORecordBinding<br />{<br />BEGIN_ADO_BINDING(CCustomRs)<br />  ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname, <br />        sizeof(m_szau_fname), lau_fnameStatus, false)<br />  ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname, <br />        sizeof(m_szau_lname), lau_lnameStatus, false)<br />  ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone,    <br />        sizeof(m_szphone),    lphoneStatus,    true)<br />END_ADO_BINDING()<br /><br />public:<br />  CHAR  m_szau_fname[22];<br />  ULONG  lau_fnameStatus;<br />  CHAR  m_szau_lname[42];<br />  ULONG  lau_lnameStatus;<br />  CHAR  m_szphone[14];<br />  ULONG  lphoneStatus;<br />};<br />其中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量，一个存放字段的值，另一个存放字段的状态。字段用从1开始的序号表示，如1，2，3等等。<br />特别要注意的是：如果要绑定的字段是字符串类型，则对应的字符数组的元素个数一定要比字段长度大2（比如m_szau_fname[22]，其绑定的字段au_fname的长度实际是20），不这样绑定就会失败。我分析多出的2可能是为了存放字符串结尾的空字符null和BSTR字符串开头的一个字（表示BSTR的长度）。这个问题对于初学者来说可能是一个意想不到的问题。<br />CADORecordBinding类的定义在icrsint.h文件里，内容是：<br />class CADORecordBinding<br />{<br />public:<br />STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;<br />};<br /><br />BEGIN_ADO_BINDING宏的定义也在icrsint.h文件里，内容是：<br />#define BEGIN_ADO_BINDING(cls) public: typedef cls ADORowClass; const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { static const ADO_BINDING_ENTRY rgADOBindingEntries[] = { <br /><br />ADO_VARIABLE_LENGTH_ENTRY2宏的定义也在icrsint.h文件里：<br />#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify) {Ordinal,  DataType,  0,  0,  Size,  offsetof(ADORowClass, Buffer),  offsetof(ADORowClass, Status),  0,  classoffset(CADORecordBinding, ADORowClass),  Modify},<br /><br />#define END_ADO_BINDING宏的定义也在icrsint.h文件里：<br />#define END_ADO_BINDING()  {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}}; return rgADOBindingEntries;}<br /><br />(2). 绑定<br />_RecordsetPtr  Rs1;<br />IADORecordBinding  *picRs=NULL;<br />CCustomRs rs;<br />......<br />Rs1-&gt;QueryInterface(__uuidof(IADORecordBinding), <br />              (LPVOID*)&amp;picRs));<br />picRs-&gt;BindToRecordset(&amp;rs);<br />派生出的类必须通过IADORecordBinding接口才能绑定，调用它的BindToRecordset方法就行了。<br /><br />(3). rs中的变量即是当前记录字段的值<br />//Set sort and filter condition:<br />// Step 4: Manipulate the data<br />Rs1-&gt;Fields-&gt;GetItem("au_lname")-&gt;Properties-&gt;GetItem("Optimize")-&gt;Value = true; <br />Rs1-&gt;Sort = "au_lname ASC";<br />Rs1-&gt;Filter = "phone LIKE '415 5*'";<br /><br />Rs1-&gt;MoveFirst();<br />while (VARIANT_FALSE == Rs1-&gt;EndOfFile)<br />{<br />printf("Name: %s\t %s\tPhone: %s\n",  <br />  (rs.lau_fnameStatus == adFldOK ? rs.m_szau_fname : ""), <br />        (rs.lau_lnameStatus == adFldOK ? rs.m_szau_lname : ""),<br />        (rs.lphoneStatus == adFldOK ? rs.m_szphone  : ""));<br />        if (rs.lphoneStatus == adFldOK)<br />            strcpy(rs.m_szphone, "777");<br />        TESTHR(picRs-&gt;Update(&amp;rs));  // Add change to the batch<br />  Rs1-&gt;MoveNext();<br />}<br />Rs1-&gt;Filter = (long) adFilterNone;<br />......<br />if (picRs) picRs-&gt;Release();<br />Rs1-&gt;Close();<br />pConn-&gt;Close();<br />只要字段的状态是adFldOK，就可以访问。如果修改了字段，不要忘了先调用picRs的Update（注意不是Recordset的Update），然后才关闭，也不要忘了释放picRs（即picRs-&gt;Release();）。<br /><br />(4). 此时还可以用IADORecordBinding接口添加新纪录<br />if(FAILED(picRs-&gt;AddNew(&amp;rs)))<br />......<br />11. 访问长数据<br />在Microsoft SQL中的长数据包括text、image等这样长类型的数据，作为二进制字节来对待。<br />可以用Field对象的GetChunk和AppendChunk方法来访问。每次可以读出或写入全部数据的一部分，它会记住上次访问的位置。但是如果中间访问了别的字段后，就又得从头来了。<br />请看下面的例子：<br />//写入一张照片到数据库：<br />VARIANT varChunk;<br />SAFEARRAY *psa;<br />SAFEARRAYBOUND rgsabound[1];<br /><br />//VT_ARRAY | VT_UI1<br />CFile f("h:\\aaa.jpg",CFile::modeRead);<br />BYTE  bVal[ChunkSize+1];<br />UINT uIsRead=0;<br />//Create a safe array to store the array of BYTES  <br />while(1)<br />{<br />uIsRead=f.Read(bVal,ChunkSize);<br />if(uIsRead==0)break;<br />rgsabound[0].cElements =uIsRead;<br />    rgsabound[0].lLbound = 0;<br />psa = SafeArrayCreate(VT_UI1,1,rgsabound);<br />for(long index=0;index&lt;uIsRead;index++)          <br />{<br />  if(FAILED(SafeArrayPutElement(psa,&amp;index,&amp;bVal[index])))<br />  ::MessageBox(NULL,"啊，又出毛病了。","提示",MB_OK | MB_ICONWARNING);<br />}<br />varChunk.vt = VT_ARRAY|VT_UI1;<br />varChunk.parray = psa;<br />try{<br />  m_pRecordset-&gt;Fields-&gt;GetItem("photo")-&gt;AppendChunk(varChunk); <br />}<br />catch (_com_error &amp;e)<br />{<br />  CString str=(char*)e.Description();<br />  ::MessageBox(NULL,str+"\n又出毛病了。","提示",MB_OK | MB_ICONWARNING);<br />}<br />::VariantClear(&amp;varChunk);<br />::SafeArrayDestroyData( psa);<br />if(uIsRead&lt;ChunkSize)break;<br />}//while(1)  <br />f.Close();<br /><br />//从数据库读一张照片：<br />CFile f;<br />f.Open("h:\\bbb.jpg",CFile::modeWrite|CFile::modeCreate);<br />long lPhotoSize = m_pRecordset-&gt;Fields-&gt;Item["photo"]-&gt;ActualSize;  <br />long lIsRead=0;<br /><br />_variant_t varChunk;<br />BYTE buf[ChunkSize];<br />while(lPhotoSize&gt;0)<br />{<br />lIsRead=lPhotoSize&gt;=ChunkSize? ChunkSize:lPhotoSize;<br />varChunk = m_pRecordset-&gt;Fields-&gt;<br />                  Item["photo"]-&gt;GetChunk(lIsRead);<br />for(long index=0;index&lt;lIsRead;index++)        <br />{          <br />  ::SafeArrayGetElement(varChunk.parray,&amp;index,buf+index);  <br />}<br />f.Write(buf,lIsRead);<br />lPhotoSize-=lIsRead;<br />}//while()<br />f.Close();<br /><br />12. 使用SafeArray问题<br />学会使用SafeArray也是很重要的，因为在ADO编程中经常要用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中，数组是不能直接传递的，而必须将其包装成SafeArray。实质上SafeArray就是将通常的数组增加一个描述符，说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用，而是将其再包装到VARIANT类型的变量中，然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY|...,那么它所封装的就是一个SafeArray，它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型，包括VARIANT类型本身。 <br />使用SafeArray的具体步骤：<br />方法一：<br />包装一个SafeArray：<br />(1). 定义变量，如：<br />VARIANT varChunk;<br />SAFEARRAY *psa;<br />    SAFEARRAYBOUND rgsabound[1];<br /><br />(2). 创建SafeArray描述符：<br />uIsRead=f.Read(bVal,ChunkSize);//read array from a file.<br />if(uIsRead==0)break;<br />rgsabound[0].cElements =uIsRead;<br />rgsabound[0].lLbound = 0;<br />psa = SafeArrayCreate(VT_UI1,1,rgsabound);<br /><br />(3). 放置数据元素到SafeArray：<br />for(long index=0;index&lt;uIsRead;index++)          <br />{<br />  if(FAILED(SafeArrayPutElement(psa,&amp;index,&amp;bVal[index])))<br />  ::MessageBox(NULL,"出毛病了。","提示",MB_OK | MB_ICONWARNING);<br />}<br />一个一个地放，挺麻烦的。<br />(4). 封装到VARIANT内：<br />varChunk.vt = VT_ARRAY|VT_UI1;<br />varChunk.parray = psa;<br />这样就可以将varChunk作为参数传送出去了。<br /><br />读取SafeArray中的数据的步骤：<br />(1). 用SafeArrayGetElement一个一个地读<br />BYTE buf[lIsRead];<br />for(long index=0;index&lt;lIsRead;index++)        <br />{          <br />  ::SafeArrayGetElement(varChunk.parray,&amp;index,buf+index);  <br />}<br />就读到缓冲区buf里了。<br /><br />方法二：<br />使用SafeArrayAccessData直接读写SafeArray的缓冲区：<br />(1). 读缓冲区：<br />BYTE *buf;<br />SafeArrayAccessData(varChunk.parray, (void **)&amp;buf);<br />f.Write(buf,lIsRead);<br />SafeArrayUnaccessData(varChunk.parray);<br /><br />(2). 写缓冲区：<br />BYTE *buf;<br />::SafeArrayAccessData(psa, (void **)&amp;buf);<br />for(long index=0;index&lt;uIsRead;index++)          <br />{<br />  buf[index]=bVal[index];  <br />}<br />::SafeArrayUnaccessData(psa);<br /><br />varChunk.vt = VT_ARRAY|VT_UI1;<br />varChunk.parray = psa;<br /><br />这种方法读写SafeArray都可以，它直接操纵SafeArray的数据缓冲区，比用SafeArrayGetElement和SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData(psa)，否则会出错的。<br /><br />13. 使用书签( bookmark )<br />书签可以唯一标识记录集中的一个记录，用于快速地将当前记录移回到已访问过的记录，以及进行过滤等等。Provider会自动为记录集中的每一条记录产生一个书签，我们只需要使用它就行了。我们不能试图显示、修改或比较书签。ADO用记录集的Bookmark属性表示当前记录的书签。<br />用法步骤：<br />(1). 建立一个VARIANT类型的变量<br />_variant_t VarBookmark;<br /><br />(2). 将当前记录的书签值存入该变量<br />也就是记录集的Bookmark属性的当前值。<br />VarBookmark = rst-&gt;Bookmark;<br /><br />(3). 返回到先前的记录<br />将保存的书签值设置到记录集的书签属性中：<br />// Check for whether bookmark set for a record<br />if (VarBookmark.vt == VT_EMPTY)<br />  printf("No Bookmark set!\n");<br />else <br />  rst-&gt;Bookmark = VarBookmark;<br />设置完后，当前记录即会移动到该书签指向的记录。<br /><br />14、设置过滤条件<br />Recordset对象的Filter属性表示了当前的过滤条件。它的值可以是以AND或OR连接起来的条件表达式（不含WHERE关键字）、由书签组成的数组或ADO提供的FilterGroupEnum枚举值。为Filter属性设置新值后Recordset的当前记录指针会自动移动到满足过滤条件的第一个记录。例如：<br />rst-&gt;Filter  = _bstr_t ("姓名='赵薇'  AND  性别=’女’");<br />在使用条件表达式时应注意下列问题：<br />（1）、可以用圆括号组成复杂的表达式<br />例如：<br />rst-&gt;Filter  =  _bstr_t ("(姓名='赵薇'  AND  性别=’女’)  OR  AGE&lt;25");<br />但是微软不允许在括号内用OR，然后在括号外用AND，例如：<br />rst-&gt;Filter  = _bstr_t ("(姓名='赵薇'  OR 性别=’女’)  AND  AGE&lt;25");<br />必须修改为：<br />rst-&gt;Filter  = _bstr_t ("(姓名='赵薇'  AND  AGE&lt;25)  OR  (性别=’女’  AND  AGE&lt;25)");<br /><br />（2）、表达式中的比较运算符可以是LIKE<br />LIKE后被比较的是一个含有通配符*的字符串，星号表示若干个任意的字符。<br />字符串的首部和尾部可以同时带星号*<br />rst-&gt;Filter  =  _bstr_t ("姓名 LIKE '*赵*' ");<br />也可以只是尾部带星号：<br />rst-&gt;Filter  =  _bstr_t ("姓名 LIKE '赵*' ");<br />Filter属性值的类型是Variant，如果过滤条件是由书签组成的数组，则需将该数组转换为SafeArray，然后再封装到一个VARIANT或_variant_t型的变量中，再赋给Filter属性。<br /><br />15、索引与排序<br />（1）、建立索引<br />当以某个字段为关键字用Find方法查找时，为了加快速度可以以该字段为关键字在记录集内部临时建立索引。只要将该字段的Optimize属性设置为true即可，例如：<br />pRst-&gt;Fields-&gt;GetItem("姓名")-&gt;Properties-&gt;<br />            GetItem("Optimize")-&gt;PutValue("True");<br />pRst-&gt;Find("姓名 = '赵薇'",1,adSearchForward);<br />......<br />pRst-&gt;Fields-&gt;GetItem("姓名")-&gt;Properties-&gt;<br />            GetItem("Optimize")-&gt;PutValue("False");<br />pRst-&gt;Close();<br />说明：Optimize属性是由Provider提供的属性（在ADO中称为动态属性），ADO本身没有此属性。<br /><br />（2）、排序<br />要排序也很简单，只要把要排序的关键字列表设置到Recordset对象的Sort属性里即可，例如：<br />pRstAuthors-&gt;CursorLocation = adUseClient;<br />pRstAuthors-&gt;Open("SELECT * FROM mytable",<br />            _variant_t((IDispatch *) pConnection),<br />            adOpenStatic, adLockReadOnly, adCmdText);<br />......<br />pRst-&gt;Sort = "姓名 DESC, 年龄 ASC";<br />关键字（即字段名）之间用逗号隔开，如果要以某关键字降序排序，则应在该关键字后加一空格，再加DESC（如上例）。升序时ASC加不加无所谓。本操作是利用索引进行的，并未进行物理排序，所以效率较高。<br />但要注意，在打开记录集之前必须将记录集的CursorLocation属性设置为adUseClient，如上例所示。Sort属性值在需要时随时可以修改。<br /><br />16、事务处理<br />ADO中的事务处理也很简单，只需分别在适当的位置调用Connection对象的三个方法即可，这三个方法是：<br />（1）、在事务开始时调用<br />pCnn-&gt;BeginTrans();<br /><br />（2）、在事务结束并成功时调用<br /><br />pCnn-&gt;CommitTrans ();<br />（3）、在事务结束并失败时调用<br />pCnn-&gt;RollbackTrans ();<br />在使用事务处理时，应尽量减小事务的范围，即减小从事务开始到结束（提交或回滚）之间的时间间隔，以便提高系统效率。需要时也可在调用BeginTrans()方法之前，先设置Connection对象的IsolationLevel属性值，详细内容参见MSDN中有关ADO的技术资料。<br /><br />三、使用ADO编程常见问题解答<br />以下均是针对MS SQL 7.0编程时所遇问题进行讨论。<br />1、连接失败可能原因<br />Enterprise Managemer内，打开将服务器的属性对话框，在Security选项卡中，有一个选项Authentication。<br />如果该选项是Windows NT only，则你的程序所用的连接字符串就一定要包含Trusted_Connection参数，并且其值必须为yes，如：<br />"Provider=SQLOLEDB;Server=888;Trusted_Connection=yes"<br />  ";Database=master;uid=lad;";<br />如果不按上述操作，程序运行时连接必然失败。<br />如果Authentication选项是SQL Server and Windows NT，则你的程序所用的连接字符串可以不包含Trusted_Connection参数，如：<br />"Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;";<br />因为ADO给该参数取的默认值就是no，所以可以省略。我认为还是取默认值比较安全一些。<br /><br />2、改变当前数据库的方法<br />使用Tansct-SQL中的USE语句即可。<br /><br />3、如何判断一个数据库是否存在<br />(1)、可打开master数据库中一个叫做SCHEMATA的视图，其内容列出了该服务器上所有的数据库名称。<br /><br />(2) 、更简便的方法是使用USE语句，成功了就存在；不成功，就不存在。例如：<br />try{<br />  m_pConnect-&gt;Execute ( _bstr_t("USE INSURANCE_2002"),NULL,<br />  adCmdText|adExecuteNoRecords );<br />}<br />catch (_com_error &amp;e)<br />{<br />  blSuccess=FALSE;<br />  CString str="数据库INSURANCE_2002不存在！\n";<br />  str+=e.Description();<br />  ::MessageBox(NULL,str,"警告",MB_OK | MB_ICONWARNING); <br />}<br /><br />4、判断一个表是否存在<br />（1）、同样判断一个表是否存在，也可以用是否成功地打开它来判断，十分方便，例如：<br />try{<br />  m_pRecordset-&gt;Open(_variant_t("mytable"),<br />            _variant_t((IDispatch *)m_pConnection,true), adOpenKeyset,<br />            adLockOptimistic, adCmdTable);<br />}<br />catch (_com_error &amp;e)<br />{<br />  ::MessageBox(NULL,"该表不存在。","提示",MB_OK | MB_ICONWARNING);<br />}<br /><br />(2)、要不然可以采用麻烦一点的办法，就是在MS-SQL服务器上的每个数据库中都有一个名为sysobjects的表，查看此表的内容即知指定的表是否在该数据库中。<br /><br />(3)、同样，每个数据库中都有一个名为TABLES的视图(View)，查看此视图的内容即知指定的表是否在该数据库中。<br /><br />5、类型转换问题<br />（1）、类型VARIANT_BOOL<br />类型VARIANT_BOOL等价于short类型。The VARIANT_BOOL is equivalent to short. see it's definition below: <br />typdef short VARIANT_BOOL<br /><br />（2）、_com_ptr_t类的类型转换<br />_ConnectionPtr可以自动转换成IDspatch*类型，这是因为_ConnectionPtr实际上是_com_ptr_t类的一个实例，而这个类有此类型转换函数。<br />同理，_RecordsetPtr和_CommandPtr也都可以这样转换。<br /><br />（3）、_bstr_t和_variant_t类<br />在ADO编程时，_bstr_t和_variant_t这两个类很有用，省去了许多BSTR和VARIANT类型转换的麻烦。<br /><br />6、打开记录集时的问题<br />在打开记录集时，在调用Recordset的Open方法时，其最后一个参数里一定不能包含adAsyncExecute，否则将因为是异步操作，在读取数据时无法读到数据。<br /><br />7、异常处理问题<br />对所有调用ADO的语句一定要用try和catch语句捕捉异常，否则在发生异常时，程序会异常退出。<br /><br />8、使用SafeArray问题<br />在初学使用中，我曾遇到一个伤脑筋的问题，一定要注意：<br />在定义了SAFEARRAY的指针后，如果打算重复使用多次，则在中间可以调用::SafeArrayDestroyData释放数据，但决不能调用::SafeArrayDestroyDescriptor，否则必然出错，即使调用SafeArrayCreate也不行。例如：<br />SAFEARRAY *psa;<br />......<br />//When the data are no longer to be used:<br />::SafeArrayDestroyData( psa);<br />我分析在定义psa指针时，一个SAFEARRAY的实例（也就是SAFEARRAY描述符）也同时被自动建立了。但是只要一调用::SafeArrayDestroyDescriptor，描述符就被销毁了。<br />所以我认为::SafeArrayDestroyDescriptor可以根本就不调用，即使调用也必须在最后调用。<br /><br />9、重复使用命令对象问题<br />一个命令对象如果要重复使用多次（尤其是带参数的命令），则在第一次执行之前，应将它的Prepared属性设置为TRUE。这样会使第一次执行减慢，但却可以使以后的执行全部加快。<br /><br />10、绑定字符串型字段问题<br />如果要绑定的字段是字符串类型，则对应的字符数组的元素个数一定要比字段长度大2（比如m_szau_fname[22]，其绑定的字段au_fname的长度实际是20），不这样绑定就会失败。<br /><br />11、使用AppendChunk的问题<br />当用AddNew方法刚刚向记录集内添加一个新记录之后，不能首先向一个长数据字段（image类型）写入数据，必须先向其他字段写入过数据之后，才能调用AppendChunk写该字段，否则出错。也就是说，AppendChunk不能紧接在AddNew之后。另外，写入其他字段后还必须紧接着调用AppendChunk，而不能调用记录集的Update方法后，才调用AppendChunk，否则调用AppendChunk时也会出错。换句话说，就是必须AppendChunk在前，Update在后。因而这个时候就不能使用带参数的AddNew了，因为带参数的AddNew会自动调用记录集的Update，所以AppendChunk就跑到Update的后面了，就只有出错了！因此，这时应该用不带参数的AddNew。<br />我推测这可能是MS SQL 7.0的问题，在MS SQL 2000中则不存在这些问题，但是AppendChunk仍然不能在Update之后。<br /><br />四、小结<br />一般情况下，Connection和Command的Execute用于执行不产生记录集的命令，而Recordset的Open用于产生一个记录集，当然也不是绝对的。特别Command主要是用于执行参数化的命令，可以直接由Command对象执行，也可以将Command对象传递给Recordset的Open。<br /><img src ="http://www.cppblog.com/tiger/aggbug/17453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tiger/" target="_blank">tiger</a> 2007-01-09 11:48 <a href="http://www.cppblog.com/tiger/archive/2007/01/09/17453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>