EXEC执行T-SQL动态语句无法获得记录集的解决

(转载)http://www.i170.com/user/zhouhuishine/Article_39793

 

今天就这个问题摸索了半天。

T-SQL语句如下:

CREATE  PROCEDURE Pro_DAB_GetStationFSM

       @FileIDsParam       varchar(160),

       @RectLeftParam           INT,       

       @RectTopParam            INT,

       @RectRightParam          INT,    

       @RectBottomParam        INT

AS

DECLARE @SQLString NVARCHAR(2048)

DECLARE @FileID       varchar(10)

DECLARE @FileIDValue varchar(260)

DECLARE @LastPosition INT

DECLARE @CharPosition INT

DECLARE @Values   NVARCHAR(160)

 

SET @LastPosition = 0

SET @CharPosition = 1

SET @FileIDValue = ''

 

WHILE(@LastPosition<LEN(@FileIDsParam))

       BEGIN

              SET @CharPosition = CHARINDEX(N'#',@FileIDsParam,@LastPosition)

              IF(@CharPosition<1)

                     BEGIN

                            BREAK

               END

SET @FileID = SUBSTRING(@FileIDsParam, @LastPosition, @CharPosition - @LastPosition )

              SET @FileIDValue = @FileIDValue + ' LogFileID =  '

              SET @FileIDValue = @FileIDValue + @FileID

              SET @FileIDValue = @FileIDValue + '  OR  '

              SET @LastPosition = @CharPosition+1

       END

 

SET @FileIDValue = SUBSTRING(@FileIDValue,0,(LEN(@FileIDValue)-3))

 

SET @RectLeftParam = @RectLeftParam - 100000

SET @RectRightParam = @RectRightParam +100000

SET @RectBottomParam = @RectBottomParam - 100000

SET @RectTopParam = @RectTopParam + 100000

SET @SQLString = 'SELECT Longitude,Latitude,[FSM Level]  FROM DAB_RSF_File WHERE '

SET @SQLString = @SQLString + ' Longitude>='+CAST(@RectLeftParam AS varchar(10))

SET @SQLString = @SQLString + ' AND  Longitude<=' + CAST(@RectRightParam AS varchar(10))

SET @SQLString = @SQLString + ' AND  Latitude>=' + CAST(@RectBottomParam AS varchar(10))

SET @SQLString = @SQLString + ' AND Latitude<=' + CAST(@RectTopParam AS varchar(10))

SET @SQLString = @SQLString + ' AND (' + @FileIDValue + ')'

 

EXEC(@SQLString)

GO

 

在VC程序中执行:

pRst = m_pConnection->Execute(_bstr_t(szSQL), NULL, adCmdStoredProc);

可以成功执行,

pRst->GetState();的返回结果是0,表示adStateClosed,

然后对记录集进行操作报错:

error code 0x800A0E78 对象关闭时,不允许操作

 

后来在网上查找解决方案,终于发现这种情况应该在存储过程的as语句后加上一句就可以了: set nocount on

现在的存储过程如下:

CREATE  PROCEDURE Pro_DAB_GetStationFSM

       @FileIDsParam       varchar(160),

       @RectLeftParam           INT,       

       @RectTopParam            INT,

       @RectRightParam          INT,    

       @RectBottomParam        INT

AS

SET NOCOUNT ON

DECLARE @SQLString NVARCHAR(2048)

DECLARE @FileID       varchar(10)

DECLARE @FileIDValue varchar(260)

DECLARE @LastPosition INT

DECLARE @CharPosition INT

DECLARE @Values   NVARCHAR(160)

 

SET @LastPosition = 0

SET @CharPosition = 1

SET @FileIDValue = ''

 

WHILE(@LastPosition<LEN(@FileIDsParam))

       BEGIN

              SET @CharPosition = CHARINDEX(N'#',@FileIDsParam,@LastPosition)

              IF(@CharPosition<1)

                     BEGIN

                            BREAK

               END

SET @FileID = SUBSTRING(@FileIDsParam, @LastPosition, @CharPosition - @LastPosition )

              SET @FileIDValue = @FileIDValue + ' LogFileID =  '

              SET @FileIDValue = @FileIDValue + @FileID

              SET @FileIDValue = @FileIDValue + '  OR  '

              SET @LastPosition = @CharPosition+1

       END

 

SET @FileIDValue = SUBSTRING(@FileIDValue,0,(LEN(@FileIDValue)-3))

 

SET @RectLeftParam = @RectLeftParam - 100000

SET @RectRightParam = @RectRightParam +100000

SET @RectBottomParam = @RectBottomParam - 100000

SET @RectTopParam = @RectTopParam + 100000

SET @SQLString = 'SELECT Longitude,Latitude,[FSM Level]  FROM DAB_RSF_File WHERE '

SET @SQLString = @SQLString + ' Longitude>='+CAST(@RectLeftParam AS varchar(10))

SET @SQLString = @SQLString + ' AND  Longitude<=' + CAST(@RectRightParam AS varchar(10))

SET @SQLString = @SQLString + ' AND  Latitude>=' + CAST(@RectBottomParam AS varchar(10))

SET @SQLString = @SQLString + ' AND Latitude<=' + CAST(@RectTopParam AS varchar(10))

SET @SQLString = @SQLString + ' AND (' + @FileIDValue + ')'

 

EXEC(@SQLString)

SET NOCOUNT OFF

GO

 

这个发现算得上是今天最大的收获!!!

 

标签:数据库,软件开发 | 浏览数(744) | 评论数(0) | 2006-09-20

全面优化ADO (ADO+ASP)

1 Connection

1.1 Pooling

  在Web Application中,常常会出现同时有很多用户同时访问数据库的情况,而且ASP中的对象作用域是页面级的,也就是说,每个页面都要联接和断开数据库,岂不是会很慢?而且每个到SQL Server数据库的联接会带来37k的系统开销,怎么办?

  可能有人会想到用Application和Session来解决问题,但是,这是不可取的,如果用Application,那么会出现多个用户同时通过一个Connection访问数据库的情况,虽然节省了建立连接的时间,但是访问数据库的速度就会变得非常慢。如果用Session,出现的问题就是,Session超时怎么办?如果把Session.Timeout设得很大,那用户离开之后,连接还会保留一段时间,也会带来额外的开销。

  其实根本不用考虑这个问题,通过OLE DB访问数据库,它会替你解决这个问题,OLE DB有一个Resource Pooling,它会代理你的连接请求,然后把别人刚用过的连接给你接着用。(具体机制不再阐述,其实我也没搞太明白,嘻嘻)

1.2 Provider

  可能没有多少人用过这个Property吧,它的缺省值是MSDASQL,还有MSIDXS和ADSDSOObject,但是在ADO2.0(见VS98)和ADO2.1(见SQL7)里面提供了一些新的Provider:

MSDAORA (OLE DB Provider for Oracle)

Microsoft.Jet.OLEDB.3.51(OLE DB Provider for Microsoft Jet( for ACCESS))

SQLOLEDB(Microsoft SQL Server OLE DB Provider)

  如果你所用的数据库是这些的话,用这些新的Provider就可以不通过ODBC而直接访问数据库,提高的效率就可想而知了。
全面优化ADO

2 Command

2.1 CommandType

  缺省值是adCmdUnknown,ADO会逐个判断你的CommandType,直到它认为合适为止,不建议采用。(在Recordset.Open和Connection.Execute的时候也可以用)

  adCmdText是照原样执行你的SQL语句,但是如果你的SQL Language是以下几种的话,通过使用别的CommandType就可以提高你的SQL语句执行效率

objCmd.Execute "Select * from table_name", adCmdText可替换为objCmd.Execute "table_name",adCmdTable

objCmd.Execute "Exec proceuure_name",adCmdText可替换为objCmd.Execute "proceuure _name", adCmdStoredProc

  还有很重要的一点就是,如果你的SQL语句没有返回记录集,如insert和update等,那么使用adExecuteNoRecords(ADO2.0)可以减低系统开销,Execute方法的Options参数(可以加到adCmdText 和adCmdStoredProc上,如adCmdStoredProc + adExecuteNoRecords)

  还有adCmdTableDirect和adCmdFile(ADO2.0),我还不太清楚怎么用,adCmdFile可用于访问一个XML文件。

2.2 Prepared

  如果你需要重复的执行类似的SQL语句,那么你可以预编译你的SQL语句,提高的效率也很可观

objCmd.CommandText = "SELECT spell from TYPER.wordspell where word = ? "

objCmd.Prepared = True

objCmd.Parameters.Append objCmd.CreateParameter("word", adVarChar, , 2)

For i = 1 To Len(strName)

strChar = Mid(strName, i, 1)

objCmd("word") = strChar

Set objRS = objCmd.Execute

If objRS.EOF Then

strNamesame = strNamesame & strChar

Else

strNamesame = strNamesame & objRS("spell")

End If

Next ’i = 1 To Len(strName)
全面优化ADO

3 Recordset

3.1 LockType

  缺省是adLockReadOnly,如果你不用修改数据,就不要改成adLockOptimistic之类的,否则也会减低速度和增加开销的

  adLockReadOnly > adLockPessimistic > adLockOptimistic > adLockBatchOptimistic

3.2 CursorType

缺省是adOpenForwardOnly,如果你只用MoveNext Method,也最好不要改,速度影响140%左右

adOpenForwardOnly > adOpenDynamic > adOpenKeyset > adOpenStatic

3.3 CursorLocation

  缺省是adUseServer,其实不好,它可以随时反映数据库服务器上的改动,但是系统开销很大,而且需要维持和数据库服务器的连接,但是在数据库服务器和Web Server在一起的时候要快些。不过在adLockOptimistic的时候使我无法使用RecordCount等Property。

使用用adUseClient的话,你可以对数据做再排序,筛选,shape等操作

如果对数据的实时性没有要求的话,尽量用adUseClient

4 其它

4.1 Early bind

用ASP这一点就不用看了,如果用VB的话

Dim objConn As ADODB.Connection 比 Set objConn = CreateObject("ADODB.Connection")要好

4.2 ADO 2.1里的shape真是好玩

4.3 ADO 2.1可以用objRS.Fields.Append来建立一个Recordset

4.4 把Recordset的一列数据直接变成一个数组来操作速度快一些,但是系统开销要大一些

标签:数据库 | 浏览数(446) | 评论数(0) | 2006-09-20

 基础的SQL语言对于每个从事SQL开发的人员来说都是非常重要的,而SQL注入是从SQL应用衍生出来的一种漏洞或攻击方法。本文在回顾基础的SQL语言的基础上,介绍了SQL注入和如何使用SQL注入法进行网络攻击。


   网络的应用和普及产生了大量的数据信息,这些数据信息多被存在数据库中。与数据库交流主要是通过SQL语言,然而对它的不熟悉和使用的模糊限制了开发者的工作效率,更严重的是,这可能会导致系统的危机。SQL使用方便,应用广泛,并且主流的关系型数据库都支持SQL的执行。正是因为SQL的使用便捷和应用广泛导致了SQL注入一出世就造成了巨大的影响。任何允许执行人工输入SQL语句的地方,就存在SQL注入的危险。因此,深层理解SQL和SQL注入,掌握SQL注入的执行原理和方法是极为重要的。


   一、SQL纵览

   SQL(Structured Query Language)语言是一种结构化查询语言,它的功能不仅是查询,具体说,它是一种通用的、功能强的关系型数据库语言。SQL语言中完成核心功能的有以下9个关键词:SELECT(数据查询),CREAT、DROP、ALTER(数据定义),INSERT、UPDATE、DELETE(数据操纵), GRANT、REVOKE(数据控制)。


   1、数据定义部分


   (1)创建基本表:


   CREAT TABLE employee (Eno CHAR(6) NOT NULL UNIQUE,

   Ename CHAR(20) UNIQUE,

   E*** CHAR(2),

   Eage INT,

   Edept CHAR(10),

   Espe CHAR(20));


   该语句创建了一个名为employee的数据表,它共有六列,分别为字符型(长度为6,非空,唯一)的雇员号Eno,字符型(长度为20,唯一)的雇员姓名Ename,字符型(长度为2)的雇员性别,整型的雇员年龄,字符型(长度为10)的雇员部门,字符型(长度为20)的雇员特长。


   (2)删除基本表:

  

   DROP TABLE employee;


   删除表employee,数据也一并删除,一旦执行成功不可逆,因此使用该条语句时要格外注意,最好使用事务处理、事前备份和确认提醒等。


   (3)更改基本表:

  

   ALTER TABLE employee ADD Esalary CHAR(5);

   在雇员表中加入一列,字符型(长度为5)的雇员薪水

   ALTER TABLE employee DROP UNIQUE(Ename);

   去掉雇员表中雇员姓名列的唯一属性

   ALTER TABLE employee MODIFY E*** CHAR(1);

   把雇员表中的性别列改为一位字符型。


   2、数据查询部分


   数据查询部分是SQL语句中最灵活、功能最强的部分,对于查询语句的使用熟练程度和对查询结构的优化能力最能体现SQL编程人员的基本功。因此,该部分必须要给予足够的重视。现详述如下:


   (1)基本查询语句:

  

   SELECT Eno,Ename,E*** FROM employee;

   查询employee表中的Eno,Ename,E***三列

   SELECT * FROM employee;

   查询employee表中的所有列

   SELECT DISTINCT Eno FROM employee;

   查询employee表中的Eno列,并去除重复行


   (2)条件(WHERE)查询语句:


   查询条件的连接词如下NOT,=,〉,〈,〉=,〈=,!=,〈〉,!〉,!〈(比较);BETWEEN AND,NOT BETWEEN AND (确定范围);IN,NOT IN(确定集合);LIKE,NOT LIKE(字符匹配);IS NULL,IS NOT NULL(空值);AND,OR(多条件连接)


   1)比较

   SELECT Eno FROM employee WHERE Eage 〈=25;;

   列出雇员表中年龄小于25的雇员号


   2)确定范围

   SELECT Eno,Ename FROM employee

   WHERE Eage [NOT]BETWEEN 20 AND 30;;

   列出雇员表中年龄(不)在20到30的雇员号和姓名


   3)确定集合

   SELECT Eno,Ename FROM employee

   WHERE Edept [NOT]IN(′SD′,′HD′);;

   列出雇员表中(不)是软硬件开发部的雇员号和姓名


   4)字符匹配

   LIKE的用法如下:

   [NOT]LIKE ′〈匹配模式〉′[ESCAPE ′〈换码符〉′]

   通配符号有 % 和 _ 两种:

   % :匹配任意长度的字符串(长度可为0)。a%b 可与ab,adfb等匹配。

   _:匹配单个任意字符。a_b 可与a#b,a@b等匹配。

   如果有ESCAPE,则跟在换码符号后的%或_不再是通配符号,只是正常的%或_。

   例如:

   SELECT * FROM employee WHERE Ename LIKE ′刘%′;

   查找雇员表中姓刘雇员的信息

   SELECT * FROM employee WHERE Ename LIKE ′刘_ _′;

   查找雇员表中姓名为刘某(两个字)的雇员的信息(汉字占2个字符的位置)

   SELECT * FROM employee WHERE Espe LIKE ′DB"_%t_′ESCAPE ′"′;

   查找雇员表中特长项为DB_ 开始,倒数第二个字符为t的雇员信息


   5)空值

   SELECT * FROM employee WHERE Espe IS [NOT] NULL;

   查找雇员表中特长项(不)为空的雇员信息


   6)多条件连接

   SELECT Ename FROM employee WHERE Edept=′SD′AND Eage 〈= 30;

   列出雇员表中软件开发部30岁以下雇员的姓名


   (3)结果排序

   对查询的结果进行排序使用ORDER BY,ASC(默认)为升序,DESC为降序。

   SELECT * FROM employee ORDER BY Edept,Eage DESC;

   把所有雇员按部门升序,年龄降序排列(缺省是升序)


   (4)结果分组

   对查询结果的分组一般都要用到SQL的集函数,因此先介绍SQL的集函数。

   SQL语言中的集函数主要有COUNT(统计总数),SUM(求总和),AVG(求均值),MAX(最大值),MIN(最小值)。

   例如:

   SELECT MAX(Eage) FROM employee WHERE Edept=′SD′;

   列出软件开发部年纪最大雇员的姓名

   SELECT Edept FROM employee GROUP BY Edept HAVING Count(*)〉10 ;

   统计各部门的雇员数,只显示雇员数大于10的部门

   SELECT Edept,COUNT(Eno) FROM employee GROUP BY Edept ;

   统计各部门的雇员数,按部门分组列出各部门的雇员数


   (5)连接查询

   连接查询指的是查询涉及多个数据表,FROM后连接多个表的情况。假如我们要统计各个项目参加人的雇员号和姓名,涉及的表Eproject(雇员参加的项目)结构如下:


   Eproject ( Eno CHAR(6),Pno CHAR(6),TimeBgn TIME,

   TimeEnd TIME,Remark CHAR(50));

   相应的查询语句为:

   SELECT Eproject.Pno,employee.Eno,Ename,

   FROM employee, Eproject

   WHERE employee.Eno = Eproject.Eno

   ORDER BY Eproject.Pno ;

   列出参加各项目的雇员号和姓名,并按项目号升序排列。


   (6)集合查询

   集合查询指的是多个SELECT查询结果间进行的集合操作,主要有UNION(并操作)、INTERSECT(交操作)、MINUS(差操作)。其中标准SQL中没有提供交操作和差操作,但它们可以使用联合查询实现。假如我们要查询硬件开发部年龄不大于25岁的雇员,可以用集合查询实现如下:


   SELECT * FROM employee WHERE Edept=′HD′

   UNION SELECT * FROM employee WHERE Eage 〈= 25;


   3、数据更新部分

   SQL中的数据更新语句有INSERT,UPDATE和DELETE三种,用法如下:

Zabcdefghijklmnopqrstuvwxy
   (1)插入数据

   INSERT INTO employee

   VALUES (′13253′,′王二′,′男′,23,′SD′, ′DB_Project′);;

   向雇员表中插入一条完整的数据

   INSERT INTO employee (Eno ,Ename)

   VALUES (′13253′,′王二′);;

   向雇员表中插入一条数据,只包含雇员号和姓名,其它列为空值


   注意:以上情况,属性为非空的列一定不能为空值。


   (2)修改数据

   UPDATE employee SET Eage=24 WHERE Eno=′13253′;

   将雇员表中13253号雇员年龄改为24岁


   (3)删除数据

   DELETE FROM employee WHERE Eno=′13253′;

   将雇员表中13253号雇员信息删除
4、数据控制部分


   (1)用户授权

   SQL 的用户授权使用GRANT关键词,它的用法举例如下:


   GRANT SELECT ON TABLE employee TO usr1;;

   允许用户usr1查询表employee

   GRANT ALL PRIVILEGES ON TABLE employee TO usr2;;

   允许用户usr2对表employee的任何操作(查询,插入、更新、删除数据)


   (2)收回权限

   SQL 中收回用户权限使用REVOKE关键词,它的用法举例如下:

  

   REVOKE UPDATE(Eno) ON TABLE employee FROM usr3;;

   收回用户usr3更新表employee中Eno列的权力

   REVOKE INSERT ON TABLE employee FROM PUBLIC;;

   不允许所有用户在表employee中添加数据


   二、SQL注入(SQL INJECTION)简介


   自从SQL Injection被发现以来,人们通过大量的实验发现,它存在于任何允许执行SQL语句的地方。简单的说,SQL injection是一种源于SQL的漏洞,也可以说成是一种攻击方法,它利用程序对用户输入数据的检验不足或程序自身对变量处理不当,把想要执行的SQL语句插入到实际的SQL语句中发送给服务器去执行,后果轻则导致敏感信息泄漏,重则使整个服务器受控。


   例如,某个登录系统(ASP+SQLServer)输入帐号和密码的SQL语句为:


   SELECT * FROM member WHERE UID =′ "& request("ID") &" ′


   AND Passwd =′ "& request("Pwd") & " ′


   如果正常使用者的帐号user1,密码abcdefg12345,那么此时的SQL语句为:


   SELECT * FROM member WHERE UID =′user1′ AND Passwd =′abcdefg12345′;;


   在这里举三个SQL Injection的实例简单说明一下这种漏洞的原理:


   1、帐号输入 user1′-- ,密码任意(如aaa),此时的SQL语句变为:


   SELECT * FROM member WHERE UID =′user1′--′ AND Passwd =′aaa′


   由于--之后的语句被忽略,AND字句被作为说明而失去了作用,用户user1就可以用任何密码登录系统。如果登录用户是系统管理员权限,后果就不堪设想了。


   2、帐号输入′OR 1=1--,密码任意(如aaa),此时的SQL语句变为:


   SELECT * FROM member WHERE UID =′′OR 1=1--′ AND Passwd =′aaa′


   由于AND字句被作为说明而失去了作用,where字句返回为真,这个SQL语句就失去了鉴别作用。


   3、帐号输入任意(如uuu) ,密码为aaa(任意)′OR 1=1--,此时的SQL语句变为:

   SELECT * FROM member WHERE UID =′uuu′AND Passwd =′aaa′OR 1=1 --

   由于--之后的语句被忽略,WHERE字句返回为真,该SQL语句就失去了鉴别作用。


   三、SQL注入纵览


   从最初的"1=1"型SQL注入,到现在的SQLServer存储过程和扩展存储过程注入,SQL注入迄今为止已经被发现数十种。总的来说,它的分类可以从SQL语言的自身进行,它可以分为授权旁路型、SELECT型、INSERT型、其它型(如SQLServer存储过程)。如前所述,SQL语言中的数据查询部分(SELECT语句)是SQL语句中最灵活、功能最强的部分,因此,该部分的SQL注入也种类繁多,主要有基本SELECT型、基本集合(UNION)型、语法错误型列举、匹配(LIKE)型、--结尾型等。现对于各种SQL注入分述如下:

  

   1、授权旁路型

   这种类型的SQL注入是最简单、最易理解的一种SQL注入,它主要存在于表格式登录系统。除了简介中所列出的几种之外,还有一种更直接的SQL注入,登录帐号和密码都为 ′OR "= ′,此时SQL语句变为


   SELECT * FROM member WHERE UID =′′OR ′′=′′ AND Passwd =′′OR ′′=′′


   显然,该SQL语句失去了鉴别功能。


   2、SELECT型SQL注入

  

   (1)基本SELECT型

  

   基本SELECT型SQL注入分为直接型和引用型。直接型SQL注入指的是用户提交的数据直接被用在SQL查询中。如果在某个合法输入值后添加一个空格和OR,系统返回了一个错误,那么就可能存在直接型SQL注入。直接值的位置可能存在于WHERE子句中,如:


   SQLString=" SELECT * FROM member WHERE UID = "& intUID


   或者存在于某个SQL关键词中,如某个表名或列名:


   SQLString=" SELECT * FROM member ORDER BY " & strColumn


   而引用型的SQL注入指的是用户提交的数据被放在引号中提交。如:

  

   SQLString=" SELECT * FROM member WHERE UID =′"& strUID & "′"

  

   此时要注入的部分要以单引号开始,与之前的单引号匹配,结尾在WHERE子句后加单引号,与之后的单引号匹配。

  

   (2)基本集合(UNION)型

   基本集合型SQL注入是在WHERE子句中插入一个UNION SELECT语句,以达到执行注入部分的目的。例如目标SQL语句为:


   SQLString=" SELECT Name,Sex,Title FROM member WHERE UID =′"& strColumn & "′"

   使用的注入字串如下:

   ′UNION SELECT otherfield FROM othertable WHERE ′′= ′

   这样一来,提交的查询语句就成了:

   SELECT Name,Sex,Title FROM member WHERE UID =′′

   UNION SELECT otherfield FROM othertable WHERE ′′= ′′


   结果就有以下操作:数据库首先检索member表查找UID为空的行,由于不存在UID为空的行,所以没有返回记录。返回的记录存在于注入部分的查询。有时使用空值会不起作用,可能是表中空值被使用或被用于实现其他的功能。这种情况下,你唯一要做的就是构造一个决不会出现在表中的字串,只要它能使UNION SELECT之前不返回记录。

  

   (3)语法错误型

  

   对于某些数据库而言,返回的错误信息中包含了语法错误的部分,因此,通过制造语法错误(错误注入),可以得到很多有价值的信息。

  

   构造的错误字符串有:′,错误值′,′错误值,′OR′,′OR,OR′,;等。

  

   (4)圆括号型

  

   如果返回的错误中包含圆括号,或者错误是丢失圆括号,那么就要在错误值和WHERE子句部分添加圆括号。例如目标SQL语句为


   SQLStr=" SELECT Name,Sex,Title FROM member WHERE(UID =′"& strID & "′)"


   使用的注入字串就要变为:


   ′)UNION SELECT otherfield FROM othertable WHERE ( ′′= ′

  

   这样一来,提交的查询语句就成了:


   SELECT Name,Sex,Title FROM member WHERE(UID =′′)


   UNION SELECT otherfield FROM othertable WHERE ( ′′= ′′)


   由于不存在UID为空的行,所以第一部分没有返回记录,返回的记录存在于注入部分的查询。

  

   (5)LIKE型

  

   LIKE型的SQL注入也很常见。如果返回错误中含有%,_或者LIKE等字眼,说明该系统存在LIKE型注入漏洞,用户提交的数据会被送给LIKE子句去执行。例如目标SQL语句为


   SQLStr=" SELECT Name,Sex,Title FROM member WHERE Name LIKE′%"& strColumn & "%′"


   而使用的注入字串为:


   ′UNION SELECT otherfield FROM othertable WHERE ′%37′= ′


   得到提交的查询语句为:


   SELECT Name,Sex,Title FROM member WHERE Name LIKE′%′


   UNION SELECT otherfield FROM othertable WHERE ′%′= ′%′


   显然,第一部分返回为表member的所有记录,第二部分为用户想要得到的记录,最终得到就是用户想要的记录。

  

   (6)错误结尾

  

   有些时候,当尝试了许多注入方法后,返回依旧是错误。这说明目标SQL语句可能并不像所猜测的那样简单,它可能存在子查询或连接查询等复杂的情况。这时,对于SQLServer,由于;”之后的语句会被忽略,所以要在注入的SQL语句末尾,加上;;;- -”。


   (7)连接查询

  

   如果目标SQL语句为


   SQLStr=" SELECT Name,Sex,Title FROM member


   WHERE UID =′"& strID & "′AND Sex=′Female′"


   如果使用的注入字串为:


   ′UNION SELECT otherfield FROM othertable WHERE ′′= ′

  

   这样一来,提交的查询语句就成了:


   SELECT Name,Sex,Title FROM member WHERE UID =′′


   UNION SELECT otherfield FROM othertable WHERE ′′= ′′AND Sex=′Female′

  

   由于othertable中不一定存在名为Sex的列,所以可能会返回;Invalid column name Sex”的错误。对于SQLServer而言,在系统表sysobjects中存有库中所有表的列名,所以,使用SELECT name FROM sysobjects WHERE xtype=′U′可以返回库中所有用户定义表的表名。


   在这种情况下,构造的SQL注入语句要成为以下结构:


   SELECT name FROM syscolumns


   WHERE id=(SELECT id FROM sysobjects WHERE name=′TableName′)


   3、INSERT型SQL注入

  

   INSERT执行在数据库中增加列的功能,它用在用户注册,发表言论,网上购物等许多地方。由于它直接改变数据库的数据,所以使用INSERT型注入比SELECT型更危险。对于攻击者而言,如果使用INSERT型注入的语句出现错误,可能因为在数据库中产生一串单引号而被检测到。所以,使用INSERT型SQL注入要格外小心。

  

   例如目标SQL语句为(注册项为姓名,性别,邮箱)

   SQLStr="INSERT INTO TableName

   VALUES(′"& strName & "′,′"& strSex & "′,′"& strEmail & "′)"

   如下填表:

   姓名:′+ SELECT TOP 1 FieldName FROM TableName+′

   性别:Male

   邮箱:aaa@yahoo.com

   这样,提交的SQL语句为:

   INSERT INTO TableName VALUES

   (′′+ SELECT TOP 1 FieldName FROM TableName+′′,′Male′,′aaa@yahoo.com′)

  

   在返回的注册信息中,就可以找到表TableName中的FieldName的值。


   由于SQL语言的设计思想就是要使用灵活,所以各种各样的SQL注入方法也会层出不穷。但是总的来说,只要对SQL语言足够熟悉,并且时刻注意SQL注入的危险,至少已经向安全迈出了第一步。

 

标签:数据库 | 浏览数(1027) | 评论数(1) | 2006-09-19

void Log(char * szString, ...)

{

         TCHAR szEntry[1024];

         ZeroMemory(szEntry,sizeof(szEntry));

         va_list args;

         va_start(args, szString);

         vsprintf(szEntry,szString,args);                    //用vsprintf函数格式化,而不是vsprintf格式化

         AfxMessageBox(szEntry);

}

 

void CTestDlg::OnOK()

{

         // TODO: Add extra validation here

         float i=30;

         int j=30;

         Log("float=%f,int=%d",i,j);

}

 

标签:软件开发 | 浏览数(610) | 评论数(0) | 2006-09-08

waitingfly 2003-3-13 00:57
请问va_list用法

请问各位高手
         va_list;
         va_start (va_list pvar ,void parmn);
              va_end (va_list pvar);
究竟是具体如何实现变换函数参数的?               Thank u

taige 2003-3-13 01:31
请问va_list用法

给你两个例子看一下:
[code]int ErrLog(char *fmt, ...)
{
    FILE *fp;
    char logfile[255];
    va_list args;

    va_start(args, fmt);

    sprintf(logfile, "%s/log/%s.err.%s.log", getenv("HOME"), srv_name, GetSysDate(0));
    fp = fopen(logfile, "a+");
    if (fp == NULL) {
        fprintf(stderr, "open run log file[%s]error: %s"n", logfile, strerror(errno));
        return -1;
    }
    fprintf(fp, "[%s %s][%ld:%ld][%s][%s]"n", GetSysDate(1), GetSysTime(), getpgrp(), getpid(), bank_name, strerror(errno));

    vfprintf(fp, fmt, args);
    fclose(fp);
    va_end(args);

    return 0;
}
[/code]
[code]
int printv(char *fmt, ...)
{
    va_list args;

    if (verbose == 0)
        return 0;

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);

    return 0;
}
[/code]

waitingfly 2003-3-13 02:05
请问va_list用法

va_list args
定义了什么?在函数中有何用处?你的错误日志中好象并没有?
只是申明?麻烦你能说明一下 :)
谢谢

taige 2003-3-13 02:35
请问va_list用法

[code]va_list args;[/code]
定义了变量args啊!这都看不懂?
错误日志中当然用到了
[code]va_start(args, fmt);[/code]


一般的用法是这样(个人理解)
va_list args; //声明变量
va_start(args, fmt); //开始解析。args指向fmt后面的参数
TYPE var = va_arg(args, TYPE); //取下一个参数并返回。args指向下一个参数
va_end(args); //结束解析

waitingfly 2003-3-13 03:32
请问va_list用法

非常感谢!
我明白了! :)

thesea 2003-7-29 05:59
请问va_list用法

请问:
这种变元参数的函数printv,我想在参数列表中增加两个参数,用以标识调用printv的文件名和当前行数,如何定义参数列表?如何调用呢?
另外:
谁有比较好用的日志函数,共享一下。
谢谢

lxbcd 2003-10-23 02:43
请问va_list用法

这是我已经编好的程序,共享吧!!!

int printv( char *psMsg, int iSqlcode, char *psFile, long lLine )
{
        FILE *fp_log;
        char sFile[255];
        char sDate[9],sTime[7];

        sprintf(sFile,"%s/log/err.log",getenv("HOME"));
        if((fp_log=fopen(sFile,"a"))!=NULL)
        {
                acc_getrqsj(sDate,sTime);
                fprintf(fp_log,"时间:%s%s|文件:%s|行数:%d|%s|数据库错码:%d|"n","
                        sDate+4,sTime,psFile,lLine,psMsg,iSqlcode);
        }
        fclose(fp_log);
        return 0;
}
[code][/code]

scholes00 2003-12-18 07:59
请问va_list用法

可以查看/usr/include/目录下va_list.h和stdarg.h

标签:软件开发 | 浏览数(2398) | 评论数(0) | 2006-09-08

请问:va_list(),va_start()是何意?

(一)写一个简单的可变参数的C函数

下面我们来探讨如何写一个简单的可变参数的C函数.写可变参数的
C函数要在程序中用到以下这些宏:
void va_start( va_list arg_ptr, prev_param );

type va_arg( va_list arg_ptr, type );

void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思.
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个
头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数
参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
int j=0;

va_start(arg_ptr, i);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf("%d %d"n", i, j);
return;
}
我们可以在我们的头文件中这样声明我们的函数:
extern void simple_va_fun(int i, ...);
我们在程序中可以这样调用:
simple_va_fun(100);
simple_va_fun(100,200);
从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变
量是指向参数的指针.
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第
一个可变参数的前一个参数,是一个固定的参数.
3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个
参数是你要返回的参数的类型,这里是int型.
4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使
用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获
取各个参数.
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)simple_va_fun(100);
结果是:100 -123456789(会变的值)
2)simple_va_fun(100,200);
结果是:100 200
3)simple_va_fun(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果
正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果
的原因和可变参数在编译器中是如何处理的.

(二)可变参数在编译器中的处理

我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,
由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下
面以VC++中stdarg.h里x86平台的宏定义摘录如下(’"’号表示折行):

typedef char * va_list;

#define _INTSIZEOF(n) "
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t) "
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函
数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我
们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再
看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的
地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆
栈的地址,如图:

高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
|....... |
|-----------------------------|
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 1 )

然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我
们看一下va_arg取int型的返回值:
j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回
ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址
(图2).然后用*取得这个地址的内容(参数值)赋给j.

高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
|....... |
|-----------------------------|<--va_arg后ap指向
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 2 )

最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再
指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不
会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.
在这里大家要注意一个问题:由于参数的地址用于va_start宏,所
以参数不能声明为寄存器变量或作为函数或数组类型.
关于va_start, va_arg, va_end的描述就是这些了,我们要注意的
是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

(三)可变参数在编程中要注意的问题

因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,
可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能
地识别不同参数的个数和类型.
有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数
printf是从固定参数format字符串来分析出参数的类型,再调用va_arg
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
过在自己的程序里作判断来实现的.
另外有一个问题,因为编译器对可变参数的函数的原型检查不够严
格,对编程查错不利.如果simple_va_fun()改为:
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
char *s=NULL;

va_start(arg_ptr, i);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s"n", i, s);
return;
}
可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现
core dump(Unix) 或者页面非法的错误(window平台).但也有可能不出
错,但错误却是难以发现,不利于我们写出高质量的程序.
以下提一下va系列宏的兼容性.
System V Unix把va_start定义为只有一个参数的宏:
va_start(va_list arg_ptr);
而ANSI C则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用system V的定义,应该用vararg.h头文件中所定义的
宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以
用ANSI C的定义就够了,也便于程序的移植.

标签:软件开发 | 浏览数(727) | 评论数(0) | 2006-09-08

表结构如下:
   id int 4
   EntryID int 4
   BlogID int 4
   现在要求在插入时,不允许插入EntryID与BlogID都相同的记录,即表中不允许任意两条记录的EntryID与BlogID都相同,EntryID与BlogID构成记录的唯一标识。
   以前我的处理方法时,在插入之前,通过select检查是否存在相同的记录。
现在我采用SQL Server唯一约束来实现,简单方便,效率又高。实现方法是:
1、在数据库关系图中右击将包含约束的表,然后从快捷菜单中选择"属性"命令。
-或-
为将包含约束的表打开表设计器,在表设计器中右击,然后从快捷菜单中选择"属性"命令。
2、选择"索引/键"选项卡。
3、选择"新建"命令。系统分配的名称出现在"索引名"框中。
4、在"列名"下展开列的列表,选择要将约束附加到的列(在这里我们选择列EntryID、BlogID)。
5、选择"创建 UNIQUE"复选框。
6、选择"约束"选项。
当保存表或关系图时,唯一约束即创建在数据库中。

现在我们再进行插入,就出现错误"违反了 UNIQUE KEY 约束..."。
我们在程序中捕获这个错误,就知道插入了重复记录。

 

标签:数据库 | 浏览数(618) | 评论数(0) | 2006-09-08

DataTypeEnum

Specifies the data type of a Field, Parameter, or Property. The corresponding OLE DB type indicator is shown in parentheses in the description column of the following table. For more information about OLE DB data types, see Chapter 13: Data Types in OLE DB and Appendix A: Data Types of the OLE DB Programmer's Reference.

 

Constant Value Description
AdArray
(Does not apply to ADOX.)
0x2000 A flag value, always combined with another data type constant, that indicates an array of that other data type.
adBigInt 20 Indicates an eight-byte signed integer (DBTYPE_I8).
adBinary 128 Indicates a binary value (DBTYPE_BYTES).
adBoolean 11 Indicates a boolean value (DBTYPE_BOOL).
adBSTR 8 Indicates a null-terminated character string (Unicode) (DBTYPE_BSTR).
adChapter 136 Indicates a four-byte chapter value that identifies rows in a child rowset (DBTYPE_HCHAPTER).
adChar 129 Indicates a string value (DBTYPE_STR).
adCurrency 6 Indicates a currency value (DBTYPE_CY). Currency is a fixed-point number with four digits to the right of the decimal point. It is stored in an eight-byte signed integer scaled by 10,000.
adDate 7 Indicates a date value (DBTYPE_DATE). A date is stored as a double, the whole part of which is the number of days since December 30, 1899, and the fractional part of which is the fraction of a day.
adDBDate 133 Indicates a date value (yyyymmdd) (DBTYPE_DBDATE).
adDBTime 134 Indicates a time value (hhmmss) (DBTYPE_DBTIME).
adDBTimeStamp 135 Indicates a date/time stamp (yyyymmddhhmmss plus a fraction in billionths) (DBTYPE_DBTIMESTAMP).
adDecimal 14 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_DECIMAL).
adDouble 5 Indicates a double-precision floating-point value (DBTYPE_R8).
adEmpty 0 Specifies no value (DBTYPE_EMPTY).
adError 10 Indicates a 32-bit error code (DBTYPE_ERROR).
adFileTime 64 Indicates a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (DBTYPE_FILETIME).
adGUID 72 Indicates a globally unique identifier (GUID) (DBTYPE_GUID).
adIDispatch 9 Indicates a pointer to an IDispatch interface on a COM object (DBTYPE_IDISPATCH).

Note   This data type is currently not supported by ADO. Usage may cause unpredictable results.

adInteger 3 Indicates a four-byte signed integer (DBTYPE_I4).
adIUnknown 13 Indicates a pointer to an IUnknown interface on a COM object (DBTYPE_IUNKNOWN).

Note   This data type is currently not supported by ADO. Usage may cause unpredictable results.

adLongVarBinary 205 Indicates a long binary value.
adLongVarChar 201 Indicates a long string value.
adLongVarWChar 203 Indicates a long null-terminated Unicode string value.
adNumeric 131 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_NUMERIC).
adPropVariant 138 Indicates an Automation PROPVARIANT (DBTYPE_PROP_VARIANT).
adSingle 4 Indicates a single-precision floating-point value (DBTYPE_R4).
adSmallInt 2 Indicates a two-byte signed integer (DBTYPE_I2).
adTinyInt 16 Indicates a one-byte signed integer (DBTYPE_I1).
adUnsignedBigInt 21 Indicates an eight-byte unsigned integer (DBTYPE_UI8).
adUnsignedInt 19 Indicates a four-byte unsigned integer (DBTYPE_UI4).
adUnsignedSmallInt 18 Indicates a two-byte unsigned integer (DBTYPE_UI2).
adUnsignedTinyInt 17 Indicates a one-byte unsigned integer (DBTYPE_UI1).
adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
adVarBinary 204 Indicates a binary value.
adVarChar 200 Indicates a string value.
adVariant 12 Indicates an Automation Variant (DBTYPE_VARIANT).

Note   This data type is currently not supported by ADO. Usage may cause unpredictable results.

adVarNumeric 139 Indicates a numeric value.
adVarWChar 202 Indicates a null-terminated Unicode character string.
adWChar 130 Indicates a null-terminated Unicode character string (DBTYPE_WSTR).

ADO/WFC Equivalent

Package: com.ms.wfc.data

 

Constant
AdoEnums.DataType.ARRAY
AdoEnums.DataType.BIGINT
AdoEnums.DataType.BINARY
AdoEnums.DataType.BOOLEAN
AdoEnums.DataType.BSTR
AdoEnums.DataType.CHAPTER
AdoEnums.DataType.CHAR
AdoEnums.DataType.CURRENCY
AdoEnums.DataType.DATE
AdoEnums.DataType.DBDATE
AdoEnums.DataType.DBTIME
AdoEnums.DataType.DBTIMESTAMP
AdoEnums.DataType.DECIMAL
AdoEnums.DataType.DOUBLE
AdoEnums.DataType.EMPTY
AdoEnums.DataType.ERROR
AdoEnums.DataType.FILETIME
AdoEnums.DataType.GUID
AdoEnums.DataType.IDISPATCH
AdoEnums.DataType.INTEGER
AdoEnums.DataType.IUNKNOWN
AdoEnums.DataType.LONGVARBINARY
AdoEnums.DataType.LONGVARCHAR
AdoEnums.DataType.LONGVARWCHAR
AdoEnums.DataType.NUMERIC
AdoEnums.DataType.PROPVARIANT
AdoEnums.DataType.SINGLE
AdoEnums.DataType.SMALLINT
AdoEnums.DataType.TINYINT
AdoEnums.DataType.UNSIGNEDBIGINT
AdoEnums.DataType.UNSIGNEDINT
AdoEnums.DataType.UNSIGNEDSMALLINT
AdoEnums.DataType.UNSIGNEDTINYINT
AdoEnums.DataType.USERDEFINED
AdoEnums.DataType.VARBINARY
AdoEnums.DataType.VARCHAR
AdoEnums.DataType.VARIANT
AdoEnums.DataType.VARNUMERIC
AdoEnums.DataType.VARWCHAR
AdoEnums.DataType.WCHAR

 

标签:数据库 | 浏览数(452) | 评论数(0) | 2006-09-08

名称背景 数值背景 颜色名称 中文名称 十六进制RGB 十进制RGB 粗细字体配色参考
    aliceblue 艾利斯兰 #f0f8ff 240,248,255 艾利斯兰[中国搜]
    antiquewhite 古董白 #faebd7 250,235,215 古董白[中国搜]
    aqua 浅绿色 #00ffff 0,255,255 浅绿色[中国搜]
    aquamarine 碧绿色 #7fffd4 127,255,212 碧绿色[中国搜]
    azure 天蓝色 #f0ffff 240,255,255 天蓝色[中国搜]
    beige 米色 #f5f5dc 245,245,220 米色[中国搜]
    bisque 桔黄色 #ffe4c4 255,228,196 桔黄色[中国搜]
    black 黑色 #000000 0,0,0 黑色[中国搜]
    blanchedalmond 白杏色 #ffebcd 255,235,205 白杏色[中国搜]
    blue 蓝色 #0000ff 0,0,255 蓝色[中国搜]
    blueviolet 紫罗兰色 #8a2be2 138,43,226 紫罗兰色[中国搜]
    brown 褐色 #a52a2a 165,42,42 褐色[中国搜]
    burlywood 实木色 #deb887 222,184,135 实木色[中国搜]
    cadetblue 军兰色 #5f9ea0 95,158,160 军兰色[中国搜]
    chartreuse 黄绿色 #7fff00 127,255,0 黄绿色[中国搜]
    chocolate 巧可力色 #d2691e 210,105,30 巧可力色[中国搜]
    coral 珊瑚色 #ff7f50 255,127,80 珊瑚色[中国搜]
    cornflowerblue 菊兰色 #6495ed 100,149,237 菊兰色[中国搜]
    cornsilk 米绸色 #fff8dc 255,248,220 米绸色[中国搜]
    crimson 暗深红色 #dc143c 220,20,60 暗深红色[中国搜]
    cyan 青色 #00ffff 0,255,255 青色[中国搜]
    darkblue 暗蓝色 #00008b 0,0,139 暗蓝色[中国搜]
    darkcyan 暗青色 #008b8b 0,139,139 暗青色[中国搜]
    darkgoldenrod 暗金黄色 #b8860b 184,134,11 暗金黄色[中国搜]
    darkgray 暗灰色 #a9a9a9 169,169,169 暗灰色[中国搜]
    darkgreen 暗绿色 #006400 0,100,0 暗绿色[中国搜]
    darkgrey 暗灰色 #a9a9a9 169,169,169 暗灰色[中国搜]
    darkkhaki 暗黄褐色 #bdb76b 189,183,107 暗黄褐色[中国搜]
    darkmagenta 暗洋红 #8b008b 139,0,139 暗洋红[中国搜]
    darkolivegreen 暗橄榄绿 #556b2f 85,107,47 暗橄榄绿[中国搜]
    darkorange 暗桔黄色 #ff8c00 255,140,0 暗桔黄色[中国搜]
    darkorchid 暗紫色 #9932cc 153,50,204 暗紫色[中国搜]
    darkred 暗红色 #8b0000 139,0,0 暗红色[中国搜]
    darksalmon 暗肉色 #e9967a 233,150,122 暗肉色[中国搜]
    darkseagreen 暗海兰色 #8fbc8f 143,188,143 暗海兰色[中国搜]
    darkslateblue 暗灰蓝色 #483d8b 72,61,139 暗灰蓝色[中国搜]
    darkslategray 暗瓦灰色 #2f4f4f 47,79,79 暗瓦灰色[中国搜]
    darkslategrey 暗瓦灰色 #2f4f4f 47,79,79 暗瓦灰色[中国搜]
    darkturquoise 暗宝石绿 #00ced1 0,206,209 暗宝石绿[中国搜]
    darkviolet 暗紫罗兰色 #9400d3 148,0,211 暗紫罗兰色[中国搜]
    deeppink 深粉红色 #ff1493 255,20,147 深粉红色[中国搜]
    deepskyblue 深天蓝色 #00bfff 0,191,255 深天蓝色[中国搜]
    dimgray 暗灰色 #696969 105,105,105 暗灰色[中国搜]
    dimgrey 暗灰色 #696969 105,105,105 暗灰色[中国搜]
    dodgerblue 闪兰色 #1e90ff 30,144,255 闪兰色[中国搜]
    firebrick 火砖色 #b22222 178,34,34 火砖色[中国搜]
    floralwhite 花白色 #fffaf0 255,250,240 花白色[中国搜]
    forestgreen 森林绿 #228b22 34,139,34 森林绿[中国搜]
    fuchsia 紫红色 #ff00ff 255,0,255 紫红色[中国搜]
    gainsboro 淡灰色 #dcdcdc 220,220,220 淡灰色[中国搜]
    ghostwhite 幽灵白 #f8f8ff 248,248,255 幽灵白[中国搜]
    gold 金色 #ffd700 255,215,0 金色[中国搜]
    goldenrod 金麒麟色 #daa520 218,165,32 金麒麟色[中国搜]
    gray 灰色 #808080 128,128,128 灰色[中国搜]
    green 绿色 #008000 0,128,0 绿色[中国搜]
    greenyellow 黄绿色 #adff2f 173,255,47 黄绿色[中国搜]
    grey 灰色 #808080 128,128,128 灰色[中国搜]
    honeydew 蜜色 #f0fff0 240,255,240 蜜色[中国搜]
    hotpink 热粉红色 #ff69b4 255,105,180 热粉红色[中国搜]
    indianred 印第安红 #cd5c5c 205,92,92 印第安红[中国搜]
    indigo 靛青色 #4b0082 75,0,130 靛青色[中国搜]
    ivory 象牙色 #fffff0 255,255,240 象牙色[中国搜]
    khaki 黄褐色 #f0e68c 240,230,140 黄褐色[中国搜]
    lavender 淡紫色 #e6e6fa 230,230,250 淡紫色[中国搜]
    lavenderblush 淡紫红 #fff0f5 255,240,245 淡紫红[中国搜]
    lawngreen 草绿色 #7cfc00 124,252,0 草绿色[中国搜]
    lemonchiffon 柠檬绸色 #fffacd 255,250,205 柠檬绸色[中国搜]
    lightblue 亮蓝色 #add8e6 173,216,230 亮蓝色[中国搜]
    lightcoral 亮珊瑚色 #f08080 240,128,128 亮珊瑚色[中国搜]
    lightcyan 亮青色 #e0ffff 224,255,255 亮青色[中国搜]
    lightgoldenrodyellow 亮金黄色 #fafad2 250,250,210 亮金黄色[中国搜]
    lightgray 亮灰色 #d3d3d3 211,211,211 亮灰色[中国搜]
    lightgreen 亮绿色 #90ee90 144,238,144 亮绿色[中国搜]
    lightgrey 亮灰色 #d3d3d3 211,211,211 亮灰色[中国搜]
    lightpink 亮粉红色 #ffb6c1 255,182,193 亮粉红色[中国搜]
    lightsalmon 亮肉色 #ffa07a 255,160,122 亮肉色[中国搜]
    lightseagreen 亮海蓝色 #20b2aa 32,178,170 亮海蓝色[中国搜]
    lightskyblue 亮天蓝色 #87cefa 135,206,250 亮天蓝色[中国搜]
    lightslategray 亮蓝灰 #778899 119,136,153 亮蓝灰[中国搜]
    lightslategrey 亮蓝灰 #778899 119,136,153 亮蓝灰[中国搜]
    lightsteelblue 亮钢兰色 #b0c4de 176,196,222 亮钢兰色[中国搜]
    lightyellow 亮黄色 #ffffe0 255,255,224 亮黄色[中国搜]
    lime 酸橙色 #00ff00 0,255,0 酸橙色[中国搜]
    limegreen 橙绿色 #32cd32 50,205,50 橙绿色[中国搜]
    linen 亚麻色 #faf0e6 250,240,230 亚麻色[中国搜]
    magenta 红紫色 #ff00ff 255,0,255 红紫色[中国搜]
    maroon 粟色 #800000 128,0,0 粟色[中国搜]
    mediumaquamarine 中绿色 #66cdaa 102,205,170 中绿色[中国搜]
    mediumblue 中兰色 #0000cd 0,0,205 中兰色[中国搜]
    mediumorchid 中粉紫色 #ba55d3 186,85,211 中粉紫色[中国搜]
    mediumpurple 中紫色 #9370db 147,112,219 中紫色[中国搜]
    mediumseagreen 中海蓝 #3cb371 60,179,113 中海蓝[中国搜]
    mediumslateblue 中暗蓝色 #7b68ee 123,104,238 中暗蓝色[中国搜]
    mediumspringgreen 中春绿色 #00fa9a 0,250,154 中春绿色[中国搜]
    mediumturquoise 中绿宝石 #48d1cc 72,209,204 中绿宝石[中国搜]
    mediumvioletred 中紫罗兰色 #c71585 199,21,133 中紫罗兰色[中国搜]
    midnightblue 中灰兰色 #191970 25,25,112 中灰兰色[中国搜]
    mintcream 薄荷色 #f5fffa 245,255,250 薄荷色[中国搜]
    mistyrose 浅玫瑰色 #ffe4e1 255,228,225 浅玫瑰色[中国搜]
    moccasin 鹿皮色 #ffe4b5 255,228,181 鹿皮色[中国搜]
    navajowhite 纳瓦白 #ffdead 255,222,173 纳瓦白[中国搜]
    navy 海军色 #000080 0,0,128 海军色[中国搜]
    oldlace 老花色 #fdf5e6 253,245,230 老花色[中国搜]
    olive 橄榄色 #808000 128,128,0 橄榄色[中国搜]
    olivedrab 深绿褐色 #6b8e23 107,142,35 深绿褐色[中国搜]
    orange 橙色 #ffa500 255,165,0 橙色[中国搜]
    orangered 红橙色 #ff4500 255,69,0 红橙色[中国搜]
    orchid 淡紫色 #da70d6 218,112,214 淡紫色[中国搜]
    palegoldenrod 苍麒麟色 #eee8aa 238,232,170 苍麒麟色[中国搜]
    palegreen 苍绿色 #98fb98 152,251,152 苍绿色[中国搜]
    paleturquoise 苍宝石绿 #afeeee 175,238,238 苍宝石绿[中国搜]
    palevioletred 苍紫罗兰色 #db7093 219,112,147 苍紫罗兰色[中国搜]
    papayawhip 番木色 #ffefd5 255,239,213 番木色[中国搜]
    peachpuff 桃色 #ffdab9 255,218,185 桃色[中国搜]
    peru 秘鲁色 #cd853f 205,133,63 秘鲁色[中国搜]
    pink 粉红色 #ffc0cb 255,192,203 粉红色[中国搜]
    plum 洋李色 #dda0dd 221,160,221 洋李色[中国搜]
    powderblue 粉蓝色 #b0e0e6 176,224,230 粉蓝色[中国搜]
    purple 紫色 #800080 128,0,128 紫色[中国搜]
    red 红色 #ff0000 255,0,0 红色[中国搜]
    rosybrown 褐玫瑰红 #bc8f8f 188,143,143 褐玫瑰红[中国搜]
    royalblue 皇家蓝 #4169e1 65,105,225 皇家蓝[中国搜]
    saddlebrown 重褐色 #8b4513 139,69,19 重褐色[中国搜]
    salmon 鲜肉色 #fa8072 250,128,114 鲜肉色[中国搜]
    sandybrown 沙褐色 #f4a460 244,164,96 沙褐色[中国搜]
    seagreen 海绿色 #2e8b57 46,139,87 海绿色[中国搜]
    seashell 海贝色 #fff5ee 255,245,238 海贝色[中国搜]
    sienna 赭色 #a0522d 160,82,45 赭色[中国搜]
    silver 银色 #c0c0c0 192,192,192 银色[中国搜]
    skyblue 天蓝色 #87ceeb 135,206,235 天蓝色[中国搜]
    slateblue 石蓝色 #6a5acd 106,90,205 石蓝色[中国搜]
    slategray 灰石色 #708090 112,128,144 灰石色[中国搜]
    slategrey 灰石色 #708090 112,128,144 灰石色[中国搜]
    snow 雪白色 #fffafa 255,250,250 雪白色[中国搜]
    springgreen 春绿色 #00ff7f 0,255,127 春绿色[中国搜]
    steelblue 钢兰色 #4682b4 70,130,180 钢兰色[中国搜]
    tan 茶色 #d2b48c 210,180,140 茶色[中国搜]
    teal 水鸭色 #008080 0,128,128 水鸭色[中国搜]
    thistle 蓟色 #d8bfd8 216,191,216 蓟色[中国搜]
    tomato 西红柿色 #ff6347 255,99,71 西红柿色[中国搜]
    turquoise 青绿色 #40e0d0 64,224,208 青绿色[中国搜]
    violet 紫罗兰色 #ee82ee 238,130,238 紫罗兰色[中国搜]
    wheat 浅黄色 #f5deb3 245,222,179 浅黄色[中国搜]
    white 白色 #ffffff 255,255,255 白色[中国搜]
    whitesmoke 烟白色 #f5f5f5 245,245,245 烟白色[中国搜]
    yellow 黄色 #ffff00 255,255,0 黄色[中国搜]

标签:其他 | 浏览数(546) | 评论数(0) | 2006-09-08

正则表达式,相关链接
http://blog.csdn.net/laily/category/19548.aspx
http://blog.csdn.net/laily/archive/2004/06/30/30525.aspx 微软的正则表达式教程(五):选择/编组和后向引用

http://blog.csdn.net/laily/archive/2004/06/30/30522.aspx 微软的正则表达式教程(四):限定符和定位符

http://blog.csdn.net/laily/archive/2004/06/30/30517.aspx 微软的正则表达式教程(三):字符匹配

http://blog.csdn.net/laily/archive/2004/06/30/30514.aspx 微软的正则表达式教程(二):正则表达式语法和优先权顺序

http://blog.csdn.net/laily/archive/2004/06/30/30511.aspx 微软的正则表达式教程(一):正则表达式简介

http://blog.csdn.net/laily/archive/2004/06/30/30360.aspx 小程序大作为:高级查找/替换、正则表达式练习器、Javascript脚本程序调试器

http://blog.csdn.net/laily/archive/2004/06/24/25872.aspx 经典正则表达式

正则表达式,正规表达式,正则表达式匹配,正则表达式语法,模式匹配,正规表达式匹配 javascript正则表达式 ASP正则表达式 ASP.NET正则表达式 C#正则表达式 JSP正则表达式 PHP正则表达式 VB.NET正则表达式 VBSCript正则表达式编程 delphi正则表达式 jscript

# 回复:经典正则表达式 2004-08-03 2:12 PM 阿赖

正则表达式 regular expression
正则表达式 RegExp
模式 pattern
匹配 Match
.NET命名空间: System.Text.RegularExpression

# 回复:经典正则表达式 2004-08-03 2:14 PM 阿赖

补充:
^"d+$  //匹配非负整数(正整数 + 0)
^[0-9]*[1-9][0-9]*$  //匹配正整数
^((-"d+)|(0+))$  //匹配非正整数(负整数 + 0)
^-[0-9]*[1-9][0-9]*$  //匹配负整数
^-?"d+$    //匹配整数
^"d+("."d+)?$  //匹配非负浮点数(正浮点数 + 0)
^(([0-9]+".[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*".[0-9]+)|([0-9]*[1-9][0-9]*))$  //匹配正浮点数
^((-"d+("."d+)?)|(0+(".0+)?))$  //匹配非正浮点数(负浮点数 + 0)
^(-(([0-9]+".[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*".[0-9]+)|([0-9]*[1-9][0-9]*)))$  //匹配负浮点数
^(-?"d+)("."d+)?$  //匹配浮点数
^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^"w+$  //匹配由数字、26个英文字母或者下划线组成的字符串
^["w-]+(".["w-]+)*@["w-]+(".["w-]+)+$    //匹配email地址
^[a-zA-z]+://匹配("w+(-"w+)*)(".("w+(-"w+)*))*("?"S*)?$  //匹配url

# 回复:经典正则表达式 2004-09-08 7:37 PM totoro

利用正则表达式去除字串中重复的字符的算法程序:

var s="abacabefgeeii"
var s1=s.replace(/(.).*"1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2) //结果为:abcefgi
===============================
如果var s = "abacabefggeeii"
结果就不对了,结果为:abeicfgg
正则表达式的能力有限

# 回复:经典正则表达式 2004-09-10 2:07 PM 阿赖

RE: totoro
谢谢你的指点,这个javascript正则表达式程序算法确实有问题,我会试着找更好的办法!!!

# 回复:经典正则表达式 2004-10-11 3:52 PM Lai

1.确认有效电子邮件格式
下面的代码示例使用静态 Regex.IsMatch 方法验证一个字符串是否为有效电子邮件格式。如果字符串包含一个有效的电子邮件地址,则 IsValidEmail 方法返回 true,否则返回 false,但不采取其他任何操作。您可以使用 IsValidEmail,在应用程序将地址存储在数据库中或显示在 ASP.NET 页中之前,筛选出包含无效字符的电子邮件地址。

[Visual Basic]
Function IsValidEmail(strIn As String) As Boolean
' Return true if strIn is in valid e-mail format.
Return Regex.IsMatch(strIn, ("^(["w-".]+)@(("[[0-9]{1,3}".[0-9]{1,3}".[0-9]{1,3}".)|((["w-]+".)+))([a-zA-Z]{2,4}|[0-9]{1,3})("]?)$")
End Function
[C#]
bool IsValidEmail(string strIn)
{
// Return true if strIn is in valid e-mail format.
return Regex.IsMatch(strIn, @"^(["w-".]+)@(("[[0-9]{1,3}".[0-9]{1,3}".[0-9]{1,3}".)|((["w-]+".)+))([a-zA-Z]{2,4}|[0-9]{1,3})("]?)$");
}


2.清理输入字符串
下面的代码示例使用静态 Regex.Replace 方法从字符串中抽出无效字符。您可以使用这里定义的 CleanInput 方法,清除掉在接受用户输入的窗体的文本字段中输入的可能有害的字符。CleanInput 在清除掉除 @、-(连字符)和 .(句点)以外的所有非字母数字字符后返回一个字符串。

[Visual Basic]
Function CleanInput(strIn As String) As String
' Replace invalid characters with empty strings.
Return Regex.Replace(strIn, "[^"w".@-]", "")
End Function
[C#]
String CleanInput(string strIn)
{
// Replace invalid characters with empty strings.
return Regex.Replace(strIn, @"[^"w".@-]", "");
}


3.更改日期格式
以下代码示例使用 Regex.Replace 方法来用 dd-mm-yy 的日期形式代替 mm/dd/yy 的日期形式。

[Visual Basic]
Function MDYToDMY(input As String) As String
Return Regex.Replace(input, _
""b(?<month>"d{1,2})/(?<day>"d{1,2})/(?<year>"d{2,4})"b", _
"${day}-${month}-${year}")
End Function
[C#]
String MDYToDMY(String input)
{
return Regex.Replace(input,
"""b(?<month>""d{1,2})/(?<day>""d{1,2})/(?<year>""d{2,4})""b",
"${day}-${month}-${year}");
}
Regex 替换模式
本示例说明如何在 Regex.Replace 的替换模式中使用命名的反向引用。其中,替换表达式 ${day} 插入由 (?<day>...) 组捕获的子字符串。

有几种静态函数使您可以在使用正则表达式操作时无需创建显式正则表达式对象,而 Regex.Replace 函数正是其中之一。如果您不想保留编译的正则表达式,这将给您带来方便


4.提取 URL 信息
以下代码示例使用 Match.Result 来从 URL 提取协议和端口号。例如,“http://www.contoso.com:8080/letters/readme.html”将返回“http:8080”。

[Visual Basic]
Function Extension(url As String) As String
Dim r As New Regex("^(?<proto>"w+)://[^/]+?(?<port>:"d+)?/", _
RegexOptions.Compiled)
Return r.Match(url).Result("${proto}${port}")
End Function
[C#]
String Extension(String url)
{
Regex r = new Regex(@"^(?<proto>"w+)://[^/]+?(?<port>:"d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}

 

标签:佳淵专用 | 浏览数(472) | 评论数(0) | 2006-09-08

作者:阿赖 (Email: A at Lai.com.cn 主页:http://www.9499.net Blog: http://blog.csdn.net/laily/ )

关键字:正则表达式  模式匹配 Javascript

摘要:收集一些常用的正则表达式。

正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。

匹配中文字符的正则表达式: ["u4e00-"u9fa5]

匹配双字节字符(包括汉字在内):[^"x00-"xff]

应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

String.prototype.len=function(){return this.replace([^"x00-"xff]/g,"aa").length;}

匹配空行的正则表达式:"n["s| ]*"r

匹配HTML标记的正则表达式:/<(.*)>.*<"/"1>|<(.*) "/>/

匹配首尾空格的正则表达式:(^"s*)|("s*$)

 

String.prototype.trim = function()
{
    return this.replace(/(^"s*)|("s*$)/g, "");
}

利用正则表达式分解和转换IP地址:

下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的Javascript程序:

function IP2V(ip)
{
 re=/("d+)".("d+)".("d+)".("d+)/g  //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
 throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下:

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匹配Email地址的正则表达式:"w+([-+.]"w+)*@"w+([-.]"w+)*"."w+([-.]"w+)*

匹配网址URL的正则表达式:http://(["w-]+".)+["w-]+(/["w- ./?%&=]*)?

利用正则表达式去除字串中重复的字符的算法程序:[注:此程序不正确,原因见本贴回复]

var s="abacabefgeeii"
var s1=s.replace(/(.).*"1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2)  //结果为:abcefgi

我原来在CSDN上发贴寻求一个表达式来实现去除重复字符的方法,最终没有找到,这是我能想到的最简单的实现方法。思路是使用后向引用取出包括重复的字符,再以重复的字符建立第二个表达式,取到不重复的字符,两者串连。这个方法对于字符顺序有要求的字符串可能不适用。

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*"/){0,}([^".]+).*/ig,"$2")
alert(s)

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^"u4E00-"u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^"u4E00-"u9FA5]/g,''))"

用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^"uFF00-"uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^"uFF00-"uFFFF]/g,''))"

用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^"d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^"d]/g,''))"

用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/["W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^"d]/g,''))"

应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现,如下:

 

标签:佳淵专用 | 浏览数(454) | 评论数(0) | 2006-09-08

读者层次:初学  整理:Vision Deng(ZT)

int i = 100;
long l = 2001;
float f=300.2;
double d=12345.119;
char username[]="程佩君";
char temp[200];
char *buf;
CString str;
_variant_t v1;
_bstr_t v2;

一、其它数据类型转换为字符串

  • 短整型(int)
    itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制
    itoa(i,temp,2); ///按二进制方式转换
  • 长整型(long)
    ltoa(l,temp,10);
  • 浮点数(float,double)
    用fcvt可以完成转换,这是MSDN中的例子:
    int decimal, sign;
    char *buffer;
    double source = 3.1415926535;
    buffer = _fcvt( source, 7, &decimal, &sign );
    运行结果:source: 3.1415926535 buffer: """'31415927"""' decimal: 1 sign: 0
    decimal表示小数点的位置,sign表示符号:0为正数,1为负数
  • CString变量
    str = "2008北京奥运";
    buf = (LPSTR)(LPCTSTR)str;
  • BSTR变量
    BSTR bstrValue = ::SysAllocString(L"程序员");
    char * buf = _com_util::ConvertBSTRToString(bstrValue);
    SysFreeString(bstrValue);
    AfxMessageBox(buf);
    delete(buf);
  • CComBSTR变量
    CComBSTR bstrVar("test");
    char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
    AfxMessageBox(buf);
    delete(buf);
  • _bstr_t变量
    _bstr_t类型是对BSTR的封装,因为已经重载了=操作符,所以很容易使用
    _bstr_t bstrVar("test");
    const char *buf = bstrVar;///不要修改buf中的内容
    AfxMessageBox(buf);

  • 通用方法(针对非COM数据类型)
    用sprintf完成转换
    char  buffer[200];
        char  c = """'1"""';
        int   i = 35;
        long  j = 1000;
        float f = 1.7320534f;
        sprintf( buffer, "%c",c);
        sprintf( buffer, "%d",i);
        sprintf( buffer, "%d",j);
        sprintf( buffer, "%f",f);
        

二、字符串转换为其它数据类型
strcpy(temp,"123");

  • 短整型(int)
    i = atoi(temp);
  • 长整型(long)
    l = atol(temp);
  • 浮点(double)
    d = atof(temp);
  • CString变量
    CString name = temp;
  • BSTR变量
    BSTR bstrValue = ::SysAllocString(L"程序员");
    ...///完成对bstrValue的使用
    SysFreeString(bstrValue);
  • CComBSTR变量
    CComBSTR类型变量可以直接赋值
    CComBSTR bstrVar1("test");
    CComBSTR bstrVar2(temp);
  • _bstr_t变量
    _bstr_t类型的变量可以直接赋值
    _bstr_t bstrVar1("test");
    _bstr_t bstrVar2(temp);

三、其它数据类型转换到CString
使用CString的成员函数Format来转换,例如:

  • 整数(int)
    str.Format("%d",i);
  • 浮点数(float)
    str.Format("%f",i);
  • 字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值
    str = username;
  • 对于Format所不支持的数据类型,可以通过上面所说的关于其它数据类型转化到char *的方法先转到char *,然后赋值给CString变量。

四、BSTR、_bstr_t与CComBSTR

  • CComBSTR 是ATL对BSTR的封装,_bstr_t是C++对BSTR的封装,BSTR是32位指针,但并不直接指向字串的缓冲区。
    char *转换到BSTR可以这样:
    BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib
    SysFreeString(bstrValue);
    反之可以使用
    char *p=_com_util::ConvertBSTRToString(b);
    delete p;
    具体可以参考一,二段落里的具体说明。

    CComBSTR与_bstr_t对大量的操作符进行了重载,可以直接进行=,!=,==等操作,所以使用非常方便。
    特别是_bstr_t,建议大家使用它。

五、VARIANT 、_variant_t 与 COleVariant

  • VARIANT的结构可以参考头文件VC98""""Include""""OAIDL.H中关于结构体tagVARIANT的定义。
    对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型,再对联合结构中相同数据类型的变量赋值,举个例子:
    VARIANT va;
    int a=2001;
    va.vt=VT_I4;///指明整型数据
    va.lVal=a; ///赋值

    对于不马上赋值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系:

    Byte bVal; // VT_UI1.
    Short iVal; // VT_I2.
    long lVal; // VT_I4.
    float fltVal; // VT_R4.
    double dblVal; // VT_R8.
    VARIANT_BOOL boolVal; // VT_BOOL.
    SCODE scode; // VT_ERROR.
    CY cyVal; // VT_CY.
    DATE date; // VT_DATE.
    BSTR bstrVal; // VT_BSTR.
    DECIMAL FAR* pdecVal // VT_BYREF|VT_DECIMAL.
    IUnknown FAR* punkVal; // VT_UNKNOWN.
    IDispatch FAR* pdispVal; // VT_DISPATCH.
    SAFEARRAY FAR* parray; // VT_ARRAY|*.
    Byte FAR* pbVal; // VT_BYREF|VT_UI1.
    short FAR* piVal; // VT_BYREF|VT_I2.
    long FAR* plVal; // VT_BYREF|VT_I4.
    float FAR* pfltVal; // VT_BYREF|VT_R4.
    double FAR* pdblVal; // VT_BYREF|VT_R8.
    VARIANT_BOOL FAR* pboolVal; // VT_BYREF|VT_BOOL.
    SCODE FAR* pscode; // VT_BYREF|VT_ERROR.
    CY FAR* pcyVal; // VT_BYREF|VT_CY.
    DATE FAR* pdate; // VT_BYREF|VT_DATE.
    BSTR FAR* pbstrVal; // VT_BYREF|VT_BSTR.
    IUnknown FAR* FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN.
    IDispatch FAR* FAR* ppdispVal; // VT_BYREF|VT_DISPATCH.
    SAFEARRAY FAR* FAR* pparray; // VT_ARRAY|*.
    VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
    void FAR* byref; // Generic ByRef.
    char cVal; // VT_I1.
    unsigned short uiVal; // VT_UI2.
    unsigned long ulVal; // VT_UI4.
    int intVal; // VT_INT.
    unsigned int uintVal; // VT_UINT.
    char FAR * pcVal; // VT_BYREF|VT_I1.
    unsigned short FAR * puiVal; // VT_BYREF|VT_UI2.
    unsigned long FAR * pulVal; // VT_BYREF|VT_UI4.
    int FAR * pintVal; // VT_BYREF|VT_INT.
    unsigned int FAR * puintVal; //VT_BYREF|VT_UINT.

  • _variant_t是VARIANT的封装类,其赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
    使用时需加上#include
    例如:
    long l=222;
    ing i=100;
    _variant_t lVal(l);
    lVal = (long)i;

  • COleVariant的使用与_variant_t的方法基本一样,请参考如下例子:
    COleVariant v3 = "字符串", v4 = (long)1999;
    CString str =(BSTR)v3.pbstrVal;
    long i = v4.lVal;

六、其它一些COM数据类型

  • 根据ProgID得到CLSID
    HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
    CLSID clsid;
    CLSIDFromProgID( L"MAPI.Folder",&clsid);
  • 根据CLSID得到ProgID
    WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
    例如我们已经定义了 CLSID_IApplication,下面的代码得到ProgID
    LPOLESTR pProgID = 0;
    ProgIDFromCLSID( CLSID_IApplication,&pProgID);
    ...///可以使用pProgID
    CoTaskMemFree(pProgID);//不要忘记释放

七、ANSI与Unicode
Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。

  • 将ANSI转换到Unicode
    (1)通过L这个宏来实现,例如: CLSIDFromProgID( L"MAPI.Folder",&clsid);
    (2)通过MultiByteToWideChar函数实现转换,例如:
    char *szProgID = "MAPI.Folder";
    WCHAR szWideProgID[128];
    CLSID clsid;
    long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
    szWideProgID[lLen] = """'"""""';
    (3)通过A2W宏来实现,例如:
    USES_CONVERSION;
    CLSIDFromProgID( A2W(szProgID),&clsid);
  • 将Unicode转换到ANSI
    (1)使用WideCharToMultiByte,例如:
    // 假设已经有了一个Unicode 串 wszSomeString...
    char szANSIString [MAX_PATH];
    WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
    (2)使用W2A宏来实现,例如:
    USES_CONVERSION;
    pTemp=W2A(wszSomeString);

八、其它

  • 对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如:
    LPARAM lParam;
    WORD loValue = LOWORD(lParam);///取低16位
    WORD hiValue = HIWORD(lParam);///取高16位

  • 对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如:
    WORD wValue;
    BYTE loValue = LOBYTE(wValue);///取低8位
    BYTE hiValue = HIBYTE(wValue);///取高8位

  • 两个16位数据(WORD)合成32位数据(DWORD,LRESULT,LPARAM,或WPARAM)
    LONG MAKELONG( WORD wLow, WORD wHigh );
    WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
    LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
    LRESULT MAKELRESULT( WORD wLow, WORD wHigh );

  • 两个8位的数据(BYTE)合成16位的数据(WORD)
    WORD MAKEWORD( BYTE bLow, BYTE bHigh );

  • 从R(red),G(green),B(blue)三色得到COLORREF类型的颜色值
    COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );
    例如COLORREF bkcolor = RGB(0x22,0x98,0x34);

  • 从COLORREF类型的颜色值得到RGB三个颜色值
    BYTE Red = GetRValue(bkcolor); ///得到红颜色
    BYTE Green = GetGValue(bkcolor); ///得到绿颜色
    BYTE Blue = GetBValue(bkcolor); ///得到兰颜色

九、注意事项
假如需要使用到ConvertBSTRToString此类函数,需要加上头文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, "comsupp.lib" )

后记:本文匆匆写成,错误之处在所难免,欢迎指正.

关于把BSTR类型数据转换成CString 类型数据时的问题?
当我在把BSTR类型数据转换成CString 或 “char* 类型”数据时,发现在BSTR类型字符串较短的情况下没问题,当较长时就会出现
内存读写错了。(在NT,2000下都测试是这样的。)
根据你所说:
1)字符串指针(char *)等已经被CString构造函数支持的数据类型 可以直接赋值 str = username;
2)当b 为BSTR类型时可以使用
char *p=_com_util::ConvertBSTRToString(b);
于是以下是对的:
CString cstr;
BSTR bstr;
....
cstr=com_util::ConvertBSTRToString(bstr);
...
可是当bstr非常大时(其实,较大时就会)就会出现内存读写错,不知何故。
此外我发现cstr=com_util::ConvertBSTRToString(bstr);
可以简化为 cstr=bstr; 但当bstr较大时同样出现这个问题。
请兄弟帮忙!急。谢谢!

如何转化((list*)fileip.bian)->liang

关于把CString转化成LPCTSTR的问题 作者:jakiesun 发表日期:2001-9-5 20:08:48
我记的我以前写过这样一段代码
void function()
{
CString str,str1,str2;
function((char*)(LPCTSTR)str1);
str=str1;
...//调试道此发现str2的值随着str的改变而改变,请问能解释一下为什么,如有回答,请通知
wangshaohong@sohu.com,tx先

}

添加lib支持 作者:磨刀霍霍 发表日期:2001-9-10 11:32:12
如果不添加会产生错误,在setting中加入comsupp.lib或者直接#pragma comment( lib, "comsupp.lib" )
微软认为缺省的设置call convention如果不设置成__cdecl也会出现同样的错误。


1。int 转成cstring ??

回复人: caigzhi(caigzhi) (2001-10-17 11:27:35) 得0分 
CString 的成员函数Format()

int a = 2131;
CString str;
str.Format("%d",a);

回复人: tenchi(C与C++之间) (2001-10-17 11:32:12) 得0分 
int i=2001;
char str[10];
_itoa(i,str,10);
CString szString=str; 
回复人: fiolin(幽深的水) (2001-10-17 11:45:40) 得0分 
他们两个的都可以!! 

回复人: sohucsdnvc(thanks) (2001-10-17 13:24:17) 得0分 
那如何把double转成cstring 
回复人: yihugang(小虎子) (2001-10-17 13:29:15) 得6分 
int i = 2131;
char *c=new char[20];
CString str;
sprintf(c,"""'%d"""',i);
str=*c;


回复人: Gu_c_h(Gu) (2001-10-17 14:07:17) 得0分 
用 _gcvt 下面是 msdn 的例子

Example

/* _GCVT.C: This program converts -3.1415e5
* to its string representation.
*/

#include
#include

void main( void )
{
char buffer[50];
double source = -3.1415e5;
_gcvt( source, 7, buffer );
printf( "source: %f buffer: """'%s"""'""""n", source, buffer );
_gcvt( source, 7, buffer );
printf( "source: %e buffer: """'%s"""'""""n", source, buffer );
}


Output

source: -314150.000000 buffer: """'-314150."""'
source: -3.141500e+005 buffer: """'-314150."""'

回复人: Gu_c_h(Gu) (2001-10-17 14:49:56) 得6分 
int a = -3.1415e5;
CString str;
str.Format("%f",a); 
回复人: ruixp(锐剑) (2001-10-17 15:06:48) 得6分 
CString 的成员函数Format()
int a = 2131;
CString str;
str.Format("%d",a);

2。基类对象怎么能转换成派生类对象?
int CDaoListView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
lpCreateStruct->style |= LVS_REPORT |LVS_EDITLABELS;
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;

//////////// 创建ImageList;
CDaoViewApp * pApp = (CDaoViewApp *) AfxGetApp();

m_pImageList = new CImageList();
ASSERT(m_pImageList !=NULL);
m_pImageList->Create(16,16,TRUE,4,4);
m_pImageList->Add(pApp->LoadIcon(IDI_KEY));
m_pImageList->Add(pApp->LoadIcon(IDI_ICON4));
m_pImageList->Add(pApp->LoadIcon(IDI_ICON5));

CListCtrlEx& ctlList = (CListCtrlEx&) GetListCtrl();//我不懂的就这句,cListCtrlEx看下面的声明。
ctlList.SetImageList (m_pImageList, LVSIL_SMALL) ;
////////////
return 0;
}

class CListCtrlEx : public CListCtrl//类cListCtrlEx定义。
{.....
}
class CDaoListView : public CListView//cDaoListView定义!
{
...
}
注:我的问题是GetListCtrl()返回的是一个cListCtrl对象的引用,怎么能把它转换成一个它的派生类对象的引用?c++的类型转换怎么支持? 


回复贴子: 
回复人: lhj(努力加油) (2002-1-29 18:56:06) 得0分 
CListCtrlEx& ctlList = (CListCtrlEx&) GetListCtrl();
这是强制类型转换,&表示是一个引用,lctList的值在这次赋值后不能被修改。

回复人: wwwsq(wwwsq) (2002-1-29 19:09:22) 得0分 
建议你找本C++方面的书看看,VC虽然号称可视,实际上C++基础还是很重要的。


回复人: xcopy(xcopy) (2002-1-29 19:26:16) 得0分 
用dynamic_cast()可以安全的转换。 

3。如何在CString和double之间转换?要求转换之后能保留小数点,保留正负号??

cstring::format(%.xe) x为精度位 

回复人: pchaos(杂讲) (2002-1-28 11:21:46) 得0分 
CString str;
double db;
str = "123.456";
db = atof((LPCTSTR)str); 
回复人: hgw111(hg) (2002-1-28 11:52:57) 得0分 
CString -> double : atof
double ->CString :Format 
回复人: pchaos(杂讲) (2002-1-28 13:46:04) 得0分 
CString str;
double db;
str = "123.456";
db = atof((LPCTSTR)str); 
db = 777.999;
str.format("%e", db); 

4。字符型要转换成int??
atoi(str) 

5。_bstr_t 到 unsigned int??
_bstr_t str;
unsigned int Length=6;
Length=Length-str.length(); 

6。VARIANT类型转换问题?
我在使用MSCOMM中SetOutput()函数时
形参必须为VARIANT变量
如何将其它的数据类型转换为VARIANT类型?
如:Cstring->VARIANT、 *char->VARIANT
我对VARIANT的类型结构体不太熟,请讲详细些(最好有范例),谢谢!

回复贴子: 
回复人: vc_xiaoxin(小新) (2001-12-26 15:43:57) 得0分 
VARIANT本身是一个复杂的结构,别的数据怎么转呀?关注 
回复人: mpg_liu(星仁) (2001-12-27 18:33:50) 得10分 
定义一个VARIANT变量后,他应该是一个结构体变量,其中有一个成员是字符型的,给这个成员赋值 
回复人: LLnju(LLnju) (2001-12-27 18:36:10) 得0分 
实在不清楚嘛就用 _variant_t , COleVariant 这些东东嘛,很好用的啊 
回复人: softarts(CDMA2000) (2001-12-27 18:41:32) 得10分 
构造一个就行了。
VARIANT varXX;
CString strYY;
varXX.vt=VT_BSTR;
varXX.bstrVal = strYY.allocsysstring();
应该可以了。
回复人: softarts(CDMA2000) (2001-12-27 18:42:11) 得0分 
我也觉得COleVariant要好用一些,呵呵,我都用它。 
回复人: bobofu(有问题要问) (2001-12-27 19:32:18) 得10分 
CString str;
_variant_t var;
var = _variant_t(str); 

7。COleVarant 如何转换为 CString?
CString 如何转换为 char *
CString 如何转换为 char[xx] ??

CString 如何转换为 char * wsprintf或者=
CString 如何转换为 char[xx] strcpy() 
回复人: SecretGarden(天堂鸟) (2002-1-14 11:55:23) 得0分 
COleVarant封装了VAREANT类型。
VAREANT类型其实是个巨大地Union,里面自然有你
想要地unsigned char *类型。
CString地GetBuffer和Format可以实现你的后两个问题


8。v_variant_t类型转换成cstring
总提示我cstring未定义
程序如下
_variant_t vfirstname;//存储的是数据库中的数据
CString str;//提示出错
vfirstname=pRs->GetCollect (_variant_t("Phone_Num"));
vfirstname.ChangeType (VT_BSTR);
str=vfirstname.bstrVal;//提示出错 


回复贴子: 
回复人: hydnoahark(诺亚方舟) (2001-11-12 11:56:51) 得10分 
>>CString str;//提示出错
要求include 并且设置Use run-time Library为Multithreaded 
回复人: zhengyun_ustc(^-^) (2001-11-12 12:04:39) 得15分 
CString未定义,说明你的工程没有引用MFC!!

要想使你的工程支持MFC,请按照以下步骤作:
1:在你的stdafx.h的头文件中加入:
#include "afxtempl.h"
这是一个囊括了MFC的集合定义的头文件,有了它,你的工程就识别Cstring类了。

2:在你的工程设置中,在General页中,选择“MFC”为“Using MFC in a shared DLL”

OK,现在再编译你的工程即可。 
回复人: zhengyun_ustc(^-^) (2001-11-12 12:06:56) 得5分 
_variant_t的bstrVal成员是BSTR类型。
它是一个指向一个OLECHART*的指针。 
回复人: vickowang(小苍) (2001-11-12 12:48:21) 得5分 
(char *)_bstr_t(vfirstname) 
回复人: smallfool(smallfool) (2001-11-12 13:52:54) 得4分 
或许你还需要一个从UNICODE字符到ANSI字符的转变函数 
回复人: sun_1112(萧) (2001-11-12 17:34:44) 得0分 
谢谢大家
给我这么大的支持!:) 
回复人: zhengyun_ustc(^-^) (2001-11-14 13:24:07) 得0分 
用vickowang(小苍)的意见可能会有问题,转换出的字符串应该是乱码。

因为(char *)转换需要一个const的字符串资源,才能强制转换。
所以应该:
_bstr_t bstrTemp = _bstr_t(vfirstname.bstrVal);
TCHAR szTemp[MAX_PATH];
szTemp = (char*)bstrTemp;

9。char * 转换为TCHAR类型??
直接转换,TCHAR相当于char了
char * s;
TCHAR * s1=(TCHAR *)s; 
回复人: dysxq() (2001-12-21 21:26:25) 得0分 
要看你的程序设置是ANSI还是UNICODE, 如果是ANSI,直接转,如果是UNICODE,TCHAR相当于WCHAR, 要用mbstowcsz转一下 
回复人: xiaoxiaohan(萧晓寒) (2001-12-21 23:52:17) 得0分 
Unicode :宽字节字符集
1. 如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数?
可以调用Microsoft Visual C++的运行期库包含函数_mbslen来操作多字节(既包括单字节也包括双字节)字符串。
调用strlen函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。
2. 如何对DBCS(双字节字符集)字符串进行操作?
函数 描述
PTSTR CharNext ( LPCTSTR ); 返回字符串中下一个字符的地址
PTSTR CharPrev ( LPCTSTR, LPCTSTR ); 返回字符串中上一个字符的地址
BOOL IsDBCSLeadByte( BYTE ); 如果该字节是DBCS字符的第一个字节,则返回非0值
3. 为什么要使用Unicode?
(1) 可以很容易地在不同语言之间进行数据交换。
(2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。
(3) 提高应用程序的运行效率。
Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成
Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结
果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序
更加有效地运行。
Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数
Windows 98 只支持ANSI,只能为ANSI开发应用程序。
Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。
4. 如何编写Unicode源代码?
Microsoft公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用
Unicode来对它进行编译。只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。
_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。
5. Windows定义的Unicode数据类型有哪些?
数据类型 说明
WCHAR Unicode字符
PWSTR 指向Unicode字符串的指针
PCWSTR 指向一个恒定的Unicode字符串的指针
对应的ANSI数据类型为CHAR,LPSTR和LPCSTR。
ANSI/Unicode通用数据类型为TCHAR,PTSTR,LPCTSTR。
6. 如何对Unicode进行操作?
字符集 特性 实例
ANSI 操作函数以str开头 strcpy
Unicode 操作函数以wcs开头 wcscpy
MBCS 操作函数以_mbs开头 _mbscpy
ANSI/Unicode 操作函数以_tcs开头 _tcscpy(C运行期库)
ANSI/Unicode 操作函数以lstr开头 lstrcpy(Windows函数)
所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。
Windows会如下定义:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE
7. 如何表示Unicode字符串常量?
字符集 实例
ANSI “string”
Unicode L“string”
ANSI/Unicode T(“string”)或_TEXT(“string”)if( szError[0] == _TEXT(‘J’) ){ }
8. 为什么应当尽量使用操作系统函数?
这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于
这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。
如:StrCat,StrChr,StrCmp和StrCpy等。
9. 如何编写符合ANSI和Unicode的应用程序?
(1) 将文本串视为字符数组,而不是chars数组或字节数组。
(2) 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
(3) 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
(4) 将TEXT宏用于原义字符和字符串。
(5) 执行全局性替换(例如用PTSTR替换PSTR)。
(6) 修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传
递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来
分配内存。这就是说,应该调用malloc(nCharacters *sizeof(TCHAR)),而不是调用malloc(nCharacters)。
10. 如何对字符串进行有选择的比较?
通过调用CompareString来实现。
标志 含义
NORM_IGNORECASE 忽略字母的大小写
NORM_IGNOREKANATYPE 不区分平假名与片假名字符
NORM_IGNORENONSPACE 忽略无间隔字符
NORM_IGNORESYMBOLS 忽略符号
NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT 将标点符号作为普通符号来处理
11. 如何判断一个文本文件是ANSI还是Unicode?
判断如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。
12. 如何判断一段字符串是ANSI还是Unicode?
用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 
IsTextUnicode有可能返回不正确的结果。
13. 如何在Unicode与ANSI之间转换字符串?
Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。

回复人: xtky_limi(痛在心中笑在脸上) (2001-12-22 0:35:58) 得0分 
上面说的已经比较全了。 
回复人: xtky_limi(痛在心中笑在脸上) (2001-12-22 0:38:13) 得0分 
TEXT是宏
相当于L##

它可以根据编译环境确定为DBMS,还是UNICODE字符集

10。int类型转换为CString类型?
回复人: tjmxf(天涯) (2001-12-17 19:59:34) 得0分 
itoa() 
回复人: zf925(天下哪来那么多高手) (2001-12-17 20:00:30) 得22分 
char m[20];
str=str + itoa(i,m,10); 
回复人: yuezifeng(wybzd) (2001-12-17 20:00:50) 得22分 
CString str;
str.Format("%d",i);

回复人: kingfish(今飞) (2001-12-17 20:06:27) 得0分 
str.Format("%s%d",str,i); 
回复人: tanyajun(谈子) (2001-12-17 20:09:25) 得0分 
CString str="test";
int i=11;
CString str1;
str1.Format("%d",i);
str = str+str1;

回复人: guanjinke(纶巾客) (2001-12-17 20:10:42) 得0分 
int i=11;
CString str="test";
CString addition;
addition.Format("%d",i);
str+=addition;
就可以了。 

11。关于sprintf类型转换的问题
sprintf(buf,"select price from ls01 where p_date>="""'%"""'",t_date)
其中t_date是CTime类型,%后面应该是什么呢?%s是String类型,%c是char,那么CTime型对应的是什么呢? 
 
回复人: yakai(日落长河) (2001-12-17 17:45:47) 得0分 
sprintf(buf,"select price from ls01 where p_date>="""'%S"""'",(LPCSTR)t_date.Format( "%A, %B %d, %Y" ));
如果不行,就
char temp[50];
CString str=t_date.Format( "%A, %B %d, %Y" );
strcpy(temp,(LPCSTR)str);
sprintf(buf,"select price from ls01 where p_date>="""'%S"""'",temp);
CTime::Format返回CString 
回复人: loh(乐啸天涯) (2001-12-17 17:52:57) 得0分 
wait

don"""'t know 
回复人: masterz() (2001-12-17 20:21:05) 得0分 
SQL语句中日期要写成字符串"yyyymmdd" 


12。类型转换 unsigned int <==>CString??
回复次数:8
发表时间:2001-12-17 10:25:23

unsigned int f;//unsigned int 0~4294967295
CString g;
f=2300000000;
g.Format("%d",f);
AfxMessageBox(g);
出错。 


回复人: ydogg(灰毛兔频频) (2001-12-17 10:31:29) 得0分 
unsigned int f;//unsigned int 0~4294967295
CString g;
f=2300000000;
g.Format("%d",f);
MessageBox(g);//使用AfxMessageBox,需要窗口的句炳参数

回复人: asdmusic8(asdmusic8) (2001-12-17 10:35:15) 得0分 
我 AfxMessageBox(g); 和MessageBox(g); 都不错。
错的是g.从 2300000000=》1994967296

回复人: asdmusic8(asdmusic8) (2001-12-17 10:36:10) 得0分 
是2300000000=》-1994967296 类型转换错。

回复人: ydogg(灰毛兔频频) (2001-12-17 10:37:54) 得6分 
g.Format("%u",f);

回复人: asdmusic8(asdmusic8) (2001-12-17 10:40:24) 得0分 
to dgsnmpoperate 那怎么从 CString ==>>unsigned int 
回复人: kingfish(今飞) (2001-12-17 10:42:10) 得6分 
既然是 unsigned int,
超过 0x7f000000 (2130706432) 当然不能用 %d (signed int)用%u 
回复人: kingfish(今飞) (2001-12-17 10:44:57) 得8分 
CString ==>>unsigned int 
char *p = (LPSTR)(LPCSTR) g;
f = atoi(p); 

13。static_cast、dynamic_cast 和直接类型转换(如 (void *)p )的区别?
发表时间:2001-12-14 9:31:13

先拷贝MSDN中的一小段话:
class B { ... };
class C : public B { ... };
class D : public C { ... };

void f(D* pd)
{
C* pc = dynamic_cast(pd); // ok: C is a direct base class
// pc points to C subobject of pd 

B* pb = dynamic_cast(pd); // ok: B is an indirect base class
// pb points to B subobject of pd 
...
}
我已经知道 static_cast 和 dynamic_cast 的作用,但MSDN中并没有提到这两个操作符与直接类型转换如
void f(D* pd)
{
C* pc = (C*)(pd);

B* pb = (B*)(pd); 
...
}
的不同啊。不知道那位知道的告诉一声,在此不胜感谢,50分奉上。

回复贴子:ysdesigned(清泉) (2001-12-14 10:03:07) 得0分 
static_cast、dynamic_cast 代 替 简 单 的 强 制 转 化, 从 而 消 除 多 继 承 带 来 的 歧 义。 使 用 这 两 个 运 算 符 号, 我 们可以 在 对 象 运 行 过 程 中 获 取 对 象 的 类 型 信 息
dynamic_cast 用于多态类型的转换
static_cast 用于非多态类型的转换

回复人: masterz() (2001-12-14 10:05:48) 得0分 
static_cast<...>compile时能发现不正确的指针类型转换
dynamic_cast<...>运行时如果发现是不正确的指针类型转换会返回NULL
(void*)强制转换,如果是不正确的指针类型转换,没有办法检查,不如上面2中安全 
回复人: meady() (2001-12-14 11:29:05) 得0分 
类型安全 
回复人: bluecrest(为什么我的VC还是那么的菜) (2001-12-14 11:45:34) 得0分 
com技术内幕介绍过
我刚看完就忘了 

14。byte数据类型转换成int型??
我用byte型读进一组数据想把他转成int型进行运算如何做呢?
如果再把int型转回byte又怎么实现呢? 

回复人: louifox(兰陵笑笑生) (2001-12-6 9:18:38) 得0分 
用下面这些宏:
WORD MAKEWORD(
BYTE bLow, 
BYTE bHigh 
);
BYTE LOBYTE(
WORD wValue 
);
BYTE HIBYTE(
WORD wValue 
);

回复人: chskim(大刀阔斧) (2001-12-6 9:21:04) 得0分 
int i;
BYTE b;
b=128;
i=(int)b;

回复人: nannet(似的) (2001-12-6 9:38:24) 得0分 
这个宏怎么用呀?有没有简单一点儿的,我现在能把BYTE 转成INT 型了,再转回去直接赋值可以吗? 
回复人: louifox(兰陵笑笑生) (2001-12-6 9:46:24) 得20分 
WORD wa;
BYTE ba=32,bb=64;
wa=MAKEWORD(ba,bb);
...
WORD wa=1234;
BYTE ba,bb;
ba=LOBYTE(wa);
bb=LOBYTE(wa);

回复人: nannet(似的) (2001-12-6 9:54:55) 得0分 
问题解决了,多谢各位 

15。类型转换的问题,unsigned int --> lptstr/lpctstr??
发表时间:2001-8-7 23:49:41
如果强制转换的话,会出现致命错误,有什么好的办法呢?
能列举一些其他的办法吗?
谢谢大虾! 

回复人: AlphaOne(总是第一个倒下) (2001-8-8 0:02:43) 得5分 
你为什么要强行转换呢?
如果你是要把int 的值作为 lptstr/lpctstr 的内容的话,
可以用CString:
unsigned int a = 100;
LPCTSTR lpText;
CString str;
str.Format("%d",a);
lpText = (LPCTSTR)str;

回复人: tryibest(编の魂) (2001-8-8 8:20:20) 得5分 
wsprintf(str,"%u",ui); 
回复人: zzh() (2001-8-8 9:04:39) 得5分 
这种情况不需要进行强制转换,直接使用wsprintf就可以了。 
回复人: GJA106(中文字符) (2001-8-8 10:10:51) 得5分 
unsigned int m_na=22;
LPTSTR lptstr;
wsprintf(lptstr,"%u",m_na);

16。关于COM类型转换问题??
我定义了两个变量,一个是void *piaRef=new unsigned char[1000];另一个是m_Temp=new CComVariant();我的问题是如何将piaRef中的值
COPY到m_Temp中。 

回复人: nichang() (2001-11-21 15:34:04) 得0分 
CComBSTR bsRef=piaRef;
m_Temp=bsRef.copy() 
回复人: VincentChin(瘟神) (2001-11-21 17:04:24) 得0分 
CComBSTR bsRef=piaRef;
//error C2440: """'initializing"""' : cannot convert from """'void *"""' to """'class ATL::CComBSTR"""'
m_Temp=bsRef.copy();
//error C2440: """'="""' : cannot convert from """'unsigned short *"""' to """'class ATL::CComVariant *"""' 
回复人: nichang() (2001-11-21 17:14:28) 得0分 
将void*改为unsigned char * 
回复人: VincentChin(瘟神) (2001-11-21 17:22:22) 得0分 
我用CComBSTR bsRef=(unsigned char*)piaRef,也不行吗? 
回复人: VincentChin(瘟神) (2001-11-21 17:28:06) 得0分 
会报错:
error C2440: """'type cast"""' : cannot convert from """'unsigned char *"""' to """'class ATL::CComBSTR"""' 
回复人: nichang() (2001-11-22 9:12:14) 得0分 
m_Temp=::SysAllocString((OLECHAR *)piaRef) 
回复人: VincentChin(瘟神) (2001-11-22 10:43:07) 得0分 
//error C2440: """'="""' : cannot convert from """'unsigned short *"""' to """'class ATL::CComVariant *"""' 
回复人: VincentChin(瘟神) (2001-11-22 11:22:35) 得0分 
m_Temp=new CComVariant(::SysAllocString(OLECHAR *)piaRef));没有出错,但是我的m_Temp是COM组件中的一个PROPERTY,我想返回的是
unsigned char类型(单字节),但经过上述转换后,就不再是单字节了呀!怎么办? 
回复人: jiangsheng(蒋晟) (2001-11-22 11:36:58) 得0分 
把这个属性的类型改成BSTR 
回复人: GrayWhite(灰白) (2001-11-22 12:01:09) 得0分 
m_Temp = new CComVariant((char*) piaRef);就可以了。VB就是用的BSTR,你要给谁用阿?VC不用VARIANT的。 
回复人: GrayWhite(灰白) (2001-11-22 12:18:18) 得19分 
哦,我明白了,你要各字节数组:
SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, 1000);
if (!psa)
_com_issue_error(ERROR_NOT_ENOUGH_MEMORY);

HRESULT hr
for (long i = 0; i < 2; i ++)
{
if (FAILED (hr = SafeArrayPutElement(psa, &i, piaRef + i)))
_com_issue_error(hr);
}

_variant_t va; // include
va.vt = VT_ARRAY | VT_UI1;
va.parray = psa;

m_Temp = new CComVariant(va); 
回复人: VincentChin(瘟神) (2001-11-22 14:21:08) 得0分 
SafeArrayPutElement(psa, &i, piaRef + i)
//error C2036: """'void *"""' : unknown size 
回复人: VincentChin(瘟神) (2001-11-22 14:46:05) 得0分 
To GrayWhite:为什么要for(long i=0;i<2;i++)? 
回复人: nichang() (2001-11-22 15:16:35) 得0分 
到底你想怎样转换嘛,是将数组内的值拷贝到CComVariant中存为字符串吗? 
回复人: VincentChin(瘟神) (2001-11-22 15:28:35) 得0分 
我是想把piaRef中的值照原样返回给其它程序使用。我正在做的是一个COM组件。谢谢各位! 
回复人: nichang() (2001-11-22 15:34:40) 得10分 
unsigned char *s=new unsigned char[1000];
strcpy((char*)s,"1234");//可以用你自己方法设置s中的值。
BSTR bstrS;
oleS=A2WBSTR((char*)s);//将char*转换成BSTR类型

CComVariant comVT;
comVT=oleS;//将BSTR转成CComVariant,这里一步也可,comVT=A2WBSTR((char*)s);

回复人: VincentChin(瘟神) (2001-11-22 16:54:07) 得0分 
谢谢你!
但我还有一个问题,就是如果在s中有"""'"""""'之类的东西我该怎么返回呢?char *遇到"""'"""""'会认为到头了。完整的设计是这样的,我定义一个void * 用来从一个外部设备获取数据,该数据应该是unsigned char,我想把这个返回的数据作为属性传出,让其它应用使用(如VB)。 
回复人: nichang() (2001-11-22 17:18:09) 得0分 
将"""'"""""'转换成其它如"""'""""1"""'就OK了, 
回复人: jiangsheng(蒋晟) (2001-11-22 18:07:16) 得0分 
用字符串数组 
回复人: VincentChin(瘟神) (2001-11-23 15:54:39) 得0分 
谢谢各位的回复!我的问题解决了!如下:
SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, 1000);
if (!psa)
return S_FALSE;
HRESULT hr;
for (long i = 0; i < 1000; i ++)
if (FAILED (hr = SafeArrayPutElement(psa, &i, ((unsigned char*)piaRefTemplate) + i)))
return S_FALSE;
VARIANT va;
va.vt = VT_ARRAY | VT_UI1;
va.parray = psa;
CComVariant *m_Temp = new CComVariant();
m_Temp->Copy(&va);

17。类型转换 static_cast reinterprete_cast 的区别??
static_cast reinterprete_cast 的区别 

回复人: tar(GPS) (2001-11-21 10:06:41) 得0分 
static_cast会检查转换类型健的相关性
如果没有的画会有编译错误
reinterprete_cast就是硬转了 
回复人: tigerwoods(tao) (2001-11-21 12:28:19) 得0分 
是否可以这样理解:在多重继承中,static_cast可实现对象指针的移动,从而指向正确的父类对象部分,而reinterprete_cast不作偏移? 
回复人: liu_feng_fly(我恨死驱动程序了,哎,就是为了混口饭吃) (2001-11-21 12:35:14) 得0分 
在多重继承中可以用dynamic_cast啊 

18。那如何取得CString中的字符串??
回复人: nichang() (2001-11-5 17:06:00) 得0分 
=(LPCTSTR)CString变量 
回复人: snake1122(领悟) (2001-11-5 17:12:16) 得0分 
方法太多了:
GetAt,Left,Mid,Right等等,就看你怎么取了! 
回复人: dusb(保时捷) (2001-11-5 17:34:29) 得0分 
可是不管是GetAt,Left,Mid,Right返回类型都是CString,还是不能用,我是要取其中的字符串,奇怪的是,VC中没有string类型。(我要的字符串是给树型控件中的分支名称) 
回复人: Alps_lou(云飞扬) (2001-11-5 17:41:36) 得0分 
有string类型的啊,要包含 
回复人: luxes() (2001-11-5 17:42:19) 得0分 
加上(LPCTSTR),相当于一个const char *了,还不能用? 
回复人: wt007(tt) (2001-11-5 17:48:33) 得0分 
GetBuffer 
回复人: espon99() (2001-11-5 17:54:06) 得20分 
(LPSTR)(LPCTSTR)

回复人: ineedyou(古寺僧) (2001-11-5 17:59:29) 得0分 
...m_str.GetBuffer(needlen)...;
....
m_str.ReleaseBuffer() 
回复人: dusb(保时捷) (2001-11-6 15:08:36) 得0分 
espon99大侠,果然是绝招,不过能否解释一下啊? 

19。如何从CString类型转换为Unicode string 类型?
回复人: ychener(贫血) (2001-10-20 10:28:48) 得0分 
CString本身就支持Unicode的。
只要你选择的是UniCode编译,生成的可执行程序就是支持UniCode的 

回复人: ychener(贫血) (2001-10-20 10:30:04) 得0分 
CString类是自适应的就像TCHAR一样,如果你定义了UniCode宏 就会以UniCode编译 

回复人: xjl1980_81(阿龙) (2001-10-20 10:35:16) 得0分 
不是呀,我有个函数中有一个参数需Unicode string 类型的,比如应该填L"abc",而且引号中的内容要有变化,现在我有一个 temp变量,是CString类型的,如何用呀? 
回复人: xt_jat(桑巴) (2001-10-20 10:39:37) 得0分 
_T()
_TEXT()
行不行? 
回复人: xjl1980_81(阿龙) (2001-10-20 10:43:18) 得0分 
不行 
回复人: Jeffery__Chen() (2001-10-20 11:04:53) 得0分 
强制转化:
CString temp;
WCHAR wTemp = (WCHAR)temp; 
回复人: xjl1980_81(阿龙) (2001-10-20 11:37:06) 得0分 
to:Jeffery__Chen() 
不对呀,出现不能转换的错误 
回复人: hongzhh(关儿) (2001-10-20 11:39:42) 得0分 
问题是这样的:
temp 是 CString类型变量,值为zhh
现在有一个API 
PCCERT_CONTEXT WINAPI CertFindCertificateInStore(
HCERTSTORE hCertStore, 
DWORD dwCertEncodingType, 
DWORD dwFindFlags, 
DWORD dwFindType, 
const void *pvFindPara, //此处用 L"zhh" 没问题 
//请问怎么转换 可以 用temp

PCCERT_CONTEXT pPrevCertContext 
);

在此谢谢大家,请帮忙看看

回复人: hongzhh(关儿) (2001-10-20 13:27:10) 得0分 
WCHAR wszDomain[256]; 
MultiByteToWideChar( CP_ACP, 0, temp,
strlen(temp)+1, wszUserName, 
sizeof(wszUserName)/sizeof(wszUserName[0]) );


wszUserName就是转换后的值

回复人: ychener(贫血) (2001-10-23 11:43:05) 得0分 
只要你用的是CString的函数就行的,如果你要用类似strcpy函数时,看看MSDN中一般情况下都有响应的函数对于Unicode的。只要换成_tcscpy等等。 
回复人: ychener(贫血) (2001-10-23 11:44:10) 得0分 
你有没有定义Unicode宏? 
20。请问在用ATL且不支持MFC的组件开发中,如何将从数据库中读到的DATE数据类型转换回为字符串?? 

复人: zhxuys(zhxuys) (2001-9-24 10:36:47) 得0分 
ATL把datetime类型的列映射为DBTIMESTAMP类型,可取出该类型的year、month、day等,然后将这些数据传递回客户端,在客户端用CTime来构造 
回复人: YUANXU(旭) (2001-9-24 11:18:14) 得0分 
to zhxuys:CTime是MFC类,在ATL 不支持MFC时不能用。DATE其实质是个double* 
回复人: zhxuys(zhxuys) (2001-9-24 11:57:01) 得0分 
你在客户端与服务器端只用ATL规定的数据类型或VARIANT类型,而在客户端,可以用MFC来重新构造想要的数据结构 

21。类型转换,CString to wchar_t ??

CString ss("aabb");
wchar_t* cc;
cc=ss.AllocSysString();

22。如何将CString类型转换为_bstr_t类型?
回复人: wei97081116(韦小宝) (2001-9-4 11:19:30) 得20分 
CString b;
_bstr_t a;
a=(_bstr_t)b; 

回复人: zhaozhen1212(赵振) (2001-9-18 1:30:18) 得0分 
_bstr_t a=b.AllocSysString();;

23。如何把一个CString类型转换成一个普通的字符串,如char*?

回复人: liu_feng_fly(我恨死驱动程序了,哎,就是为了混口饭吃) (2001-9-17 18:00:52) 得0分 
所以,直接用就可以,因为类里边有这样的转换函数 
回复人: ydogg(灰毛兔频频) (2001-9-17 18:01:21) 得0分 
CString show;

char *p = show.GetBuffer(show.GetLength()); 
回复人: jiangping_zhu(娜可露露之风之刃) (2001-9-17 18:02:05) 得0分 
(char*)(LPCTSTR)str 
回复人: bmouse(老鼠) (2001-9-18 0:10:56) 得0分 
同意楼上! 
回复人: bmouse(老鼠) (2001-9-18 0:13:22) 得0分 
你还可以通过GetBuff来直接操作CString的缓冲区,不过要记着释放缓冲区. 

24。CString 类型转换成 unsigned char类型吗??
回复人: LJN(*)风流倜傥无人及,玉树偏又临风立(*) (2001-9-17 12:46:01) 得0分 
可以用CString.GetBuffer函数 
回复人: xpmao() (2001-9-17 13:09:09) 得0分 
CString strWork;
MessageBox(0,(LPSTR)strWork,0,0);
或MessageBox(0,strWork.GetBuffer(0),0,0);

回复人: sandd(降龙掌) (2001-9-17 13:17:32) 得0分 
CString string;

(LPCTSTR)string; 
回复人: jeff_hunter(PandaLee) (2001-9-17 13:45:30) 得0分 
(unsigned char *)(LPCTSTR) 
回复人: fandh(好了) (2001-9-17 14:00:57) 得0分 
用(unsigned char *)(LPCTSTR)即可 
回复人: ygd(ygd) (2001-9-17 16:11:17) 得0分 
unsigned char *p;
CString str;
int length=str.GetLength();
for(int i=0;ip[i]=str.GetAt(i); 
回复人: swordbroken(断剑书生) (2001-9-17 16:25:57) 得0分 
CString str;
unsigned char string[30];
strcpy(string,str); 

25。何将一个unsigned int 类型变量值赋给类型为unsigned short的变量,并保证数值不丢失(当然数值在一定范围内)?
回复人: maxsuy(魔法师兔子) (2001-8-14 16:37:30) 得0分 
直接=就OK了 
回复人: oppmm(ppmm) (2001-8-14 16:38:11) 得0分 
直接赋值 
回复人: milefo(弥勒佛) (2001-8-14 16:40:40) 得0分 
如果数值在一定范围内怎么回丢失呢?
unsigned short a;
unsigned int b;
a=( b & 0xffff);
你试试看吧!

26。CString ----char* 
定义了char* aa的变量,现在有一个CString的变量bb,怎样把bb的值赋给aa呢? 

回复人: emmai(WaTaXiWaWaTaXi) (2001-8-10 11:57:33) 得0分 
aa=bb.GetBuffer(); 
回复人: hswqs(??????????????????) (2001-8-10 11:59:01) 得0分 
aa= (LPSTR)(LPCTSTR)bb; 
回复人: ydogg(灰毛兔) (2001-8-10 12:27:23) 得0分 
1.aa=bb.GetBuffer(bb.GetLenth());//第一种方法
2.aa= (LPSTR)(LPCTSTR)bb; //第二种方法 
回复人: zhizhi() (2001-8-10 13:16:23) 得0分 
aa= (char *)(LPCTSTR)bb,hehe 

27。在一个COM的接口函数中有一个 BSTR* 类型的参数,需要把一个 char * 类型转换为 BSTR* 类型,不知道如何转换? 由于调用这个函数后需要把这个参数值再取出来所以只能用指针,另外在调用的时候应该用什么类型的数据传递参数呢?大虾帮忙。

BSTR bstr = SysAllocString(L"字符串");
这样转换,用的时候你用地址操作符&啊,要不指针还得new 

回复人: yongyue2000i(小吕) (2001-9-9 18:38:26) 得13分 
CString str = "abcd";
BSTR bstr = str.AllocSysString(); 
回复人: houjzs() (2001-9-9 19:14:44) 得13分 
BSTR b = SysAllocString(OLESTR("your string"));

28。要把一个double的数字输出到CEdit控件是否需要类型转换?
回复人: jiangsheng(蒋晟) (2001-8-24 14:46:17) 得0分 
void AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, double& value ); 
回复人: xiezhsh(雪中行) (2001-8-24 14:56:22) 得0分 
假如你的CEdit相关的成员变量是Double型的,那根本用不着.(ClassWizard增加成员变量的对话框中,Variable Type选择Double可) 
回复人: xiezhsh(雪中行) (2001-8-24 14:58:16) 得0分 
假如你的CEdit相关的成员变量不是Double型的,是CString型,那就需要用ltoa()来转换成CString型, 
回复人: haven(大天) (2001-8-24 14:58:32) 得0分 
m_Edit.Fromat("%l",VarBouble);
updatedata(false); 
回复人: 12345678() (2001-8-24 14:59:54) 得0分 
CString m_Edit.Format("%lf", doubleVar); 
GetDlgItem(EditID)->SetWindowText(m_strEdit); 

29。该如何把 WINDOWPLACEMENT * 转换成 char **类型??

(char**)&pWP 

30。怎样把CString的类型转换成char*型的?
回复人: dcz(dcz) (2001-8-19 19:13:27) 得5分 
// str is CString var
char* temp = strdup(str);
...
free(temp); 
回复人: yu900(疾风之狼) (2001-8-19 19:57:25) 得0分 
getbuffer();即可! 
回复人: aileen_long(挑战2001) (2001-8-19 21:10:35) 得0分 
同意楼上的意见! 
回复人: czh912() (2001-8-19 21:27:08) 得0分 
char buf[20];
printf(buf,"%s",string);

回复人: casl(casl) (2001-8-19 22:59:44) 得5分 
CString s("abc");
char* temp=s.GetBuffer(10);
...
s.ReleaseBuffer(); 
回复人: cocia(高亚) (2001-8-19 23:04:23) 得0分 
char* temp=s.GetBuffer(10);
10是什么意思啊

回复人: kevin_dong(梦仙人) (2001-8-20 10:26:35) 得0分 
// str is CString var
char* temp = strdup(str);
// free
free(temp); 
我的这段代码在一个程序中能通过编译,但是在另外一个中总是出现cannot convert parameter 1 from """'class CString"""' to """'const char *"""'的错误。str和temp的类型都一样。这是为什么?

回复人: dcz(dcz) (2001-8-20 14:13:45) 得0分 
you may setting your compiler option to UNICODE, in this case, declare the var:

// str is CString var
_TCHAR* temp = _tcsdup(str);

// free
free(str);

31。SA,SB为两个结构类型??
SA* A;
SB* B;
(SB*)A->...(调用函数)
请问此时A的类型,是指向SA还是SB
此时编译器是生成一个临时指针吗?
另外,
B=(SB*)A;此时A又是什么类型???

回复贴子: 
回复人: ddeng(登登) (2001-8-9 17:13:58) 得0分 
A的类型始终是SA *
B的类型始终是SB *
当进行强制类型转换时使的是临时指针 
回复人: gold_water(风雨无阻) (2001-8-9 17:30:46) 得0分 
同意楼上的。 

32。char buff[100],char UserName[50][100],怎么将buff的值传给UserName,是其成为UserName数组中的某一项呢??

//0=strcpy(UserName[i],buff); 
回复人: Ashura(阿修罗) (2001-7-26 10:08:20) 得0分 
呵呵,benbensan抢先一步。 
回复人: tuita(斗牛士) (2001-7-26 10:13:22) 得0分 
for (i=0;i<100;i++)
*(*(username+x)+i)=*(buffer+i)
其中0《X〈50
benbensan写的也对

回复人: kekeke(我是来向大家学习的) (2001-7-26 10:24:22) 得0分 
那反过来呢?把UserName中的某一项读出赋值给buff呢?怎么弄? 
回复人: benbensan(笨笨三) (2001-7-26 10:26:53) 得0分 
//0=strcpy(UserName[i],buff); 
回复人: benbensan(笨笨三) (2001-7-26 10:28:15) 得0分 
对不起,能错了,不过建议你看一下C语言了的指针和数组
//0=strcpy(buff,UserName[i]); 

回复人: jfzsl(剿匪总司令) (2001-7-26 10:32:57) 得0分 
好好看看老潭的书先!OK? 
回复人: kekeke(我是来向大家学习的) (2001-7-26 10:44:25) 得0分 
好。。。。! 

33。请问怎样把SYSTEMTIME类型转换成time_t类型?
SYSTEMTIME st;
GetLocalTime(&st);
CTime tm(st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
time_t t = tm.GetTime();

34。unsigned char Exponent[10]; //e
unsigned long eBytes; //e的字节数
如何转换成DWord型! ??

用强制类型转换呀
(DWord)eBeytes;
(DWord)Exponent[i];//(0<=i<=10);

回复人: xjl1980_81(阿龙) (2001-7-26 16:47:29) 得0分 
我是说把e转换成DWORD型
也就是说把Exponent中的内容转换成DWORD型

回复人: cloudshadow1(云影) (2001-7-26 17:13:30) 得0分 
用强制类型转换就可以了,(DWORD的高24位自动加0)
DWORD Des[10]
for (int i=0;i<11;i++)
Des[i]=Exponent[i];
至于那个ULONG的也是用强制类型软换就可以了

35。请问怎样把time_t类型转换成SYSTEMTIME类型?
回复人: haven(大天) (2001-7-26 17:12:36) 得0分 
typedef struct _SYSTEMTIME
typedef long time_t
很明显不行嘛! 
回复人: facexy(FACE仔) (2001-7-26 17:17:38) 得0分 
哎呀,问错了,前后对象相反了;-(
忙昏了的结果…………

另外,TO 楼上的:
转换是可以的
struct tm *tblock;
SYSTEMTIME SystemTime;
memset(&SystemTime,0,sizeof(SYSTEMTIME));
tblock=localtime(&timer);
SystemTime.wYear=tblock->tm_year+1900;
SystemTime.wMonth=tblock->tm_mon+1;
SystemTime.wDay=tblock->tm_mday;
SystemTime.wHour=tblock->tm_hour;
SystemTime.wMinute=tblock->tm_min;
SystemTime.wSecond=tblock->tm_sec;
SystemTime.wDayOfWeek=tblock->tm_wday;
return &SystemTime; 
回复人: zjh73(大章鱼) (2001-7-26 20:28:28) 得0分 
有两种方法:
1、用CTime类
先用time_t类型构造一个CTime对象,再定义一个SYSTEMTIME结构,最后用CTime类的成员函数GetAsSystemTime将时间转换到SYSTEMTIME结构中
即可。
2、用gmtime函数
gmtime函数将time_t时间转换到tm结构中并返回一个tm指针,再将tm结构的相对应的项赋给SYSTEMTIME相对应的项即可,不过用这种方法要注
意这两种结构在天、星期等方面的记数方法有点区别,一个一般从0开始,一个一般从1开始,赋值时要注意校正,还有要注意的是SYSTEMTIME
结构中有一项是毫秒,而time_t是以秒记数的。 
回复人: zjh73(大章鱼) (2001-7-26 20:32:13) 得0分 
反过来也可以用Ctime类的方法
就是先用SYSTEMTIME结构构造一个CTime对象,在用CTime类中的成员函数GetTime返回一个对应的time_t即可。 36。我现在正在学习SDK编程,遇到的问题是:
我定义了一个静态长整形变量,
static long lScore=0;
我想把窗口的标题换成长整形数值,用SetWindowText函数来实现,
由于它的第二个参数要求数据类型为 unsigned short *,但用其来实现强制转换时
总是出现编译错误:
cannot convert parameter 2 from """'unsigned short *"""' to """'const char *"""'
后来改成来LPCTSTR 来实现强制转换,没有出现编译错误,但函数总是执行不成功,
请教各位高人,这倒底是怎么回事???

回复贴子: 
回复人: prog_st(st) (2001-8-4 21:20:07) 得0分 
/* ITOA.C: This program converts integers of various
* sizes to strings in various radixes.
*/

#include
#include

void main( void )
{
char buffer[20];
int i = 3445;
long l = -344115L;
unsigned long ul = 1234567890UL;

_itoa( i, buffer, 10 );
printf( "String of integer %d (radix 10): %s""""n", i, buffer );
_itoa( i, buffer, 16 );
printf( "String of integer %d (radix 16): 0x%s""""n", i, buffer );
_itoa( i, buffer, 2 );
printf( "String of integer %d (radix 2): %s""""n", i, buffer );

_ltoa( l, buffer, 16 );
printf( "String of long int %ld (radix 16): 0x%s""""n", l, 
buffer );

_ultoa( ul, buffer, 16 );
printf( "String of unsigned long %lu (radix 16): 0x%s""""n", ul,
buffer );
}


Output

String of integer 3445 (radix 10): 3445
String of integer 3445 (radix 16): 0xd75
String of integer 3445 (radix 2): 110101110101
String of long int -344115 (radix 16): 0xfffabfcd
String of unsigned long 1234567890 (radix 16): 0x499602d2


回复人: lwg7603(刑满释放人员) (2001-8-4 21:36:15) 得0分 
TCHAR str[255]={_T("""'"""""')};
_stprintf(str,_T("%d"),lScore);
SetWindowText(hwnd,str);

37。我用socket发送的的buf中间需要的是 char *类型的数据,我想将一个 struct 直接转换成 char * 发过去。
我用
struct ABCD *abcd;
char *buf;
abcd = (ABCD *)calloc(1,sizeof(ABCD));
buf = (char *)calloc(1,sizeof(ABCD));
///
给abcd 中间赋值,其中有多个char[]的值和int 的值
///
memcpy(buf,abcd,sizeof(ABCD));
//strcpy(buf,(char *)abcd);也不可以
sock(host,buf,....);
//sock(host,(char *)buf,...);也不可以
问题就是在这里,这个buf中间的值总是不对,大家知道为什么否。

回复人: wolf721() (2001-7-30 18:18:34) 得5分 
你传的是个指针值,而不是数据 
回复人: kiko_lee(到处瞧瞧) (2001-7-30 18:50:49) 得0分 
但是用memcpy这个是将整个数据都复制过去 
回复人: lz_0618(lz_0618) (2001-7-30 19:26:44) 得5分 
你用的VC???改成ABCD *abcd;后编译一点问题也没有啊!
sock(host,buf,....);这不知是什么,自定义函数?

typedef struct _ABCD
{
int ID;
char Name[10];
}ABCD;

.......


ABCD *abcd;
char *buf;
abcd = (ABCD *)calloc(2,sizeof(ABCD));
buf = (char *)calloc(2,sizeof(ABCD));
///
//给abcd 中间赋值,其中有多个char[]的值和int 的值
abcd[0].ID =1;
abcd[1].ID =2;
///
memcpy(buf,abcd,2*sizeof(ABCD));
strcpy(buf,(char *)abcd);//也不可以

buf中的内容也正确!!

回复人: kiko_lee(到处瞧瞧) (2001-7-31 8:57:52) 得0分 
我按照楼上的兄弟说的,做了一下,但是仍然做不下来,我用
memcpy(buf,abcd,sizeof(ABCD));
中间的abcd,不知道是不是地址的问题。 
回复人: supersusheng(小苏) (2001-7-31 14:30:42) 得0分 
老大,你sizeof()得出的数值事多大,看看吧。 
回复人: ydogg(灰毛兔) (2001-7-31 14:41:52) 得0分 
只能传递流数据,结构是传递不过去的。 
回复人: IamNotMan(NorGirl) (2001-7-31 14:50:53) 得5分 
我常这么用
ABCD a ;
//给a的各个域赋值(一定不能含有指针项)
char* buff = new char[sizeof(ABCD)];
memcpy(buff,&a,sizeof(ABCD));
//或者 *(ABCD*)buff =

 

标签:软件开发 | 浏览数(554) | 评论数(0) | 2006-09-07

/*

Log File Library(WIN98/NT/2000)

 

Compile by BC++ 5; C++ BUILDER; VC++; VC.NET;

 

copyright(c) 2004.5 - 2005.3  llbird wushaojian@21cn.com http://blog.csdn.net/wujian53

 

*/

/*

Use:

//这个代码我用工业现场24X7值守的程序纪录各种信息, 简单易用;

//一般用一个全局日志对象, 有临界排斥可以多线程安全使用。

//有两个类

class LogFile;//用户定义日志文件名

class LogFileEx;//有日志文件名自动生成功能 , 可分年月日频率生成文件名, 可指定日志存放的目录

 

LogFile gLog("My.Log");

gLog.Log("test", 4);//记录日志

gLog.Log("系统启动");

 

LogFileEx gLog(".", LogFileEx :: YEAR);//一年生成一个日志文件

LogFileEx gLog(".""Log", LogFileEx :: MONTH);//一月生成一个日志文件

LogFileEx gLog(".""Log", LogFileEx :: DAY);//一天生成一个日志文件

//注意日志所属目录创建失败会自动退出, 请注意目录的合法性, 文件生成频率看情况掌握

//24小时运行的程序可以每天生成一个日志文件, 以免内容过多

*/

 

#ifndef _LOGFILE_H

#define _LOGFILE_H

 

#include <assert.h>

#include <time.h>

#include <stdio.h>

#include <windows.h>

 

class LogFile

{

protected:

 

 CRITICAL_SECTION _csLock;

 char * _szFileName;

 HANDLE _hFile;

 

 bool OpenFile()//打开文件, 指针到文件尾

 {

  if(IsOpen())

   return true;

 

  if(!_szFileName)

   return false;

 

  _hFile CreateFile(

   _szFileName,

   GENERIC_WRITE,

   FILE_SHARE_READ | FILE_SHARE_WRITE,

   NULL,

   OPEN_EXISTING,

   FILE_ATTRIBUTE_NORMAL,

   NULL

  );

 

  if(!IsOpen() && GetLastError() == 2)//打开不成功, 且因为文件不存在, 创建文件

   _hFile CreateFile(

    _szFileName,

    GENERIC_WRITE,

    FILE_SHARE_READ | FILE_SHARE_WRITE,

    NULL,

    OPEN_ALWAYS,

    FILE_ATTRIBUTE_NORMAL,

    NULL

   ); 

 

  if(IsOpen())

   SetFilePointer(_hFile, 0, NULL, FILE_END);

 

  return IsOpen();

 }

 

 DWORD Write(LPCVOID lpBuffer, DWORD dwLength)

 {

  DWORD dwWriteLength = 0;

 

  if(IsOpen())

   WriteFile(_hFile, lpBuffer, dwLength, &dwWriteLength, NULL);

 

  return dwWriteLength;

 }

 

 virtual void WriteLog( LPCVOID lpBuffer, DWORD dwLength)//写日志, 可以扩展修改

 {

  time_t now;

  char temp[21];

  DWORD dwWriteLength;

 

  if(IsOpen())

  {

   time(&now);

   strftime(temp, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));

 

   WriteFile(_hFile, ""xd"xa#-----------------------------", 32, &dwWriteLength, NULL);

   WriteFile(_hFile, temp, 19, &dwWriteLength, NULL);

   WriteFile(_hFile, "-----------------------------#"xd"xa", 32, &dwWriteLength, NULL);

   WriteFile(_hFile, lpBuffer, dwLength, &dwWriteLength, NULL);

   WriteFile(_hFile, ""xd"xa", 2, &dwWriteLength, NULL);

  

   FlushFileBuffers(_hFile);

 

  }

 }

 

 void Lock()  { ::EnterCriticalSection(&_csLock); }

 void Unlock() { ::LeaveCriticalSection(&_csLock); }

 

public:

 

 LogFile(const char *szFileName = "Log.log")//设定日志文件名

 {

  _szFileName = NULL;

  _hFile = INVALID_HANDLE_VALUE;

  ::InitializeCriticalSection(&_csLock);

 

  SetFileName(szFileName);

 }

 

 virtual ~LogFile()

 {

  ::DeleteCriticalSection(&_csLock);

  Close();

 

  if(_szFileName)

   delete []_szFileName;

 }

 

 const char * GetFileName()

 {

  return _szFileName;

 }

 

 void SetFileName(const char *szName)//修改文件名, 同时关闭上一个日志文件

 {

  assert(szName);

 

  if(_szFileName)

   delete []_szFileName;

 

  Close();

 

  _szFileName = new char[strlen(szName) + 1];

  assert(_szFileName);

  strcpy(_szFileName, szName);

 }

 

 bool IsOpen()

 {

  return _hFile != INVALID_HANDLE_VALUE;

 }

 

 void Close()

 {

  if(IsOpen())

  {

   CloseHandle(_hFile);

   _hFile = INVALID_HANDLE_VALUE;

  }

 }

 

 void AddLog(LPCVOID lpBuffer, DWORD dwLength)//追加日志内容

 {

  assert(lpBuffer);

  __try

  {

   Lock();

 

   if(!OpenFile())

    return;

 

   WriteLog(lpBuffer, dwLength);

  }

  __finally

  {

   Unlock();

  }

 }

 

 void Log(const char * szString, ...)

 {

          TCHAR szEntry[1024];

          va_list args;

          va_start(args, szString);

          vsprintf(szEntry,szString,args);

          AddLog(szEntry, strlen(szEntry));

 }

 

void GetErrorMessage(LPCTSTR lpFunction, DWORD dwError)

 {

          LPVOID lpMsgBuf;

          TCHAR szEntry[1024];

          FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language

                    (LPTSTR)&lpMsgBuf, 0, NULL);

          sprintf(szEntry, "Function: %s, %s", lpFunction, (LPCTSTR)lpMsgBuf);

          AddLog(szEntry, strlen(szEntry));

          LocalFree(lpMsgBuf);

 }

private://屏蔽函数

 

 LogFile(const LogFile&);

 LogFile&operator = (const LogFile&);

};

 

class LogFileEx : public LogFile

{

protected:

 

 char *_szPath;

 char _szLastDate[9];

 int _iType;

 

 void SetPath(const char *szPath)

 {

  assert(szPath);

 

  WIN32_FIND_DATA wfd;

  char temp[MAX_PATH + 1] = {0};

 

  if(FindFirstFile(szPath, &wfd) == INVALID_HANDLE_VALUE && CreateDirectory(szPath, NULL) == 0)

  {

   strcat(strcpy(temp, szPath), " Create Fail. Exit Now! Error ID :");

   ltoa(GetLastError(), temp + strlen(temp), 10);

   MessageBox(NULL, temp, "Class LogFileEx", MB_OK);

   exit(1);

  }

  else

  {

   GetFullPathName(szPath, MAX_PATH, temp, NULL);

   _szPath = new char[strlen(temp) + 1];

   assert(_szPath);

   strcpy(_szPath, temp);

  }

 }

 

public:

 

 enum LOG_TYPE{YEAR = 0, MONTH = 1, DAY = 2};

 

 LogFileEx(const char *szPath = ".", LOG_TYPE iType = MONTH)

 {

  _szPath = NULL;

  SetPath(szPath);

  _iType = iType;

  memset(_szLastDate, 0, 9);

 }

 

 ~LogFileEx()

 {

  if(_szPath)

   delete []_szPath;

 }

 

 const char * GetPath()

 {

  return _szPath;

 }

 

 void AddLog(LPCVOID lpBuffer, DWORD dwLength)

 {

  assert(lpBuffer);

 

  char temp[10];

  static const char format[3][10] = {"%Y", "%Y-%m", "%Y%m%d"};

 

  __try

  {

   Lock();

  

   time_t now = time(NULL);

 

   strftime(temp, 9, format[_iType], localtime(&now));

 

   if(strcmp(_szLastDate, temp) != 0)//更换文件名

   {

    strcat(strcpy(_szFileName, _szPath), """");

    strcat(strcat(_szFileName, temp), ".log");

    strcpy(_szLastDate, temp);

    Close();

   }

 

   if(!OpenFile())

    return;

  

   WriteLog(lpBuffer, dwLength);

  }

  __finally

  {

   Unlock();

  }

 }

 

 void Log(const char * szString, ...)

 {

          TCHAR szEntry[1024];

          va_list args;

          va_start(args, szString);

          vsprintf(szEntry,szString,args);

          AddLog(szEntry, strlen(szEntry));

 }

 

private://屏蔽函数

 

 LogFileEx(const LogFileEx&);

 LogFileEx&operator = (const LogFileEx&);

 

};

 

#endif

 

 

 

 

标签:软件开发 | 浏览数(582) | 评论数(0) | 2006-09-06

1.Visual Assist(强烈推荐)

       http://www.wholetomato.com/

       VA从5.0一直到现在的VAX,功能越来越强大,除了以前版本中的自动识别各种关键字,系统函数,成员变量,自动给出输入提示,自动更正大小写错误,自动标示错误等等以外,最新的版本中还在

       WorkSpace窗口中加入一个VA View,可以更方便的查找工程中的文件、类和变量。

 

    2.WndTabs(强烈推荐)

       http://www.wndtabs.com/

WndTabs主要是在编辑窗口中显示了所有已经打开的文件,在VC中能够更方便的操作这些文件,比如修改文件属性,copy文件路径、文件名等,并且还开放源代码,你要是愿意的话,可以添加自己很兴趣的功能。

 

3.LineCounter

       http://www.wndtabs.com/

     用来统计整个工程的代码行数,包括总行数、代码行数、注释行数、空行数等,并且对多个工程一起统计时,不会把相同的文件计算多次.

 

       4.Spelly

        http://www.wndtabs.com/ 

一个拼写检查的插件,可以对整个文件或所选部分进行拼写检查,支持C/C++/C#, VB, Fortran 和HTML。

 

       5.SourceStyler C++

       http://www.sourcestyler.com/

此插件是针对C++的一个格式化工具,可以针对自己的编码习惯,选择一种编码风格,也可以自己定义,而且定义非常详细,有表达式、指针、模板、类、枚举等十几种,肯定能满足你的需要。


 

6.Numega BoundsChecker(强烈推荐)

下载:百度一下……

是针对Visual C++6.0应用程序的最为全面的错误检测工具。BoundsChecker 能自动指出静态,堆栈内存错误和资源泄漏问题。BoundsChecker 能够校验最新的 Windows APIs,包括 ActiveX, DirectX, OLE/COM, ODBC等等。能够发现与 Windows 平台兼容性。

 

7.BCGControlBar Library 

下载:百度一下……

非常好的一套应用于vc6的界面扩展类库,轻松的作出 vc2003 的界面。并且给了各种界面例子,如vc.net、outlook、更换皮肤等等。

 

8.Comment Wizard

下载:百度一下……

Visual C++插件,提供了Visual C++源代码注解标准化与自动化功能。在它的帮助下,您可快速创建标头文件信息注解,文件中模块注解, C++处理方式,以及C语言功能与历史校正功能注解,等等。

标签:软件开发 | 浏览数(1518) | 评论数(1) | 2006-09-06

使用ADO调用存储过程
    在ADO中调用存储过程一直是一个困扰大家的问题。其实,关于ADO调用存储过程的
   方法在很多书中都有讲到,标准的做法无非是按照以下步骤进行:
    1、生成并初始化一个_CommandPtr对象;
    2、生成调用存储过程需要的参数,这些参数都是_ParameterPtr对象;
    3、按照顺序将使用_CommandPtr的Append方法为存储过程提供参数(包括输入参数
       和输出参数);
    4、为_CommandPtr对象指定需要使用的ADO连接;
    5、使用_CommandPtr的Execute方法调用存储过程;
    6、从结果中获取返回参数的值(如果有的话)。
   具体的过程在此我不详细描述,我想看看本文附带的代码就应该很明白了。
   在这里我想就我使用ADO调用存储过程时的一些体会说明一下。
  1、关于CreateParameter函数
   该函数的原型为:CreateParameter (Name, Type, Direction, Size, Value)
   其中Name是参数的名称,可以指定也可以不指定;
   Type是一个DataTypeEnum值,指定参数的类别,取值有adInteger(整型)、adChar(字符/字符串型)等;
   Direction是一个ParameterDirectionEnum值,其取值为adParamInput、adParamOutput、
   adParamOutput、adParamReturnValue、adParamUnknown;
   Size是一个Long类型的值,指示该参数值以字节计算的最大长度,例如对int型,该值可以取为sizeof(int),
   对Long型,该值可以取为sizeof(long),对字符串型,可以使用该字符串的长度;
   Value是一个variant类型的值,是该参数的取值。
   在这里需要注意的是,Type参数、Direction参数以及Size参数一定要和存储过程定义时的参数相吻合,
   例如,如果有下面一个存储过程
   CREATE  PROCEDURE SMS_Proc_Handle_All
   (@UserID Integer,
    @SourAddr Varchar(15),
    @DestAddr varchar(5000),
    @AvValue Single output,
    @ReturnInfo varchar(100) output
   )
   则Type参数的取值依次为adInteger、adChar、adChar、adSingle,adChar;
   Direction参数的取值依次为adParameterIn、adParameterIn、adParameterIn、adParameterOut、adParameterOut;
   对于输入参数,Size的值可以根据实际数值来定,对于输出参数,最好是根据定义确定(上例中ReturnInfo参数的
   Size值可以取为100)。
   2,关于获取Output的参数
     获取ourput参数是大家最关注的问题,同时也是最“难”的问题,因为按照书本上的写法,经常获得不了
   Output参数,其实这个问题很容易解决:在调用_CommandPtr的Execute方法时,写成
   cmmd->Execute(NULL, NULL, adCmdStoredProc);
   而不要写成
   RecordsetPtr rec = cmmd->Execute(NULL, NULL, adCmdStoredProc);
   也就是说,不取返回值(我不知道这是为什么,但是相信我,事情就是这样)。
   这句执行完后,使用
   cmmd->Parameters->GetItem("XXXXXX")->GetValue();
                          ^^^^^^^
                        输出参数的名称
   就可以获得输出参数的值了。
   以下是一个通过ADO调用存储过程的部分代码:
  _CommandPtr cmmd;
  HRESULT hr = cmmd.CreateInstance(__uuidof(Command));
  if(FAILED(hr))
{
   AfxMessageBox("NewNetDatabase()中创建_CommandPtr对象失败");
   return 0;
  }
  _ParameterPtr param;
  param = cmmd->CreateParameter(""/*NetType*/,adTinyInt, adParamInput,
    sizeof(BYTE),(BYTE)(m_nNetType+1));
  cmmd->Parameters->Append(param);
  param = cmmd->CreateParameter(""/*Name*/,adVarChar, adParamInput,
   m_strName.GetLength()+1, _variant_t(m_strName));
  cmmd->Parameters->Append(param);
  param = cmmd->CreateParameter(""/*Desp*/,adVarChar, adParamInput,
   m_strDesp.GetLength()+1, _variant_t(m_strDesp));
  cmmd->Parameters->Append(param);
  param = cmmd->CreateParameter("NewNetID"/*NetID*/,adInteger, adParamOutput,
   sizeof(long), (long)m_nNewNetID);//返回参数,返回新建的网络的ID
  cmmd->Parameters->Append(param);
   cmmd->CommandText=_bstr_t("GSDT_NewNet");//存储过程的名称
  cmmd->ActiveConnection = m_pConPtr;//需要使用的ADO连接
  cmmd->CommandType=adCmdStoredProc;
  //注意下面的一行代码,如果你写成这样,就获得不了返回参数的值
  //_RecordsetPtr rec = cmmd->Execute(NULL, NULL, adCmdStoredProc);
  //我不知道这是为什么,但事实就是这样:)
  cmmd->Execute(NULL, NULL, adCmdStoredProc);
  m_nNewNetID=(long)cmmd->Parameters->GetItem("NewNetID")->GetValue();//通过参数返回值
  cmmd.Detach();

 

ActiveX Data Objects (ADO) enables you to write a client application to access and manipulate data in a database server through a provider.
ADO's primary benefits are ease of use, high speed, low memory overhead, and a small disk footprint.
This sample project is for ADODB, an implementation of ADO optimized for use with Microsoft OLE DB providers, including the Microsoft ODBC provider for OLE DB.
Using this we can execute stored procedure, pass arguments and retrieve value. To use this sample you will have to create the two stored procedures mentioned below.
For using this project you need MFC 5.0 OR above + ADO in your machine.

{
   CString strTmp;

   CString m_sdatasource; // Data source name
   CString m_sUserID;     // User Id
   CString m_sPassword;   // Password

   // GET the above values from the user
   //Without creating Datasource we can use database by the following   code
   /* strTmp.Format( "driver={sql server};"
                           "server=%s;"
                           "Database=%s;""UID=%s;""PWD=%s;",
                           m_server,m_sdatabase,m_sUserID,m_sPassword );*/

   strTmp.Format( "dsn=%s;""UID=%s;""PWD=%s;",m_sdatasource,m_sUserID,m_sPassword );
   _bstr_t         bstrSQLServerConnect;
   _bstr_t bstrProc =( L"sp_StartByteImport" );; //Stored procedure name
   _variant_t Final;
   bstrSQLServerConnect = (LPCTSTR) strTmp;
   m_status="Empty File";
   _ConnectionPtr  Conn1; // connection object pointer
   _CommandPtr     Cmd1;  // command object pointer
   _RecordsetPtr   Rs1; // recordset object pointer
   bool            bvalid = false;
   try
   {
      Conn1.CreateInstance( __uuidof( Connection ) ); // Instantiating connection object
   Conn1->ConnectionString = bstrSQLServerConnect; // giving the sqlconnection
   Conn1->Open( bstrEmpty, bstrEmpty, bstrEmpty ); // open the connection object
   Cmd1.CreateInstance( __uuidof( Command ) ); // creating command object
   Cmd1->ActiveConnection = Conn1;             // giving the connection handle
   Cmd1->CommandText      = _bstr_t( bstrProc ); // passing the stored procedue
   Cmd1->CommandType      = adCmdStoredProc;     // type
   Cmd1->Parameters->Refresh();                 // passing string value as argument to stored procedure
   Cmd1->Parameters->Item[ _variant_t( (long) 1 ) ]->Value = _variant_t( (LPCTSTR)m_sfilename );
   Rs1 = Cmd1->Execute( &vtEmpty, &vtEmpty2, adCmdUnknown ); // executing the stored procedure and storing the recordset value
   bvalid = true;
   Final  = Rs1->Fields->GetItem( _variant_t( 0L ) )->Value; // getting the first column value of the result row
   strTmp.Format( "%s", CrackStrVariant( Final) ); // to see the value
   // put your code to see all column values
   }
   catch( CException *e ) // trapping all error messages
   {
   TCHAR    szCause[255];
      e->GetErrorMessage(szCause, 255);
   m_status=szCause;
   }
   catch( _com_error &e )
   {
 m_status=e.ErrorMessage( );
   }
   catch(...)
   {
 m_status="Error while executing the Import";

   }
    //we need to create the stored procedures below before running the application
 //CREATE PROCEDURE sp_AddAccountingInfo @nfinal int, @pcDate datetime,
 //@pcURL varchar (250), @pcTop varchar (250),
 //@pcQueryString varchar (250), @pcBytes int, @pcRequests int AS
       /*
  Do your operation here
 */
 //CREATE PROCEDURE sp_AddAccountingInfo
 //@nfinal int,
 //@pcDate datetime,
 //@pcURL varchar (250),
 //@pcTop varchar (250),
 //@pcQueryString varchar (250),
 //@pcBytes int,
 //@pcRequests int
 //AS
 /*
  Put your code here
 */
}
vc下用ado调用存储过程

1 _ConnectionPtr m_pConnection;
2 _CommandPtr m_pCommand;
.cpp中在函数中执行
//建立ado连接
3 HRESULT hr;
4 hr=m_pConnection.CreateInstance(__uuidof(Connection));
5 try
6 {
7 if(SUCCEEDED(hr))
8 {
9 hr=m_pConnection->Open(_bstr_t(L"Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Viper;Data Source=Viper"),_bstr_t (L"sa"),_bstr_t (L""),adModeUnknown);
10 }
11 }
12 catch(_com_error & err)
13 {
14 AfxMessageBox(err.Description(),MB_OK,0);
15 AfxMessageBox(err.ErrorMessage(),MB_OK,0);
16 AfxMessageBox("无法连接SQL SERVER 服务器,程序将退出。请检查网络设备",MB_OK,0);
17 exit(0);
18 }

//执行储存过程
19 CString cvar1,cvar2;
20 int cvar3;
21 cvar1="ddd";
22 cvar2="";
23 cvar3=0;
24 try
25 {
26 m_pCommand.CreateInstance(__uuidof(Command));
27 m_pCommand->ActiveConnection=app->m_pConnection;
28 m_pCommand->CommandType=adCmdStoredProc;
29 m_pCommand->CommandText=_bstr_t("pr_zs_dzdy");
30
31 _variant_t vvar1,vvar2,vvar3;
32 vvar1=_variant_t(_bstr_t(cvar1));
33 vvar2=_variant_t(_bstr_t(cvar2));
34 vvar3=_variant_t(cvar3);
35 _ParameterPtr mp_var1,mp_var2,mp_var3;
36 mp_var1.CreateInstance(__uuidof(Parameter));
37 mp_var2.CreateInstance(__uuidof(Parameter));
38 mp_var3.CreateInstance(__uuidof(Parameter));

39 mp_var1=m_pCommand->CreateParameter
40 (
41 _bstr_t("var1"),
42 adVarChar,
43 adParamInput,
44 3,
45 vvar1
46 );
47 m_pCommand->Parameters->Append(mp_var1);
48
49 mp_var2=m_pCommand->CreateParameter
50 (
51 _bstr_t("var2"),
52 adVarChar,
53 adParamOutput,
54 3,
55 vvar2
56 );
57 m_pCommand->Parameters->Append(mp_var2);
58
59 mp_var3=m_pCommand->CreateParameter
60 (
61 _bstr_t("var3"),
62 adIntger,
63 adParamOutput,
64 9,
65 vvar3
66 );
67 m_pCommand->Parameters->Append(mp_var3);
68
69
70 _variant_t vNull;
71 vNull.vt=VT_ERROR;
72 vNull.scode=DISP_E_PARAMNOTFOUND;
73 m_pCommand->Execute(&vNull,&vNull,adCmdStoredProc);
74 cvar2=mp_var2->Value.bstrVal;
75 cvar3=mp_var3->Value;
76 }
77 catch(_com_error &error)
78 {
79 MessageBox(error.ErrorMessage(),"ADO错误!");
80 MessageBox(error.Description(),"ADO错误!");
81 }

【打印文档】【大 中 小】【关闭窗口】

上一篇文章: VC Studio 使用技巧大全 2.0版本

下一篇文章: VC里一些容易混淆的地方


关于存储过程的ADO调用的一些心得(输出参数,返回值)

[本页面推荐在1024x768分辩率下浏览]
文章类别:数据库开发   
网站目录: 网站首页 —> 数据库开发

转载自:www.csdn.net

  
在一个项目中,我需要用到存储过程来访问数据,为了提供一个比较一致的接口以便调用,我没有使用CreateParameter(),而是调用CommandPtr的Refresh()函数先从数据库中查询参数.
_ConnectionPtr m_pConn;
m_pConn.CreateInstance(__uuidof(Connection));
m_pConn->Open("driver={SQL Server};server=127.0.0.1;DATABASE=pub;UID=sa;PWD=", "","",0);
_CommandPtr  m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));

m_pCommand->ActiveConnection = m_pConn;
m_pCommand->CommandText = "SP_XX";//存储过程名
m_pCommand->PutCommandType(adCmdStoredProc);
m_pCommand->Parameters->Refresh();//从数据库查询参数信息
接下来就可以对每一个参数赋值了:
long cnt = m_pCommand->Parameters->GetCount();//取得参数的个数
for(long k=1;k<cnt;k++)
{//由于ADO中认为返回值是第一个参数即k=0时是返回值,因此这里用k=1滤掉第一个参数
 m_pCommand->Parameters->GetItem(k)->Value = XXX;//按存储过程的参数顺序给参数赋值
}
现在可以执行这个存储过程了
m_pRecordset = m_pCommand->Execute(0,0,adCmdStoredProc);
这个时候,如果接下来用
_variant_t ret_val = m_pCommand->Parameters->GetItem((long)0)->Value;
那么将得不到值
而如果像下面这样调用的话就可以得到返回值了
m_pRecordset->Close();
_variant_t output_para = m_pCommand->Parameters->GetItem((long)0)->Value;
MS ADO.net给这一现象的回复是:
 You can think of a stored procedure as a function in your code. The function doesn’t return a value until it has executed all of its code. If the stored procedure returns results and you haven’t finished processing these result

昨天做项目时发现此处不正确,m_pRecordset不能close。而且释放指针时要先释放m_pCommand,再释放m_pRecordset

 

标签:数据库 | 浏览数(1018) | 评论数(0) | 2006-09-04

色彩对照表

颜色名称: 颜色数值: 白色 烟白色 黄色 黄绿色
艾利斯兰 古董白 浅绿色 碧绿色 天蓝色 米色 桔黄色 黑色
白杏色 蓝色 紫罗兰色 褐色 实木色 军兰色 黄绿色 巧可力色
珊瑚色 菊兰色 米绸色 暗深红色 青色 暗蓝色 暗青色 暗金黄色
暗灰色 暗绿色 暗黄褐色 暗洋红 暗橄榄绿 暗桔黄色 暗紫色 暗红色
暗肉色 暗海兰色 暗灰蓝色 墨绿色 暗宝石绿 暗紫罗兰色 深粉红色 深天蓝色
暗灰色 闪兰色 火砖色 花白色 森林绿 紫红色 淡灰色 幽灵白
金色 金麒麟色 灰色 绿色 黄绿色 蜜色 热粉红色 印第安红
靛青色 象牙色 黄褐色 淡紫色 淡紫红 草绿色 柠檬绸色 亮蓝色
亮珊瑚色 亮青色 亮金黄色 亮绿色 亮灰色 亮粉红色 亮肉色 亮海蓝色
亮天蓝色 亮蓝灰 亮钢兰色 亮黄色 酸橙色 橙绿以 亚麻色 红紫色
粟色 间绿色 间兰色 间紫色 间紫色 间海蓝 间暗蓝色 间春绿色
间绿宝石 间紫罗兰色 中灰兰色 薄荷色 浅玫瑰色 鹿皮色 纳瓦白 海军色
老花色 橄榄色 深绿褐色 橙色 红橙色 淡紫色 苍麒麟色 苍绿色
苍宝石绿 苍紫罗兰色 番木色 桃色 秘鲁色 粉红色 洋李色 粉蓝色
紫色 红色 褐玫瑰红 皇家蓝 重褐色 鲜肉色 沙褐色 海绿色
海贝色 赭色 银色 天蓝色 石蓝色 灰石色 雪白色 春绿色
钢兰色 茶色 水鸭色 蓟色 西红柿色 青绿色 紫罗兰色 浅黄色
颜色代码表Ⅰ
  #000000   #2F0000   #600030   #460046   #28004D
  #272727   #4D0000   #820041   #5E005E   #3A006F
  #3C3C3C   #600000   #9F0050   #750075   #4B0091
  #4F4F4F   #750000   #BF0060   #930093   #5B00AE
  #5B5B5B   #930000   #D9006C   #AE00AE   #6F00D2
  #6C6C6C   #AE0000   #F00078   #D200D2   #8600FF
  #7B7B7B   #CE0000   #FF0080   #E800E8   #921AFF
  #8E8E8E   #EA0000   #FF359A   #FF00FF   #9F35FF
  #9D9D9D   #FF0000   #FF60AF   #FF44FF   #B15BFF
  #ADADAD   #FF2D2D   #FF79BC   #FF77FF   #BE77FF
  #BEBEBE   #FF5151   #FF95CA   #FF8EFF   #CA8EFF
  #d0d0d0   #ff7575   #ffaad5   #ffa6ff   #d3a4ff
  #E0E0E0   #FF9797   #FFC1E0   #FFBFFF   #DCB5FF
  #F0F0F0   #FFB5B5   #FFD9EC   #FFD0FF   #E6CAFF
  #FCFCFC   #FFD2D2   #FFECF5   #FFE6FF   #F1E1FF
  #FFFFFF   #FFECEC   #FFF7FB   #FFF7FF   #FAF4FF
  #000079   #000079   #003E3E   #006030   #006000
  #000093   #003D79   #005757   #01814A   #007500
  #0000C6   #004B97   #007979   #019858   #009100
  #0000C6   #005AB5   #009393   #01B468   #00A600
  #0000E3   #0066CC   #00AEAE   #02C874   #00BB00
  #2828FF   #0072E3   #00CACA   #02DF82   #00DB00
  #4A4AFF   #0080FF   #00E3E3   #02F78E   #00EC00
  #6A6AFF   #2894FF   #00FFFF   #1AFD9C   #28FF28
  #7D7DFF   #46A3FF   #4DFFFF   #4EFEB3   #53FF53
  #9393FF   #66B3FF   #80FFFF   #7AFEC6   #79FF79
  #AAAAFF   #84C1FF   #A6FFFF   #96FED1   #93FF93
  #B9B9FF   #97CBFF   #BBFFFF   #ADFEDC   #A6FFA6
  #CECEFF   #ACD6FF   #CAFFFF   #C1FFE4   #BBFFBB
  #DDDDFF   #C4E1FF   #D9FFFF   #D7FFEE   #CEFFCE
  #ECECFF   #D2E9FF   #ECFFFF   #E8FFF5   #DFFFDF
  #FBFBFF   #ECF5FF   #FDFFFF   #FBFFFD   #F0FFF0
  #467500   #424200   #5B4B00   #844200   #642100
  #548C00   #5B5B00   #796400   #9F5000   #842B00
  #64A600   #737300   #977C00   #BB5E00   #A23400
  #73BF00   #8C8C00   #AE8F00   #D26900   #BB3D00
  #82D900   #A6A600   #C6A300   #EA7500   #D94600
  #8CEA00   #C4C400   #D9B300   #FF8000   #F75000
  #9AFF02   #E1E100   #EAC100   #FF9224   #FF5809
  #A8FF24   #F9F900   #FFD306   #FFA042   #FF8040
  #B7FF4A   #FFFF37   #FFDC35   #FFAF60   #FF8F59
  #C2FF68   #FFFF6F   #FFE153   #FFBB77   #FF9D6F
  #CCFF80   #FFFF93   #FFE66F   #FFC78E   #FFAD86
  #D3FF93   #FFFFAA   #FFED97   #FFD1A4   #FFBD9D
  #DEFFAC   #FFFFB9   #FFF0AC   #FFDCB9   #FFCBB3
  #E8FFC4   #FFFFCE   #FFF4C1   #FFE4CA   #FFDAC8
  #EFFFD7   #FFFFDF   #FFF8D7   #FFEEDD   #FFE6D9
  #F5FFE8   #FFFFF4   #FFFCEC   #FFFAF4   #FFF3EE
  #613030   #616130   #336666   #484891   #6C3365
  #743A3A   #707038   #3D7878   #5151A2   #7E3D76
  #804040   #808040   #408080   #5A5AAD   #8F4586
  #984B4B   #949449   #4F9D9D   #7373B9   #9F4D95
  #AD5A5A   #A5A552   #5CADAD   #8080C0   #AE57A4
  #B87070   #AFAF61   #6FB7B7   #9999CC   #B766AD
  #C48888   #B9B973   #81C0C0   #A6A6D2   #C07AB8
  #CF9E9E   #C2C287   #95CACA   #B8B8DC   #CA8EC2
  #D9B3B3   #CDCD9A   #A3D1D1   #C7C7E2   #D2A2CC
  #E1C4C4   #D6D6AD   #B3D9D9   #D8D8EB   #DAB1D5
  #EBD6D6   #DEDEBE   #C4E1E1   #E6E6F2   #E2C2DE
  #F2E6E6   #E8E8D0   #D1E9E9   #F3F3FA   #EBD3E8
颜色代码表Ⅱ
red
green
blue
magenta
yellow
chocolate
black
aquamarine
lime
fuchsia
brass
azure
brown
bronze
deeppink
aliceblue
gray
copper
coral
feldspar
orange
orchid
pink
plum
quartz
purple
aliceblue
antiquewith
blanchedalmond
blueviolet
beige
burlywood
bisque
cadetblue
pink
saddlebrown
royalblue
rosybrown
purple
orengered
olivedrab
powderblue
peachpuff
papayawhip
paleturquoise
palevioletred
palegreen
navyblue
navajowhite
palegodenrod
violetred
yellowgreen
tomato
turquoise
thistle
springgreen
steelblue
salmon
scarlet
sienna
silver
tan
thistle
turquoise
violet
snow
salmon
scarlet
sienna
silver
tan
thistle
turquoise
violet
chartreuse
darkslategray
darkseagreen
darkred
mediumslateblue
mediumvioletred
oldlace
maroom
goldenrod
wheat
whitesmoke
orange
moccasin
mistyrose
mintcream
midnightblue
dimgray
darksalmon
slategray
skyblue
sienna
seashell
salmon
seagreen
sandybrown
firebrick
gold
khaki
maroom
goldenrod
wheat
whitesmoke
mediumturquoise
navy
mediumspringgreen
mediumseagreen
mediumpurpul
peru
mediumorchid
mediumblue
mediumaquamarine
maroon
limegreen
lightyellow
lightsteelblue
magenta
lightslateblue
lightslategray
lightskyblue
inen
lightseagreen
lightsalmon
lightpink
plum
lightgray
lightgreen
lightgodenrodyellow
indianred
lavender
lightblue
lavenderblush
lightcoral
lightcyan
lightgodenrod
hotpink
greenyellow
lemonchiffon
lawngreen
darkorchid
deepskyblue
honeydew
golenrod
forestgreen
gostwhite
greenyellow
gainsboro
firebrick
dodgerblue
darkturquoise
darkslateblue
darkslategray
darkseagreen
darkred
darkorchid
darkorenge
darkslateblue
darkviolet
floralwhite
cyan
darkgoldenrod
cornsilk
darkolivegreen

bisquedarkgray

darkblue
darkcyan
darkgreen
darkhaki
ivory
darkmagenta
darkgray
cornfloewrblue
cornfloewrblue
darkviolet
floralwhite
darkorenge
darkslateblue

 

标签:其他 | 浏览数(592) | 评论数(1) | 2006-09-04

数据库设计

SQL Server、Oracle等等等等
------ http://blog.csdn.net/thinkingforever/

事务处理是在数据处理时经常遇到的问题,经常用到的方法有以下3种总结整理如下:

方法1:直接写入到sql 中

在存储过程中使用 BEGIN TRANS, COMMIT TRANS, ROLLBACK TRANS 实现
begin trans
declare @orderDetailsError int,@procuntError int
delete from [order details] where productid=42
select @orderDetailsError =@@error
delete from products where productid=42
select @procuntError=@@error
if(@orderDetailsError =0 and @procuntError=0)
COMMIT TRANS
else
ROLLBACK TRANS

优点:
所有事务逻辑包含在一个单独的调用中
拥有运行一个事务的最佳性能
独立于应用程序
限制:
事务上下文仅存在于数据库调用中
数据库代码与数据库系统有关

方法2 :使用ADO.NET 实现

使用ADO.NET 实现,使用这种方式的优点是可以在中间层来管理事务,当然你也可以选择在数据层来实现。
SqlConnection 和OleDbConnection 对象有一个 BeginTransaction 方法,它可以返回 SqlTransaction
或者OleDbTransaction 对象。而且这个对象有 Commit 和 Rollback 方法来管理事务
SqlConnection sqlConnection = new SqlConnection("workstation id=WEIXIAOPING;packet size=4096;user id=sa;initial catalog=Northwind;persist security info=False");
sqlConnection.Open();
SqlTransaction myTrans = sqlConnection.BeginTransaction();
SqlCommand sqlInsertCommand = new SqlCommand();
sqlInsertCommand.Connection = sqlConnection
sqlInsertCommand.Transaction=myTrans;
try{
sqlInsertCommand.CommandText="insert into tbTree(Context,ParentID) values('北京',1)";
sqlInsertCommand.ExecuteNonQuery();
sqlInsertCommand.CommandText="insert into tbTree(Context,ParentID) values('上海',1)";
sqlInsertCommand.ExecuteNonQuery();
myTrans.Commit();
}catch(Exception ex)
{
myTrans.Rollback();
}
finally
{
sqlConnection.Close();
}

优点:
简单性
和数据据事务差不多的快
独立于数据库,不同数据库的专有代码被隐藏了
缺点:
事务不能跨越多个数据库连接
事务执行在数据库连接层上,所以需要在事务过程中维护一个数据库连接
ADO.NET分布事务也可以跨越多个数据库,但是其中一个SQL SERVER 数据库的话,通过用SQL SERVER连接服务器连接到别的数据库,但是如果是在DB2和Orcal之间就不可以。
以上两种事务是经常用到的事务处理方法。

方法3 COM+事务(分布式事务)

.NET Framework 依靠 MTS/COM+ 服务来支持自动事务。COM+ 使用 Microsoft Distributed Transaction Coordinator (DTC) 作为事务管理器和事务协调器在分布式环境中运行事务。

这样可使 .NET 应用程序运行跨多个资源结合不同操作(例如,将定单插入 SQL Server 数据库、将消息写入 Microsoft 消息队列 (MSMQ) 队列、以及从 Oracle 数据库检索数据)的事务。

COM+事务处理的类必须继承System.EnterpriseServices.ServicedComponent,其实web service就是继承System.EnterpriseServices.ServicedComponent,所以web service也支持COM+事务。

定义一个COM+事务处理的类

[Transaction(TransactionOption.Required)]
public class DataAccess:System.EnterpriseServices.ServicedComponent
{
}

TransactionOption枚举类型支持5个COM+值(Disabled,NotSupported,Required,RequiresNew,Supported)
Disabled 忽略当前上下文中的任何事务。
NotSupported 使用非受控事务在上下文中创建组件。
Required 如果事务存在则共享事务,并且如有必要则创建新事务。
RequiresNew 使用新事务创建组件,而与当前上下文的状态无关。
Supported 如果事务存在,则共享该事务。

一般来说COM+中的组件需要Required 或Supported。当组件用于记录或查帐时RequiresNew 很有用,因为组件应该与活动中其他事务处理的提交或回滚隔离开来。派生类可以重载基类的任意属性。如DataAccess选用Required,派生类仍然可以重载并指定RequiresNew或其他值。

COM+事务有手动处理和自动处理,自动处理就是在所需要自动处理的方法前加上[AutoComplete],根据方法的正常或抛出异常决定提交或回滚。手动处理就是调用ContextUtil类中EnableCommit,SetComplete,SetAbort方法。

public string testTransaction()
{
try
{
ContextUtil.EnableCommit();
InsertARecord1();
InsertARecord2();
ContextUtil.SetComplete();
return "succeed!";
}
catch(Exception ex)
{
ContextUtil.SetAbort();
return "failed!";
}
}
public void InsertARecord1()
{
string strconn="workstation id=WEIXIAOPING;packet size=4096;user id=sa;initial catalog=Northwind;persist security info=False";
SqlConnection conn=new SqlConnection(strconn);
conn.Open();
SqlCommand command=new SqlCommand("insert into tbTree(Context,ParentID) values('北京',1)",conn);
command.ExecuteNonQuery();
conn.Close();
}
public void InsertARecord2()
{
string strconn="workstation id=WEIXIAOPING;packet size=4096;user id=sa;initial catalog=Northwind;persist security info=False";
SqlConnection conn=new SqlConnection(strconn);
conn.Open();
SqlCommand command=new SqlCommand("insert into tbTree(Context,ParentID) values('上海',1)",conn);
command.ExecuteNonQuery();
conn.Close();
}

在需要事务跨 MSMQ 和其他可识别事务的资源(例如,SQL Server 数据库)运行的系统中,只能使用 DTC 或 COM+ 事务,除此之外没有其他选择。DTC 协调参与分布式事务的所有资源管理器,也管理与事务相关的操作。
这种做法的缺点是,由于存在 DTC 和 COM 互操作性开销,导致性能降低。COM+事务处理的类必须强命名。




Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=202230

关于获取SQL Server2000的自增长字段值

很久没有翻译东西了,乘晚上整理资料的时候翻一篇。


原文:Check Your SQL Server Identity
来源:sqlservercentral.com
作者:Andy Warren

Over the past few months I've had the opportunity to interview dozens of candidates......


过去的几个月里,我有幸面试了众多应聘DBA和DB开发岗位的求职者。我们希望开发人员能够创建存储过程,编写合理的复杂SQL语句,以及触发器。我喜欢问这些求职者一个问题:

“假设我们使用SQL Server2000进行开发。现在我需要传递给存储过程两个变量:firstname和lastname。存储过程负责向名字为TEST的表插入这两个变量,其中TEST表有两个字段,名字就叫firstname和lastname。TEST表的主键是一个自增长类型的字段,名字叫ContactID。问题是我如何获取插入的那条数据的主键值。”

让我们想一下答案是什么。你是否知道如何创建存储过程,获取数值并返回给调用的应用程序?

有人会直接问我-这个问题重要吗?对于我来说,我问这个问题的目的是为了测试求职者是否有求解非常规需求的能力。设想一下标准的订单/订单明细表应用场景--你是否知道如何不知道订单主键的情况下插入订单的详细信息?当你因为没有使用主键时,可能会带来锁问题。这时,自增长字段加入是常用的一个手段。但是使用@@Identity来获取插入数据的标识,有可能会带来问题,比如在触发器内使用就会发生问题。这并不是一个可以给出唯一答案的问题,但是这个问题可以让我们对处理类似表的问题,来展开讨论。

我收到了很多不同的回答,但是绝大多数并非最优。几乎每个人都知道如何插入数据、如何返回值,但是几乎每个人都在获取自增长字段取值上回答得不是很好。

错误回答 #1 - Select max(contactid) from Test.
因为你无法避免别人也同时在插入数据,因此这个回答是错误的。当然,你可以通过提升隔离级别来达到目的,但是这将会大幅降低并发性能,因此不好。

错误回答 #2 - Select top 1 contactid from test order by contactid desc.
错误的原因和回答#1一样。

错误回答 #3 - 通过插入的数据来组合成一个唯一的标识,从而获得自增长字段的值。如果插入的数据确实组合起来是唯一的,能达到目的,但是如果不唯一,怎么办呢?因此这也不是好办法。

错误回答 #4 - 这个回答很接近正解了。这些回答者建议使用@@Identity,自然这是可以的 (小心,要知道如何正确使用@@Identity), 但是当我问他们关于@@Identity的相关技术细节时,我收到最多的答案如下:

- 对不起,我不是很清楚。
- 你应该尽快获取@@Identity的值,因为其它人的对表插入数据,也会改变这个值。
- 是的,获取最后一个identity值,在大多数情况下是可行的,但是如果在TEST表上有触发器,这个触发器会自动向别的表插入数据,如果那个表也同样有一个自增长字段,那么错误就会发生。此时,你获取的@@Identity取值是那个表的identity取值(注意:这个回答正确地描述了@@Identity的行为)。

 

正确答案 - 因为我们使用的是SQL Server 2000,因此使用Scope_Identity() , 如果用的是SQL Server 7,那么只有只用@@Identity,并且以output参数方式传递(return值一般是用来作为错误代码用)。使用@@Identity意味着将来也许会发生错误,例如审核时使用自增长字段。

现在我们来做一系列的试验来验证:

create database IdentityTest
use identitytest
create table TEST (ContactID int not null identity (1, 1), firstname varchar(100) null, lastname varchar(100) null)
insert into TEST Default Values
select @@Identity

 

运行后会返回1。如果在此运行,则返回 2。

insert into TEST Default Values
select Scope_Identity()

 

运行后返回 3。

现在我们来设计如何使@@Identity返回错误结果。我们先创建一个包含一个新的自增长字段的表TESTHISTORY,然后在TEST表上加触发器。

create table TESTHISTORY (HistoryID int not null identity (1, 1), ContactID int not null, firstname varchar(100) null, lastname varchar(100) null)
create trigger i_TEST on dbo.TEST for insert as

set nocount on

insert into TESTHISTORY (ContactID, FirstName, LastName) select ContactID, FirstName, LastName from Inserted

 

现在看看会发生什么:

insert into TEST Default Values
select @@Identity

 

返回值为1。注意,此时TEST表最后插入的记录,主键值为4,而TESTHISTORY表作后插入记录的主键值=1。

insert into TEST Default Values
select @@Identity

 

返回值为5。TEST表最后插入记录的主键值为5,而且TESTHISTORY表的确也插入了第二条记录。现在我们再测试如果同时有别的连接来向插入TEST表插入数据,情况会如何。首先我们用当前的连接,运行:

insert into TEST Default Values

 

此时,TEST插入了第6条记录。然后新建一个连接,并运行相同的SQL语句:

insert into TEST Default Values

 

此时,TEST表插入了第7条记录。然后我们在原先那个连接里,来获取“错误”的结果,值为3。

select @@Identity

 

现在我们用scope_identity()来测试。我们希望得到的结果是6,不是7!

select Scope_Identity()

 

结果确实如此,证明使用scope_identity()是正确的。我知道,这样测试很麻烦,你也许不会去仔细探究。但是如果你准备使用SQL Server 2000,你就必须知道它是如何工作的。有兴趣用这个问题来考考你的开发人员,可以教他们一些专业的开发技巧,这样也许会使你在将来省却很多本可以避免的麻烦。


说到数据库,我认为不能不先谈数据结构。1996年,在我初入大学学习计算机编程时,当时的老师就告诉我们说:计算机程序=数据结构+算法。尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主,但我还是深深赞同8年前老师的告诉我们的公式:计算机程序=数据结构+算法。面向对象的程序开发,要做的第一件事就是,先分析整个程序中需处理的数据,从中提取出抽象模板,以这个抽象模板设计类,再在其中逐步添加处理其数据的函数(即算法),最后,再给类中的数据成员和函数划分访问权限,从而实现封装。

 

  数据库的最初雏形据说源自美国一个奶牛场的记账薄(纸质的,由此可见,数据库并不一定是存储在电脑里的数据^_^),里面记录的是该奶牛场的收支账目,程序员在将其整理、录入到电脑中时从中受到启发。当按照规定好的数据结构所采集到的数据量大到一定程度后,出于程序执行效率的考虑,程序员将其中的检索、更新维护等功能分离出来,做成单独调用的模块,这个模块后来就慢慢发展、演变成现在我们所接触到的数据库管理系统(DBMS)——程序开发中的一个重要分支。

  下面进入正题,首先按我个人所接触过的程序给数据库设计人员的功底分一下类:
  1、没有系统学习过数据结构的程序员。这类程序员的作品往往只是他们的即兴玩具,他们往往习惯只设计有限的几个表,实现某类功能的数据全部塞在一个表中,各表之间几乎毫无关联。网上不少的免费管理软件都是这样的东西,当程序功能有限,数据量不多的时候,其程序运行起来没有什么问题,但是如果用其管理比较重要的数据,风险性非常大。
  2、系统学习过数据结构,但是还没有开发过对程序效率要求比较高的管理软件的程序员。这类人多半刚从学校毕业不久,他们在设计数据库表结构时,严格按照教科书上的规定,死扣E-R图和3NF(别灰心,所有的数据库设计高手都是从这一步开始的)。他们的作品,对于一般的access型轻量级的管理软件,已经够用。但是一旦该系统需要添加新功能,原有的数据库表差不多得进行大换血。
  3、第二类程序员,在经历过数次程序效率的提升,以及功能升级的折腾后,终于升级成为数据库设计的老鸟,第一类程序员眼中的高人。这类程序员可以胜任二十个表以上的中型商业数据管理系统的开发工作。他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率,而且其设计的数据库可拓展性较好,当用户需要添加新功能时,原有数据库表只需做少量修改即可。
  4、在经历过上十个类似数据库管理软件的重复设计后,第三类程序员中坚持下来没有转行,而是希望从中找出“偷懒”窍门的有心人会慢慢觉悟,从而完成量变到质变的转换。他们所设计的数据库表结构有一定的远见,能够预测到未来功能升级所需要的数据,从而预先留下伏笔。这类程序员目前大多晋级成数据挖掘方面的高级软件开发人员。
  5、第三类程序员或第四类程序员,在对现有的各家数据库管理系统的原理和开发都有一定的钻研后,要么在其基础上进行二次开发,要么自行开发一套有自主版权的通用数据库管理系统。

  我个人正处于第三类的末期,所以下面所列出的一些设计技巧只适合第二类和部分第三类数据库设计人员。同时,由于我很少碰到有兴趣在这方面深钻下去的同行,所以文中难免出现错误和遗漏,在此先行声明,欢迎大家指正,不要藏私哦8)

  一、树型关系的数据表
  不少程序员在进行数据库设计的时候都遇到过树型关系的数据,例如常见的类别表,即一个大类,下面有若干个子类,某些子类又有子类这样的情况。当类别不确定,用户希望可以在任意类别下添加新的子类,或者删除某个类别和其下的所有子类,而且预计以后其数量会逐步增长,此时我们就会考虑用一个数据表来保存这些数据。按照教科书上的教导,第二类程序员大概会设计出类似这样的数据表结构:

类别表_1(Type_table_1)
名称     类型    约束条件   说明
type_id      int        无重复     类别标识,主键
type_name   char(50)    不允许为空   类型名称,不允许重复
type_father   int         不允许为空   该类别的父类别标识,如果是顶节点的话设定为某个唯一值

  这样的设计短小精悍,完全满足3NF,而且可以满足用户的所有要求。是不是这样就行呢?答案是NO!Why?

  我们来估计一下用户希望如何罗列出这个表的数据的。对用户而言,他当然期望按他所设定的层次关系一次罗列出所有的类别,例如这样:
总类别
  类别1
    类别1.1
      类别1.1.1
    类别1.2
  类别2
    类别2.1
  类别3
    类别3.1
    类别3.2
  ……

  看看为了实现这样的列表显示(树的先序遍历),要对上面的表进行多少次检索?注意,尽管类别1.1.1可能是在类别3.2之后添加的记录,答案仍然是N次。这样的效率对于少量的数据没什么影响,但是日后类型扩充到数十条甚至上百条记录后,单单列一次类型就要检索数十次该表,整个程序的运行效率就不敢恭维了。或许第二类程序员会说,那我再建一个临时数组或临时表,专门保存类型表的先序遍历结果,这样只在第一次运行时检索数十次,再次罗列所有的类型关系时就直接读那个临时数组或临时表就行了。其实,用不着再去分配一块新的内存来保存这些数据,只要对数据表进行一定的扩充,再对添加类型的数量进行一下约束就行了,要完成上面的列表只需一次检索就行了。下面是扩充后的数据表结构:

类别表_2(Type_table_2)
名称     类型    约束条件                       说明
type_id      int        无重复                     类别标识,主键
type_name   char(50)    不允许为空                   类型名称,不允许重复
type_father   int         不允许为空                   该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer    char(6)     限定3层,初始值为000000       类别的先序遍历,主要为减少检索数据库的次数

  按照这样的表结构,我们来看看上面例子记录在表中的数据是怎样的:

type_id      type_name          type_father          type_layer
1             总类别               0                 000000
2             类别1                1                 010000
3             类别1.1              2                 010100
4             类别1.2              2                 010200
5             类别2                1                 020000
6             类别2.1              5                 020100
7             类别3                1                 030000
8             类别3.1              7                 030100
9             类别3.2              7                 030200
10            类别1.1.1            3                 010101
……

  现在按type_layer的大小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer

列出记录集如下:

type_id      type_name          type_father          type_layer
1             总类别               0                 000000
2             类别1                1                 010000
3             类别1.1              2                 010100
10            类别1.1.1            3                 010101
4             类别1.2              2                 010200
5             类别2                1                 020000
6             类别2.1              5                 020100
7             类别3                1                 030000
8             类别3.1              7                 030100
9             类别3.2              7                 030200
……

  现在列出的记录顺序正好是先序遍历的结果。在控制显示类别的层次时,只要对type_layer字段中的数值进行判断,每2位一组,如大于0则向右移2个空格。当然,我这个例子中设定的限制条件是最多3层,每层最多可设99个子类别,只要按用户的需求情况修改一下type_layer的长度和位数,即可更改限制层数和子类别数。其实,上面的设计不单单只在类别表中用到,网上某些可按树型列表显示的论坛程序大多采用类似的设计。

  或许有人认为,Type_table_2中的type_father字段是冗余数据,可以除去。如果这样,在插入、删除某个类别的时候,就得对type_layer 的内容进行比较繁琐的判定,所以我并没有消去type_father字段,这也正符合数据库设计中适当保留冗余数据的来降低程序复杂度的原则,后面我会举一个故意增加数据冗余的案例。

  
  二、商品信息表的设计
  假设你是一家百货公司电脑部的开发人员,某天老板要求你为公司开发一套网上电子商务平台,该百货公司有数千种商品出售,不过目前仅打算先在网上销售数十种方便运输的商品,当然,以后可能会陆续在该电子商务平台上增加新的商品出售。现在开始进行该平台数据库的商品信息表的设计。每种出售的商品都会有相同的属性,如商品编号,商品名称,商品所属类别,相关信息,供货厂商,内含件数,库存,进货价,销售价,优惠价。你很快就设计出4个表:商品类型表(Wares_type),供货厂商表(Wares_provider),商品信息表(Wares_info):

商品类型表(Wares_type)
名称     类型    约束条件                       说明
type_id      int        无重复                     类别标识,主键
type_name   char(50)    不允许为空                   类型名称,不允许重复
type_father   int         不允许为空                   该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer    char(6)     限定3层,初始值为000000       类别的先序遍历,主要为减少检索数据库的次数

供货厂商表(Wares_provider)
名称     类型    约束条件                       说明
provider_id   int        无重复                     供货商标识,主键
provider_name char(100)   不允许为空                   供货商名称

商品信息表(Wares_info)
名称      类型    约束条件                       说明
wares_id       int       无重复                       商品标识,主键
wares_name     char(100)  不允许为空                     商品名称
wares_type   int        不允许为空           商品类型标识,和Wares_type.type_id关联
wares_info     char(200)  允许为空                       相关信息
provider       int        不允许为空                     供货厂商标识,和Wares_provider.provider_id关联
setnum         int        初始值为1                      内含件数,默认为1
stock          int        初始值为0                      库存,默认为0
buy_price      money      不允许为空                     进货价
sell_price     money      不允许为空                     销售价
discount       money      不允许为空                     优惠价

  你拿着这3个表给老板检查,老板希望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增加了一个haspic的BOOL型字段,然后再建了一个新表——商品图片表(Wares_pic):

商品图片表(Wares_pic)
名称      类型    约束条件                       说明
pic_id        int        无重复                       商品图片标识,主键
wares_id      int         不允许为空                     所属商品标识,和Wares_info.wares_id关联
pic_address  char(200)   不允许为空           图片存放路径

  程序开发完成后,完全满足老板目前的要求,于是正式启用。一段时间后,老板打算在这套平台上推出新的商品销售,其中,某类商品全部都需添加“长度”的属性。第一轮折腾来了……当然,你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslength的BOOL型字段,又建了一个新表——商品长度表(Wares_length):

商品长度表(Wares_length)
名称      类型    约束条件                       说明
length_id     int        无重复                       商品图片标识,主键
wares_id      int         不允许为空                     所属商品标识,和Wares_info.wares_id关联
length       char(20)    不允许为空           商品长度说明

  刚刚改完没多久,老板又打算上一批新的商品,这次某类商品全部需要添加“宽度”的属性。你咬了咬牙,又照方抓药,添加了商品宽度表(Wares_width)。又过了一段时间,老板新上的商品中有一些需要添加“高度”的属性,你是不是开始觉得你所设计的数据库按照这种方式增长下去,很快就能变成一个迷宫呢?那么,有没有什么办法遏制这种不可预见性,但却类似重复的数据库膨胀呢?我在阅读《敏捷软件开发:原则、模式与实践》中发现作者举过类似的例子:7.3 “Copy”程序。其中,我非常赞同敏捷软件开发这个观点:在最初几乎不进行预先设计,但是一旦需求发生变化,此时作为一名追求卓越的程序员,应该从头审查整个架构设计,在此次修改中设计出能够满足日后类似修改的系统架构。下面是我在需要添加“长度”的属性时所提供的修改方案:

  去掉商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。

商品额外属性表(Wares_ex_property)
名称      类型    约束条件                       说明
ex_pid        int        无重复                       商品额外属性标识,主键
p_name        char(20)    不允许为空                     额外属性名称

商品额外信息表(Wares_ex_info)
名称        类型    约束条件                       说明
ex_iid          int        无重复                       商品额外信息标识,主键
wares_id        int         不允许为空                     所属商品标识,和Wares_info.wares_id关联
property_id    int         不允许为空           商品额外属性标识,和Wares_ex_property.ex_pid关联
property_value  char(200)   不允许为空                     商品额外属性值

  在商品额外属性表(Wares_ex_property)中添加2条记录:
ex_pid            p_name
1                商品图片
2                商品长度

  再在整个电子商务平台的后台管理功能中追加一项商品额外属性管理的功能,以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表(Wares_ex_property)中添加一条记录即可。不要害怕变化,被第一颗子弹击中并不是坏事,坏的是被相同轨道飞来的第二颗、第三颗子弹击中。第一颗子弹来得越早,所受的伤越重,之后的抵抗力也越强8)

三、多用户及其权限管理的设计
  开发数据库管理类的软件,不可能不考虑多用户和用户权限设置的问题。尽管目前市面上的大、中型的后台数据库系统软件都提供了多用户,以及细至某个数据库内某张表的权限设置的功能,我个人建议:一套成熟的数据库管理软件,还是应该自行设计用户管理这块功能,原因有二:
  1.那些大、中型后台数据库系统软件所提供的多用户及其权限设置都是针对数据库的共有属性,并不一定能完全满足某些特例的需求;
  2.不要过多的依赖后台数据库系统软件的某些特殊功能,多种大、中型后台数据库系统软件之间并不完全兼容。否则一旦日后需要转换数据库平台或后台数据库系统软件版本升级,之前的架构设计很可能无法重用。

 

  下面看看如何自行设计一套比较灵活的多用户管理模块,即该数据库管理软件的系统管理员可以自行添加新用户,修改已有用户的权限,删除已有用户。首先,分析用户需求,列出该数据库管理软件所有需要实现的功能;然后,根据一定的联系对这些功能进行分类,即把某类用户需使用的功能归为一类;最后开始建表:
  
功能表(Function_table)
名称     类型    约束条件   说明
f_id          int        无重复     功能标识,主键
f_name        char(20)    不允许为空   功能名称,不允许重复
f_desc        char(50)    允许为空     功能描述

用户组表(User_group)
名称     类型    约束条件   说明
group_id      int         无重复        用户组标识,主键
group_name    char(20)    不允许为空    用户组名称
group_power   char(100)   不允许为空    用户组权限表,内容为功能表f_id的集合

用户表(User_table)
名称     类型    约束条件   说明
user_id       int         无重复        用户标识,主键
user_name     char(20)    无重复        用户名
user_pwd      char(20)    不允许为空    用户密码
user_type     int         不允许为空    所属用户组标识,和User_group.group_id关联

  采用这种用户组的架构设计,当需要添加新用户时,只需指定新用户所属的用户组;当以后系统需要添加新功能或对旧有功能权限进行修改时,只用操作功能表和用户组表的记录,原有用户的功能即可相应随之变化。当然,这种架构设计把数据库管理软件的功能判定移到了前台,使得前台开发相对复杂一些。但是,当用户数较大(10人以上),或日后软件升级的概率较大时,这个代价是值得的。


  四、简洁的批量m:n设计
  碰到m:n的关系,一般都是建立3个表,m一个,n一个,m:n一个。但是,m:n有时会遇到批量处理的情况,例如到图书馆借书,一般都是允许用户同时借阅n本书,如果要求按批查询借阅记录,即列出某个用户某次借阅的所有书籍,该如何设计呢?让我们建好必须的3个表先:

书籍表(Book_table)
名称     类型    约束条件   说明
book_id       int         无重复        书籍标识,主键
book_no       char(20)    无重复        书籍编号
book_name     char(100)   不允许为空    书籍名称
……

借阅用户表(Renter_table)
名称     类型    约束条件   说明
renter_id     int         无重复        用户标识,主键
renter_name   char(20)    不允许为空    用户姓名
……

借阅记录表(Rent_log)
名称     类型    约束条件   说明
rent_id       int         无重复        借阅记录标识,主键
r_id          int         不允许为空    用户标识,和Renter_table.renter_id关联
b_id          int         不允许为空    书籍标识,和Book_table.book_id关联
rent_date     datetime    不允许为空    借阅时间
……

  为了实现按批查询借阅记录,我们可以再建一个表来保存批量借阅的信息,例如:

批量借阅表(Batch_rent)
名称     类型    约束条件   说明
batch_id      int         无重复        批量借阅标识,主键
batch_no      int         不允许为空    批量借阅编号,同一批借阅的batch_no相同
rent_id       int         不允许为空    借阅记录标识,和Rent_log.rent_id关联
batch_date    datetime    不允许为空    批量借阅时间

  这样的设计好吗?我们来看看为了列出某个用户某次借阅的所有书籍,需要如何查询?首先检索批量借阅表(Batch_rent),把符合条件的的所有记录的rent_id字段的数据保存起来,再用这些数据作为查询条件带入到借阅记录表(Rent_log)中去查询。那么,有没有什么办法改进呢?下面给出一种简洁的批量设计方案,不需添加新表,只需修改一下借阅记录表(Rent_log)即可。修改后的记录表(Rent_log)如下:

借阅记录表(Rent_log)
名称     类型    约束条件   说明
rent_id       int         无重复        借阅记录标识,主键
r_id          int         不允许为空    用户标识,和Renter_table.renter_id关联
b_id          int         不允许为空    书籍标识,和Book_table.book_id关联
batch_no      int         不允许为空    批量借阅编号,同一批借阅的batch_no相同
rent_date     datetime    不允许为空    借阅时间
……

  其中,同一次借阅的batch_no和该批第一条入库的rent_id相同。举例:假设当前最大rent_id是64,接着某用户一次借阅了3本书,则批量插入的3条借阅记录的batch_no都是65。之后另外一个用户租了一套碟,再插入出租记录的rent_id是68。采用这种设计,查询批量借阅的信息时,只需使用一条标准T_SQL的嵌套查询即可。当然,这种设计不符合3NF,但是和上面标准的3NF设计比起来,哪一种更好呢?答案就不用我说了吧。


  五、冗余数据的取舍
  上篇的“树型关系的数据表”中保留了一个冗余字段,这里的例子更进一步——添加了一个冗余表。先看看例子:我原先所在的公司为了解决员工的工作餐,和附近的一家小餐馆联系,每天吃饭记账,费用按人数平摊,月底由公司现金结算,每个人每个月的工作餐费从工资中扣除。当然,每天吃饭的人员和人数都不是固定的,而且,由于每顿工作餐的所点的菜色不同,每顿的花费也不相同。例如,星期一中餐5人花费40元,晚餐2人花费20,星期二中餐6人花费36元,晚餐3人花费18元。为了方便计算每个人每个月的工作餐费,我写了一个简陋的就餐记账管理程序,数据库里有3个表:

员工表(Clerk_table)
名称     类型    约束条件   说明
clerk_id      int         无重复        员工标识,主键
clerk_name    char(10)    不允许为空    员工姓名

每餐总表(Eatdata1)
名称     类型    约束条件   说明
totle_id      int         无重复        每餐总表标识,主键
persons       char(100)   不允许为空    就餐员工的员工标识集合
eat_date      datetime    不允许为空    就餐日期
eat_type      char(1)     不允许为空    就餐类型,用来区分中、晚餐
totle_price   money       不允许为空    每餐总花费
persons_num   int         不允许为空    就餐人数

就餐计费细表(Eatdata2)
名称     类型    约束条件   说明
id            int         无重复        就餐计费细表标识,主键
t_id          int         不允许为空    每餐总表标识,和Eatdata1.totle_id关联
c_id          int         不允许为空    员工标识标识,和Clerk_table.clerk_id关联
price         money       不允许为空    每人每餐花费

  其中,就餐计费细表(Eatdata2)的记录就是把每餐总表(Eatdata1)的一条记录按就餐员工平摊拆开,是个不折不扣的冗余表。当然,也可以把每餐总表(Eatdata1)的部分字段合并到就餐计费细表(Eatdata2)中,这样每餐总表(Eatdata1)就成了冗余表,不过这样所设计出来的就餐计费细表重复数据更多,相比来说还是上面的方案好些。但是,就是就餐计费细表(Eatdata2)这个冗余表,在做每月每人餐费统计的时候,大大简化了编程的复杂度,只用类似这么一条查询语句即可统计出每人每月的寄餐次数和餐费总帐:

SELECT clerk_name AS personname,COUNT(c_id) as eattimes,SUM(price) AS ptprice FROM Eatdata2 JOIN Clerk_tabsle ON (c_id=clerk_id) JOIN eatdata1 ON (totleid=tid) WHERE eat_date>=CONVERT(datetime,'"&the_date&"') AND eat_date<DATEADD(month,1,CONVERT(datetime,'"&the_date&"')) GROUP BY c_id

  想象一下,如果不用这个冗余表,每次统计每人每月的餐费总帐时会多麻烦,程序效率也够呛。那么,到底什么时候可以增加一定的冗余数据呢?我认为有2个原则:

  1、用户的整体需求。当用户更多的关注于,对数据库的规范记录按一定的算法进行处理后,再列出的数据。如果该算法可以直接利用后台数据库系统的内嵌函数来完成,此时可以适当的增加冗余字段,甚至冗余表来保存这些经过算法处理后的数据。要知道,对于大批量数据的查询,修改或删除,后台数据库系统的效率远远高于我们自己编写的代码。
  2、简化开发的复杂度。现代软件开发,实现同样的功能,方法有很多。尽管不必要求程序员精通绝大部分的开发工具和平台,但是还是需要了解哪种方法搭配哪种开发工具的程序更简洁,效率更高一些。冗余数据的本质就是用空间换时间,尤其是目前硬件的发展远远高于软件,所以适当的冗余是可以接受的。不过我还是在最后再强调一下:不要过多的依赖平台和开发工具的特性来简化开发,这个度要是没把握好的话,后期维护升级会栽大跟头的。


在动态网站的设计中,数据库设计的重要性不言而喻。如果设计不当,查询起来就非常吃力,程序的性能也会受到影响。无论你使用的是mySQL或者Oracle数据库,通过进行正规化的表格设计,可以令你的PHP代码更具可读性,更容易扩展,从而也会提升应用的性能。
  简单说来,正规化就是在表格设计时,消除冗余性和不协调的从属关系。在本文中,我将通过五个渐进的过程来告诉你在设计中应该了解的正规化技巧。从而建立一个可行而且效率高的数据库。本文也会详细分析一下可以利用的关系类型。

  这里假定我们要建立一个用户信息的表格,其中要存储用户的名字、公司、公司地址和一些个人的收藏夹或url。在开始时,你可能定义一个如下的表格结构:

零状态形式

[users]
name company company_address url1 url2
Joe ABC 1 Work Lane abc.com xyz.com
Jill XYZ 1 Job Street abc.com xyz.com

  由于没有进行任何的正规化处理,我们将这种形式的表称为零状态形式的表。留意其中的url1和url2字段---如果我们在应用中需要第三个url呢?这样你就要在表格中多加一列,很明显,这不是一个好办法。如果你要创建一个富有扩展性的系统,你就要考虑使用第一个正规化的形式,并且应用到该表格中。

第一级正规化形式

1.消除每个表格中重复的组
2.为每套相关的数据建立一个独立的表格
3.使用一个主键来标识每套相关的数据

  以上的表格明显违反了上面第一条的规定,那么第三条的主键又是什么意思呢?很简单,它只是在每个记录中加入一个唯一的、自动增加的整型值。通过这个值,就可以将两个姓名一样的记录区分开来。通过应用第一级正规化形式,我们得到了以下的表格:

[users]
userId name company company_address url
1 Joe ABC 1 Work Lane abc.com
1 Joe ABC 1 Work Lane xyz.com
2 Jill XYZ 1 Job Street abc.com
2 Jill XYZ 1 Job Street xyz.com

  现在我们的表格可以说已经处在第一级正规化的形式了,它已经解决了url字段的限制问题,不过这样的处理后又带来了一个新的问题。每次在user表中插入一条记录的时候,我们都必须重复所有的公司和用户数据。这样不仅令数据库比以前大了,而且很容易出错。因此还要经过第二级正规化处理。

第二级正规化形式

1.为应用在多条记录的字段建立独立的表格
2.通过一个foreign key来关联这些表格的值

  我们将url的值放在一个独立的表格中,这样我们就可以在以后加入更多的数据,而无需担心产生重复的值。我们还通过主键值来关联这些字段:

[users]
userId name company company_address
1 Joe ABC 1 Work Lane
2 Jill XYZ 1 Job Street

[urls]
urlId relUserId url
1 1 abc.com
2 1 xyz.com
3 2 abc.com
4 2 xyz.com

  如上所示,我们创建了独立的表格,users表中的主键userid现在与url表中的foreign key relUserId关联。现在的情况好象已经得到了明显的改善。不过,如果我们要为ABC公司加入一个员工记录呢?或者更多,200个?这样我们就必须重复使用公司名和地址,这明显不够冗余。因此我们将应用第三级正规化方法:

第三级正规化形式

1.消除不依赖于该键的字段
公司名及地址与User Id都是没有关系的,因此它们应用拥有自己的公司Id:

[users]
userId name relCompId
1 Joe 1
2 Jill 2

[companies]
compId company company_address
1 ABC 1 Work Lane
2 XYZ 1 Job Street

[urls]
urlId relUserId url
1 1 abc.com
2 1 xyz.com
3 2 abc.com
4 2 xyz.com

  这样我们就将companies表中的主键comId和users表中名字为relCompId的foreign key关联起来,就算为ABC公司加入200个员工,在companies中也只有一条记录。我们的users和urls表可以不断地扩大,而无需担心插入不必要的数据。大部分的开发者都认为经过三步的正规化就足够了,这个数据库的设计已经可以很方便地处理整个企业的负担,此看法在大多数的情况下是正确的。

  我们可以留意一下url的字段--你注意到数据的冗余了吗?如果给用户用户输入这些url数据的HTML页面是一个文本框,可任意输入的话,这并没有问题,两个用户输入同样收藏夹的概率较少,不过,如果是通过一个下拉式的菜单,只让用户选择两个url输入,或者更多一点。这种情况下,我们的数据库还可以进行下一级别的优化--第四步,对于大多数的开发者来说,这一步都是忽略的,因为它要依赖一个很特别的关系--一个多对多的关系,这在我们的应用中是还没有遇到过的。

数据关系

  在定义第四个正规化的形式前,我想首先提一下三种基本的数据关系:一对一,一对多和多对多。我们回头看一下经过第一个正规化的users表。要是我们将url的字段放在一个独立的表中,每次在users表中插入一个记录,我们就会在urls表中插入一行。我们将得到一个一对一的关系:用户表中的每一行,都将在urls表中找到相应的一行。对于我们的应用来说,这既不实用也不标准。

  然后看看第二个正规化的例子。对于每个用户记录,我们的表格允许有多个urls的记录与之关联。这是一个一对多的关系,这是一个很常见的关系。

  对于多对多的关系来说,就有点复杂了。在我们的第三个正规化形式的例子中,我们的一个用户与很多的url有关,而我们想将该结构变为允许多个用户与多个的urls有关,这样我们就可以得到一个多对多的结构。在讨论前,我们先看看表格结构会有些什么变化

[users]
userId name relCompId
1 Joe 1
2 Jill 2

[companies]
compId company company_address
1 ABC 1 Work Lane
2 XYZ 1 Job Street

[urls]
urlId url
1 abc.com
2 xyz.com

[url_relations]
relationId relatedUrlId relatedUserId
1 1 1
2 1 2
3 2 1
4 2 2

  为了进一步减低数据的冗余,我们运用第四级正规化形式。我们创建了一个颇奇怪的url_relations表,里面的字段均为主键或者foreign key。通过这个表,我们就可以消除urls表中的重复项目。以下是第四个正规化形式的具体要求:

第四个正规化形式

1.在一个多对多的关系中,独立的实体不能存放在同一个表格中

  由于它仅应用于多对多的关系,因此大多数的开发者可以忽略这条规定。不过在某些情况下,它是非常实用的,这个例子就是这样,我们通过将相同的实体分离出来,并且将关系移到它们自己的表格中,从而改进了urls表格。

为了令你更容易明白,我们举个具体的例子,以下将用一个SQL语句选择出所有属于joe的urls:

SELECT name, url FROM users, urls, url_relations WHERE url_relations.relatedUserId = 1 AND users.userId = 1 AND urls.urlId = url_relations.relatedUrlId

如果我们想要遍历每个人的个人信息和url信息,我们可以这样做:

SELECT name, url FROM users, urls, url_relations WHERE users.userId = url_relations.relatedUserId AND urls.urlId = url_relations.relatedUrlId

第五级正规化形式

还有一级正规化的形式,它并不常见,有点深奥,并且在大部分的情况下都是不必要的。它的原则是:

1.原来的表格必须可以通过由它分离出去的表格重新构建

  使用这个规定的好处是,你可以确保不会在分离的表格中引入多余的列,所有你创建的表格结构都与它们的实际需要一样大。应用这条规定是一个好习惯,不过除非你要处理一个非常大型的数据,否则你将不需要用到它。

  希望这篇文章对你有用,并且可以帮助你在所有的项目中应用这些正规化的规定。你可能想知道这些方法是从哪来的,我可以告诉你,前面三个正规化的规定是1972年,Dr. E.F. Codd在他的论文“进一步正规化数据库的关系模型中”提出的,其余的规定是经过后来的集合理论和关系数学家理论化的。 评论:正所谓物级必反,将表格分得过细有时并不好,因为这样需要将各表进行各种的关联,这会令查询时变得复杂,而且效率也可能降低,这些正规化的规定可以参考,在实际应用时,要根据项目的大小,必要时可以进行一些测试,以设计出更合理的表格结构。



1. 原始单据与实体之间的关系
  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对应多个实体,或多张原始单证对应一个实体。这里的实体可以理解为基本表。明确这种对应关系后,对我们设计录入界面大有好处。
  〖例1〗:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表、社会关系表、工作简历表。这就是“一张原始单证对应多个实体”的典型例子。

   2. 主键与外键
  一般而言,一个实体不能既无主键又无外键。在E—R 图中, 处于叶子部位的实体, 可以定义主键,也可以不定义主键(因为它无子孙), 但必须要有外键(因为它有父亲)。
  主键与外键的设计,在全局数据库的设计中,占有重要地位。当全局数据库的设计完成以后,有个美国数据库设计专家说:“键,到处都是键,除了键之外,什么也没有”,这就是他的数据库设计经验之谈,也反映了他对信息系统核心(数据模型)的高度抽象思想。因为:主键是实体的高度抽象,主键与外键的配对,表示实体之间的连接。

   3. 基本表的性质
  基本表与中间表、临时表不同,因为它具有如下四个特性:
   (1) 原子性。基本表中的字段是不可再分解的。
   (2) 原始性。基本表中的记录是原始数据(基础数据)的记录。
   (3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。
   (4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。
  理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

   4. 范式标准
  基本表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。
  〖例2〗:有一张存放商品的基本表,如表1所示。“金额”这个字段的存在,表明该表的设计不满足第三范式,因为“金额”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加“金额”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。
  在Rose 2002中,规定列有两种类型:数据列和计算列。“金额”这样的列被称为“计算列”,而“单价”和“数量”这样的列被称为“数据列”。
  表1 商品表的表结构
  商品名称 商品型号 单价 数量 金额
  电视机 29吋 2,500 40 100,000
  
   5. 通俗地理解三个范式
  通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):
  第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;
  第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
  第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。
  没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是:在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。

   6. 要善于识别与正确处理多对多的关系
  若两个实体之间存在多对多的关系,则应消除这种关系。消除的办法是,在两者之间增加第三个实体。这样,原来一个多对多的关系,现在变为两个一对多的关系。要将原来两个实体的属性合理地分配到三个实体中去。这里的第三个实体,实质上是一个较复杂的关系,它对应一张基本表。一般来讲,数据库设计工具不能识别多对多的关系,但能处理多对多的关系。
  〖例3〗:在“图书馆信息系统”中,“图书”是一个实体,“读者”也是一个实体。这两个实体之间的关系,是一个典型的多对多关系:一本图书在不同时间可以被多个读者借阅,一个读者又可以借多本图书。为此,要在二者之间增加第三个实体,该实体取名为“借还书”,它的属性为:借还时间、借还标志(0表示借书,1表示还书),另外,它还应该有两个外键(“图书”的主键,“读者”的主键),使它能与“图书”和“读者”连接。

   7. 主键PK的取值方法
   PK是供程序员使用的表间连接工具,可以是一无物理意义的数字串, 由程序自动加1来实现。也可以是有物理意义的字段名或字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空间大,而且速度也慢。

   8. 正确认识数据冗余
  主键与外键在多表中的重复出现, 不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚。非键字段的重复出现, 才是数据冗余!而且是一种低级冗余,即重复性的冗余。高级冗余不是字段的重复出现,而是字段的派生出现。
  〖例4〗:商品中的“单价、数量、金额”三个字段,“金额”就是由“单价”乘以“数量”派生出来的,它就是冗余,而且是一种高级冗余。冗余的目的是为了提高处理速度。只有低级冗余才会增加数据的不一致性,因为同一数据,可能从不同时间、地点、角色上多次录入。因此,我们提倡高级冗余(派生性冗余),反对低级冗余(重复性冗余)。

   9. E--R图没有标准答案
  信息系统的E--R图没有标准答案,因为它的设计与画法不是惟一的,只要它覆盖了系统需求的业务范围和功能内容,就是可行的。反之要修改E--R图。尽管它没有惟一的标准答案,并不意味着可以随意设计。好的E—R图的标准是:结构清晰、关联简洁、实体个数适中、属性分配合理、没有低级冗余。

   10. 视图技术在数据库设计中很有用
  与基本表、代码表、中间表不同,视图是一种虚表,它依赖数据源的实表而存在。视图是供程序员使用数据库的一个窗口,是基表数据综合的一种形式, 是数据处理的一种方法,是用户数据保密的一种手段。为了进行复杂处理、提高运算速度和节省存储空间, 视图的定义深度一般不得超过三层。 若三层视图仍不够用, 则应在视图上定义临时表, 在临时表上再定义视图。这样反复交迭定义, 视图的深度就不受限制了。
  对于某些与国家政治、经济、技术、军事和安全利益有关的信息系统,视图的作用更加重要。这些系统的基本表完成物理设计之后,立即在基本表上建立第一层视图,这层视图的个数和结构,与基本表的个数和结构是完全相同。并且规定,所有的程序员,一律只准在视图上操作。只有数据库管理员,带着多个人员共同掌握的“安全钥匙”,才能直接在基本表上操作。请读者想想:这是为什么?

   11. 中间表、报表和临时表
  中间表是存放统计数据的表,它是为数据仓库、输出报表或查询结果而设计的,有时它没有主键与外键(数据仓库除外)。临时表是程序员个人设计的,存放临时记录,为个人所用。基表和中间表由DBA维护,临时表由程序员自己用程序自动维护。

   12. 完整性约束表现在三个方面
  域的完整性:用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Check按钮,通过它定义字段的值城。
  参照完整性:用PK、FK、表级触发器来实现。
  用户定义完整性:它是一些业务规则,用存储过程和触发器来实现。

   13. 防止数据库设计打补丁的方法是“三少原则”
   (1) 一个数据库中表的个数越少越好。只有表的个数少了,才能说明系统的E--R图少而精,去掉了重复的多余的实体,形成了对客观世界的高度抽象,进行了系统的数据集成,防止了打补丁式的设计;
   (2) 一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的外键,所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;
   (3) 一个表中的字段个数越少越好。只有字段的个数少了,才能说明在系统中不存在数据重复,且很少有数据冗余,更重要的是督促读者学会“列变行”,这样就防止了将子表中的字段拉入到主表中去,在主表中留下许多空余的字段。所谓“列变行”,就是将主表中的一部分内容拉出去,另外单独建一个子表。这个方法很简单,有的人就是不习惯、不采纳、不执行。
  数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。“三少”是一个整体概念,综合观点,不能孤立某一个原则。该原则是相对的,不是绝对的。“三多”原则肯定是错误的。试想:若覆盖系统同样的功能,一百个实体(共一千个属性) 的E--R图,肯定比二百个实体(共二千个属性) 的E--R图,要好得多。
  提倡“三少”原则,是叫读者学会利用数据库设计技术进行系统的数据集成。数据集成的步骤是将文件系统集成为应用数据库,将应用数据库集成为主题数据库,将主题数据库集成为全局综合数据库。集成的程度越高,数据共享性就越强,信息孤岛现象就越少,整个企业信息系统的全局E—R图中实体的个数、主键的个数、属性的个数就会越少。
  提倡“三少”原则的目的,是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据库变成了随意设计数据库表的“垃圾堆”,或数据库表的“大杂院”,最后造成数据库中的基本表、代码表、中间表、临时表杂乱无章,不计其数,导致企事业单位的信息系统无法维护而瘫痪。
   “三多”原则任何人都可以做到,该原则是“打补丁方法”设计数据库的歪理学说。“三少”原则是少而精的原则,它要求有较高的数据库设计技巧与艺术,不是任何人都能做到的,因为该原则是杜绝用“打补丁方法”设计数据库的理论依据。

    14. 提高数据库运行效率的办法
  在给定的系统硬件和系统软件条件下,提高数据库系统的运行效率的办法是:
   (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。
   (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。
   (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表。
   (4) 对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。
   (5) 在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。
  总之,要提高数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化,这三个层次上同时下功夫。

  上述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的。对于这些经验的运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握。并逐步做到:在应用中发展,在发展中应用。

 

posted on 2008-10-12 17:35  leenshan  阅读(9258)  评论(0编辑  收藏  举报