[转载]浅谈C#中常见的委托

mikel阅读(1085)

[转载]浅谈C#中常见的委托 – 程序诗人 – 博客园.

一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的。

关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微,尤其是张子阳的那一篇。我就不用多废话了。

今天我要说的是C#中的三种委托方式:Func委托,Action委托,Predicate委托以及这三种委托的常见使用场景。

Func,Action,Predicate全面解析

首先来说明Func委托,通过MSDN我们可以了解到,Func委托有如下的5种类型:

1) *delegate TResult Func<TResult>(); 

            (2)*delegate TResult Func<T1,TResult>(T1 arg1);

            (3) *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);

            (4)*delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);

            (5)*delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4);

其中(1)只能委托无参但是有返回值的函数,TResult就是其返回类型。

而(2)只能委托具有一个传入参数,有返回值的函数,T1为一个传入参数,TResult为返回类型。

(3)只能委托具有二个传入参数,有返回值的函数,T1和T2为两个传入参数,TResult为返回类型,(4)和(5)以此类推。

那么如何来使用呢? 下面给出一个简单的几个例子:

           #region Func委托
            
            ///Func<TResult>的用法
            ///这里TResult代表函数的返回值类型
            ///只能代理返回值为TResult类型的无参函数
            Func<string> func = delegate()
            {
                return "我是Func<TResult>委托出来的结果";
            };
            Console.WriteLine(func());
            Console.ReadKey();

            ///Func<T,TResult>的用法
            ///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型
            ///只能代理参数为T类型,返回值为TResult类型的函数
            Func<string, string> funcOne = delegate(string s)
            {
                return s.ToUpper();
            };
            Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果"));
            Console.ReadKey();

            ///Func<T1,T2,TResult>的用法
            ///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型
            ///只能代理参数为T1,T2类型,返回值为TResult类型的函数
            Func<string, string, string> funcTwo = delegate(string value1, string value2)
            {
                return value1 + " " + value2;
            };
            Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果"));
            Console.ReadKey();

            #endregion

上面代码中,我用了匿名方法来代替函数,其中delegate()代表无参函数,delegate(string s)代表有一个传入参数的函数,以下的以此类推。

然后需要说明的就是Action委托,这个委托也是非常常用的,尤其是在涉及到线程和界面交互的时候,配合着lamada表达式使用,非常方便的实现二者的交互。后面我会提到用法。

来看看Action委托的几种表现形式:

1) * delegate void Action(); 无参,无返回值

              (2)* delegate void Action<T>(T1 arg1);

            (3)* delegate void Action<T1,T2>(T1 arg1, T2 arg2);

            (4)* delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3);

            (5)* delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4);

从上面可以看出,总共有5中表现形式,其中(1)既没有传入参数,也没有返回值,那么它适合代理那些无参,无返回值的函数;(2)有一个传入参数,无返回值,适合代理无参,有一个返回值的函数,(3)(4)(5)以此类推。最都容纳四个传入参数。

那么如何使用呢?下面有一些简单的例子:

          #region Action的用法
            ///Action<T>的用法
            ///这里的T为代理函数的传入类型,无返回值
            Action<string[]> action = delegate(string[] x)
            {
                var result = from p in x
                             where p.Contains("s")
                             select p;
                foreach (string s in result.ToList())
                {
                    Console.WriteLine(s);
                }
            };
            string[] str={ "charlies","nancy","alex","jimmy","selina"};
            action(str);
            Console.ReadKey();

            #endregion

上面的例子是通过传入的String类型的数组,找出其中包含有字符s的项,然后输出到控制台。

最后一个就是Predicate委托,这个的形式比较少一些,就是一个传入参数,返回值为bool类型,具体示例如下:

            #region Predicate
          ///bool Predicate<T>的用法
            ///输入一个T类型的参数,返回值为bool类型
            Predicate<string[]> predicate = delegate(string[] x)
            {
                var result = from p in x
                             where p.Contains("s")
                             select p;
                if (result.ToList().Count > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            };
            string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" };
            if (predicate(_value))
            {
                Console.WriteLine("They contain.");
            }
            else
            {
                Console.WriteLine("They don't contain.");
            }
            Console.ReadKey();

            #endregion

上面的代码其实也是判断String数组中有没有包含s的项,有的话就在控制台打印出  They contain.没有的话就打印出They don’t contain.

总结一下这三个的特点就是:

Func可以接受0个至4个传入参数,必须具有返回值

Action可以接受0个至4个传入参数,无返回值

Predicate只能接受一个传入参数,返回值为bool类型

下面附上全部实现代码:

View Code

在WinForm和WPF中,利用Func,Action,Predicate进行线程UI交互

下面这部分主要讲解如何在WinForm中利用这些委托进行线程和界面的交互。

首先对于Func来说,由于其必须具有返回值,所以我们可以利用如下代码来实现线程和界面的交互:

        #region 利用Func实现线程和界面交互
        private void AlternationUsingFunc(object text)
        {
            //无参数,但是返回值为bool类型
            this.Invoke(new Func<bool>(delegate()
            {
                button1.Text = text.ToString();
                return true; //返回值
            }));
        }

        private void AlternationUsingFuncThread()
        {
            WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc);
            ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            AlternationUsingFuncThread();
        }
        #endregion

其中

 this.Invoke(new Func<bool>(delegate()
            {
                button1.Text = text.ToString();
                return true; //返回值
            }));

这段代码中利用了Func<TResult>这种类型,也就是没有传入参数,但是有一个bool类型的返回值,然后将这个函数利用加入到线程池中,最后运行,这里我们成功的设置了button1的text为“Func的使用”。

然后,对于Action来说,由于其可以无参,无返回值,那么它的交互方式最为简便,同时也是使用最多的,先看有参的调用方式:

        #region 利用Action实现线程和界面交互
        private void AlternationUsingAction(object text)
        {
            //需要一个T类型的参数,无返回值
            this.Invoke(new Action<object>(delegate(object myText)
            {
                myText = text;
                button2.Text = text.ToString();
            }),text);
        }

        private void AlternationUsingActionThread()
        {
            WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction);
            ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            AlternationUsingActionThread();
        }
        #endregion

在上面的代码示例中,我们使用了带有一个传入参数的Action委托,当然了,匿名类型delegate(object myText)匿名代理了具有一个传入参数的函数。

其实简单点来说,可以像如下方式使用:

this.Invoke((Action)(()=>
            {
                button2.Text = text.ToString();
            }));

这样就显得非常的方便。

最后一个当然是Predicate委托,和上面类似,只是写起来麻烦一些,它需要一个传入参数,并且返回一个bool类型:

        #region 利用Predicate实现线程和界面的交互
        private void AlternationUsingPrecidate(object text)
        {
            //需要一个T类型的参数,返回bool类型
            this.Invoke(new Predicate<object>(delegate(object myText)  
            {
                myText = text;
                button3.Text = myText.ToString();
                return true;   //返回值
            }),text);
        }

        private void AlternationUsingPrecidateThread()
        {
            WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate);
            ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用");
        }

        private void button3_Click(object sender, EventArgs e)
        {
            AlternationUsingPrecidateThread();
        }
        #endregion

具体的注释我已经写在代码中了,最后运行,能成功的将button3的Text置为“Predicate的使用.”

下面是全部实现代码:

View Code

那么,现在对于WPF来说,该如何来使用呢?其实在WPF中,和winform中类似,只是在WPF中要实现线程和界面的交互,我们需要用 Dispatcher来实现,也就是形如Control.Dispatcher.Invoke()的方式,由于与Winform实现方式无多大差别,这里 我就直接附上全部代码:

View Code

逐个点击界面上的按钮,我们可以看到成功实现了线程和UI的交互:

当然,上面我们只是说到了在WinForm中和WPF中如何来使用的情况,代码比较简单,也没有具体的应用场景,下面我们将结合中WPF来模拟一个具体的应用场景:

实现异步和线程同步

现在假设我有一个txt文档,名称为newEXO.txt,里面大概有5w行记录,文件大小为30MB左右;同时我手边还有一个 oldEXO.txt里面也有5w数据,但是其中有一些记录和newEXO.txt中的不同,我现在需要对比两个txt文档,找出不同的记录,并对不同的 记录进行上色操作。

那么现在这里很明确了,我们需要两个函数,一个是读取记录的函数ChangeText(),一个是上色的函数ChangeColor()。

但是在实际操作中,发现如果我直接利用wpf读取数据的话,逐行读取,耗时10s左右,也就是用户界面会被阻塞10s,然后才会显示给用户,这个体验性是相当不好的,所以拟采用异步方式来导入数据。

同时,考虑到差异比较也会比较耗时,所以也准备采用异步方式来进行对比。

那么问题来了,两个均采用异步方式进行,难免会发生数据未导入完成就开始进行差异比较的可能,所以这里还涉及到一个线程同步的问题。

现在,这里有三个操作,异步的数据导入,异步的差异比较并上色,线程同步。

首先我们看异步导入,你也可以自己实现一个异步类,通过委托的BeginInvoke方法和EndInvoke方法来实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ThreadSynchorous
{
    public  class AsyncInvoke
    {
        public void BeginAsync(Func<bool> MyFunction)
        {
            Func<bool> func = new Func<bool>(MyFunction);
            IAsyncResult iar = func.BeginInvoke(new AsyncCallback(EndAsync), func);
        }

        public void EndAsync(IAsyncResult iar)
        {
            Func<bool> func = (Func<bool>)iar.AsyncState;
            func.EndInvoke(iar);
        }
    }
}

由于Action委托的使用方式最为便捷,这里我采用Action委托方式来进行,当然了,:

 private void ChangeText()
        {
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                string filename = @"C:\newEXO.txt";
                using (StreamReader sr = new StreamReader(filename, Encoding.Default))
                {
                    string result;
                    while ((result = sr.ReadLine()) != null)
                    {
                        //here perform action
                    }
                }
                //label1.Dispatcher.Invoke((new Action(delegate()
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                }));
            }));
        }

首先是当点击button1按钮的时候,就启动ChangeText()函数,也即数据导入函数,然后label1会在加载完毕的时候,给出提示信息。

下面再看看ChangeColor()函数:

 private void ChangeColor()
        {
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                this.button1.Background = Brushes.Red;

                //here perform large amount of data action and color the result

                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                }));

            }));
        }

可以看到也是当button1点击的时候,会触发ChangeColor函数。由于二者操作比较耗时,为了防止用户界面阻塞,我们放到线程池中:

  ThreadPool.QueueUserWorkItem(o => ChangeText());

  ThreadPool.QueueUserWorkItem(o => ChangeColor());

从上面可以看出,当点击按钮button1的时候,两个函数同时被引发,也就是点击按钮的时候进行如下操作:

private void button1_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o => ChangeText());
            ThreadPool.QueueUserWorkItem(o => ChangeColor());
            label1.Content += " \r\n-------------------------\r\n";
        }

看到了什么?

看到了线程运行的混乱,我们本想让数据先加载,然后比较得出差异着色,可惜上面的结果中却与想象中的相差甚远.

这里的ChangeText()函数和ChangeColor()函数肯定不会像想象的那样顺序执行,那么代码就有问题了,所以为了避免这个问题,我们必须进行线程同步,如何来进行呢? 方法很多,这里我采用EventWaitHandle方式来进行。

EventWaitHandle的Reset方式用来重置信号量,告诉其他运行的进程,你们需要被阻塞;Set方式用来释放信号量,告诉其他运行的进程,你们的阻塞已经被解除,可以继续运行了。

但是其他进行通过什么来知道自己是否可以解除阻塞状态呢? 那就是利用WaitOne方式来判断:

也就是按照如下的代码模式来:

EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);
private void ChangeText()
{
     waitMeHandle.Reset();  //即将进入下列执行过程,其他需要阻塞
  //....
     waitMeHandle.Set(); //释放
}
private void ChangeColor()
{
      waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行
     //  ...
}

当然上面我举出的例子只是一个Sample,我写过这个软件,利用的是BeginInvoke和EndInvoke方式实现的异步调用,有兴趣可以参见我的这篇文章中提到的软件:

我的作品:PlainTextCompare

下面是软件截图:

另外在写这篇文章的时候,我在StackOverFlow上面有过提问,就是关于当前的Thread的ThreadId为什么一致的问题, 应该说两个函数放到了ThreadPool中,结果出来的ThreadId应该不一样才对呀.

其实,正确的答案是我的打印出ThreadId的信息都放在了label1.Dispatcher.Invoke这句话中,而这个Lable1属于 界面UI,也就是前台线程,所以ThreadId会是一样的,如果你直接在进入函数的时候,输出ThreadId,你就会发现两个函数运行在不同的线程上 了.

StackOverFlow的问答,请点击这里

下面是全部代码:

View Code

using System;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Threading;
using System.IO;

namespace ThreadSynchorous
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            asyncInvoke = new AsyncInvoke();
        }
        AsyncInvoke asyncInvoke;
        EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset);

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            ThreadPool.QueueUserWorkItem(o => ChangeText());
            ThreadPool.QueueUserWorkItem(o => ChangeColor());

            label1.Content += " \r\n-------------------------\r\n";
        }

        
        private void ChangeText()
        {
            waitMeHandle.Reset();  //即将进入下列执行过程,其他需要阻塞
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                string filename = @"C:\MyLearn\eqrms_hk_20111219_listedposn_ff\EQRMS_HK_20111219_EXO.txt";
                using (StreamReader sr = new StreamReader(filename, Encoding.Default))
                {
                    string result;
                    while ((result = sr.ReadLine()) != null)
                    {
                        //here perform action
                    }
                }
                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") ";
                    waitMeHandle.Set(); //释放
                }));
            }));
        }

        
        private void ChangeColor()
        {
            waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行
            this.button1.Dispatcher.Invoke((Action)(()=>
            {
                this.button1.Background = Brushes.Red;

                //here perform large amount of data action and color the result

                label1.Dispatcher.Invoke((Action)(()=>
                {
                    label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") ";
                }));

            }));
        }
    }
}

本文中涉及到的源码,可以从这里下载

[转载]wordpress优化 使用SAE提供的jquery.js替代wordpress原生的

mikel阅读(1203)

[转载]wordpress优化 使用SAE提供的jquery.js替代wordpress原生的 – 圣者 – 博客园.

JQuery可以说是一款非常强大的js库,提供了dom节点的寻找规则支持xpath,还有很多页面特效,淡出淡入,滑动等等

所以wordpress引用了这块非常大的js,最新版本没压缩的话 竟然达到了249,159 bytes 每次访问页面,不同IP的情况下都会去加载如此庞大的库,真正使用到的估计寥寥无几。

前几天我为我的新浪云豆消耗真的是想尽了办法,包括前面所谈到的《wordpress优化第一招 压缩css和js减少流量提高博客速度(尤其适用SAE)》 参见地址: http://www.ij2ee.com/2012/01/04/wordpress%E4%BC%98%E5%8C%96%E7%AC%AC%E4%B8%80%E6%8B%9B-%E5%8E%8B%E7%BC%A9css%E5%92%8Cjs%E5%87%8F%E5%B0%91%E6%B5%81%E9%87%8F%E6%8F%90%E9%AB%98%E5%8D%9A%E5%AE%A2%E9%80%9F%E5%BA%A6%E5%B0%A4%E5%85%B6.html

但是还是效果不佳,因为JQuery实在是太大了。每天要是有100个访问量估计云豆耗的不少了,更多的话,我辛辛苦苦挣出的云豆就没了。

所以我打算开始着手替换JQuery文件。这时我比较庆幸我学过一点html,js。

一般jQuery.js会在wordpress主题目录wp-content\themes\twentyeleven\下的header.php里引用。找出来替换成sae提供的 http://lib.sinaapp.com/?path=/jquery 至于什么版本大家自己看着办吧。我用的1.6的 记得用min版。那是压缩过的。SAE的这个库是非常好的.比google及microsoft的速度快多了。

但是我的主题比较的奇怪。好吧,这主题是别人的,而且我还非常不厚道的去了人家的链接。它是引用的wordpress内置的方法去调用jQuery 所以我实在是不大好改。

中途使用过一个方法来变相的调用sae的jQuery,但是没成功,这里我就不说了。

wordpress的jquery js 是在下面的目录里wp-includes\js\jquery 你可以和我一样 吧目录下的jquery内容去掉。这样就算调用了 也不会有流量,最多就是多个http请求 ,当然后面的优化会提到减少http请求提高网站速度。

在清空内容后,你可以在你的wp-content\themes\twentyeleven\下的header.php里的</head>标 签后 加上sae jquery的引用。 记得一定要加载所有js前面,防止后面会有js依赖于jquery.

我这是懒人的做法,当然你也可以去修改function里的代码。我这就不了。

[转载]Jquery+Ajax+phpmailer类库实现自动向指定邮箱发送邮件

mikel阅读(1403)

[转载]Jquery+Ajax+phpmailer类库实现自动向指定邮箱发送邮件 – 八戒Done – 博客园.

最近闲的无聊,写了个小不道德的整人的暗恋测试网站,实现我向友人发送带我邮箱的测试网址,朋友的测试结果自动发送到我的邮箱!(当然啦,你们也可以加上自己的邮箱去整好友)

不知道各位听懂否?不懂的就直接去试试吧~~哈,小弟邮箱肯定又要多很多测试结果的邮件了。

带我的邮箱的测试地址:http://www.arqi.cc/demo/love/love.html?em=arqicc@126.com

申请你们自己的测试地址:http://www.arqi.cc/demo/love/result.html(阿奇arqi.cc关注web,关注前端开发)

实现方式:

通过访问网址获得邮箱地址,然后利用JQuery的ajax方法传输数据到PHP邮箱服务器(此处上网下了个phpmailer类库实现)

网站源码全部公开,大家感兴趣可以访问阿奇arqi.cc复制粘贴相关源码!(love.html+result.html+JQuery库+PHPMailer类库)

<script language="javascript">
function sendemail(n1,n2,n3,n4){
    var mY_url = window.location.search;
    mY_url = mY_url.substr(1,(mY_url.length-1));
    var arr_url = mY_url.split("=");
    if(arr_url==""){
        //假如别人发现网址中含有你的邮箱,去掉了邮箱访问,就认为整人失败!
        alert("测试失败!整蛊失败!纯属娱乐,切莫较真!");
        window.location.href="http://www.arqi.cc/demo/love/result.html";
    }else{
        var emailstr=arr_url[1];
        var titlestr="你的好友"+n1+"的暗恋测试结果!";
        var contentstr="你的好友"+n1+"的暗恋对象是:"+n2+""+n3+""+n4+"!快去逗逗TA吧!暗恋测试网址:www.arqi.cc/demo/love/result.html";
        $.ajax({
                 type:"POST",
                 url:"sendemail.php", 
                 data:"email="+emailstr+"&title="+titlestr+"&content="+contentstr,
                 success:function(data){
                             alert(data);
                            alert("你被骗啦!你的暗恋对象已经发送给你的好友!"+emailstr+"!\n整蛊成功!纯属娱乐,切莫较真!");
                            window.location.href="http://www.arqi.cc/demo/love/result.html";
                     }
        });
        
    }
}
</script>
    <div id="main">
        <form id="form" method="post" onsubmit="return toResult()">
            <h2>您的姓名:</h2>
            <input type="text" id="name_self" name="name_self" class="textbox" />
            <h2>暗恋的TA的姓名:<span>(输入三个你暗恋的人的姓名,顺序越前表示越喜欢)</span></h2>
            <input type="text" id="name_lover1" name="name_lover1" class="textbox" />
            <input type="text" id="name_lover2" name="name_lover2" class="textbox" />
            <input type="text" id="name_lover3" name="name_lover3" class="textbox" />
            <div id="btnbox">
                <input type="submit" name="submit" value="马上开始暗恋配对测试吧!" class="btn" />
                <span>(点击“马上开始”表示您同意<a href="javascript:void(0);" onclick="view()">免责声明</a>)</span>
            </div>
        </form>
    </div>

PHPMailer类库的配置:当然咯还能配置附件等等功能,此处不需要,有需要的可以百度下,搜索相关文章。

PHPMailer下载(百度一下下个也行)

<?php  
    header('Content-Type: text/html; charset=utf-8');  
     require("class.phpmailer.php");   
     error_reporting(E_ERROR);  
     function smtp_mail ( $sendto_email, $subject, $body ) {  
        $mail = new PHPMailer();   
        $mail->IsSMTP();            
        $mail->Host = "smtp.126.com"; //发送邮件的SMTP服务器  
        $mail->Username = "arqicc@126.com";//发送邮件的邮箱用户名   
        $mail->Password = "arqicc";//发送邮件的邮箱密码          
        $mail->FromName =  "www.arqi.cc";//发送邮件的发件人昵称   
        $mail->SMTPAuth = true;            
        $mail->From = $mail->Username;  
        $mail->CharSet = "utf-8"; //设定邮件编码,默认ISO-8859-1,如果发中文此项必须设置,否则乱码            
        $mail->Encoding = "base64";   
        $mail->AddAddress($sendto_email); //收件人地址   
        $mail->IsHTML(true);   
        $mail->Subject = $subject;  //邮件标题
        $mail->Body = $body;  //邮件内容
        $mail->AltBody ="text/html";   
        if(!$mail->Send()) {   
             return "ERROR";   
        }
        else{  
             return "SUCCESS!";   
        }  
    }
/////获取前途传输过来的收件人地址、邮件标题、邮件内容///////////
    $email=$_POST['email'];
    $title=$_POST['title'];
    $content=$_POST['content'];
    $str=smtp_mail($email,$title,$content); //执行函数
    echo $str;//输出返回值    
?>

文章转载请注明:八戒Done–阿奇arqi.cc

[转载]SQL截取字符串(substring与patindex的使用)

mikel阅读(1360)

[转载]SQL截取字符串(substring与patindex的使用) – 爱与决择 – 博客园.

首先学习两个函数

1.substring 返回字符、binary、text 或 image 表达式的一部分。
基本语法:SUBSTRING ( expression , start , length )
expression:字符串、二进制字符串、text、image、列或包含列的表达式
start:整数,指定子串的开始位置      注:SQL中”1″表示字符串中的第一个字符,而.NET中”0″表示第一个字符
length:整数,指定子串的长度(要返回的字符数或字节数)

2.patindex 返回指定表达式中某模式第一次出现的起始位置;如果在全部有效的文本和字符数据类型中没有找到该模式,则返回零。
基本语法:PATINDEX ( ‘%pattern%‘ , expression )
pattern:字符串。可以使用通配符,但 pattern 之前和之后必须有 % 字符(搜索第一个和最后一个字符时除外)。pattern 是短字符数据类型类别的表达式
expression:表达式,通常为要在其中搜索指定模式的列,expression 为字符串数据类型类别

下面结合上面的两个函数,截取字符串中指定的字符

declare @a varchar(50)
set @a=2009年7月15日星期五
select substring(@a,1,4) 获取年份2009
declare @b int
set @b=patindex(%日%,@a) 获取’日’这个字符在字符串中的位置,即10
select substring(@a,6,@b5) 获取日期’7月15日’

[转载]WinRAR(WinZip)压缩与解压实现(C#版Window平台)

mikel阅读(850)

[转载]WinRAR(WinZip)压缩与解压实现(C#版Window平台) – Stone_W – 博客园.

本文的原理是借助Windows平台安装的WinRAR(WinZip)实现C#程序的调用(注:WinRAR压缩解压WinZip同样适用)。

先来看WinRAR(WinZip)自身的支持调用命令:

压缩命令:a {0} {1} -r 【{0}:压缩后文件名|{1}:待压缩的文件物理路径】

ex:”a 你妹.rar f:\\MM -r” (含义为将f盘下MM的文件夹压缩为”你妹.rar”文件)

解压命令:x {0} {1} -y 【{0}:待解压文件名称|{1}:待解压文件物理路径】

ex:”x 幺妹.rar f:\\幺妹 -y”(待压缩文件物理路径:”f:\\幺妹\\幺妹.rar”)

参数说明

参数

含义

a

添加文件到压缩包

x

以完整路径从压缩包解开压缩

WinZip(WinRAR)调用通用类

using System;
using System.Collections.Generic;
using System.Text;

//--------------using
using System.Diagnostics;
using Microsoft.Win32;
using System.IO;


/// <summary>
/// Name:Stone
/// DateTime: 2011/12/31 16:39:26
/// Description:WinRAR压缩
/// </summary>
public class WinRARCSharp
{
    // WinRAR安装注册表key
    private const string WinRAR_KEY = @"WinRAR.ZIP\shell\open\command";

    /// <summary>
    /// 利用 WinRAR 进行压缩
    /// </summary>
    /// <param name="path">将要被压缩的文件夹(绝对路径)</param>
    /// <param name="rarPath">压缩后的 .rar 的存放目录(绝对路径)</param>
    /// <param name="rarName">压缩文件的名称(包括后缀)</param>
    /// <returns>true 或 false。压缩成功返回 true,反之,false。</returns>
    public bool RAR(string path, string rarPath, string rarName)
    {
        bool flag = false;
        string rarexe;       //WinRAR.exe 的完整路径
        RegistryKey regkey;  //注册表键
        Object regvalue;     //键值
        string cmd;          //WinRAR 命令参数
        ProcessStartInfo startinfo;
        Process process;
        try
        {
            regkey = Registry.ClassesRoot.OpenSubKey(WinRAR_KEY);
            regvalue = regkey.GetValue("");  // 键值为 "d:\Program Files\WinRAR\WinRAR.exe" "%1"
            rarexe = regvalue.ToString();
            regkey.Close();
            rarexe = rarexe.Substring(1, rarexe.Length - 7);  // d:\Program Files\WinRAR\WinRAR.exe

            Directory.CreateDirectory(path);
            //压缩命令,相当于在要压缩的文件夹(path)上点右键->WinRAR->添加到压缩文件->输入压缩文件名(rarName)
            cmd = string.Format("a {0} {1} -r",
                                rarName,
                                path);
            startinfo = new ProcessStartInfo();
            startinfo.FileName = rarexe;
            startinfo.Arguments = cmd;                          //设置命令参数
            startinfo.WindowStyle = ProcessWindowStyle.Hidden;  //隐藏 WinRAR 窗口

            startinfo.WorkingDirectory = rarPath;
            process = new Process();
            process.StartInfo = startinfo;
            process.Start();
            process.WaitForExit(); //无限期等待进程 winrar.exe 退出
            if (process.HasExited)
            {
                flag = true;
            }
            process.Close();
        }
        catch (Exception e)
        {
            throw e;
        }
        return flag;
    }
    /// <summary>
    /// 利用 WinRAR 进行解压缩
    /// </summary>
    /// <param name="path">文件解压路径(绝对)</param>
    /// <param name="rarPath">将要解压缩的 .rar 文件的存放目录(绝对路径)</param>
    /// <param name="rarName">将要解压缩的 .rar 文件名(包括后缀)</param>
    /// <returns>true 或 false。解压缩成功返回 true,反之,false。</returns>
    public bool UnRAR(string path, string rarPath, string rarName)
    {
        bool flag = false;
        string rarexe;
        RegistryKey regkey;
        Object regvalue;
        string cmd;
        ProcessStartInfo startinfo;
        Process process;
        try
        {
            regkey = Registry.ClassesRoot.OpenSubKey(WinRAR_KEY);
            regvalue = regkey.GetValue("");
            rarexe = regvalue.ToString();
            regkey.Close();
            rarexe = rarexe.Substring(1, rarexe.Length - 7);

            Directory.CreateDirectory(path);
            //解压缩命令,相当于在要压缩文件(rarName)上点右键->WinRAR->解压到当前文件夹
            cmd = string.Format("x {0} {1} -y",
                                rarName,
                                path);
            startinfo = new ProcessStartInfo();
            startinfo.FileName = rarexe;
            startinfo.Arguments = cmd;
            startinfo.WindowStyle = ProcessWindowStyle.Hidden;

            startinfo.WorkingDirectory = rarPath;
            process = new Process();
            process.StartInfo = startinfo;
            process.Start();
            process.WaitForExit();
            if (process.HasExited)
            {
                flag = true;
            }
            process.Close();
        }
        catch (Exception e)
        {
            throw e;
        }
        return flag;
    }
}

调用方法

WinRARCSharp win = new WinRARCSharp();

win.RAR("F:\\aaa\\", "f:\\", "a.rar"); // 压缩(将“f:\\aaa\\”目录文件压缩到“f:\\a.rar”)

win.UnRAR("f:\\呦M.zip", "f:\\MM", "GG"); // 解压(将“f:\\呦M.zip”解压到“f:\\MM\\GG”目录下)

7z压缩通用类:

using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Win32;
using System.Diagnostics;
using System.IO;


/// <summary>
/// Name:Stone
/// DateTime: 2012/1/4 16:26:08
/// Description:7Z解压管理类
/// </summary>
public class _7zRAR
{

    // 7z.exe 安装地址
    private const string _7zEXE = @"D:\Program Files (x86)\7-Zip\7z.exe";

    /// <summary>
    /// 利用 7zExE 进行压缩
    /// </summary>
    /// <param name="_7zPath">将要被压缩的文件夹(物理路径)</param>
    /// <param name="filePath">压缩后的的存放目录(物理路径)</param>
    /// <returns>true 或 false。压缩成功返回 true,反之,false。</returns>
    public static bool Un7zRAR(string _7zPath, string filePath)
    {
        bool flag = false;

        string cmd;
        ProcessStartInfo startinfo;
        Process process;
        try
        {
            cmd = String.Format(@"x {0} -o{1} -y",
                _7zPath, filePath);
            startinfo = new ProcessStartInfo();
            startinfo.FileName = _7zEXE;
            startinfo.Arguments = cmd;
            startinfo.WindowStyle = ProcessWindowStyle.Hidden;

            process = new Process();
            process.StartInfo = startinfo;
            process.Start();
            process.WaitForExit();
            if (process.HasExited)
            {
                flag = true;
            }
            process.Close();
        }
        catch (Exception e)
        {
            throw e;
        }
        return flag;
    }
}


[转载]三步搞定android应用图片缓存

mikel阅读(766)

[转载]三步搞定android应用图片缓存 – 框架溜达 – 博客园.

目前很多商业应用都会涉及到从网络上读取图片数据的问题,为了节约用户流量,应用一般会将图片缓存起来。图片缓存一般分为内存缓存和外存缓存。内存缓存运 用java的缓存机制,在程序完全退出后,缓存所在的内存空间可能被其它应用程序占用从而丢失。外存缓存一般放在程序特有的访问空间或者sd卡中,在sd 卡中存放的资源为公有资源,其它程序也可以访问,且对用户来讲没有一个强制清除缓存的规范机制。综合以上,本文采用将缓存图片放置在程序的特有空间中, 其它应用程序无法访问,且用户可以在应用程序管理中的”清除数据”选项中清除缓存。

本文提供三种缓存策略:(1)LRU算法,固定缓存图片数量(max_num),当图片数量超出max_num时,将缓存中最近用的最少的图片删除。 (2)FTU算法,固定每张图片的缓存时限,以最后一次使用算起,超过时限后删除。(3)FMU算法,在存储器中固定一定大小的存储空间,超过固定空间后 将缓存中占用最大尺寸的图片删除。使用时只需要向方法体中传递图片的URL即可。

使用方法:

1.导入jar;

2. 获取服务;

3.提交url,交给程序去判断是否下载。

public class ImagecachetacticsdemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.item);

/*FMU*/
imageCacheManager = ImageCacheManager.getImageCacheService(this,
ImageCacheManager.MODE_FIXED_MEMORY_USED, “memory”);
imageCacheManager.setMax_Memory(1024 * 1024);

/*FTU*/
// imageCacheManager = ImageCacheManager.getImageCacheService(this,
// ImageCacheManager.MODE_FIXED_TIMED_USED, “time”);
// imageCacheManager.setDelay_millisecond(3 * 60 * 1000);

/*LRU*/
// imageCacheManager = ImageCacheManager.getImageCacheService(this,
// ImageCacheManager.MODE_LEAST_RECENTLY_USED, “num”);
// imageCacheManager.setMax_num(5);

// imageCacheManager = ImageCacheManager.getImageCacheService(this,
// ImageCacheManager.MODE_NO_CACHE_USED, “nocache”);

mImageView = (ImageView) findViewById(R.id.imageView);
new DownloadTask()
.execute(“http://www.touxiang99.com/uploads/allimg/110417/1_110417112640_2.jpg”);
}

private class DownloadTask extends AsyncTask<String, Void, Bitmap> {

@Override
protected Bitmap doInBackground(String… params) {
try {
return imageCacheManager.downlaodImage(new URL(params[0]));
catch (IOException e) {
e.printStackTrace();
}
return null;
}

@Override
protected void onPostExecute(Bitmap result) {

mImageView.setImageBitmap(result);
super.onPostExecute(result);
}

@Override
protected void onPreExecute() {
mImageView.setImageResource(R.drawable.ic_launcher);
super.onPreExecute();
}

}

private ImageView mImageView;
private ImageCacheManager imageCacheManager;

}

demo(内有jar包)

[转载]C#项目如何打包,或者叫生成安装文件

mikel阅读(910)

[转载]C#项目如何打包,或者叫生成安装文件 – 码农熊 – 博客园.

C#项目打包,安装部署

开发好的网站或程序如何打包呢?或者是叫如何生成安装文件呢?
其实这个很简单,只需要在解决方案中添加部署项目就可以了,如果是 WinForm程序的话,一般都会生成.msi为后缀的自动安装文件,当你把生成完毕的安装包发给别人安装的时候,别人只需像安装QQ那样,一步一步点击 就OK,但是这个过程中也会遇到问题,就是数字签名,如果没有数字签名,杀毒软件会有危险警告,这样别人在安装的时候就会受阻,具体的这个问题怎么解决我 在这篇文章里面就暂不描述了,先说说怎样把程序打包吧!

第一步:建立空白解决方案

第二步:添加现有项目或现有网站

这个部分就看你的需求了,你要是想发布网站的话,就选择现有网站;
在解决方案上右键,点击添加现有项目,

在打开对话框里面选择要发布的项目,点击确定

第三步:添加安装部署项目

在解决方案上右键,点击添加,添加新项目,然后选择其他项目类型,选择安装和部署,在右边选择安装项目,点击确定 

第四步:设置项目输出

在setup1上点击右键,点击添加,项目输入,在弹出的对话框中选择你要生成部署的项目,点击确定。

第五步:生成安装文件

在setup1上点击右键,点击生成,然后打开你刚才保存解决方案的那个文件夹,然后找到Setup1文件夹,然后选择Debuge文件夹,里面有两个文件,就是你想要的了,呵呵

原创:http://www.wanghaiyu.com/2011/05/c%e9%a1%b9%e7%9b%ae%e5%a6%82%e4%bd%95%e6%89%93%e5%8c%85-%e7%94%9f%e6%88%90%e5%ae%89%e8%a3%85%e6%96%87%e4%bb%b6-%e7%94%9f%e6%88%90exe-msi/

[转载]Android的UI布局总览

mikel阅读(897)

[转载]Android的UI布局总览 – 亚卓 – 博客园.

Android布局的目的是为了实现不同屏幕比例适配而设计的,有五种布局类:FrameLayout、LinearLayout、 AbsoluteLayout、RelativeLayout和TableLayout。五大布局类的继承关系如下(以FrameLayout为例):

java.lang.Object
android.view.View
android.view.ViewGroup
Android.widget.FrameLayout

。FrameLayout简介

FrameLayout框架布局,在此布局下的所有对象都固定在屏幕的做上角显示,不能指定位置。最大的特点是可以添加多个子类通过Android:layout_gravity来指定子类视图的位置。同一位置的子类视图处于层叠状,最上面的子类视图是最后添加的子类视图。

继承关系如下

java.lang.Object
android.view.View
android.view.ViewGroup
android.widget.FrameLayout

它的子类:

Known Direct Subclasses

AppWidgetHostView, CalendarView, DatePicker, GestureOverlayView, HorizontalScrollView, MediaController, ScrollView, TabHost, TimePicker, ViewAnimator

Known Indirect Subclasses

ImageSwitcher, TextSwitcher, ViewFlipper, ViewSwitcher

二。RelativeLayout简介

RelativeLayout相对布局,允许子元素指定他们相对于其他元素或者父元素的位置(通过ID指定),可以左右对齐,上下对齐,指定屏幕位置等形式来排列元素。

继承关系如下

java.lang.Object
android.view.View
android.view.ViewGroup
android.widget.RelativeLayout

常用属性介绍:

android:layout_above
Positions the bottom edge of this view above the given anchor view ID.

android:layout_alignBaseline
Positions the baseline of this view on the baseline of the given anchor view ID.

android:layout_alignBottom
Makes the bottom edge of this view match the bottom edge of the given anchor view ID.

android:layout_alignLeft
Makes the left edge of this view match the left edge of the given anchor view ID.

android:layout_alignParentBottom
If true, makes the bottom edge of this view match the bottom edge of the parent.

android:layout_alignParentLeft
If true, makes the left edge of this view match the left edge of the parent.

android:layout_alignParentRight
If true, makes the right edge of this view match the right edge of the parent.

android:layout_alignParentTop
If true, makes the top edge of this view match the top edge of the parent.

android:layout_alignRight
Makes the right edge of this view match the right edge of the given anchor view ID.

android:layout_alignTop
Makes the top edge of this view match the top edge of the given anchor view ID.

android:layout_alignWithParentIfMissing
If set to true, the parent will be used as the anchor when the anchor cannot be be found for layout_toLeftOf, layout_toRightOf, etc.

android:layout_below
Positions the top edge of this view below the given anchor view ID.

android:layout_centerHorizontal
If true, centers this child horizontally within its parent.

android:layout_centerInParent
If true, centers this child horizontally and vertically within its parent.

android:layout_centerVertical
If true, centers this child vertically within its parent.

android:layout_toLeftOf
Positions the right edge of this view to the left of the given anchor view ID.

android:layout_toRightOf
Positions the left edge of this view to the right of the given anchor view ID.

LinearLayout简介

LinearLayout线性布局,线性布局是所有布局中最常用的,他可以让其中的子元素按垂直或水平的方式排列(通过排列的方向设置),通常复杂的布局都是在LinearLayout布局中嵌套而成的

继承关系如下

java.lang.Object

android.view.View

android.view.ViewGroup

android.widget.LinearLayout

他的子类有:

Known Direct Subclasses

NumberPicker, RadioGroup, SearchView, TabWidget, TableLayout, TableRow, ZoomControls

四。AbsoluteLayout简介

AbsoluteLayout绝对布局。指定了子元素的x/y坐标值,并显示在屏幕上。该布局没有屏幕边框,允许元素之间互相重叠,在实际中不提倡使用这种布局,因为固定了位置,所以在屏幕旋转式会有不完整。

java.lang.Object

继承关系如下
android.view.View
android.view.ViewGroup
android.widget.AbsoluteLayout

他的子类有:

Known Direct Subclasses

WebView

*************This class is deprecated.
Use FrameLayout, RelativeLayoutor a custom layout instead.

五。TableLayout简介

TableLayout表格布局。将子元素的位置分配到行或列中,TableLayout布局有许多TableRow(行)组成,但没有列的概念,列是又 行中的控件数目来决定的,TableLayout也是常有布局。TableLayout不会显示行、列、单元格的边框线。

继承关系如下

java.lang.Object
android.view.View
android.view.ViewGroup
android.widget.LinearLayout
android.widget.TableLayout

他的子类有:

android:collapseColumns
setColumnCollapsed(int,boolean)
The zero-based index of the columns to collapse.

android:shrinkColumns
setShrinkAllColumns(boolean)
The zero-based index of the columns to shrink.

android:stretchColumns
setStretchAllColumns(boolean)
The zero-based index of the columns to stretch.

[转载]ASP.NET 页生命周期和页面事件处理管道

mikel阅读(1135)

[转载]ASP.NET 页生命周期和页面事件处理管道 – DebugLZQ – 博客园.

ASP.NET 页运行时,此页将经历一个生命周期,在生命周期中将执行一系列处理步骤。这些步骤包括 初始化、实例化控件、还原和维护状态、运行事件处理程序代码以及进行呈现。了解页生命周期非常重要,因为这样做您就能在生命周期的合适阶段编写代码,以达 到预期效果。此外,如果您要开发自定义控件,就必须熟悉页生命周期,以便正确进行控件初始化,使用视图状态数据填充控件属性以及运行任何控件行为代码。 (控件的生命周期基于页的生命周期,但是页引发的控件事件比单独的 ASP.NET 页中可用的事件多。)

一般来说,页要经历下表概述的各个阶段。除了页生命周期阶段以外,在请求前后还存在应用程序阶段,但是这些阶段并不特定于页。(有关更多信息,请参见 MSDN:ASP.NET 应用程序生命周期概述。)

我们知道最终我们在服务器上创建的页面对象是通过Page的一个派生类来创建的,这个类通常情况下是看不到的,包括类的名字也不是我们直接指定的。这个类是由ASP.NET服务器根据我们的页面模板文件aspx创建出来的,页面对象的ProcessRequest方法将会启动页面的生成过程。这个过程是通过页面的处理管道来完成的。

下表列出了最常用的页生命周期事件。除了列出的事件外还有其他事件;不过,大多数页处理方案不使用这些事件。而是主要由 ASP.NET 网页上的服务器控件使用,以初始化和呈现它们本身。如果要编写自己的 ASP.NET 服务器控件,则需要详细了解这些阶段。(有关创建自定义控件的信息,请参见MDDN:开发自定义 ASP.NET 服务器控件。 )

注意事项(来自MSDN):

各个 ASP.NET 服务器控件都有自己的生命周期,该生命周期与页生命周期类似。例如,控件的 InitLoad 事件在相应的页事件期间发生。

虽然 InitLoad 都在每个控件上以递归方式发生,但它们的发生顺序相反。每个子控件的 Init 事件(还有 Unload 事件)在为其容器引发相应的事件之前发生(由下到上)。但是,容器的 Load 事件是在其子控件的 Load 事件之前发生(由上到下)。

可以通过处理控件的事件(如 Button 控件的 Click 事件和 ListBox 控件的 SelectedIndexChanged 事件)来自定义控件的外观或内容。在某些情况下,可能也需处理控件的 DataBindingDataBound 事件。有关更多信息,请参见各个控件的类参考主题以及开发自定义 ASP.NET 服务器控件

当从 Page 类继承类时,除了可以处理由页引发的事件以外,还可以重写页的基类中的方法。例如,可以重写页的 InitializeCulture 方法,以便动态设置区域性信息。注意,在使用 Page_事件语法创建事件处理程序时,将隐式调用基实现,因此无需在方法中调用它。例如,无论是否创建 Page_Load 方法,始终都会调用页基类的 OnLoad 方法。但是,如果使用 override 关键字(在 Visual Basic 中为 Overrides)重写页的 OnLoad 方法,则必须显式调用基方法。例如,如果在页中重写 OnLoad 方法,则必须调用 base.Load(在 Visual Basic 中为 MyBase.Load)以运行基实现。

行文至此,希望能对各位博友有点帮助~(DebugLZQ恳请:不喜勿喷~)

[转载]分析腾讯网页登陆的加密机制

mikel阅读(1170)

[转载]分析腾讯网页登陆的加密机制 – DebugLZQ – 博客园.

问题的引入:前些天,几乎是再同时,CSDN等几大门户网站用户密码被外泄了,我没有感到很惊讶。因为现在很多大型的网站对用户信息,特别是,想用户名、 密码这样的信息保护不够完善!造成这种问题的原因是多种多样的,很多小网站本来建站的成本很低,随便找几个在校的计算机系大学生来花个几千RMB,网站就 算搭建起来了。大学生的水平是参差不齐的,特别是再计算机系(关于这一点,我绝对深信不疑,DebugLZQ对自己的老本行还是略知一二的)。这样子建立 起来的网站会有很多很多问题,当然包括安全问题(不是DebugLZQ装X啊,DebugLZQ看待这个问题也痛心疾首,DebugLZQ只是想陈述现 实,而现实往往是残酷的!)。好吧,放下小网站不说,腾讯大家都熟悉吧!就连腾讯也不是密不透风!!!下面首先分析下腾讯网页登陆的加密机制:

先介绍一下有关加密的信息:

1、打开http://xiaoyou.qq.com/

2、开启fiddler监听HTTP包

3、登录QQ,我这里就随手写了个QQ:123456密码123456验证码efta

U代表QQ号

P代表加密后的密码

verifycode代表验证码

其余都是固定的,不叙述了

要登录农场,必须要模拟发送以上数据来登录校友,而密码必须加密成如上才能通过验证。

现在讲解一下如何把明文的1234加密成09967317CCFC266ADA83C9B1BEA30825(这么恶心的密码)

这个是腾讯加密的JavaScript脚本 密码加密JavaScript。 初步看了一下觉得相当之恶心,所以在网上搜索了半天找到的一点线索就是  3次MD5加密后转换大些 + 验证码转换大写,再次MD5加密可是我按照这方法试了得到的结果却不是正确的。百思不得其解,求助了园子里认识的朋友,他给我发了段C#的代码,确实可以 得到正确的。代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Collections;

namespace QQ校友助手
{
    class QQPassword
    {
        public static string binl2hex(byte[] buffer)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < buffer.Length; i++)
            {
                builder.Append(buffer[i].ToString("x2"));
            }
            return builder.ToString();
        }
        public static string md5_3(string input)
        {
            MD5 md = MD5.Create();
            byte[] buffer = md.ComputeHash(Encoding.Default.GetBytes(input));
            buffer = md.ComputeHash(buffer);
            buffer = md.ComputeHash(buffer);
            return binl2hex(buffer);
        }

        public static string md5(string input)
        {
            byte[] buffer = MD5.Create().ComputeHash(Encoding.Default.GetBytes(input));
            return binl2hex(buffer);
        }

        public static string getPassword(string password, string verifycode)
        {
            return md5(md5_3(password).ToUpper() + verifycode.ToUpper()).ToUpper();
        }

    }
}

Encoding.Default.GetBytes(input)获取的是字符串的ASCII码。用VS跟踪调试了好久,发现问题出在 C#的computehash是初级hash,得到的并不是16进制的MD5字符串,需要经过binl2hex才能转换成MD5字符串。也就是说初级hash了3次,才合并大写验证码进行最后一次MD5加密

知道了原理后,我参考了Lojon的Delphi源码:

function Tfrm2.getKey: string;
var
  hash: T4x4LongWordRecord;
  hashstr: string;
begin
  SetLength(hashstr, 16);
  with TIdHashMessageDigest5.Create do
  begin
    hash := HashValue(edt2.Text);    //edt2.Text为QQ密码
    Move(hash, hashstr[1], 16);
    hash := HashValue(hashstr);
    Move(hash, hashstr[1], 16);
    hashstr := AsHex(HashValue(hashstr));
    Result := hashstr;
    Free;
  end;
end;

以上代码可以得到09967317CCFC266ADA83C9B1BEA30825这加密后的字符串。

看来腾讯也没有做的很好,也许就不存在绝对的安全!

以此为契机,DebugLZQ将分析制作在江苏大学教学行政区的上网帮助工具,和在已知校内上网账号(学号)的前提下,破解密码的工具!请期待DebugLZQ的下两篇博文。