袁瑞's profile袁瑞的共享空间PhotosBlogGuestbookMore Tools Help

Blog


    May 08

    C#的结构和类的区别

    结构:属于值类型,隐式派生自System.ValueType。结构是隐式密封的,因此不能被继承,但可以有构造函数(必须保留默认构造函数,不能显式创建无参数的构造函数,即自定义构造函数必须都有参数),可以实现接口,可以包含属性、方法、事件及重载运算符。成员默认是public。结构可以实现IDisposbale接口,但不能重写Finalized方法,因为其不会分配到托管堆上。

    创建结构时如果不使用new关键字,需要为未赋值的成员赋值后才能使用。

    类:属于引用类型,支持继承,但不能多继承。类成员默认私有访问权限。可以实现IDisposbale接口,可以间接重写Finalized方法(格式模仿C++的析构函数,不能带修饰符、参数和返回值)。类为了防止继承,可以声明为sealed。不想类被实例化,可以声明为abstract或静态类。

    March 24

    在DHTML中响应ActiveX控件的事件

    转载自www.diybl.com  作者:佚名

    ActiveX控件可以用连接点创建事件,此事件可以在DHTML网页中被javascript函数处理,在js中响应ActiveX控件事件的方法如下:
    1. 静态创建方法     
    <script>
    function  OnEvent1()
    {
    }
    </script>
    <OBJECT id="myControl"
                       codeBase="myControl.cab"              
                       classid="clsid:1ACA02DF-AF52-4371-89B9-B9245BD21831"
    ></OBJECT>
    <script       language=javascript
                       FOR="myControl"
                       EVENT="event1()">   
                       OnEvent1();
    </script>
    2. 动态创建方法
    function onBodyLoad()
    {
            myControl = document.createElement("OBJECT");
            document.body.appendChild(myControl);
            myControl.codeBase = "myControl.cab";
            myControl.classid = "clsid:1ACA02DF-AF52-4371-89B9-B9245BD21831";
            myControl.attachEvent("event1", OnEvent1);
    }
    <body onload="onBodyLoad()">
    ...
    3. 错误的动态创建方法
    function onBodyLoad()
    {
            myControl = new ActiveXObject(myControl.Test);
            myControl.attachEvent("event1", OnEvent1);
            ~~~~~~~~~~~~~~~~~~此时会报告“无此方法”,这是因为
    用new ActiveXObject创建的对象并非一个DOM对象,因此不能
    调用attachEvent方法。而用document.createElement("OBJECT")
    创建的才是一个DOM对象。
    }
    <body onload="onBodyLoad()">

    March 16

    在SuSE 11.1 中安装mono 2.2

    国内关于mono的介绍文章比较少,笔者这里将自己安装使用mono的经验拿出来同大家共同分享。

    mono(西班牙语中的“monky”,这里有编程者的昵称:code monkey寓意)是一个Novell公司资助的开源项目。是一个满足ECMA-334、335规范的CLI多平台实现,即公共语言基础设施。是.NET平台无关性的一个很有力的证明。目前其支持Windows、OSX、Solaris及各种Linux。已经支持.NET2.0全部特性及部分.NET3.5特性(目前已经有对WCF支持),新版兼容VisualStudio.NET 2005/2003的工程文件(sln文件),即在windows上创建的工程可以直接转到其他平台上编译运行,Mono自带的例子即是一个证明。同时基于mono的mono developer也是一个开源的.NET IDE工具,目前最新版本是2.0beta。

    首先在VMWare6.5中安装SuSE11.1操作系统。

    clip_image002

    安装系统时,语言选择简体中文。

    安装过程中,基本采用默认安装。在选择安装软件包中,建议取消.NET中的老版本mono包。

    clip_image004

    安装中有一个问题,我一直没有解决,就是安装盘中自带的老版本的Open Virtual Machine Tools一直无法取消安装。结果是VMWare高版本VMTool无法同guest操作系统的SuSE进行文件系统共享。

    clip_image006

    操作系统安装完毕后,可以从mono网站上下载2.2版本的安装包。

    如果手工安装,可以按以下顺序进行:

    mono-core-2.2-14.1.i586.rpm

    mono-data-2.2-14.1.i586.rpm

    mono-data-sqlite-2.2-14.1.i586.rpm

    mono-core-debuginfo-2.2-14.1.i586.rpm

    mono-core-debugsource-2.2-14.1.i586.rpm

    笔者推荐使用安装源的方式进行,这样可以有效避免安装中出现的包依赖问题。打开YaST,运行“软件安装源”。

    clip_image008

    在安装源内可以添加URL方式的安装源:

    http://ftp.novell.com/pub/mono/download-stable/openSUSE_11.1/

    也可以将RPM文件下载到本地后,添加本地目录。笔者采用本地安装:

    clip_image010

    配置好安装源后,就可以打开YaST的软件管理器进行mono安装了。

    clip_image012

    只要安装好mono-core,mono的基本编译和运行环境就具备了。可以在命令行内运行测试:gmcs –version

    clip_image014

    编写一个C#的源文件helloworld.cs。内容如下:

    clip_image016

    编译运行结果如下:

    clip_image018

    编写具有WinForm的HelloWorld,前提需要安装mono-winforms。代码如下:

    clip_image020

    编译运行结果如下:

    clip_image022

    clip_image024

    March 04

    自己编写的一个Ogame外挂,TW_U2测试通过,有喜欢的朋友去下吧!

    目前版本号:0.1.5.11 tw_u2 测试版。

    增加:支持对敌意舰队活动进行告警,舰队活动数量增加告警,最小化到系统托盘等功能。

    http://www.namipan.com/d/44a5c58f58c0a533ea73d286356256123e7f88ab00ba0400

    February 02

    2009年的窗花

    DSC03538 []

    DSC03539 []

    动态创建的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版本存在这个问题。

    January 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下运行无法读取数据,程序仍然遇到了类似的问题。

    January 08

    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

    December 26

    闺女满百天了

    圆滚滚胖嘟嘟粉可爱

    滑溜溜柔软软萌掉渣

     

    DSC02389

    November 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;
    }
    October 09

    BDS 2009 Update1 破解工具

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

    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,在很多细节上还有问题。

    September 05

    使用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做了什么工作?是否出现了问题?

    August 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.
    August 04

    如何处理算数表达式

    今天看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;
    }

    July 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算法就是一个解决办法不是。

    May 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) );

    }