2011年6月28日

 一个类不能被继承,也就是说它的子类不能构造父类,这样子类就没有办法实例化整个子类从而实现子类无法继承父类。我们可以将一个类的构造函数声明为私有,使得这个类的构造函数对子类不可见,那么这个类也就不能继承了。但是,这引出一个问题,客户程序岂不是也无法实例化这个类了?OK,让我们参考一下Singleton模式,用一个static函数来帮助创建这个类的实例,问题就解决了!

class CParent
{
private:
    CParent(int v){m_v = v;}
    ~CParent(){}

    int m_v;
    static CParent * m_instance;
public:
void fun(){cout << "The value is: " << m_v << endl;}
    static CParent * getInstance(int v);
};
CParent * CParent::m_instance = NULL;

CParent * CParent::getInstance(int v)
{
    m_instance = new CParent(v);
    return m_instance;
}

         这是一个有效的方法,但是static函数创建出来的实例必然是static的。而且,这个类不能像普通的类那样构建对象,比如:

CParent c;  // impossible

换个思路考虑一下,友元不也是不能被继承的么?我们可以把类的构造函数定义为private的同时,定义友元函数来帮助构造类的实例。


class CParent
{
private:
    CParent(int v){m_v = v;}
    ~CParent();

    int m_v;
public:
    void fun(){cout << "The value is: " << m_v << endl;}
    friend CParent * getInstance(int v);
};

CParent * getInstance(int v)
{
    CParent * pinstance = new CParent(v);
    return pinstance;
}   

这个类也是不能被继承的,但是出现的问题和前面一样:我们还是不能像对普通类那样对待这个类。

现在设想一下,有一个CParent类,我们不希望他能够被继承。在友元不能被继承的思路指引下,我们要考虑让CParent的友元属性不能被继承。假设有一个辅助类CNoHeritance,CParent是CNoHeritance类的友元。还要假设一个CChild类,它试图去继承CParent类(如果它有这个能耐的话)。

先把CNoHeritance类的构造函数定义成private,然后将CParent声明为CNoHeritance的友元类。同时CParent继承了CNoHeritance类。到目前为止,CNoHeritance除了CParent类以外,谁也无法对它进行访问和实例化。CChild因为无法继承CParent的友元特性,所以CChild无法对CNoHeritance直接进行实例化(但是可以通过CParent)。

        到目前为止,CParent还是可以被继承的。这是一个trick.让我们整理一下思路,下面的图说明了CNoHeritance, CParent和CChild三个类之间的关系。

 


如果我们让CParent类虚继承CNoHeritance类,根据虚继承的特性,虚基类的构造函数由最终的子类负责构造。因此CChild如果要想继承CParent,它必须能够构造CNoHeritance,这是不可能的!因此,我们的CParent也就终于成为了一个无法继承的类。

class CNoHeritance
{
private:
    CNoHeritance(){}
    ~CNoHeritance(){}
    friend class CParent;
};

class CParent : virtual public CNoHeritance
{
public:
    CParent(int v){m_v = v;}
    ~CParent(){};
private:
    int m_v;

public:
    void fun(){cout << "The value is: " << m_v << endl;}
};

class CChild : public CParent
{
public:
    CChild():CParent(10){}
    ~CChild(){}
};


    需要注意的是,我们这里引入的CNoHeritance类对整个程序而言,只引入了Private的构造函数和析构函数,所以不会因为可能的菱形继承带来二义性.

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lazy_tiger/archive/2008/03/28/2224899.aspx

posted @ 2011-06-28 00:56 黄超 阅读(370) | 评论 (0)编辑 收藏

论函数调用约定
  在C语言中,假设我们有这样的一个函数:
  
  int function(int a,int b)
  
  调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。

  栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。

  函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

  在参数传递中,有两个很重要的问题必须得到明确说明:
  
  当参数个数多于一个时,按照什么顺序把参数压入堆栈
  函数调用后,由谁来把堆栈恢复原装
  在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:

  stdcall
  cdecl
  fastcall
  thiscall
  naked call


  stdcall调用约定
  stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定就是stdcall。在Microsoft C++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。

  stdcall调用约定声明的语法为(以前文的那个函数为例):
  
  int __stdcall function(int a,int b)
  
  stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈 3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸

  以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处翻译成汇编语言将变成:

  push 2        第二个参数入栈
  push 1        第一个参数入栈
  call function    调用参数,注意此时自动把cs:eip入栈

  而对于函数自身,则可以翻译为:
  push ebp       保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
  mov ebp, esp    保存堆栈指针
  mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a
  add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b
  mov esp, ebp    恢复esp
  pop ebp
  ret 8

  而在编译时,这个函数的名字被翻译成_function@8

  注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。其中在函数开始处保留esp到ebp中,在函数结束恢复是编译器常用的方法。

  从函数调用看,2和1依次被push进堆栈,而在函数中又通过相对于ebp(即刚进函数时的堆栈指针)的偏移量存取参数。函数结束后,ret 8表示清理8个字节的堆栈,函数自己恢复了堆栈。

  
  cdecl调用约定
  cdecl调用约定又称为C调用约定,是C语言缺省的调用约定,它的定义语法是:

  int function (int a ,int b) //不加修饰就是C调用约定
  int __cdecl function(int a,int b)//明确指出C调用约定

  在写本文时,出乎我的意料,发现cdecl调用约定的参数压栈顺序是和stdcall是一样的,参数首先由右向左压入堆栈。所不同的是,函数本身不清理堆栈,调用者负责清理堆栈。由于这种变化,C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大特色。对于前面的function函数,使用cdecl后的汇编码变成:

  调用处
  push 1
  push 2
  call function
  add esp, 8     注意:这里调用者在恢复堆栈

  被调用函数_function处
  push ebp       保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
  mov ebp,esp     保存堆栈指针
  mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a
  add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b
  mov esp,ebp     恢复esp
  pop ebp
  ret         注意,这里没有修改堆栈

  MSDN中说,该修饰自动在函数名前加前导的下划线,因此函数名在符号表中被记录为_function,但是我在编译时似乎没有看到这种变化。

  由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来,就可以使用不定参数,例如对于CRT中的sprintf函数,定义为:
  int sprintf(char* buffer,const char* format,...)
  由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。

  fastcall
  fastcall调用约定和stdcall类似,它意味着:
  
  函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过从右向左的顺序压栈
  被调用函数清理堆栈
  函数名修改规则同stdcall
  其声明语法为:int fastcall function(int a, int b)

  thiscall
  thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理,thiscall意味着:

  参数从右向左入栈
  如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈为了说明这个调用约定,定义如下类和使用代码:

  class A
  {
  public:
    int function1(int a,int b);
    int function2(int a,...);
  };

  int A::function1 (int a,int b)
  {
    return a+b;
  }

  #include <stdarg.h>
  int A::function2(int a,...)
  {
    va_list ap;
    va_start(ap,a);
    int i;
    int result = 0;
    for(i = 0 ; i < a ; i ++)
    {
     result += va_arg(ap,int);
    }
    return result;
  }

  void callee()
  {
    A a;
    a.function1(1, 2);
    a.function2(3, 1, 2, 3);
  }

callee函数被翻译成汇编后就变成:
  //函数function1调用
  00401C1D  push    2
  00401C1F  push    1
  00401C21  lea     ecx,[ebp-8]
  00401C24  call    function1     注意,这里this没有被入栈

  //函数function2调用
  00401C29  push    3
  00401C2B  push    2
  00401C2D  push    1
  00401C2F  push    3
  00401C31  lea     eax, [ebp-8]    这里引入this指针
  00401C34  push    eax
  00401C35  call    function2
  00401C3A  add     esp, 14h
  
  可见,对于参数个数固定情况下,它类似于stdcall,不定时则类似cdecl

  naked call
  这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果。这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为:

  __declspec(naked) int add(int a,int b)
  {
    __asm mov eax,a
    __asm add eax,b
    __asm ret
  }

  注意,这个函数没有显式的return返回值,返回通过修改eax寄存器实现,而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成:

  mov eax,[ebp+8]
  add eax,[ebp+12]
  ret 8

  注意这个修饰是和__stdcall及cdecl结合使用的,前面是它和cdecl结合使用的代码,对于和stdcall结合的代码,则变成:

  __declspec(naked) int __stdcall function(int a,int b)
  {
    __asm mov eax,a
    __asm add eax,b
    __asm ret 8    //注意后面的8
  }

  至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。

  函数调用约定导致的常见问题
  如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:

  函数原型声明和函数体定义不一致
  DLL导入函数时声明了不同的函数约定
  以后者为例,假设我们在dll种声明了一种函数为:

  __declspec(dllexport) int func(int a,int b);//注意,这里没有stdcall,使用的是cdecl
  使用时代码为:

  typedef int (*WINAPI DLLFUNC)func(int a,int b);
  hLib = LoadLibrary(...);

  DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定
  result = func(1,2);//导致错误

  由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏
 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fly2k5/archive/2005/12/05/544112.aspx

posted @ 2011-06-28 00:48 黄超 阅读(335) | 评论 (0)编辑 收藏

Declare @Id Int 
Set @Id = 5; ---在此修改父节点 

With RootNodeCTE(Id,ParentId) 
As 

Select Id,ParentId From BOM Where ParentId In (@Id) 
Union All 
Select BOM.Id,BOM.ParentId From RootNodeCTE 
Inner Join BOM
On RootNodeCTE.Id = BOM.ParentId 


Select * From RootNodeCTE

需求在SQL Server 2000环境下的实现:

SQL code
create table AAA(ID INT,strNum1 varchar(8),strNum2 varchar(8),intTaxis int) insert into AAA select 1,'01' ,'001',1 insert into AAA select 2,'001','011',2 insert into AAA select 3,'001','010',1 insert into AAA select 6,'01' ,'002',2 insert into AAA select 4,'002','021',1 insert into AAA select 5,'002','022',2 go --创建用户定义函数 create function f_getChild(@strNum1 VARCHAR(10)) returns @t table(strNum1 VARCHAR(10),strNum2 VARCHAR(10),Level INT,Ord varchar(100)) as begin declare @i int set @i=1 insert into @t select strNum1,strNum2,@i,right('0'+strNum2,3) from AAA where strNum1 = @strNum1 while @@rowcount<>0 begin set @i=@i+1 insert into @t select a.strNum1,a.strNum2,@i,b.Ord+a.strNum2 from AAA a,@t b where a.strNum1=b.strNum2 and b.Level=@i-1 and not exists(select 1 from @t where strNum1=a.strNum1 and strNum2=a.strNum2) end return end go --执行查询 select * from dbo.f_getChild('01') order by Ord go --输出结果 /* strNum1 strNum2 Level Ord ---------- ---------- ----------- ----------- 01 001 1 001 001 010 2 001010 001 011 2 001011 01 002 1 002 002 021 2 002021 002 022 2 002022 */ --删除测试数据 drop function f_getChild drop table AAA go

posted @ 2011-06-28 00:35 黄超 阅读(459) | 评论 (0)编辑 收藏

继续上一篇文章,继续SQLServer索引调优实践。这次探讨一下索引覆盖 - SQL Server主要使用索引去查询你需要的数据,当索引包括所有的你请求查询的字段,SQL Server将不需要去在表中查询。这个概念称做“索引覆盖”。

  SQLServer2005的Non-clustered INDEX增加了一个“包含列(included column) ”选项。在 SQL Server 2005 中,可以通过将非键列添加到非聚集索引的叶级别来扩展非聚集索引的功能。通过包含非键列,可以创建覆盖更多查询的非聚集索引。当查询中的所有列都作为键列或非键列包含在索引中时,带有包含性非键列的索引可以显著提高查询性能。这样可以实现性能提升,因为查询优化器可以在索引中找到所有列值;不访问表或聚集索引数据,从而减少磁盘 I/O 操作。

  但应避免添加不必要的列。添加过多的索引列(键列或非键列)会对性能产生不良影响,应该合理使用。和Clustered INDEX,或者组合索引,结合使用,扩大索引覆盖,但不大可能所有列都有索引覆盖,磁盘开销和数据insert updat时索引的重新计算的时间开销是巨大的。总之,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案。

  继续实践,先建个实验表 Table1:

SQLServer索引调优实践(2) - 索引覆盖

  建两个索引:

  1. 主键ID是Clustered INDEX

  2. 非聚簇索引Non-Clustered INDEX建立在Age列上,包含列:Count。

CREATE NONCLUSTERED INDEX [cnt] ON [dbo].[table1] 
( 
    [Age] ASC 
) 
INCLUDE ( [Count]) 
ON [PRIMARY]

  我们的测试SQL语句是:从10万条记录中取出4条记录,两种写法

  1. SELECT * FROM table1 WHERE age < 100;

  2. SELECT count FROM table1 WHERE age < 100;

  看看运行效率如何:

  磁盘IO和时间:

SQLServer索引调优实践(2) - 索引覆盖

  实际执行计划:

SQLServer索引调优实践(2) - 索引覆盖

  性能居然相差20多倍。为什么?

  原来第二句Select Count在索引覆盖范围内,因为查询优化器可以在索引中找到所有列值;不访问表或聚集索引数据,从而减少磁盘 I/O 操作。而第一句Select * 选择了所有字段,其中有一个字段Name不在索引覆盖范围内(既不在聚簇索引列,也不在非聚簇索引覆盖列内),而一次查询对于一个数据表只能使用一个索引,多个索引是无法使用叠加的。查询分析优化器必须进行选择, 上述执行计划就是优化的结果,依然比第二个index seek慢了20倍。

  然后我又把sql改了一下,变成从10万条记录中取得大部分数据( 返回99900条),小于号改成大于号:

  1. SELECT * FROM table1 WHERE age > 100;

  2. SELECT count FROM table1 WHERE age > 100;

  看看结果:

  磁盘IO和时间:

SQLServer索引调优实践(2) - 索引覆盖

  实际执行计划:

SQLServer索引调优实践(2) - 索引覆盖

  依然是第二句索引覆盖的快,这是毋庸置疑的。但第一句执行计划有所不同,SQLServer查询分析优化器选择了不同的策略,改为聚簇索引扫描。上面说过了,一次查询对于一个数据表只能使用一个索引,多个索引是无法使用叠加的。查询分析优化器必须进行选择, 上述执行计划就是优化的结果。但为何SQLServer统计了结果集就非常智能的选择了这个方案,恐怕要等高手来解释了。

  看到这儿,恐怕喜欢用Select*的同学也要节制一下使用了,有时候SQLServer中Select*代价是很高的。当然类似这种SQL是没有问题的,(where exists (select * from ...)),因为SQLServer查询分析优化器会聪明的知道此Select*非彼Select*。

  数据库是一个很复杂的系统,即使你不是数据库专家,是应用开发人员,知道一点SQLServer内部更多的东西会有好处,而合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来, 才能产生最佳的优化方案。

posted @ 2011-06-28 00:25 黄超 阅读(224) | 评论 (0)编辑 收藏

索引的重要性

  数据库性能优化中索引绝对是一个重量级的因素,可以说,索引使用不当,其它优化措施将毫无意义。

  聚簇索引(Clustered Index)和非聚簇索引 (Non- Clustered Index) 

  最通俗的解释是:聚簇索引的顺序就是数据的物理存储顺序,而对非聚簇索引的索引顺序与数据物理排列顺序无关。举例来说,你翻到新华字典的汉字“爬”那一页就是P开头的部分,这就是物理存储顺序(聚簇索引);而不用你到目录,找到汉字“爬”所在的页码,然后根据页码找到这个字(非聚簇索引)。

  下表给出了何时使用聚簇索引与非聚簇索引:

  动作   使用聚簇索引   使用非聚簇索引
  列经常被分组排序   应   应
  返回某范围内的数据   应   不应
  一个或极少不同值   不应   不应
  小数目的不同值   应   不应
  大数目的不同值   不应   应
  频繁更新的列   不应   应
  外键列   应   应
  主键列   应   应
  频繁修改索引列   不应   应

  聚簇索引的唯一性

  正式聚簇索引的顺序就是数据的物理存储顺序,所以一个表最多只能有一个聚簇索引,因为物理存储只能有一个顺序。正因为一个表最多只能有一个聚簇索引,所以它显得更为珍贵,一个表设置什么为聚簇索引对性能很关键。

  初学者最大的误区:把主键自动设为聚簇索引

  因为这是SQLServer的默认主键行为,你设置了主键,它就把主键设为聚簇索引,而一个表最多只能有一个聚簇索引,所以很多人就把其他索引设置为非聚簇索引。这个是最大的误区。甚至有的主键又是无意义的自动增量字段,那样的话Clustered index对效率的帮助,完全被浪费了。

  刚才说到了,聚簇索引性能最好而且具有唯一性,所以非常珍贵,必须慎重设置。一般要根据这个表最常用的SQL查询方式来进行选择,某个字段作为聚簇索引,或组合聚簇索引,这个要看实际情况。

  事实上,建表的时候,先需要设置主键,然后添加我们想要的聚簇索引,最后设置主键,SQLServer就会自动把主键设置为非聚簇索引(会自动根据情况选择)。如果你已经设置了主键为聚簇索引,必须先删除主键,然后添加我们想要的聚簇索引,最后恢复设置主键即可。

  记住我们的最终目的就是在相同结果集情况下,尽可能减少逻辑IO。

  我们先从一个实际使用的简单例子开始。

  一个简单的表:

CREATE TABLE [dbo].[Table1](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [Data1] [int] NOT NULL DEFAULT ((0)),
  [Data2] [int] NOT NULL DEFAULT ((0)),
  [Data3] [int] NOT NULL DEFAULT ((0)),
  [Name1] [nvarchar](50) NOT NULL DEFAULT (''),
  [Name2] [nvarchar](50) NOT NULL DEFAULT (''),
  [Name3] [nvarchar](50) DEFAULT (''),
  [DTAt] [datetime] NOT NULL DEFAULT (getdate())


 

SQLServer索引调优实践

  来点测试数据(10w条):

declare @i int
set @i = 1 
while @i < 100000 
begin 
insert into Table1 ([Data1] ,[Data2] ,[Data3] ,[Name1],[Name2] ,[Name3]) 
values(@i, 2* @i,3*@i, CAST(@i AS NVARCHAR(50)), CAST(2*@i AS NVARCHAR(50)), CAST(3*@i AS NVARCHAR(50)))
set @i = @i + 1
end
update table1 set dtat= DateAdd (s, data1, dtat)

  打开查询分析器的IO统计和时间统计:

SET STATISTICS IO ON;
SET STATISTICS TIME ON;


 

  显示实际的“执行计划”:

SQLServer索引调优实践

  我们最常用的SQL查询是这样的:

SELECT * FROM Table1 WHERE Data1 = 2 ORDER BY DTAt DESC;


 

  先在Table1设主键ID,系统自动为该主键建立了聚簇索引。

  然后执行该语句,结果是:

Table 'Table1'. Scan count 1, logical reads 911, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 7 ms.


 

SQLServer索引调优实践

  然后我们在Data1和DTat字段分别建立非聚簇索引:

CREATE NONCLUSTERED INDEX [N_Data1] ON [dbo].[Table1] 
(
[Data1] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [N_DTat] ON [dbo].[Table1] 
(
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]


 

  再次执行该语句,结果是:

Table 'Table1'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 39 ms.


 

SQLServer索引调优实践

  图片看不清楚?请点击这里查看原图(大图)。

  可以看到设立了索引反而没有任何性能的提升而且消耗的时间更多了,继续调整。

  然后我们删除所有非聚簇索引,并删除主键,这样所有索引都删除了。建立组合索引Data1和DTAt,最后加上主键:

CREATE CLUSTERED INDEX [C_Data1_DTat] ON [dbo].[Table1] 
(
[Data1] ASC,
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]


 

  再次执行语句:

Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.

SQLServer索引调优实践

  可以看到只有聚簇索引seek了,消除了index scan和nested loop,而且执行时间也只有1ms,达到了最初优化的目的。

  组合索引小结

  小结以上的调优实践,要注意聚簇索引的选择。首先我们要找到我们最多用到的SQL查询,像本例就是那句类似的组合条件查询的情况,这种情况最好使用组合聚簇索引,而且最多用到的字段要放在组合聚簇索引的前面,否则的话就索引就不会有好的效果,看下例:

SQLServer索引调优实践

  图片看不清楚?请点击这里查看原图(大图)。

  查询条件落在组合索引的第二个字段上,引起了index scan,效果很不好,执行时间是:

Table 'Table1'. Scan count 1, logical reads 238, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 22 ms.

  而如果仅查询条件是第一个字段也没有问题,因为组合索引最左前缀原则,实践如下:

SQLServer索引调优实践

Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.

  从中可以看出,最多用到的字段要放在组合聚簇索引的前面。

  Index seek 为什么比 Index scan好?

  索引扫描也就是遍历B树,而seek是B树查找直接定位。

  Index scan多半是出现在索引列在表达式中。数据库引擎无法直接确定你要的列的值,所以只能扫描整个整个索引进行计算。index seek就要好很多.数据库引擎只需要扫描几个分支节点就可以定位到你要的记录。回过来,如果聚集索引的叶子节点就是记录,那么Clustered Index Scan就基本等同于full table scan。

  一些优化原则

  1、缺省情况下建立的索引是非聚簇索引,但有时它并不是最佳的。在非群集索引下,数据在物理上随机存放在数据页上。合理的索引设计要建立在对各种查询的分析和预测上。一般来说:
a.有大量重复值、且经常有范围查询( > ,< ,> =,< =)和order by、group by发生的列,可考
虑建立群集索引;
b.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
c.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。索引虽有助于提高性能但不是索引越多越好,恰好相反过多的索引会导致系统低效。用户在表中每加进一个索引,维护索引集合就要做相应的更新工作。
2、ORDER BY和GROPU BY使用ORDER BY和GROUP BY短语,任何一种索引都有助于SELECT的性能提高。

  3、多表操作在被实际执行前,查询优化器会根据连接条件,列出几组可能的连接方案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的表;内外表的选择可由公式:外层表中的匹配行数*内层表中每一次查找的次数确定,乘积最小为最佳方案。
4、任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
5、IN、OR子句常会使用工作表,使索引失效。如果不产生大量重复值,可以考虑把子句拆开。拆开的子句中应该包含索引。

  Sql的优化原则2:
1、只要能满足你的需求,应尽可能使用更小的数据类型:例如使用MEDIUMINT代替INT
2、尽量把所有的列设置为NOT NULL,如果你要保存NULL,手动去设置它,而不是把它设为默认值。
3、尽量少用VARCHAR、TEXT、BLOB类型
4、如果你的数据只有你所知的少量的几个。最好使用ENUM类型 
 

  有关Join的一些原则

  SQL Server 有三种类型的JOIN操作:

  Nested loops joins

  Merge joins

  Hash joins

  如果Join的输入很小,例如小于10行,然后其他的Join输入很大并且索引在其列上,则Nested loops joins是最快的。(原因参考Understanding Nested Loops Joins)

  如果两个Join输入都不小,但在索引列上排序(例如是在扫描排序的索引后获得的 scanning sorted indexes),则Merge joins是最快的。(原因参考Understanding Merge Joins)

  Hash joins可以有效的处理大量的、没有排序的、没有索引的输入。尤其对复杂查询的中间结果处理很有效。(更多参考Understanding Hash Joins)

  如何分析SQL语句

  微软MSDN给出了答案:http://msdn.microsoft.com/en-us/library/ms191227.aspx

  找出数据库中性能最差的SQL

  优化哪个表?从何入手?首先需要定位性能瓶颈,找到运行最慢的SQL。可以采用如下步骤:

  1. 运行 dbcc  freeProcCache  清除缓存

  2. 运行你的程序,或者你的SQL或存储过程,操作数据库

  3. 完了以后运行以下SQL找到运行最慢的SQL:

SELECT  DB_ID(DB.dbid) '数据库名' 
      , OBJECT_ID(db.objectid) '对象' 
      , QS.creation_time '编译计划的时间' 
      , QS.last_execution_time '上次执行计划的时间' 
      , QS.execution_count '执行的次数' 
      , QS.total_elapsed_time / 1000 '占用的总时间(秒)' 
      , QS.total_physical_reads '物理读取总次数' 
      , QS.total_worker_time / 1000 'CPU 时间总量(秒)' 
      , QS.total_logical_writes '逻辑写入总次数' 
      , QS.total_logical_reads N'逻辑读取总次数' 
      , QS.total_elapsed_time / 1000 N'总花费时间(秒)' 
      , SUBSTRING(ST.text, ( QS.statement_start_offset / 2 ) + 1, 
                  ( ( CASE statement_end_offset 
                        WHEN -1 THEN DATALENGTH(st.text) 
                        ELSE QS.statement_end_offset 
                      END - QS.statement_start_offset ) / 2 ) + 1) AS '执行语句' 
FROM    sys.dm_exec_query_stats AS QS CROSS APPLY 
        sys.dm_exec_sql_text(QS.sql_handle) AS ST INNER JOIN 
        ( SELECT    * 
          FROM      sys.dm_exec_cached_plans cp CROSS APPLY 
                    sys.dm_exec_query_plan(cp.plan_handle) 
        ) DB 
            ON QS.plan_handle = DB.plan_handle 
where   SUBSTRING(st.text, ( qs.statement_start_offset / 2 ) + 1, 
                  ( ( CASE statement_end_offset 
                        WHEN -1 THEN DATALENGTH(st.text) 
                        ELSE qs.statement_end_offset 
                      END - qs.statement_start_offset ) / 2 ) + 1) not like '%fetch%' 
                      ORDER BY QS.total_elapsed_time / 1000 DESC 

  使用SQLServer Profiler找出数据库中性能最差的SQL

  首先打开SQLServer Profiler:

SQLServer索引调优实践

  然后点击工具栏“New Trace”,使用默认的模板,点击RUN。

  也许会有报错:"only TrueType fonts are supported. There id not a TrueType font"。不用怕,点击Tools菜单->Options,重新选择一个字体例如Vendana 即可。(这个是微软的一个bug)

  运行起来以后,SQLServer Profiler会监控数据库的活动,所以最好在你需要监控的数据库上多做些操作。等觉得差不多了,点击停止。然后保存trace结果到文件或者table。

  这里保存到Table:在菜单“File”-“Save as ”-“Trace table”,例如输入一个master数据库的新的table名:profileTrace,保存即可。

  找到最耗时的SQL:

use master
select * from profiletrace order by duration desc;


 

  找到了性能瓶颈,接下来就可以有针对性的一个个进行调优了。

  对使用SQLServer Profiler的更多信息可以参考:

  http://www.codeproject.com/KB/database/DiagnoseProblemsSQLServer.aspx

  使用SQLServer Database Engine Tuning Advisor数据库引擎优化顾问

  使用上述的SQLServer Profiler得到了trace还有一个好处就是可以用到这个优化顾问。用它可以偷点懒,得到SQLServer给您的优化顾问,例如这个表需要加个索引什么的…

  首先打开数据库引擎优化顾问:

SQLServer索引调优实践

  然后打开刚才profiler的结果(我们存到了master数据库的profileTrace表):

SQLServer索引调优实践

  图片看不清楚?请点击这里查看原图(大图)。

  点击“start analysis”,运行完成后查看优化建议(图中最后是建议建立的索引,性能提升72%)

SQLServer索引调优实践

  图片看不清楚?请点击这里查看原图(大图)。

  这个方法可以偷点懒,得到SQLServer给您的优化顾问。

posted @ 2011-06-28 00:24 黄超 阅读(778) | 评论 (0)编辑 收藏


2011年5月23日

A named pipe client uses the CreateFile function to open a handle to a named pipe. If the pipe exists but all of its instances are busy, CreateFile returns zero and the GetLastError function returns ERROR_PIPE_BUSY. When this happens, the named pipe client uses the WaitNamedPipe function to wait for an instance of the named pipe to become available.

The CreateFile function fails if the access specified is incompatible with the access specified (duplex, outbound, or inbound) when the server created the pipe. For a duplex pipe, the client can specify read, write, or read/write access; for an outbound pipe (write-only server), the client must specify read-only access; and for an inbound pipe (read-only server), the client must specify write-only access.

The handle returned by CreateFile defaults to byte-read mode, blocking-wait mode, overlapped mode disabled, and write-through mode disabled. The pipe client can use CreateFile to enable overlapped mode by specifying FILE_FLAG_OVERLAPPED or to enable write-through mode by specifying FILE_FLAG_WRITE_THROUGH. The client can use the SetNamedPipeHandleState function to enable nonblocking mode by specifying PIPE_NOWAIT or to enable message-read mode by specifying PIPE_READMODE_MESSAGE.

The following example shows a pipe client that opens a named pipe, sets the pipe handle to message-read mode, uses the WriteFile function to send a request to the server, and uses the ReadFile function to read the server's reply. This pipe client can be used with any of the message-type servers listed at the bottom of this topic. With a byte-type server, however, this pipe client fails when it calls SetNamedPipeHandleState to change to message-read mode. Because the client is reading from the pipe in message-read mode, it is possible for the ReadFile operation to return zero after reading a partial message. This happens when the message is larger than the read buffer. In this situation, GetLastError returns ERROR_MORE_DATA, and the client can read the remainder of the message using additional calls to ReadFile.

This pipe client can be used with any of the pipe servers listed under See Also.

 

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
LPTSTR lpvMessage=TEXT("Default message from client");
TCHAR chBuf[BUFSIZE];
BOOL fSuccess;
DWORD cbRead, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if( argc > 1 )
lpvMessage = argv[1];
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
hPipe = CreateFile(
lpszPipename,   // pipe name
GENERIC_READ |  // read and write access
GENERIC_WRITE,
0,              // no sharing
NULL,           // default security attributes
OPEN_EXISTING,  // opens existing pipe
0,              // default attributes
NULL);          // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
printf("Could not open pipe");
return 0;
}
// All pipe instances are busy, so wait for 20 seconds.
if (!WaitNamedPipe(lpszPipename, 20000))
{
printf("Could not open pipe");
return 0;
}
}
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe,    // pipe handle
&dwMode,  // new pipe mode
NULL,     // don't set maximum bytes
NULL);    // don't set maximum time
if (!fSuccess)
{
printf("SetNamedPipeHandleState failed");
return 0;
}
// Send a message to the pipe server.
fSuccess = WriteFile(
hPipe,                  // pipe handle
lpvMessage,             // message
(lstrlen(lpvMessage)+1)*sizeof(TCHAR), // message length
&cbWritten,             // bytes written
NULL);                  // not overlapped
if (!fSuccess)
{
printf("WriteFile failed");
return 0;
}
do
{
// Read from the pipe.
fSuccess = ReadFile(
hPipe,    // pipe handle
chBuf,    // buffer to receive reply
BUFSIZE*sizeof(TCHAR),  // size of buffer
&cbRead,  // number of bytes read
NULL);    // not overlapped
if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
break;
_tprintf( TEXT("%s\n"), chBuf );
} while (!fSuccess);  // repeat loop if ERROR_MORE_DATA
getch();
CloseHandle(hPipe);
return 0;
}


posted @ 2011-05-23 14:03 黄超 阅读(1387) | 评论 (0)编辑 收藏

The following example is a single-threaded pipe server that creates a message-type pipe and uses overlapped operations. It uses the extended functions ReadFileEx and WriteFileEx to perform overlapped I/O using a completion routine, which is queued for execution when the operation is finished. The pipe server uses the WaitForSingleObjectEx function, which performs an alertable wait operation that returns when a completion routine is ready to execute. The wait function also returns when an event object is signaled, which in this example indicates that the overlapped ConnectNamedPipe operation has finished (a new client has connected). This pipe server can be used with the pipe client described in Named Pipe Client.

Initially, the pipe server creates a single instance of the pipe and starts an overlapped ConnectNamedPipe operation. When a client connects, the server allocates a structure to provide storage for that pipe instance and then calls the ReadFileEx function to start a sequence of I/O operations to handle communications with the client. Each operation specifies a completion routine that performs the next operation in the sequence. The sequence terminates when the client is disconnected and the pipe instance closed. After starting the sequence of operations for the new client, the server creates another pipe instance and waits for the next client to connect.

The parameters of the ReadFileEx and WriteFileEx functions specify a completion routine and a pointer to an OVERLAPPED structure. This pointer is passed to the completion routine in its lpOverLap parameter. Because the OVERLAPPED structure points to the first member in the structure allocated for each pipe instance, the completion routine can use its lpOverLap parameter to access the structure for the pipe instance.

 

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096
typedef struct
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
TCHAR chRequest[BUFSIZE];
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;
VOID DisconnectAndClose(LPPIPEINST);
BOOL CreateAndConnectInstance(LPOVERLAPPED);
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetAnswerToRequest(LPPIPEINST);
VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
HANDLE hPipe;
int _tmain(VOID)
{
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbRet;
BOOL fSuccess, fPendingIO;
// Create one event object for the connect operation.
hConnectEvent = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object
if (hConnectEvent == NULL)
{
printf("CreateEvent failed with %d.\n", GetLastError());
return 0;
}
oConnect.hEvent = hConnectEvent;
// Call a subroutine to create one instance, and wait for
// the client to connect.
fPendingIO = CreateAndConnectInstance(&oConnect);
while (1)
{
// Wait for a client to connect, or for a read or write
// operation to be completed, which causes a completion
// routine to be queued for execution.
dwWait = WaitForSingleObjectEx(
hConnectEvent,  // event object to wait for
INFINITE,       // waits indefinitely
TRUE);          // alertable wait enabled
switch (dwWait)
{
// The wait conditions are satisfied by a completed connect
// operation.
case 0:
// If an operation is pending, get the result of the
// connect operation.
if (fPendingIO)
{
fSuccess = GetOverlappedResult(
hPipe,     // pipe handle
&oConnect, // OVERLAPPED structure
&cbRet,    // bytes transferred
FALSE);    // does not wait
if (!fSuccess)
{
printf("ConnectNamedPipe (%d)\n", GetLastError());
return 0;
}
}
// Allocate storage for this instance.
lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
{
printf("GlobalAlloc failed (%d)\n", GetLastError());
return 0;
}
lpPipeInst->hPipeInst = hPipe;
// Start the read operation for this client.
// Note that this same routine is later used as a
// completion routine after a write operation.
lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst);
// Create new pipe instance for the next client.
fPendingIO = CreateAndConnectInstance(
&oConnect);
break;
// The wait is satisfied by a completed read or write
// operation. This allows the system to execute the
// completion routine.
case WAIT_IO_COMPLETION:
break;
// An error occurred in the wait function.
default:
{
printf("WaitForSingleObjectEx (%d)\n", GetLastError());
return 0;
}
}
}
return 0;
}
// CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as a completion routine after writing to
// the pipe, or when a new client has connected to a pipe instance.
// It starts another read operation.
VOID WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The write operation has finished, so read the next request (if
// there is no error).
if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chRequest,
BUFSIZE*sizeof(TCHAR),
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
// Disconnect if an error occurred.
if (! fRead)
DisconnectAndClose(lpPipeInst);
}
// CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED)
// This routine is called as an I/O completion routine after reading
// a request from the client. It gets data and writes it to the pipe.
VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;
// lpOverlap points to storage for this instance.
lpPipeInst = (LPPIPEINST) lpOverLap;
// The read operation has finished, so write a response (if no
// error occurred).
if ((dwErr == 0) && (cbBytesRead != 0))
{
GetAnswerToRequest(lpPipeInst);
fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chReply,
lpPipeInst->cbToWrite,
(LPOVERLAPPED) lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}
// Disconnect if an error occurred.
if (! fWrite)
DisconnectAndClose(lpPipeInst);
}
// DisconnectAndClose(LPPIPEINST)
// This routine is called when an error occurs or the client closes
// its handle to the pipe.
VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
// Disconnect the pipe instance.
if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) )
{
printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
}
// Close the handle to the pipe instance.
CloseHandle(lpPipeInst->hPipeInst);
// Release the storage for the pipe instance.
if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}
// CreateAndConnectInstance(LPOVERLAPPED)
// This function creates a pipe instance and connects to the client.
// It returns TRUE if the connect operation is pending, and FALSE if
// the connection has been completed.
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
hPipe = CreateNamedPipe(
lpszPipename,             // pipe name
PIPE_ACCESS_DUPLEX |      // read/write access
FILE_FLAG_OVERLAPPED,     // overlapped mode
PIPE_TYPE_MESSAGE |       // message-type pipe
PIPE_READMODE_MESSAGE |   // message read mode
PIPE_WAIT,                // blocking mode
PIPE_UNLIMITED_INSTANCES, // unlimited instances
BUFSIZE*sizeof(TCHAR),    // output buffer size
BUFSIZE*sizeof(TCHAR),    // input buffer size
PIPE_TIMEOUT,             // client time-out
NULL);                    // default security attributes
if (hPipe == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with %d.\n", GetLastError());
return 0;
}
// Call a subroutine to connect to the new client.
return ConnectToNewClient(hPipe, lpoOverlap);
}
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
BOOL fConnected, fPendingIO = FALSE;
// Start an overlapped connection for this pipe instance.
fConnected = ConnectNamedPipe(hPipe, lpo);
// Overlapped ConnectNamedPipe should return zero.
if (fConnected)
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
switch (GetLastError())
{
// The overlapped connection in progress.
case ERROR_IO_PENDING:
fPendingIO = TRUE;
break;
// Client is already connected, so signal an event.
case ERROR_PIPE_CONNECTED:
if (SetEvent(lpo->hEvent))
break;
// If an error occurs during the connect operation...
default:
{
printf("ConnectNamedPipe failed with %d.\n", GetLastError());
return 0;
}
}
return fPendingIO;
}
VOID GetAnswerToRequest(LPPIPEINST pipe)
{
_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
StringCchCopy( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}

See Also

Named Pipe Client


Send comments about this topic to Microsoft

Build date: 8/10/2007

posted @ 2011-05-23 14:01 黄超 阅读(394) | 评论 (0)编辑 收藏


2011年5月16日

一个完整windows 服务源代码下载地址

开发环境 vs 2010 (但方法适用于VS2005 及 VS 2008)c++ atl 版

在编译出exe以后使用release下的批处理安装卸载服务
在windows里的服务控制面板进行服务的启动和停止操作
本程序是完整的windows 服务开发框架,直接在里面添加业务代码就可以
产生一个标准的服务程序

带有一个日志文件

http://b.qjwm.com/down.aspx?down=ok&filepath=dante300%2fwindows+service%2fwindow+service.rar

 知识性说明

原文出处:http://topic.csdn.net/u/20070126/09/c1d1279a-bbac-45b9-8e88-835b8956d97e.html

1,调试:怎样才能在VS2005中调试。 
看CatlServiceModuleT中的WinMain,检查输入参数有效就调用START。START先检测服务有没有注册过,没有注册的就直接返回退出。所以一定要先注册。检测到有注册就查看是否注册为LocalService(也就是   -Service),如果为LocalService就把_ServiceMain连接到SCM(服务管理器),但在调试模式下不能连接成功,或者必须由SCM来启动,要不它认为已启动。所以不能注册为服务,只能注册为RegSever(-RegSever)。调试完后,再注册为服务。如果只有在服务里面才出错,可用CatlServiceModuleT带的LogEvent   把调试信息写到windows事件里面。MSDN中讲可以服务进程连到VS2005我没试过,因为全在RegSever模式下解决了。 
  
2,更改服务的启动模式。 
没找到更改服务启动模式的方法。查看代码发现在CatlServiceModuleT的Install中写死的。 
                  SC_HANDLE   hService   =   ::CreateService( 
                            hSCM,   m_szServiceName,   m_szServiceName, 
                            SERVICE_ALL_ACCESS,   SERVICE_WIN32_OWN_PROCESS, 
                            SERVICE_DEMAND_START,   SERVICE_ERROR_NORMAL, 
                  szFilePath,   NULL,   NULL,   _T( "RPCSS\0 "),   NULL,   NULL); 
  
SERVICE_DEMAND_START表示手动在SCM启动。有个方法,在重写的RegisterAppId用ChangeServiceConfig修改,但看一下ChangeServiceConfig的参数,好多呀,太麻烦。加上我觉得写一个服务就应该自动启动,什么时候处理任务是服务的事,而且不想用了可以在SCM里禁用。所以我做了一个不道德的地方――把CatlServiceModuleT源程序改了。把SERVICE_DEMAND_START改为SERVICE_AUTO_START。 
                  SC_HANDLE   hService   =   ::CreateService( 
                            hSCM,   m_szServiceName,   m_szServiceName, 
                            SERVICE_ALL_ACCESS,   SERVICE_WIN32_OWN_PROCESS, 
                            SERVICE_AUTO_START,   SERVICE_ERROR_NORMAL, 
                  szFilePath,   NULL,   NULL,   _T( "RPCSS\0 "),   NULL,   NULL); 
谁叫他不道德没流一个好的方法。当然你可以用ChangeServiceConfig,自己查一下。 
  
              3,暂停和继续, 
我想用暂停和继续来实现配置的更新,因为不想改配置的时候,要停止服务,再启动,那样服务会断。还有就是用命名管道或全局消息让配置工具和服务通讯,但太麻烦,我只是想重新应有配置。 
              第一个问题,在SMC中暂停和继续的菜单是灰的。找了很久,找到::SetServiceStatus(m_hServiceStatus,   &m_status)   ,还好m_status是Public   ,用m_status.   dwControlsAccepted=m_status.dwControlsAccepted|   SERVICE_ACCEPT_PAUSE_CONTINUE;搞定。 
              另一个问题,重写了OnPause但更本没进去。用LogEvent调试,发现CatlServiceModuleT的_Handler都没进去(LocalService就把_ServiceMain连接到SCM,_ServiceMain调用ServiceMain,ServiceMain中注册Handler为SCM处理函数,处理STOP、PAUSE等),_Handler   中是这样调用的((T*)_pAtlModule)-> Handler(dwOpcode);   不知到_pAtlModule是指向谁的实例。我重写了Handler,就可以进来了。在自己的Handler中调用PAUSE,OnContinue后,就调用父类的Handler。 
  
  
终于完成了,代码如下,工作线程没写出来。 
  
class   CNVSStoreServerModule   :   public   CAtlServiceModuleT <   CNVSStoreServerModule,   IDS_SERVICENAME   > 

private: 
          CWorkThread   *   pWork;   //工作类,没写出来。 
public   : 
          DECLARE_LIBID(LIBID_NVSStoreServerLib) 
          DECLARE_REGISTRY_APPID_RESOURCEID(IDR_NVSSTORESERVER,   "{CF40AF29-C742-4D52-906C-5915A611F2D6} ") 
          HRESULT   InitializeSecurity()   throw() 
          { 
                  return   S_OK; 
          } 
          void   OnPauze()   throw() 
          { 
                  SetEvent(pWork-> mServerStopEvent); 
                  SetServiceStatus(SERVICE_PAUSED); 
                  __super::OnPause(); 
          } 
          void   OnStop()   throw() 
          { 
                  SetEvent(pWork-> mServerStopEvent); 
                  WaitForSingleObject(pWork-> mCanStopEvent,INFINITE); 
                  __super::OnStop(); 
          } 
          void   Handler(DWORD   dwOpcode)   throw() 
          { 
                  switch   (dwOpcode) 
                  { 
                  case   SERVICE_CONTROL_PAUSE: 
                            OnPauze(); 
                            break; 
                  case   SERVICE_CONTROL_CONTINUE: 
                            OnContinue(); 
                            break; 
                  } 
                  __super::Handler(dwOpcode); 
          } 
          CServiceStatus   GetServiceStatus()   throw() 
          { 
                  return   this-> m_ServiceStatus; 
          } 
          void   OnContinue(   )   throw(   ) 
          { 
                  SetServiceStatus(SERVICE_RUNNING); 
                  __super::OnContinue(); 
          } 
          HRESULT   PreMessageLoop(int   nShowCmd)   throw() 
          { 
                  m_status.dwControlsAccepted   =m_status.dwControlsAccepted   |   SERVICE_ACCEPT_PAUSE_CONTINUE; 
                  HRESULT   hr   =   __super::PreMessageLoop(nShowCmd); 
                  if   (hr   ==   S_FALSE)   hr   =   S_OK;   //要这句才能走下去 
                  pWork=new   CWorkThread(); 
                  return   hr; 
          } 
          HRESULT   RegisterAppId(bool   bService   =   false)   throw() 
          {                       
                  HRESULT   hr   =   S_OK; 
                  BOOL   res   =   __super::RegisterAppId(bService);   
                  if   (bService) 
                  { 
                            if   (IsInstalled()) 
                            {                 
                                      SC_HANDLE   hSCM   =   ::OpenSCManagerW(NULL,   NULL,   SERVICE_CHANGE_CONFIG); 
                                      SC_HANDLE   hService   =   NULL; 
                                      if   (hSCM   ==   NULL) 
                                              hr   =   AtlHresultFromLastError(); 
                                      else 
                                      { 
                                              hService   =   ::OpenServiceW(hSCM,   m_szServiceName,   SERVICE_CHANGE_CONFIG); 
                                              if   (hService   !=   NULL) 
                                              { 
                                                        const   int   m_szServiceNameLen   =   4096; 
                                                        const   int   m_szServiceDescriptionLen   =   2000; 
                                                        WCHAR   m_szServiceDescription[m_szServiceDescriptionLen]=L "你的服务描述 "; 
                                                        SERVICE_DESCRIPTION   sdBuf   =   {m_szServiceDescription}; 
                                                        res   =   ChangeServiceConfig2W(hService,   SERVICE_CONFIG_DESCRIPTION,   &sdBuf); 
                                                        ::CloseServiceHandle(hService); 
                                              } 
                                              else 
                                                        hr   =   AtlHresultFromLastError(); 
                                              ::CloseServiceHandle(hSCM); 
                                      } 
  
                            } 
                  } 
                  return   hr; 
          } 
}; 
  
CNVSStoreServerModule   _AtlModule; 
  
extern   "C "   int   WINAPI   _tWinMain(HINSTANCE   /*hInstance*/,   HINSTANCE   /*hPrevInstance*/,   
                                                                          LPTSTR   /*lpCmdLine*/,   int   nShowCmd) 

          return   _AtlModule.WinMain(nShowCmd); 
}

posted @ 2011-05-16 09:00 黄超 阅读(805) | 评论 (0)编辑 收藏


2011年3月17日

#
# Apache服务器主配置文件. 包括服务器指令的目录设置.
# 详见 URL:http://www.apache.org/docs/> 
#
# 请在理解用途的基础上阅读各指令。
#
# 再读取此文档后,服务器将继续搜索运行
# E:/Program Files/Apache Group/Apache/conf/srm.conf 
# E:/Program Files/Apache Group/Apache/conf/access.conf
# 除非用ResourceConfig或AccessConfig覆盖这儿的标识
#
# 配置标识由三个基本部分组成:
# 1. 作为一个整体来控制Apache服务器进程的标识 (the 'global environment').
# 2. 用于定义主(默认)服务器参数的标识。
# 响应虚拟主机不能处理的请求。
# 同时也提供所有虚拟主机的设置值。
# 3. 虚拟主机的设置。在一个Apache服务器进程中配置不同的IP地址和主机名。
#
# 配置和日志文件名:指定服务器控制文件命名时,
# 以 "/" (或 "drive:/" for Win32)开始,服务器将使用这些绝对路径。
# 如果文件名不是以"/"开始的,预先考虑服务器根目录--
# 因此 "logs/foo.log",如果服务器根目录是"/usr/local/apache",
# 服务器将解释为 "/usr/local/apache/logs/foo.log".
#
# 注: 指定的文件名需要用"/"代替"\"。
# (例, "c:/apache" 代替 "c:\apache").
# 如果省略了驱动器名,默认使用Apache.exe所在的驱动器盘符
# 建议指定盘符,以免混乱。
#
### 部分 1: 全局环境
#
# 本部分的表示将影响所有Apache的操作
# 例如,所能处理的并发请求数或配置文件地址
#
#
# ServerType 可取值 inetd 或 standalone. Inetd 只适用于Unix平台
#
ServerType standalone
#
# ServerRoot: 目录树的根结点。服务器配置、出错信息、日志文件都保存在根目录下。
#
# 不要再目录末尾加"/"
#
ServerRoot "C:/Program Files/Apache Group/Apache"
#
# PidFile: 服务器用于记录启动时进程ID的文件。
#
PidFile logs/httpd.pid
#
# ScoreBoardFile: 用于保存内部服务器进程信息的文件。
# 并非必须。 但是如果指定了(此文件当运行Apache时生成)
# 那么必须确保没有两个Apache进程共享同一个scoreboard文件。
#
ScoreBoardFile logs/apache_runtime_status
#
# 在标准配置下,服务器将顺序读取 httpd.conf(此文件可通过命令行中-f参数指定),
# srm.conf 和 access.conf。 
# 目前后两个文件是空的。为了简单起见,建议将所有的标识放在一个文件中。 
# 以下两条注释的标识,是默认设置。
# 要让服务器忽略这些文件可以用 "/dev/null" (for Unix) 
# 或"nul" (for Win32) 作为参数。
#
#ResourceConfig conf/srm.conf
#AccessConfig conf/access.conf
#
# Timeout: 接受和发送timeout的时间
#
Timeout 300
#
# KeepAlive: 是否允许保持连接(每个连接有多个请求)
# "Off" -无效
#
KeepAlive On
#
# MaxKeepAliveRequests: 每个连接的最大请求数。
# 设置为0表示无限制
# 建议设置较高的值,以获得最好的性能。
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: 同一连接同一客户端两个请求之间的等待时间。
#
KeepAliveTimeout 15
#
# 在Win32下,Apache每次产生一个子进程来处理请求。
# 如果这个进程死了,会自动产生另一个子进程。
# 所有的进入请求在子进程中多线程处理。
# 以下两个标识控制进程的运行
#
#
# MaxRequestsPerChild: 每个子进程死亡之前最大请求数
# 如果超过这个请求数,子程序会自动退出,避免延期使用导致内存溢出或其他问题。
# 大部分系统,并不需要此设置,
# 但是部分,象Solaris,确实值得注意。
# 对Win32, 可设置为0 (无限制)
# 除非有另外的考虑。
#
# 注: 此值不包括在每个连接初始化请求后,"keptalive"请求
# 例如, 如果一个子进程处理一个初始化请求和10个后续"keptalive"请求,
# 在这个限制下,只会记为一个请求。
#
MaxRequestsPerChild 0
#
# ThreadsPerChild: 服务器所允许的并发线程数。
# 此值的设置取决于服务器的响应能力(约多的请求在同一时间激活,则每个请求的处理时间越慢)
# 和服务器所允许消耗的系统资源。
#
ThreadsPerChild 50
#
# Listen: 允许将Apache绑顶到指定的IP地址和端口,作为默认值的辅助选项。
# 参见 VirtualHost>
#
#Listen 3000
#Listen 12.34.56.78:80
#
# BindAddress: 通过此选项可支持虚拟主机。
# 此标识用于告诉服务器监听哪个IP地址。
# 包括:"*", IP地址, 或域名.
# 参见 VirtualHost> 和 Listen directives.
#
BindAddress 166.111.178.144
#
# Apache模块编译成标准的Windows结构。
#
# 以下模块绑定到标准的Apache二进制windows分布。
# 要修改标准操作,取消以下行的注释并且修改指定模块列表。 
#
# 警告:这是高级选项。可能导致服务器崩溃。
# 没有专家的指导,不要轻易修改。
#
#ClearModuleList
#AddModule mod_so.c mod_mime.c mod_access.c mod_auth.c mod_negotiation.c
#AddModule mod_include.c mod_autoindex.c mod_dir.c mod_cgi.c mod_userdir.c
#AddModule mod_alias.c mod_env.c mod_log_config.c mod_asis.c mod_imap.c
#AddModule mod_actions.c mod_setenvif.c mod_isapi.c
#
# 动态共享对象(Dynamic Shared Object,DSO) 
#
# 要使用基于DSO的功能模块,需要替换此处相应的
# `LoadModule' 行。这样在使用之前这些包含的标识都将生效。
# 有关DSO及至的详细资料请看Apache1.3版中的README.DSOSO。
# 运行"apche -l"将列表显示Apache内奸的模块(类似标准的连接已经生效)
#
# 注:模块载入的顺序很重要。没有专家的建议,不要修改以下的顺序。
#
#LoadModule anon_auth_module modules/ApacheModuleAuthAnon.dll
#LoadModule dbm_auth_module modules/ApacheModuleAuthDBM.dll
#LoadModule digest_auth_module modules/ApacheModuleAuthDigest.dll
#LoadModule cern_meta_module modules/ApacheModuleCERNMeta.dll
#LoadModule digest_module modules/ApacheModuleDigest.dll
#LoadModule expires_module modules/ApacheModuleExpires.dll
#LoadModule headers_module modules/ApacheModuleHeaders.dll
#LoadModule proxy_module modules/ApacheModuleProxy.dll
#LoadModule rewrite_module modules/ApacheModuleRewrite.dll
#LoadModule speling_module modules/ApacheModuleSpeling.dll
#LoadModule info_module modules/ApacheModuleInfo.dll
#LoadModule status_module modules/ApacheModuleStatus.dll
#LoadModule usertrack_module modules/ApacheModuleUserTrack.dll
#
# ExtendedStatus 在服务器状态句柄被呼叫时控制是产生“完整”的状态信息(ExtendedStatus On)
# 还是仅返回基本信息(ExtendedStatus Off)
# 默认是:Off
#
#ExtendedStatus On
### 部分 2: 主服务器配置
#
# 此部分的标识用于主服务器所有的设置值,
# 响应任何VirtualHost>定义不处理的请求
# 这些值同时给你稍后在此文件中定义的VirtualHost>提供默认值。
#
# 所有的标识可能会在VirtualHost>中出现。
# 对应的默认值会被虚拟主机重新定义覆盖。
#
#
# Port: Standalone服务器监听的端口。 
# 在Apache能够监听指定端口前,需要在防火墙中进行设置。
# 其它运行httpd的服务器也可能影响此端口。 Disable
# 如果遇到问题,请关闭所有的防火墙、安全保护和其他的服务。
# Windos NT的"NETSTAT -a"指令会有助于问题的分析。
#
Port 80
#
# ServerAdmin: 你的地址。如果服务器有任何问题将发信到这个地址。
# 这个地址会在服务器产生的某些页面中出现,例如,错误报告。
#
ServerAdmin chenyl98@mails.tsinghua.edu.cn
#
# ServerName 允许设置主机名。如果与程序获得的不同,主机名将返回客户端。
# (例如,用"www"代替主机真实的名字)
#
# 注: 主机名不能随便指定。必须是你的机器有效的DNS名称。否则无法正常工作。
# 如果不能理解,倾向你的网络管理员询问。
# 如果你的主机没有注册DNS名,可在此输入IP地址。
# 此时必须用IP地址来访问。(如, http://123.45.67.89/)
# 这样扔可以完成重新定向的工作。
#
# 127.0.0.1 是TCP/IP的本地环路地址, 通常命名为localhost. 
# 机器默认此地置为本身。 如果只是使用Apache来进行本地测试和开发, 
# 可使用127.0.0.1 作为服务器名.
#
#ServerName new.host.name
#
# DocumentRoot: 放置服务文档的目录。
# 默认状态下,所有的请求都以这个目录为基础。
# 但是直接符号连接和别名可用于指向其他位置。
#
DocumentRoot "D:/www_root"
#
# Apache访问的每个目录可设置相关的服务和特性是允许或(和)不允许。
# (同样影响其子目录) 
#
# 首先,设置"default"地址只有最基本的权限。
#
Directory />
    Options FollowSymLinks
    AllowOverride None
/Directory>
#
# 注意从现在开始必须制定开启特殊的权限。
# 这样就不会产生意想不到的结果。
# 请仔细确认。
#
#
# 这个地址应与DocumentRoot保持一致
#
Directory "D:/www_root">
#
# 此值可是: "None", "All", 或下列的组合: "Indexes",
# "Includes", "FollowSymLinks", "ExecCGI", 或 "MultiViews".
#
# 注意"MultiViews"必须明确指定--- "Options All"不包括此特性。
#
    Options Indexes FollowSymLinks MultiViews
#
# 此项控制目录中哪些.htaccess文件可覆盖。
# 允许值: "All"或者以下项的组合:"Options", "FileInfo", 
# "AuthConfig", "Limit"
#
    AllowOverride None
#
# 控制哪些用户可从此服务器获得资料。
#
    Order allow,deny
    Allow from all
/Directory>
#
# UserDir: 当请求~user时,追加到用户主目录的路径地址。
#
# 在Win32下,并不要求指定为用户登陆的主目录。
# 因此可使用以下的格式。 
# 详细参照文档UserDir
#
IfModule mod_userdir.c>
    UserDir "f:/homepages/"
/IfModule>
#
# 控制访问UserDir目录. The following is an example
# 以下是一个站点的例子,权限限制为只读。
#
#Directory "E:/Program Files/Apache Group/Apache/users">
# AllowOverride FileInfo AuthConfig Limit
# Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
# Limit GET POST OPTIONS PROPFIND>
# Order allow,deny
# Allow from all
# /Limit>
# LimitExcept GET POST OPTIONS PROPFIND>
# Order deny,allow
# Deny from all
# /LimitExcept>
#/Directory>
#
# DirectoryIndex: 预设的HTML目录索引文件名。
# 用空格来分隔多个文件名。
#
IfModule mod_dir.c>
    DirectoryIndex index.html
/IfModule>
#
# AccessFileName: 每个目录中用于控制访问信息的文件名。
#
AccessFileName .htaccess
#
# 以下行防止客户端可访问 .htaccess 文件。
# 因为 .htaccess文件通常包含授权信息,
# 处于安全的考虑不允许访问。 
# 如果想让访客看到.htaccess文件的内容,
# 可将这些行注释。
# 如果修改了上面的AccessFileName,
# 请在此进行相应的修改。
#
# 同时,一般会用类似.htpasswd的文件保存密码。
# 这些文件同样可以得到保护。
#
Files ~ "^\.ht">
    Order allow,deny
    Deny from all
/Files>
#
# CacheNegotiatedDocs: 默认下,Apache对每个文档发送"Pragma: no-cache"
# 这将要求代理服务器不缓存此文档。
# 取消下列行的可取消这个属性,这样代理服务器将缓存这些文档。
#
#CacheNegotiatedDocs
#
# UseCanonicalName: (1.3新增) 当此设置为on时,
# 无论何时Apache需要构建一个自引用的URL(指向响应来源服务器),
# 它将用ServerName和Port来构建一个规范的格式。
# 当此设置为off时,Apache将使用客户端提供的"主机名:端口"
# 这将同时影响CGI脚本中的SERVER_NAME和SERVER_PORT
#
UseCanonicalName On
#
# TypesConfig 记录媒体类型(mime.types)文件或类似的东东放置的位置
#
IfModule mod_mime.c>
    TypesConfig conf/mime.types
/IfModule>
#
# DefaultType 是服务器处理未确认类型的文件,如为止的扩展名,的默认类型。
# 如果你的服务器上主要包含的是文本或HTML文档,"text/plain"是较好的设置 
# 如果服务器上主要包含二进制文件,如应用程序或图片,
# 最好设置成"application/octet-stream"防止浏览器将二进制文件以文本的方式显示。
#
DefaultType text/plain
#
# mod_mime_magic模块允许服务器使用文件自身的不同标识来确定文件类型。
# MIMEMagicFile指示模块文件标识的定义所在的位置。
# mod_mime_magic不是默认服务器的一部分。
# (必须自行用LoadModule来追加 [见'全局环境'部分的 DSO 章节],
# 或者在编译服务器时包含mod_mime_magic部分)
# 包含在 IfModule> 中.
# 就是说,如果该模块是服务器的一部分,MIMEMagicFile标识将执行。
#
IfModule mod_mime_magic.c>
    MIMEMagicFile conf/magic
/IfModule>
#
# HostnameLookups: 注册客户端的机器名或IP地址。
# 例如: www.apache.org (on) 或 204.62.129.132 (off).
# 默认为off,因为对于网络来说,最好让人们有意识的设置为on,
# 因为开启此功能意味着每个客户请求将导致至少向name服务器发送一个lookup请求
#
HostnameLookups Off
#
# ErrorLog: 错误记录文件的地址
# 如果不在VirtualHost>内指定ErrorLog 
# 改虚拟主机的错误心细将记录到此处。
# 如果在VirtualHost>中明确指定了错误记录文件,
# 则错误将记录在那儿而不是这儿。
#
ErrorLog logs/error.log
#
# LogLevel: 控制记录在error.log中信息的个数.
# 可能的值:debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn
#
# 以下标识定义CustomLog标识使用的格式。(见下)
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
#
# 访问记录的位置和格式 (功用的记录文件格式).
# 如果不在VirtualHost>中定义记录文件,
# 那些访问记录就将保存在这儿。 Contrariwise, if you *do*
# 反之,如果指定了记录文件,那么访问记录将记录在那儿而不是这个文件中。
#
CustomLog logs/access.log common
#
# 如果希望使用代理和参考的记录文件, 取消以下标识的注释符
#
#CustomLog logs/referer.log referer
#CustomLog logs/agent.log agent
#
# 如果想在一个文件中记录访问、代理、参考信息(复合的记录格式)
# 可使用以下标识
#
#CustomLog logs/access.log combined
#
# 在服务器产生的页面(如错误文档信息,FTP目录列表等等,不包括CGI产生的文档)中
# 增加一条服务器版本和虚拟主机名的信息。
# 设置为"EMail"将包含mailto: ServerAdmin的连接.
# 可选值: On | Off | EMail
#
ServerSignature On

# 默认下,Apache用工作行解析所有CGI脚本
# 此注释行(脚本的第一行)包括'#'和'!'后面跟着执行特殊脚本的程序路径,
# 对perl脚本来说是C:\Program Files\Perl目录中的perl.exe。
# 工作行如下:
   #!c:/program files/perl/perl
# 注意真实的工作行不能有缩进,必须是文件的第一行。
# 当然,CGI进程必须通过适当的scriptAlias或ExecCGI选项标识来启动。
#
# 然而,Windows下的Apache即允许以上的Unix方式,也可以通过注册表的形式。
# 用注册表执行文件的方法同在Windows资源管理器中双击运行的注册方法相同。 
# 此脚本操作可在Windows资源管理器的“查看”菜单中设置。 
# “文件夹选项”,然后查看“文件类型”。点击编辑按钮。
# 修改操作属性。Apache 1.3会尝试执行‘Open'操作,
# 如果失败则会尝试工作行
# 这个属性在Apache release 2.0中会有改变.
#
# 每个机制都有自身特定的安全弱点,这样可能导致别人运行你不希望调用的程序。
# 最佳的解决方案还在讨论中。
#
# 要是这个Windows的特殊属性生效 (同时会是Unix属性无效)
# 取消下列标识的注释符。
#
#scriptInterpreterSource registry
#
# 上面的标识可在Directory>块或.htaccess文件中单独替换。
# 可选择'registry' (Windows behavior)或 'script' 
# (Unix behavior) option, 将覆盖服务器的默认值。
#
#
# Aliases: 可无限制的追加别名。格式如下: 
# Alias 假名 真名
#
IfModule mod_alias.c>
    #
    # 注意如果假名中包含'/',服务器会在当前URL中发出请求。
    # 因此"/icons"不能用于别名
    # 必须用 "/icons/"..
    #
    Alias /icons/ "C:/Program Files/Apache Group/Apache/icons/"
    Directory "C:/Program Files/Apache Group/Apache/icons">
        Options Indexes MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all
    /Directory>
    #
    # scriptAlias: 控制哪个目录包含服务器脚本。
    # scriptAlias本质行和Aliases一样。, except that
    # 区别在于真名目录中的文档被看作是一个应用程序。
    # 请求时由服务器运行而不是发往客户端。
    # "/"符号的规则同
    # Alias相同.
    #
    scriptAlias /cgi-bin/ "C:/Program Files/Apache Group/Apache/cgi-bin/"
    #
    # "C:/Program Files/Apache Group/Apache/cgi-bin" 可修改为任何放置CGI脚本的目录
    #
    Directory "C:/Program Files/Apache Group/Apache/cgi-bin">
        AllowOverride None
        Options None
        Order allow,deny
        Allow from all
    /Directory>
/IfModule>
# 别名结束
#php脚本说明
scriptAlias /php/ "d:/php/"
AddType application/x-httpd-php .php
AddType application/x-httpd-php .php3
AddType application/x-httpd-php .phtml
Action application/x-httpd-php "/php/php.exe"
#php脚本说明结束
#
# Redirect 允许告诉客户端服务器上曾经有的文档,但是现在不存在了。
# 并且可以告诉客户端到哪儿去寻找。
# 格式: Redirect old-URL new-URL
#
#
# 控制服务器目录列表显示的标识
#
IfModule mod_autoindex.c>
    #
    # FancyIndexing标识是使用特定的目录检索还是标准的(standard)
    #
    IndexOptions FancyIndexing
    #
    # AddIcon*表明不同文件或扩展名显示的图标。
    # 这些图标只在特定检索状态下显示。
    #
    AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
    AddIconByType (TXT,/icons/text.gif) text/*
    AddIconByType (IMG,/icons/image2.gif) image/*
    AddIconByType (SND,/icons/sound2.gif) audio/*
    AddIconByType (VID,/icons/movie.gif) video/*
    AddIcon /icons/binary.gif .bin .exe
    AddIcon /icons/binhex.gif .hqx
    AddIcon /icons/tar.gif .tar
    AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
    AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
    AddIcon /icons/a.gif .ps .ai .eps
    AddIcon /icons/layout.gif .html .shtml .htm .pdf
    AddIcon /icons/text.gif .txt
    AddIcon /icons/c.gif .c
    AddIcon /icons/p.gif .pl .py
    AddIcon /icons/f.gif .for
    AddIcon /icons/dvi.gif .dvi
    AddIcon /icons/uuencoded.gif .uu
    AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
    AddIcon /icons/tex.gif .tex
    AddIcon /icons/bomb.gif core
    AddIcon /icons/back.gif ..
    AddIcon /icons/hand.right.gif README
    AddIcon /icons/folder.gif ^^DIRECTORY^^
    AddIcon /icons/blank.gif ^^BLANKICON^^
    #
    # DefaultIcon 用于为制定图标的文件所显示的图标。
    #
    DefaultIcon /icons/unknown.gif
    #
    # AddDescription在服务器生成的检索的某个文件后追加小段说明。
    # 此项只在设置为FancyIndexed时有效
    # 格式:AddDescription "描述" 文件名
    #
    #AddDescription "GZIP compressed document" .gz
    #AddDescription "tar archive" .tar
    #AddDescription "GZIP compressed tar archive" .tgz
    #
    # ReadmeName是服务器默认的README文件。
    # 并且会追加到目录列表的最后。
    #
    # HeaderName 是目录中需要预先显示内容的文件名。
    #
    # 如果MultiViews在选项中,作为结果,服务器将先找name.html,
    # 如果存在就包含它。如果name.html不存在,
    # 服务器会继续寻找name.txt。如果存在就作为纯文本包含进来。
    #
    ReadmeName README
    HeaderName HEADER
    #
    # IndexIgnore是一系列的文件名。目录索引将忽略这些文件并且不包含在列表中。
    # 允许使用通配符。
    #
    IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
/IfModule>
# indexing标识结束
#
# 文件类型
#
IfModule mod_mime.c>
    #
    # AddEncoding 可用于特殊浏览器(Mosaic/X 2.1+)快速传输压缩信息。
    # 注:并不是所有的服务器都支持。
    # 除了名字相似,以下Add*标识对上面的FancyIndexing定制标识无影响。
    #
    AddEncoding x-compress Z
    AddEncoding x-gzip gz tgz
    #
    # AddLanguage用于指定文档的语言。
    # 可以使用content标签指定每个文件的语言。
    #
    # 注 1: 后缀不必与所用语言的关键字相同。
    # --- 波兰语(Polish,标准代码为pl)的文档可以用
    # "AddLanguage pl .po" 来避免与perl脚本文件混淆。
    #
    # 注 2: 以下例子表明两个字母的语言缩写和两个字母的国家缩写并不一定相同。
    # E.g. 'Danmark/dk' 对比 'Danish/da'.
    #
    # 注 3: 其中'ltz'使用了三个字符,与RFC的规定不同。
    # 但是这个问题正在修订中,并且重新清理RFC1766
    #
    # 丹麦Danish (da) - 荷兰Dutch (nl) - 英国English (en) - 爱萨尼亚Estonian (ee)
    # 法国French (fr) - 德国German (de) - 现代希腊文Greek-Modern (el)
    # 意大利Italian (it) - 朝鲜Korean (kr) - 挪威Norwegian (no)
    # 葡萄牙Portuguese (pt) - 卢森堡Luxembourgeois* (ltz)
    # 西班牙Spanish (es) - 瑞典Swedish (sv) - 加泰罗尼亚Catalan (ca) - 捷克Czech(cz)
    # 波兰Polish (pl) - 巴西Brazilian Portuguese (pt-br) - 日本Japanese (ja)
    # 俄国Russian (ru)
    #
    AddLanguage da .dk
    AddLanguage nl .nl
    AddLanguage en .en
    AddLanguage et .ee
    AddLanguage fr .fr
    AddLanguage de .de
    AddLanguage el .el
    AddLanguage he .he
    AddCharset ISO-8859-8 .iso8859-8
    AddLanguage it .it
    AddLanguage ja .ja
    AddCharset ISO-2022-JP .jis
    AddLanguage kr .kr
    AddCharset ISO-2022-KR .iso-kr
    AddLanguage no .no
    AddLanguage pl .po
    AddCharset ISO-8859-2 .iso-pl
    AddLanguage pt .pt
    AddLanguage pt-br .pt-br
    AddLanguage ltz .lu
    AddLanguage ca .ca
    AddLanguage es .es
    AddLanguage sv .se
    AddLanguage cz .cz
    AddLanguage ru .ru
    AddLanguage tw .tw
    AddCharset Big5 .Big5 .big5
    AddCharset WINDOWS-1251 .cp-1251
    AddCharset CP866 .cp866
    AddCharset ISO-8859-5 .iso-ru
    AddCharset KOI8-R .koi8-r
    AddCharset UCS-2 .ucs2
    AddCharset UCS-4 .ucs4
    AddCharset UTF-8 .utf8
    # LanguagePriority 可设置语言的优先级。
    #
    # 优先级降序排列
    # 在此处按照字母顺序,可自行修改
    #
    IfModule mod_negotiation.c>
        LanguagePriority en da nl et fr de el it ja kr no pl pt pt-br ru ltz ca es sv tw
    /IfModule>
    #
    # AddType 可临时改变mime.types或者指定特殊文件的格式。
    #
    # 例如:PHP 3.x 模块 (非Apache标准配件,参见http://www.php.net)可用下面格式定义:
    #
    #AddType application/x-httpd-php3 .php3
    #AddType application/x-httpd-php3-source .phps
    #
    # PHP 4.x, 使用:
    #
    #AddType application/x-httpd-php .php
    #AddType application/x-httpd-php-source .phps
    AddType application/x-tar .tgz
    #
    # AddHandler 可将特定文件扩展名映射到处理方法上。
    # 与文件类型无关。此特性可内建到服务器中或者追加在操作指令中(见下)
    #
    # 如果希望用服务器端应用或scriptAliased外的CGI,取消以下行的注释符
    #
    # 用CGI脚本:
    #
    #AddHandler cgi-script .cgi
    #
    # 用服务器解析的HTML文档
    #
    #AddType text/html .shtml
    #AddHandler server-parsed .shtml
    #
    # 取消以下注释符可激活Apache的send-asis HTTP file特性
    #
    #AddHandler send-as-is asis
    #
    # 如果使用服务器端解析的图像定位文件,使用以下标识:
    #
    #AddHandler imap-file map
    #
    # 要激活type maps使用:
    #
    #AddHandler type-map var
/IfModule>
# 文档类型说明结束
#
# Action 定义在文件匹配时执行相应的脚本。
# 可简化常用CGI文件的调用。
# 格式: Action media/type /cgi-script/location
# 格式: Action handler-name /cgi-script/location
#
#
# MetaDir: 指定保存meta信息文件的目录。
# 这些文件包含附加的HTTP头,在发送文档是一并发送。
#
#MetaDir .web
#
# MetaSuffix: 指定包含meta信息的文件的后缀。
#
#MetaSuffix .meta
#
# 可定制的错误响应(Apache类型)
# 共三种风格:
#
# 1) 纯文本
#ErrorDocument 500 "The server made a boo boo.
# 注: 第一个"号用于表示是文本,实际不输出
#
# 2) 本地重定向
#ErrorDocument 404 /missing.html
# to redirect to local URL /missing.html
#ErrorDocument 404 /cgi-bin/missing_handler.pl
# 注:可重定向到任何一个服务器端的脚本或文档
#
# 3) 外部重定向
#ErrorDocument 402 http://some.other_server.com/subscription_info.html
# 注: 大部分与初始请求关联的环境变量对这样的脚本无效。
#
#
# 基于浏览器的定制操作
#
IfModule mod_setenvif.c>
    #
    # 以下标识修改普通的HTTP响应操作。
    # 第一个标识针对Netscape2.x和其他无此功能的浏览器取消保持激活状态的功能
    # 这些浏览器在执行这些功能时会出错。
    # 第二个标识针对IE4.0b2设置。其中有一条不完整的HTTP/1.1指令
    # 在301或302(重定向)响应时不能正确的保持激活状态
    #
    BrowserMatch "Mozilla/2" nokeepalive
    BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
    #
    # 下面的标识通过不产生基本的1.1响应取消对违反HTTP/1.0标准的浏览器的响应。
    #
    BrowserMatch "RealPlayer 4\.0" force-response-1.0
    BrowserMatch "Java/1\.0" force-response-1.0
    BrowserMatch "JDK/1\.0" force-response-1.0
/IfModule>
# 浏览器定制标识结束
#
# 允许使用URL"http://servername/server-status"的形式查看服务器状态报告
# 修改 ".your_domain.com"来匹配相应的域名以激活此功能
#
#Location /server-status>
# SetHandler server-status
# Order deny,allow
# Deny from all
# Allow from .your_domain.com
#/Location>
#
# 允许使用URL"://servername/server-info"(要求加载mod_info.c),
# 来远程察看服务器配置报告。
# 修改 ".your_domain.com"来匹配相应的域名以激活此功能
#
#Location /server-info>
# SetHandler server-info
# Order deny,allow
# Deny from all
# Allow from .your_domain.com
#/Location>
#
# 据报有人试图利用一个老的1.1漏洞。
# 这个漏洞与CGI脚本在Apache服务器上分布有关。
# 通过取消下面几行的注释符,可以将此类攻击记录转移到phf.apache.org上的记录脚本上。 
# 或者也可以利用脚本scriptsupport/phf_abuse_log.cgi记录在本地服务器上。
#
#Location /cgi-bin/phf*>
# Deny from all
# ErrorDocument 403 http://phf.apache.org/phf_abuse_log.cgi
#/Location>
#
# 代理服务器标识。取消下列行的注释符可激活代理服务器。
#
#IfModule mod_proxy.c>
# ProxyRequests On
# Directory proxy:*>
# Order deny,allow
# Deny from all
# Allow from .your_domain.com
# /Directory>
    #
    # 激活/取消处理HTTP/1.1 "Via:" 报头
    # ("Full":加入服务器版本; "Block":取消所有外发的Via: 报头)
    # 可设置值: Off | On | Full | Block
    #
# ProxyVia On
    #
    # 可修改下列各行并取消注释符来激活缓存。
    # (没有CacheRoot标识就不使用缓存)
    #
# CacheRoot "E:/Program Files/Apache Group/Apache/proxy"
# CacheSize 5
# CacheGcInterval 4
# CacheMaxExpire 24
# CacheLastModifiedFactor 0.1
# CacheDefaultExpire 1
# NoCache a_domain.com another_domain.edu joes.garage_sale.com
#/IfModule>
# 代理标识结束
### 部分 3: 虚拟主机
#
# 虚拟主机: 如果希望在一台服务器上实现多个域名和主机名的服务,
# 可设置VirtualHost来实现。Most configurations
# 大部分的设置使用基于名称的虚拟主机,这样服务器就不必为IP地址操心。
# 这些用星号在下面的标识中标出。
#
# 在试图设置虚拟主机前
# 请阅读URL:http://www.apache.org/docs/vhosts/>中的文档。
# 以了解细节问题。
#
# 可用命令行参数 '-S'来确认虚拟主机的设置。
#
#
# 使用基于名称的虚拟主机
#
#NameVirtualHost *
#
# 虚拟主机实例:
# 几乎所有的Apache标识都可用于虚拟主机内。
# 第一个VirtualHost部分用于申请一个无重复的服务器名。
#
#VirtualHost *>
# ServerAdmin webmaster@dummy-host.example.com
# DocumentRoot /www/docs/dummy-host.example.com
# ServerName dummy-host.example.com
# ErrorLog logs/dummy-host.example.com-error_log
# CustomLog logs/dummy-host.example.com-access_log common
#/VirtualHost>

posted @ 2011-03-17 10:28 黄超| 编辑 收藏


2008年9月4日

转载请注明出处http://www.cppblog.com/greatws/archive/2008/08/31/60546.html

最近有人问我关于这个的问题,就此写一篇blog

Ansi字符串我们最熟悉,英文占一个字节,汉字2个字节,以一个\0结尾,常用于txt文本文件
Unicode字符串,每个字符(汉字、英文字母)都占2个字节,以2个连续的\0结尾,NT操作系统内核用的是这种字符串,常被定义为typedef unsigned short wchar_t;所以我们有时常会见到什么char*无法转换为unsigned short*之类的错误,其实就是unicode
UTF8是Unicode一种压缩形式,英文A在unicode中表示为0x0041,老外觉得这种存储方式太浪费,因为浪费了50%的空间,于是就把英文压缩成1个字节,成了utf8编码,但是汉字在utf8中占3个字节,显然用做中文不如ansi合算,这就是中国的网页用作ansi编码而老外的网页常用utf8的原因。
UTF8在还游戏里运用的很广泛,比如WOW的lua脚本等

下面来说一下转换,主要用代码来说明吧
写文件我用了CFile类,其实用FILE*之类的也是一样,写文件和字符串什么类别没有关系,硬件只关心数据和长度

Ansi转Unicode
介绍2种方法


void CConvertDlg::OnBnClickedButtonAnsiToUnicode()
{
    
// ansi to unicode
    char* szAnsi = "abcd1234你我他";
    
//预转换,得到所需空间的大小
    int wcsLen = ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), NULL, 0);
    
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
    wchar_t* wszString = new wchar_t[wcsLen + 1];
    
//转换
    ::MultiByteToWideChar(CP_ACP, NULL, szAnsi, strlen(szAnsi), wszString, wcsLen);
    
//最后加上'\0'
    wszString[wcsLen] = '\0';
    
//unicode版的MessageBox API
    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

    
//接下来写入文本
    
//写文本文件,头2个字节0xfeff,低位0xff写在前
    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite | CFile::modeCreate);
    
//文件开头
    cFile.SeekToBegin();
    cFile.Write(
"\xff\xfe"2);
    
//写入内容
    cFile.Write(wszString, wcsLen * sizeof(wchar_t));
    cFile.Flush();
    cFile.Close();
    delete[] wszString;
    wszString 
=NULL;


    
//方法2
    
//设置当前地域信息,不设置的话,使用这种方法,中文不会正确显示
    
//需要#include<locale.h>
    setlocale(LC_CTYPE, "chs"); 
    wchar_t wcsStr[
100];
    
//注意下面是大写S,在unicode中,代表后面是ansi字符串
    
//swprintf是sprintf的unicode版本
    
//格式的前面要加大写L,代表是unicode
    swprintf(wcsStr, L"%S", szAnsi);
    ::MessageBoxW(GetSafeHwnd(), wcsStr, wcsStr, MB_OK);

}


Unicode转Ansi
也是2种方法

void CConvertDlg::OnBnClickedButtonUnicodeToAnsi()
{
    
// unicode to ansi
    wchar_t* wszString = L"abcd1234你我他";
    
//预转换,得到所需空间的大小,这次用的函数和上面名字相反
    int ansiLen = ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
    
//同上,分配空间要给'\0'留个空间
    char* szAnsi = new char[ansiLen + 1];
    
//转换
    
//unicode版对应的strlen是wcslen
    ::WideCharToMultiByte(CP_ACP, NULL, wszString, wcslen(wszString), szAnsi, ansiLen, NULL, NULL);
    
//最后加上'\0'
    szAnsi[ansiLen] = '\0';
    
//Ansi版的MessageBox API
    ::MessageBoxA(GetSafeHwnd(), szAnsi, szAnsi, MB_OK);

    
//接下来写入文本
    
//写文本文件,ANSI文件没有BOM
    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite | CFile::modeCreate);
    
//文件开头
    cFile.SeekToBegin();
    
//写入内容
    cFile.Write(szAnsi, ansiLen * sizeof(char));
    cFile.Flush();
    cFile.Close();
    delete[] szAnsi;
    szAnsi 
=NULL;


    
//方法2
    
//和上面一样有另一种方法
    setlocale(LC_CTYPE, "chs"); 
    
char szStr[100];
    
//注意下面是大写,在ansi中,代表后面是unicode字符串
    
//sprintf
    sprintf(szStr, "%S", wszString);
    ::MessageBoxA(GetSafeHwnd(), szStr, szStr, MB_OK);
}


Unicode转UTF8

void CConvertDlg::OnBnClickedButtonUnicodeToU8()
{
    
// unicode to UTF8
    wchar_t* wszString = L"abcd1234你我他";
    
//预转换,得到所需空间的大小,这次用的函数和上面名字相反
    int u8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), NULL, 0, NULL, NULL);
    
//同上,分配空间要给'\0'留个空间
    
//UTF8虽然是Unicode的压缩形式,但也是多字节字符串,所以可以以char的形式保存
    char* szU8 = new char[u8Len + 1];
    
//转换
    
//unicode版对应的strlen是wcslen
    ::WideCharToMultiByte(CP_UTF8, NULL, wszString, wcslen(wszString), szU8, u8Len, NULL, NULL);
    
//最后加上'\0'
    szU8[u8Len] = '\0';
    
//MessageBox不支持UTF8,所以只能写文件

    
//接下来写入文本
    
//写文本文件,UTF8的BOM是0xbfbbef
    CFile cFile;
    cFile.Open(_T(
"1.txt"), CFile::modeWrite | CFile::modeCreate);
    
//文件开头
    cFile.SeekToBegin();
    
//写BOM,同样低位写在前
    cFile.Write("\xef\xbb\xbf"3);
    
//写入内容
    cFile.Write(szU8, u8Len * sizeof(char));
    cFile.Flush();
    cFile.Close();
    delete[] szU8;
    szU8 
=NULL;

}

UTF8转UNICODE

void CConvertDlg::OnBnClickedButtonU8ToUnicode()
{
    
//UTF8 to Unicode
    
//由于中文直接复制过来会成乱码,编译器有时会报错,故采用16进制形式
    char* szU8 = "abcd1234\xe4\xbd\xa0\xe6\x88\x91\xe4\xbb\x96\x00";
    
//预转换,得到所需空间的大小
    int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);
    
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
    wchar_t* wszString = new wchar_t[wcsLen + 1];
    
//转换
    ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);
    
//最后加上'\0'
    wszString[wcsLen] = '\0';
    
//unicode版的MessageBox API
    ::MessageBoxW(GetSafeHwnd(), wszString, wszString, MB_OK);

    
//写文本同ansi to unicode
}



Ansi转换utf8和utf8转换Ansi就是上面2个的结合,把unicode作为中间量,进行2次转换即可

posted @ 2008-09-04 14:47 黄超 阅读(253) | 评论 (0)编辑 收藏


仅列出标题  下一页

posts - 12, comments - 0, trackbacks - 0, articles - 1

Copyright © 黄超