袁瑞 的个人资料袁瑞的共享空间照片日志留言簿更多 工具 帮助

日志


动态创建的TIdTime控件无法获取准确时间的问题

在BCB2009内测试发现,使用代码动态创建的TIdTime获取到的服务器时间,总是比服务器的实际时间少两天。于是创建一个测试程序,将TIdTiime控件直接拖放到窗口中,测试发现获取到的时间是正确的。一切都是默认设置,代码中和界面中都只是设置了Host属性而已,到底区别在哪里呢?

我开始怀疑BaseDate属性,这个属性在拖放到界面上后会自动设置为一个默认值,如下图:1900/1/1,由于我使用的是英文操作系统,在其他系统上也许不是用/来分隔的。

image

查看TIdTime类的头文件,发现其属性BaseTime并没有默认值

__property System::TDateTime BaseDate = {read=FBaseDate, write=FBaseDate};

在IDE属性编辑器中的默认值也许是在窗口内创建控件时加入的,而通过代码动态创建时似乎未设置。

RFC868中要求的时间是从00:00 (midnight) 1 January 1900 GMT开始,而TDateTime是从12/30/1899 12:00开始计算,恰好是2天!

于是我在动态创建TIdTime后,再设置BaseDate,测试获取时间正常了。

这里需要提醒一下,BaseDate是TDateTime类型,如果要将字符串“1900-1-1”转换为TDateTime类型,也许会出现错误,原因是日期分隔符不一定是"-",日期格式也不一定是YYYY-MM-DD,需要设置日期格式和分隔符,再进行转换:

    DateSeparator = '-';
    ShortDateFormat = "yyyy-mm-dd";
    StrToDateTime("1900-1-1");

值得注意的是,似乎TIdTime控件在BCB6下的版本无此问题,BCB2009的TIdTime版本存在这个问题。

1月11日

bcb.exe.manifest 引发的“血案”

升级到英文Vista后,最近发现BCB6中一些使用了ImageList类的程序会启动失败报Failed to read ImageList data的错误,让人很是费解。还有的程序可以在Vista下正常运行,但是在Xp下启动却会出现这个错误。一怒之下只能怪罪于TImageList,不曾想却是一桩冤案。

在使用Xp系统时,我就为了让BCB6.0的IDE也具有XP界面风格,将bcb.exe.manifest拷贝到安装目录下,这样可以强制BCB使用XP界面风格,即启用COMCTL32.DLL版本6(Vista下应该是更高版本)支持。这样界面在IDE的设计期内使用的是新版本,但是程序运行时如果不设置启用新版本支持就出现TImageList运行时无法正常读取数据的问题。

为了反向验证,将BCB目录下的bcb.exe.manifest文件删除,再打开问题程序时,打开界面时同样开始报错了。

同样也可以解释Vista下运行正常的程序,返回到Xp下运行失败的问题。Vista下程序内支持了更高版本的界面特性,导致TImageList在XP下运行无法读取数据,程序仍然遇到了类似的问题。

1月8日

setlocale同mbstowcs函数的关系

程序中,如果要将ASCII码字符串转换为宽字符(Unicode),可以利用标准C的mbstowcs函数。

微软在MSDN中有示例,如下:

image

然而,这段代码在处理含有汉字的字符串时就会出现问题。比如将:

image 替换为image

查看运行结果就会发现,mbstowcs函数将汉字视作两个ASCII字符,这样一个汉字就变成了两个wchar_t。原因是mbstowcs需要我们明确的告诉他要转换的字符语言。这里需要使用setlocale函数。在网上发现不少人遇到这个问题,微软的MSDN也是,为什么这里就不说明一下呢?

只要在调用前,使用setlocale(LC_ALL, "chs")设置,结果就正常了。

我测试在英文Vista操作系统内,Visual Studio 2008下setlocale(LC_ALL, "chs")可以执行成功。

但是在Borland C++Builder 6、CodeGear RAD Studio 2009下执行都失败,BCB提供的帮助文件内也未找到,反复测试使用setlocale(LC_ALL, "Chinese (Simplified)_People's Republic of China")可以奏效(这么大一个长串,BCB对搞中文编码的程序员也够狠)。

更讽刺的是,在BCB内使用setlocale(LC_ALL, "jpn"),或"cht"都可以成功。唯独就不支持"chs",对BCB的做法彻底无语了。我相信在大多数unix或linux上也还是支持"chs"的。如果有时间,我再搞一个MinGW试试看。

另外如果程序运行在非中文操作系统内,使用setlocale修改运行时字符集环境,会影响当前应用程序的编码方式,因此使用前需要保留一下老的编码,使用后再恢复。

 

for 在linux上coding的兄弟们,locale别名表大概在 /usr/lib/X11/locale/locale.alias

12月26日

闺女满百天了

圆滚滚胖嘟嘟粉可爱

滑溜溜柔软软萌掉渣

 

DSC02389

11月29日

判断是否为数字型字符串

周六早晨睡醒,看见CSDN上有人求判断字符串是否为数字的代码,给出一个方法
 
bool __fastcall CheckIsNumber(AnsiString InputStr)
{
    for(int i=1;i<=InputStr.Length();i++)
    {
        if( InputStr[i]<'0' || InputStr[i]>'9')
            return false;
    }
    return true;
}
10月9日

BDS 2009 Update1 破解工具

今天BDS的Update1放出了,于是写了破解工具,目前放到纳米上了,有需要的朋友去下吧。
 
9月9日

C++Builder 2009 的一个Bug

编译ActiveX control时,按照原来在CBuilder中的做法,将控件的GUID改成一个固定的GUID值(因为不想每次升级控件后,注册表中出现一堆无用的GUID)

一切看起来都很正常,编译成功。注册ocx控件时,却出现了0x8002802b错误。意思应该是找不到元素。

我开始并没有怀疑是修改了GUID的问题。反复定位才发现不修改GUID时是好的。

可不修改GUID以后怎么玩啊!这个2009也太扯了吧!很多使用控件的地方可都是用GUID实例化的。

 

我反复尝试,最终发现原因。

在 ridl接口设计器上修改接口时,ridl文件内容都是同步修改的,但只有修改GUID时,貌似ridl文件是修改了,但是最终的rtl文件CBuilder似乎没同步。即使你选择全部保存,单独保存都没有用。汗!这样就出现编译出来的内容跟接口对不上。

解决办法是,修改了GUID后,必须在ridl文件的设计界面上点击刷新按键,问题才能解决。

 

看来C++Builder2009,在很多细节上还有问题。

9月5日

使用C++Builder2009编写ActiveX控件的Unicode问题

以前使用C++Builder6编写ActiveX控件, Event中的参数如果有字符串,编译总是有问题。

如果参数类型是AnsiString,创建ActiveX控件时,CBuiler根本就不会为你创建这个事件接口。

如果参数类型是char*,生成的ActiveX控件的参数类型则变成short,我是修改生成的代码,将参数手工变为LPSTR解决的问题。

 

而现在的2009,默认使用的是unicode,参数如果是UnicodeString,CBuilder创建的事件参数则是BSTR,不过我试验了很多次,编译出来的ActiveX控件在微软的测试容器中运行时,触发该事件都会引起内存访问错误。

另外一种方式,将参数声明为wchar_t*,生成的ActiveX控件的参数会变成short,这次改换成LPWSTR,手工修改CBuilder生成的实现类的cpp和h文件,修改TLB文件的h文件,将所有wchar_t类型替换成wchar_t*,同时将short也替换成wchar_t*,编译通过。

不过问题又出现了,在容器中运行,字符串会出现被截断的情况。

比如点燃事件中的字符串本来是"test",作为CBuilder控件进行测试是正常的,但编译成ActiveX控件到了容器里收到的却是"te"。

 

翻了翻2009的帮助文件,其中有这么一段:

WideString

WideString was previously used for multibyte character data. Its format is essentially the same as a Windows BSTR. WideString is still appropriate for use in COM applications. WideString is not reference counted, and so UnicodeString is more flexible and efficient in other types of applications.

New String Type: UnicodeString

The new default for the type string in RAD Studio is the UnicodeString type. This type can contain either Unicode or ANSI string data.  

For Delphi, Char and PChar types still "float" to WideChar and PWideChar, respectively.  

For C++, the _TCHAR maps to option controls the floating definition of _TCHAR, which can be either wchart_t or char.  

Format of UnicodeString Data Type  

CodePage 

Element Size 

Reference Count  

Length  

String Data (element sized)  

Null Term 

-12  

-10  

-8  

-4  

0  

Length * elementsize  

Both UnicodeString and AnsiString have this format, though UnicodeString prefers multibyte data and AnsiString prefers single byte data. 

 

总的来说,新的UnicodeString为了跟AnsiString兼容做了部分处理,而WideString是纯粹的多字节字符串。

 

我看看我的点燃函数:

    if(FOnTextEvent)

        FOnTextEvent(this, Edit->Text.w_str());

 

这里的Edit->Text是UnicodeString类型,w_ctr()返回类型虽然是wchar_t*但是我怀疑问题就是出在这里。

 

于是改成如下代码:

    if(FOnTextEvent)

        FOnTextEvent(this, WideString(Edit->Text).c_bstr());

 

重新测试,一切正常了。这里不禁要提出疑问,2009在转换ActiveX控件时究竟让UnicodeString做了什么工作?是否出现了问题?

8月26日

A simple Design.

A designer knows he has arrived at perfection not when there is no longer anything to add, but when there is no longer anything to take away.
8月4日

如何处理算数表达式

今天看CSDN上有人问如何计算算数表达式的结果,比如"(2+3)*6"

原帖见:http://topic.csdn.net/u/20080731/11/50de3417-53d4-4228-99e6-505f64f8edde.html?seed=1989390904

我的算法是利用后缀表达式进行计算。后缀表达式的好处是在O(N)时间内完成表达式计算。

比如 "(2+3)*6"  转换为 " 2 3 + 6 * "

如果不清楚什么是后缀表达式,我就无语了。

得到后续表达式后,就是一个顺序压入堆栈执行的问题了

比如先获取第一个2入堆栈 ,然后是3入堆栈,然后发现是运算符+,这时执行运算符+,将2和3弹出,相加后入堆栈,然后是6,6入堆栈,然后是*,执行*,将堆栈中的两个数弹出执行,得到最后结果。

 

下面代码是我用BCB写的中缀转后缀表达式代码。图省事用了AnsiString,有空改成String就通用了。

//---------------------------------------------------------------------------
bool __fastcall IsOpertator(char aChar)
{
    switch(aChar)
    {
        case '+':
        case '-':
        case '*':
        case '/':
        case '(':
        case ')':
            return true;
        default:
            return false;
    }
}

int __fastcall OperatorLevel(char Opt)
{
    switch(Opt)
    {
        case '*':
            return 2;
        case '/':
            return 2;
        case '(':
            return 99;
        case ')':
            return 0;
        case '+':
            return 1;
        case '-':
            return 1;
        default:
            return 0;
    }
}

AnsiString __fastcall InfixToPostFix(AnsiString inputString)
{
    vector<char> OperatorStack;
    AnsiString PostFixString;

    //将表达式改为后缀表达式
    AnsiString tmpNumber;
    for(int i=0;i<inputString.Length();i++)
    {
        char tmpChar = inputString.c_str()[i];
        if(IsOpertator(tmpChar)) // 如果是操作符或括号
        {
            if(tmpNumber.Length()>0) // 如果还有正在处理的数字,数字读取完整,入后缀队列。
                PostFixString += tmpNumber+" ";
            tmpNumber = "";

            if(tmpChar == ')') // 如果是闭括号,需要一直弹出到开括号
            {
                while(!OperatorStack.empty())
                {
                    char thePopOpt = OperatorStack.back();

                    if(thePopOpt == '(')
                    {
                        OperatorStack.pop_back();
                        break;
                    }
                    else
                    {
                        PostFixString += AnsiString(thePopOpt)+" ";
                        OperatorStack.pop_back();
                    }
                }
                continue; // continue for;
            }

            // 同操作符堆栈栈顶元素比较,如果优先级高,就入栈,否则直到弹出所有元素
            while(!OperatorStack.empty())
            {
                char thePopOpt = OperatorStack.back();

                if(OperatorLevel(thePopOpt)>OperatorLevel(tmpChar))
                {
                    if(thePopOpt=='(')
                    {
                        OperatorStack.push_back(tmpChar);
                        tmpChar = '\0';
                        break;
                    }
                    else
                    {
                        OperatorStack.pop_back();
                        PostFixString += AnsiString(thePopOpt)+" ";
                    }
                }
                else if(OperatorLevel(thePopOpt)==OperatorLevel(tmpChar))
                {
                    OperatorStack.pop_back();
                    PostFixString += AnsiString(thePopOpt)+" ";

                    OperatorStack.push_back(tmpChar);
                    tmpChar = '\0';
                    break;
                }
                else
                {
                    OperatorStack.push_back(tmpChar);
                    tmpChar = '\0';
                    break; // break while
                }
            } // end while

            if(tmpChar!='\0')
                OperatorStack.push_back(tmpChar);
        }
        else    // 如果是数字
        {
            tmpNumber += AnsiString(tmpChar);

            if(i == inputString.Length()-1)
            {
                PostFixString += AnsiString(tmpNumber)+" ";
                break;
            }
        }
    }
    //----end for

    while(!OperatorStack.empty())
    {
        PostFixString += AnsiString(OperatorStack.back())+" ";
        OperatorStack.pop_back();
    }

    return PostFixString;
}

7月16日

研究散列(Hashing)算法的体会

散列,虽然只支持二叉查找树所允许的一部分操作,但是散列具有以常数平均时间执行插入、删除和查找的特点。

散列无法有效支持元素间排序的操作,因此像findMax、findMin以及在线性时间内按顺序打印所有元素的操作都无法支持。

为了解决散列冲突问题,普遍存在两种方式,

  1. 分离链接法(separate chaining),其做法是将散列到同一个位置的元素以一个链表的方式保存。通常产生冲突的元素被插入到链表的最前面,这样不仅方便,而且由于最后插入的元素最有可能不久再使用,这样在查找起来效率会增加。分离链接实现简单,但是双向链表会占用额外的内存,冲突严重时,查找效率也会严重下降。
  2. 探测散列表(probing hash tables),其做法是发生冲突时,尝试使用其他的单元,知道找到空闲的单元为止。因为要确保所有的元素都能插入到表中,所需要的表要比分离链接法的表大,其装填因子应该低于λ=0.5。

探测散列表又有三种解决冲突的方法,一种是线性探测,即逐个探测f(i)=i。优点是算法简单,缺点是存在"一次聚集"问题(Primary Clustering)。另一种是平方探测,即f(i)=i2。对于平方探测,表的大小是素数,那么当表至少有一半是空的时候,总能插入一个新的元素。虽然平方探测排除了一次聚集,但是散列到同一位置上的那些元素将探测相同的备选单元,即"二次聚集"(Secondary Clustering)。最后一种是双散列(double hashing),一种流行的选择是f(i)=i*hash2(x)。这个公式表明需要用到第二个散列函数用来解决冲突。诸如hash2(x)=R-(x mod R),R是小于TableSize的素数这样的函数将起到良好的作用,它可以尽可能做到在第一次散列到同样单元的元素,第二次散列到不同的其他元素上,即使会发生最坏的情况,但是非常少。

 

研究散列算法让我联系到另外一个应用,就是产生不重复的随机数,例如卡管理系统。都需要产生不规则的随机数作为卡号或密码,记得遇到一个类似的系统是通过产生可以重复的随机数(20位左右),利用数据库唯一索引检查,避免出现重复。不过运行时间长后,由于数据量越来越大,执行效率低的可怕。现在想起来,其实就是一个解决插入冲突的问题,分段的hashing算法就是一个解决办法不是。

5月23日

操作符“=”的巧妙用法和陷阱

写程序久了,一直习惯了a=b=c的代码方式。今天才发现其中也有玄机。

模仿STL的list写了一个list的迭代器

其中的插入方法如下:

Iterator insert(iterator itr, const Object &x)

{

    ListNode* p= itr.current;

    ListNode* newnd = new ListNode(x, p->prev, p); //创建一个新的节点,参数分表为:节点数据、上一个节点、下一个节点

    p->prev->next = newnd;

    p->prev = newnd;

    return iterator(newnd);

}

我将此段代码简化后为:

Iterator insert(iterator itr, const Object &x)

{

    ListNode* p= itr.current;

    return iterator(p->prev->next = p->prev = new ListNode(x, p->prev, p) );

}

执行结果错误。

分析后才发现,a=b=c的代码,分解后是b=c;a=c ,导致以上错误出现。

 

正确代码应该是:

Iterator insert(iterator itr, const Object &x)

{

    ListNode* p= itr.current;

    return iterator(p->prev = p->prev->next = new ListNode(x, p->prev, p) );

}

11月30日

TCP中的状态说明

From RFC793
 

  A connection progresses through a series of states during its  lifetime.  The states are:  LISTEN, SYN-SENT, SYN-RECEIVED,  ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK,  TIME-WAIT, and the fictional state CLOSED.  CLOSED is fictional  because it represents the state when there is no TCB, and therefore,  no connection.  Briefly the meanings of the states are:
    LISTEN - represents waiting for a connection request from any remote TCP and port.
    SYN-SENT - represents waiting for a matching connection request after having sent a connection request.
    SYN-RECEIVED - represents waiting for a confirming connection request acknowledgment after having both received and sent a connection request.
    ESTABLISHED - represents an open connection, data received can be delivered to the user.  The normal state for the data transfer phase of the connection.
    FIN-WAIT-1 - represents waiting for a connection termination request from the remote TCP, or an acknowledgment of the connection termination request previously sent.
    FIN-WAIT-2 - represents waiting for a connection termination request from the remote TCP.
    CLOSE-WAIT - represents waiting for a connection termination request from the local user.
    CLOSING - represents waiting for a connection termination request acknowledgment from the remote TCP.
    LAST-ACK - represents waiting for an acknowledgment of the connection termination request previously sent to the remote TCP(which includes an acknowledgment of its connection termination request).
    TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.
    CLOSED - represents no connection state at all.
 
  A TCP connection progresses from one state to another in response to events.  The events are the user calls, OPEN, SEND, RECEIVE, CLOSE, ABORT, and STATUS; the incoming segments, particularly those containing the SYN, ACK, RST and FIN flags; and timeouts. The state diagram in figure 6 illustrates only state changes, together with the causing events and resulting actions, but addresses neither error conditions nor actions which are not connected with state changes.  In a later section, more detail is offered with respect to the reaction of the TCP to events. NOTE BENE:  this diagram is only a summary and must not be taken as the total specification.
 
                                   
                              +---------+ ---------\      active OPEN 
                              |  CLOSED |            \    ----------- 
                              +---------+<---------\   \   create TCB 
                                |     ^              \   \  snd SYN   
                   passive OPEN |     |   CLOSE        \   \          
                   ------------ |     | ----------       \   \        
                    create TCB  |     | delete TCB         \   \      
                                V     |                      \   \    
                              +---------+            CLOSE    |    \  
                              |  LISTEN |          ---------- |     | 
                              +---------+          delete TCB |     | 
                   rcv SYN      |     |     SEND              |     | 
                  -----------   |     |    -------            |     V 
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------                 
   |                  x         |     |     snd ACK                   
   |                            V     V                               
   |  CLOSE                   +---------+                             
   | -------                  |  ESTAB  |                             
   | snd FIN                  +---------+                             
   |                   CLOSE    |     |    rcv FIN                    
   V                  -------   |     |    -------                    
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  | 
   | --------------   snd ACK   |                           ------- | 
   V        x                   V                           snd FIN V 
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN | 
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- | 
   |  -------              x       V    ------------        x       V 
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+
11月14日

函数调用的区别

 

左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。

1
_stdcallPascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。


2
C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。


_cdecl
CC++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。


3
__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECXEDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。


_fastcall
方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。


4
thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。


5
naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESIEDIEBXEBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。


关键字 __stdcall__cdecl__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz/Gd/Gr。缺省状态为/Gd,即__cdecl


要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs



VC++
对函数的省缺声明是"__cedcl",将只能被C/C++调用
.

CB
在输出函数声明时使用4种修饰符号

//__cdecl
cb
的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdeclcdecl形式。

//__fastcall
她修饰的函数的参数将尽可能地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈;

//__pascal
它说明的函数名使用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈;

//__stdcall
使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall

 

 

 

 

11月13日

CTI的主要标准协议

20世纪80年代起,在用户三方通讯应用需求的推动下,欧洲计算机制造协会(ECMA)发起了制定计算机和电信平台间的通讯标准运动。从那时起,产生了两个CTI标准:计算机支持的电话应用(CSTA)和交换机计算机应用接口(SCAI),随后,又有众多的厂商制定了相关的接口协议。下面对主要的CTI标准进行介绍。

 

1.       CSTA标准

为了响应ECMA关于建立CTI标准的运动,包括AT&T、北电、AicatelSiemensIBMHP等电信设备制造商专门成立了一个委员会。该委员会制定了标准的ECMA-179-计算机支持的电信应用服务(CSTA)和标准ECMA-180-计算机支持的电信应用协议,CSTA协议是计算机与电信网间的OSI7层通讯协议。

CSTA获得了大多数交换机制造商和呼叫中心开发公司的支持,目前的版本有CSTAI1992)、CSTAII1994)、CSTAIII1998)。

CSTA为集成计算机和电信网平台定义了总体结构、要求和协议。这项技术标准强调了计算机和交换的灵活性、双向通讯和分布模型。由于CSTA起初是针对专用网的,因此它关注的是基于交换的对象,比如电话、中继线和队列等。CSTA服务是独立于交换平台的,它并不知道交换机是如何完成CSTA服务请求的特殊细节。

 

2.       SCAI标准

交换机计算机应用接口(SCAI)标准是由美国国家标准协会(ANSI)制定的。SCAI详述了一种结构和OSI应用协议,以便在计算机和交换机应用程序间进行对等的数据通讯,从而实现计算机和电话交换平台的集成。

SCAI受其他标准的影响,包括受ISDN和智能网(IN)的影响。新一版的SCAI的目标是实现在公众网和专用网上的呼叫中心的应用。与CSTA不同的是,CSTA强调通讯集成而不是应用集成。由于CSTA提供了更多的事件报告能力,因此吸引了更多的应用开发者。

 

3.       Versit标准

Versit是有四家主导计算机通讯产品的生产商——Apple公司、AT&TIBMSiemens Rolm组成的,目的是签署使计算机、电话及其他网络应用能共同工作的标准。该协会将保证那些获得其批准的产品能够互相作用。

Versit的另一个目标是定义技术规范,而且也已选择了一些现有的标准作为技术规范基础。NovellTSAPIIBMCallPath交换正规化方案、单方呼叫功能以及下一代电话应用的一系列目标都由Versit支持。

电话和计算机目录接口方面的不统一会导致CTI产业分裂成两个阵营的危险。Versit选择了AplleGeoPort作为计算机到电话的物理链接。而Intel则提出了另一个接口,称为高速串行接口。而且拥有CompaqMicrosoft和北电诸多公司的支持。

Intel和其竞争对手Versit正在为完成多媒体通讯的标准展开竞争。如果谁的标准被标准化组织选用,谁就会扩展的更远。双方起初使用同一个宏结构,该结构的顶层的5Mbit/s串行总线用以同步数据,该结构的API层及其上的格式规范为各种PC应用提供数据。

 

4.       CallPath标准

IBMCallPath20世纪80年代后期引入的主机CTI结构,是为了给从PBX到大呼叫中心环境中的IBM的主要链接提供接口。随后软件被扩展为能够容纳诸如OS/2AIX的附加的IBM操作系统。最近,IBM宣布了一个CallPath的客户/服务器版本,反映了此环境的普遍性和重要性。

客户/服务器CallPath是一个几乎服务器的软件包。IBM将其产品定位在通用CTI服务器的概念上,而不是定位于特定的LAN环境(应与NovellNetware LANTSAPI所做的处理一样)。PC、工作站及主机都可以与支持CallPath的服务器相连接,具有同时支持多达4PBX的能力。该标准可以在一系列操作系统上允许,包括WindowsSolarisSCO UNIXIBMAIXOS/2系统。

从应用方面来说,IBM鼓励三方连接的开发者开发与CallPath兼容的应用程序,但IBM的方法没有NovellMicrosoft的那样富有攻击性。IBM的软件开发者工具箱的价格相当高,而且IBM也提供客户软件产品。

另外,IBM正在为它的将CallPath消息转换为指定电话系统形式的网关软件申请许可证。这个软件可以使交换机与现有的CallPath应用程序一同工作,而且方便了这些应用程序从一个主机到另一个主机的可移动性。ACD的主导生产商——Aspect Telecommunication,是第一个可以发放CallPath服务结构(CSA)许可证的电话系统销售商,并获得了IBM的确认。虽然正在向更大的呼叫中心环境迈进,但是从未来的趋势看这个做法在整个CTI工业界是相当恰当的。

5.       SCSA MVIP标准

有两种主要方法将PC电话模块接口与其他设备相关联:一种是信号计算系统结构(SCSA),另一种是多厂家集成协议(MVIP)。这两个标准都是数字电话总线,使电话数据流的传输和交换在PC内或PC与其他设备之间进行。其他的设备可以是任一种已经存在的电话系统,包括PBXKey系统、混合系统、IVR、预拨号机、ACD等。两个标准都有相似的目标:使集成者用不同的来自不同厂家的基于PC的应用卡创建一个终极用户解决办法。因此,客户可以从一个由语音卡、传真卡、视频卡、会议卡、语音辨认卡及其他卡构成的多资源系统获得好处,所有这些都使用同一条总线和软件接口。

MVIP是由一群电话卡厂商倡导的,包括Natural MicroSystemsRhetorexPikaDialogic(基于PC的语音模块的市场领导者)则引入了SCSA总线。这两类总线是互不兼容的,因此应用程序提供者和集成者需要在二者之间做出选择。

 

6.       TAPI标准

Windows电话API被称为TAPIMicrosoftTAPI提供单方连接。TAPI的目的是为Windows平台提供“个人电话”,它包含在Windows95及以后的各种Windows版本中。Windows95/98/Me系列产品支持TAPI2.0Windows2000/XP系列则支持TAPI3.0

Windows电话是由Windows电话动态链接库和两个标准组成的。第一个标准是服务提供者接口(SPI)。SPI被发送给那些希望遵从Windows电话的硬件生产商。如果硬件生产商的产品遵从SPI,就可以与Windows电话的DLL对话。第二个标准称为应用编程接口(API),它被发送给编写应用程序的软件开发者。如果这些开发者的软件遵守API,就可以利用Windows电话DLL的便利驱动任何电话设备或遵从SPI的服务。

 

7.       TSAPI标准

TSAPI在电话和PC之间提供三方连接。CTI应用使用TSAPI控制任何一个呼叫,即使该呼叫进入了公司内部任何一条电话线。可以实现一些功能,比如转移呼叫,交互式语音应答(IVR)以及自动呼叫分配(ACD)等。NovellAT&T发展TSAPI是为了给Netware操作系统增加电话服务。其结果是电话服务API需要使用LAN,不在每个电话和PC上建立直接的连接。而是在服务器电话系统间建立一个连接。这个连接称为PBX-to-Server连接,它在物理上可以由几种方式建立。

 

除了以上的标准外,Apple公司的MTA(将Mac机定位于多目的可处理电话应用的计算机系统)、北电的T-MAP(在TAPITSAPI之间提供转换功能)、SunXTL远程服务和ITU制定的TASC标准也是比较重要的。

11月12日

注册一个文件类型,并指定打开该类型的程序

//---------------------------------------------------------------------------
//函数说明:    注册一个文件类型,并指定打开该类型的程序
//参数说明:    strExt为要注册的文件扩展名
//              strFileType为扩展名所对应的文件类型
//              strExeFile打开该类型文件的程序路径
void RegisterFileType(AnsiString strExt,AnsiString strFileType,
    AnsiString strExeFile)
{
    AnsiString strRegKey="SOFTWARE\\Classes\\";
    AnsiString sValue="";
    TRegistry* reg=new TRegistry();
    reg->RootKey =HKEY_LOCAL_MACHINE;
    //注册扩展名
    reg->OpenKey(strRegKey+strExt,true);
    reg->WriteString("",strFileType);
    reg->CloseKey();
    //注册扩展名对应的文件类型
    strRegKey+=strFileType;
    reg->OpenKey(strRegKey,true);
    sValue="我的文件类型";
    //文件类型描述
    reg->WriteString("",sValue);
    reg->CloseKey();
    //注册文件图标
    reg->OpenKey(strRegKey+"\\DefaultIcon",true);
    sValue=strExeFile+",0";
    reg->WriteString("",sValue);
    reg->CloseKey();
    strRegKey+="\\Shell\\Open\\Command";
    reg->OpenKey(strRegKey,true);
    sValue="\""+strExeFile+"\" %1";
    reg->WriteString("",sValue);
    reg->CloseKey();
    delete reg;
}

windows下进程间通信的手段有哪些?

1   进程与进程通信  

  进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码、数据以及它可利用的系统资源(如文件、管道等)组成。多进程/多线程是Windows操作系统的一个基本特征。Microsoft   Win32应用编程接口(Application   Programming   Interface,   API)提供了大量支持应用程序间数据共享和交换的机制,这些机制行使的活动称为进程间通信(InterProcess   Communication,   IPC),进程通信就是指不同进程间进行数据共享和数据交换。
  正因为使用Win32   API进行进程通信方式有多种,如何选择恰当的通信方式就成为应用开发中的一个重要问题,下面本文将对Win32中进程通信的几种方法加以分析和比较。  

2   进程通信方法  

2.1   文件映射
  文件映射(Memory-Mapped   Files)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。
  Win32   API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。
  应用程序有三种方法来使多个进程共享一个文件映射对象。
  (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。
  (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。
  (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。
  文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。
2.2   共享内存
  Win32   API中共享内存(Shared   Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。
2.3   匿名管道
  管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以是双向的一管道的两端点既可读也可写。
  匿名管道(Anonymous   Pipe)是   在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道,然后由要通信的子进程继承通道的读端点句柄或写   端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程可以使用管道直接通信,不需要通过父进程。
  匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。
2.4   命名管道
  命名管道(Named   Pipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。
  命名管道提供了相对简单的编程接口,使通过网络传输数据并不比同一计算机上两进程之间通信更困难,不过如果要同时和多个进程通信它就力不从心了。
2.5   邮件槽
  邮件槽(Mailslots)提   供进程间单向通信能力,任何进程都能建立邮件槽成为邮件槽服务器。其它进程,称为邮件槽客户,可以通过邮件槽的名字给邮件槽服务器进程发送消息。进来的消   息一直放在邮件槽中,直到服务器进程读取它为止。一个进程既可以是邮件槽服务器也可以是邮件槽客户,因此可建立多个邮件槽实现进程间的双向通信。
  通过邮件槽可以给本地计算机上的邮件槽、其它计算机上的邮件槽或指定网络区域中所有计算机上有同样名字的邮件槽发送消息。广播通信的消息长度不能超过400字节,非广播消息的长度则受邮件槽服务器指定的最大消息长度的限制。
  邮件槽与命名管道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包)完成的,一旦网络发生错误则无法保证消息正确地接收,而命名管道传输数据则是建立在可靠连接基础上的。不过邮件槽有简化的编程接口和给指定网络区域内的所有计算机广播消息的能力,所以邮件槽不失为应用程序发送和接收消息的另一种选择。
2.6   剪贴板
  剪贴板(Clipped   Board)实质是Win32   API中一组用来传输数据的函数和消息,为Windows应用程序之间进行数据共享提供了一个中介,Windows已建立的剪切(复制)-粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在应用程序中执行剪切或复制操作时,应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择适合自己的格式。
  剪贴板是一个非常松散的交换媒介,可以支持任何数据格式,每一格式由一无符号整数标识,对标准(预定义)剪贴板格式,该值是Win32   API定义的常量;对非标准格式可以使用Register   Clipboard   Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用,不能在网络上使用。
2.7   动态数据交换
  动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式。应用程序可以使用DDE进行一次性数据传输,也可以当出现新数据时,通过发送更新值在应用程序间动态交换数据。
  DDE和剪贴板一样既支持标准数据格式(如文本、位图等),又可以支持自己定义的数据格式。但它们的数据传输机制却不同,一个明显区别是剪贴板操作几乎总是用作对用户指定操作的一次性应答-如从菜单中选择Paste命令。尽管DDE也可以由用户启动,但它继续发挥作用一般不必用户进一步干预。DDE有三种数据交换方式:
  (1)   冷链:数据交换是一次性数据传输,与剪贴板相同。
  (2)   温链:当数据交换时服务器通知客户,然后客户必须请求新的数据。
  (3)   热链:当数据交换时服务器自动给客户发送数据。
  DDE交换可以发生在单机或网络中不同计算机的应用程序之间。开发者还可以定义定制的DDE数据格式进行应用程序之间特别目的IPC,它们有更紧密耦合的通信要求。大多数基于Windows的应用程序都支持DDE。
2.8   对象连接与嵌入
  应用程序利用对象连接与嵌入(OLE)技术管理复合文档(由多种数据格式组成的文档),OLE提供使某应用程序更容易调用其它应用程序进行数据编辑的服务。例如,OLE支持的字处理器可以嵌套电子表格,当用户要编辑电子表格时OLE库可自动启动电子表格编辑器。当用户退出电子表格编辑器时,该表格已在原始字处理器文档中得到更新。在这里电子表格编辑器变成了字处理器的扩展,而如果使用DDE,用户要显式地启动电子表格编辑器。
  同DDE技术相同,大多数基于Windows的应用程序都支持OLE技术。
2.9   动态连接库
  Win32动态连接库(DLL)中的全局数据可以被调用DLL的所有进程共享,这就又给进程间通信开辟了一条新的途径,当然访问时要注意同步问题。
  虽然可以通过DLL进行进程间数据共享,但从数据安全的角度考虑,我们并不提倡这种方法,使用带有访问权限控制的共享内存的方法更好一些。
2.10   远程过程调用
  Win32   API提供的远程过程调用(RPC)使应用程序可以使用远程调用函数,这使在网络上用RPC进行进程通信就像函数调用那样简单。RPC既可以在单机不同进程间使用也可以在网络中使用。
  由于Win32   API提供的RPC服从OSF-DCE(Open   Software   Foundation   Distributed   Computing   Environment)标准。所以通过Win32   API编写的RPC应用程序能与其它操作系统上支持DEC的RPC应用程序通信。使用RPC开发者可以建立高性能、紧密耦合的分布式应用程序。
2.11   NetBios函数
  Win32   API提供NetBios函数用于处理低级网络控制,这主要是为IBM   NetBios系统编写与Windows的接口。除非那些有特殊低级网络功能要求的应用程序,其它应用程序最好不要使用NetBios函数来进行进程间通信。
2.12   Sockets
  Windows   Sockets规范是以U.C.Berkeley大学BSD   UNIX中流行的Socket接口为范例定义的一套Windows下的网络编程接口。除了Berkeley   Socket原有的库函数以外,还扩展了一组针对Windows的函数,使程序员可以充分利用Windows的消息机制进行编程。
  现在通过Sockets实现进程通信的网络应用越来越多,这主要的原因是Sockets的跨平台性要比其它IPC机制好得多,另外WinSock   2.0不仅支持TCP/IP协议,而且还支持其它协议(如IPX)。Sockets的唯一缺点是它支持的是底层通信操作,这使得在单机的进程间进行简单数据传递不太方便,这时使用下面将介绍的WM_COPYDATA消息将更合适些。
2.13   WM_COPYDATA消息
  WM_COPYDATA是一种非常强大却鲜为人知的消息。当一个应用向另一个应用传送数据时,发送方只需使用调用SendMessage函数,参数是目的窗口的句柄、传递数据的起始地址、WM_COPYDATA消息。接收方只需像处理其它消息那样处理WM_COPY   DATA消息,这样收发双方就实现了数据共享。
  WM_COPYDATA是一种非常简单的方法,它在底层实际上是通过文件映射来实现的。它的缺点是灵活性不高,并且它只能用于Windows平台的单机环境下。