铁匠

做人要低调
随笔 - 133, 评论 - 255 , 引用 - 184

导航

公告

文章分类

存档

随笔分类

相册

BLOG

网站

论坛

2008年12月22日

获取其它进程的启动参数

当我们使用ProcessExplorer的时候,发现它可以得到各个进程的启动参数(也就是在查看一个进程的属性里,在commandline里面显示的内容)。但是翻遍了MSDN也没有对应的API可以去作这样的事情,最开始的时候很无奈,只好先用内存查看器,看一下commmandline在内存啥位置,然后再用ReadProcessMemory去读一个尽可能大的块出来,虽然简单,但是不可靠,而且有时候读出来的东西后面是一堆无用的数据,影响美观。

经常不断的搜索,终于找到了方法:

1.先用OpenProcess 打开目标进程的进程空间,得到句柄

2.使用NtQueryInformationProcess这个API去读取进程里面的PE块的基地址也就是:PebBaseAddress

3.继续使用ReadProcessMemory,从这个PebBaseAddress,开始读取PEB(PE Block),这时候可以得到ProcessParameters,进程的参数地址

4.继续使用ReadProcessMemory,从这个ProcessParameters,开始读取PROCESS_PARAMETERS,这时候可以得到CommandLine.Length和CommandLine.Buffer,也就是启动参数的长度和启动参数的地址。

5.最后再使用ReadProcessMemory,根据记动参数的地址和长度去读取启动参数。特别要注意的事情是,如果在unicode的系统中,这时候读到的启动参数也是unicode的,所以得定义对应的字串类型去读取,不然打印出来的字串只有第一个字母(比如说参数是:abc,如果用ansi的字串,结果就是:a\0b\0c\0,\0这个就表示字串的结束了)。

最后,如果发现在第2步的时候出现读取错误,这时候应该是程序没有debug的权限了,可以用以下方法来提升程序的权限:

1.先用LookupPrivilegeValue来查看能否拥有:SeDebugPrivilege这个权限

2.如果可以,就用以下代码来提升权限:

Privileges.Privileges[0].Luid:=DebugNameValue;
Privileges.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED;
Result:=AdjustTokenPrivileges(TokenHandle,False,Privileges,SizeOf(Privileges),nil,RetLen);

posted @ 5:50 | Feedback (0)

2008年12月12日

招行的网银专业版提示:无效用户,请重新输入#21 的解决办法

最近突然发现招行的网银不好使了,输入密码后,提示:无效用户,请重新输入#21。于是以为是恢复的证书坏掉了,把用户删掉重新恢复。经过几天痛苦的尝试,终于把恢复问题的答案给答对了。重建了帐户,再次登录还是这样的错误。

最后没有招了,google一下,发现原来这是招行网银的一个防盗策略:如果里面的密码不是本机的键盘输入的话,就作出这样的提示。偶在本本的外挂键盘下输入的密码,所以不成功。改用本本自己的键盘输入就没有问题了。

posted @ 10:11 | Feedback (0)

2008年12月8日

string.format()是否应该多用?

前一阵子,项目中的一个页面每秒只能处理300次,而这个页面的逻辑也不复杂,就是根据条件拼出一个字串然后输出。开始以为这里面逻辑太复杂,所以有问题。不过后面发现了vs里面带了性能分析工具,于是抱着试试看的想法,作了一下性能分析。最后的结果让人大吃一惊:string.format这个操作竟然用掉了一半的时间,为啥它会这么费时间呢?为了真相,我用.net reflector查看了string的实现:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}


很让人吃惊,string.format竟然是调用了StringBuilder的AppendFormat来实现的。再继续根下去(这个源码只是通过IL得来的,可能和原始的不太一样,但是差不多了),注意里面的红色的那句:

public StringBuilder AppendFormat(IFormatProvider provider, string format, params object[] args)
{
    int num3;
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
    char[] chArray = format.ToCharArray(0, format.Length);
    int index = 0;
    int length = chArray.Length;
    char ch = '\0';
    ICustomFormatter formatter = null;
    if (provider != null)
    {
        formatter = (ICustomFormatter) provider.GetFormat(typeof(ICustomFormatter));
    }
Label_004E:
    num3 = index;
    int num4 = index;
    while (index < length)
    {
        ch = chArray[index];
        index++;
        if (ch == '}')
        {
            if ((index < length) && (chArray[index] == '}'))
            {
                index++;
            }
            else
            {
                FormatError();
            }
        }
        if (ch == '{')
        {
            if ((index < length) && (chArray[index] == '{'))
            {
                index++;
            }
            else
            {
                index--;
                break;
            }
        }
        chArray[num4++] = ch;
    }
    if (num4 > num3)
    {
        this.Append(chArray, num3, num4 - num3);
    }
    if (index == length)
    {
        return this;
    }
    index++;
    if (((index == length) || ((ch = chArray[index]) < '0')) || (ch > '9'))
    {
        FormatError();
    }
    int num5 = 0;
    do
    {
        num5 = ((num5 * 10) + ch) - 0x30;
        index++;
        if (index == length)
        {
            FormatError();
        }
        ch = chArray[index];
    }
    while (((ch >= '0') && (ch <= '9')) && (num5 < 0xf4240));
    if (num5 >= args.Length)
    {
        throw new FormatException(Environment.GetResourceString("Format_IndexOutOfRange"));
    }
    while ((index < length) && ((ch = chArray[index]) == ' '))
    {
        index++;
    }
    bool flag = false;
    int num6 = 0;
    if (ch == ',')
    {
        index++;
        while ((index < length) && (chArray[index] == ' '))
        {
            index++;
        }
        if (index == length)
        {
            FormatError();
        }
        ch = chArray[index];
        if (ch == '-')
        {
            flag = true;
            index++;
            if (index == length)
            {
                FormatError();
            }
            ch = chArray[index];
        }
        if ((ch < '0') || (ch > '9'))
        {
            FormatError();
        }
        do
        {
            num6 = ((num6 * 10) + ch) - 0x30;
            index++;
            if (index == length)
            {
                FormatError();
            }
            ch = chArray[index];
        }
        while (((ch >= '0') && (ch <= '9')) && (num6 < 0xf4240));
    }
    while ((index < length) && ((ch = chArray[index]) == ' '))
    {
        index++;
    }
    object arg = args[num5];
    string str = null;
    if (ch == ':')
    {
        index++;
        num3 = index;
        num4 = index;
        while (true)
        {
            if (index == length)
            {
                FormatError();
            }
            ch = chArray[index];
            index++;
            switch (ch)
            {
                case '{':
                    if ((index < length) && (chArray[index] == '{'))
                    {
                        index++;
                    }
                    else
                    {
                        FormatError();
                    }
                    break;

                case '}':
                    if ((index < length) && (chArray[index] == '}'))
                    {
                        index++;
                    }
                    else
                    {
                        index--;
                        if (num4 > num3)
                        {
                            str = new string(chArray, num3, num4 - num3);
                        }
                        goto Label_0253;
                    }
                    break;
            }
            chArray[num4++] = ch;
        }
    }
Label_0253:
    if (ch != '}')
    {
        FormatError();
    }
    index++;
    string str2 = null;
    if (formatter != null)
    {
        str2 = formatter.Format(str, arg, provider);
    }
    if (str2 == null)
    {
        if (arg is IFormattable)
        {
            str2 = ((IFormattable) arg).ToString(str, provider);
        }
        else if (arg != null)
        {
            str2 = arg.ToString();
        }
    }
    if (str2 == null)
    {
        str2 = string.Empty;
    }
    int repeatCount = num6 - str2.Length;
    if (!flag && (repeatCount > 0))
    {
        this.Append(' ', repeatCount);
    }
    this.Append(str2);
    if (flag && (repeatCount > 0))
    {
        this.Append(' ', repeatCount);
    }
    goto Label_004E;
}

发现里面会有new string,这时候会有新的内存分配出现,也就是说string.format会产生很多临时的string对象,这个会费时间,同时也会使GC的工作量增加.既然这里面调用了stringbuilder来实现的,那为啥不直接调用stringbuilder.append来实现。于是我就把原来的实现改成了stringbuilder的append,同时设置它初始容量为我们预期的大小,通过测试,这部分的性能提高了十倍。于是性能问题解决了。

最后,我觉得如果程序的性能很重要,而在这里面又经常有string.format的时候,还是改用stringbuilder.append来实现,虽然麻烦一些,代码也不好看,但是效果还是会很明显的。

posted @ 3:06 | Feedback (0)

2008年12月3日

离真相总是一步之遥------终于完成了AVI文件格式的解析

虽然AVI的文件格式很熟悉了,根据RIFF file refence里面的说明生成的AVI拿mpc一类的播放器可以正常播放,但是windows media player却死活也播放不了。用偶的程序生成的AVI文件在目录里面也没有预览,也读不出文件里面的信息,搞得偶好郁闷。有时候甚至想把自己写的这个上千行的avibuilder删了,换成系统自带的avi相关的API来实现,不过看着那么多的API自己感觉很复杂,也没有办法支持压缩的格式,所以只好放弃。

今天无聊了,又拿起了visual dub来看avi的格式,突然我发现我生成的avi文件和正常的avi文件的文件头的差别了(左边是错误的,右边是正确的,黄颜色背景的那个):

捕获

一个极小的失误,费了好长的工夫。只怪当时看文档的时候没有多想想,怎么可能会把header的标识放到了stream上面。

修改完了后,偶的avibuidler生成的AVI终于和正常的一样了。

posted @ 16:52 | Feedback (0)

2008年11月8日

自己安装温控器

首先看一下所有的材料和工具
按此在新窗口浏览图片
费了半天的劲,终于把接收器的线装好了
按此在新窗口浏览图片
看个详细点的(里面的文字写错了,接收器应该是控制器--!!!)
按此在新窗口浏览图片
看看壁挂炉原来的控制器
按此在新窗口浏览图片
换上温控器后的效果图
按此在新窗口浏览图片
测试可以使用后,再把它们固定到墙上
按此在新窗口浏览图片
最后看看完成后的效果图
按此在新窗口浏览图片

posted @ 6:25 | Feedback (0)

2008年10月25日

性能调优实录

一个产品又开发完成了,按例又是一轮的测试:功能测试,性能测试,容量测试...眼看着一切顺利,可是在最后一环,发现有问题:系统的响应时间很长,有时候一个操作十几秒才完成。

按惯例,又得查一下资源的占用情况:应用服务器和数据库服务器的CPU并不高,内存占用也不高,磁盘的IO也不高。一切显得很恑异。通过几个回合的操作,终于解决了问题:

第一回合

抓了DUMP后,用windbg分析,发现里面有近200个工作线程,而这些工作线程的堆栈上面显示,他们要么在等待SQL的连接,要么正在读数据,总之一句话,就是卡在数据库这里了。于是想通过sqlserver2005的性能报表来查看哪些SQL最费时间,结果发现竟然没有安装这个功能,所以只好用以下SQL来实现:

SELECT TOP 10

[Average CPU used] = total_worker_time / qs.execution_count

,[Total CPU used] = total_worker_time

,[Execution count] = qs.execution_count

,[Individual Query] = SUBSTRING (qt.text,qs.statement_start_offset/2,

(CASE WHEN qs.statement_end_offset = -1

THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2

ELSE qs.statement_end_offset END -

qs.statement_start_offset)/2)

,[Parent Query] = qt.text

,DatabaseName = DB_NAME(qt.dbid)

FROM sys.dm_exec_query_stats qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt

ORDER BY [Average CPU used] DESC

发现排名前十名的SQL里有6个是这个应用里的,仔细分析里面的查询和表结构,竟然发现原来设计的一些索引在这个环境里面被删了,所以现在这几个查询变成了Table scan,难怪SQL的CPU会这么高,查询时间会这么长。把这些表的索引重新建上,这下好了。数据库的性能,索引致关重要,对于sql server来说,我们可以用查询分析器里面的“显示估计的执行计划”功能(在查询区域里面,选中SQL,右键就可以看到)来看这个查询是Table Scan还是Index scan还是Index seek还是clusted Index Seek.性能上来说,按性从好到差的顺序就是:clusted Index Seek》Index seek》Index scan》Table Scan。

重新给系统加上压力,这下好了。可是好景不长,才又压了十几分钟,响应时间又变长了。于是又得进行优化。

第二回合

正当我不知道如何是好的时候,比较有经验的同事,就发现了在系统响应时间比较长的时候,在系统的日志里面竟然有对应的错误日志,原来系统里面有未捕获的异常,当出现异常的时候,应用程序池重启了,于是这时候所有的请求都被挂起,所以响应时间比较长。修改完BUG后,继续上压力。但是好景不长,响应时间又高上来了。

第三回合

再次抓DUMP,可是发现里面的线程的执行时间并不长,基本上都是几百毫秒就完成了。真是让人头痛呀。还是那个经验丰富的同事,发现了问题。通过性能监视器,发现SQL里面的等待时间很长,也就是说SQL里面出现了锁等待。于是用SQL性能监视器,结果发现好家伙,里面的一些查询竟然已经执行了几秒钟了。于是再次打开这些查询,通过显示执行计划,发现这里面最费时间的一个XML解析,而这个同样的XML解析竟然在这里面有2个,而且这个解析发生在delete的时候出现,大家都知道在Delete数据的时候,会把表锁了,要解决问题就得缩短这个锁表的时间,于是把XML解析到一个临时表里面,然后再用临时表和要删除数据的表进行关联。通过长时间的压力测试,发现响应时间长的问题终于好了。

posted @ 14:17 | Feedback (0)

2008年10月20日

在线程中创建webbrowser的问题

今天碰到了一个怪问题,在主界面上有一个方法"CreateBrowser"来动态创建webbrowser控件,同时设置一个按钮来测试这个方法,结果一切OK.然后再写一个线程,在这个线程里,通过事件来调用这个CreateBrowser方法,结果这个Browser死活不出来.try一把竟然提示:尚未调用 CoInitialize.

于是想当然地在主界面加上CoInitialize,可是,没有效果,但是在那个线程初始化或是在这个CreateBrowser方法中加它,结果是把整个程序挂在那里了.看来这问题不好解决.

只好想个办法绕过去,我的解决办法是用自定义的系统消息来实现.

posted @ 17:52 | Feedback (0)

用命令来设置IP

网络和共享中心打不开了,没有办法设置IP了,找到了以下设置IP的命令:

 C:\>netsh (然后执行netsh这个命令)
  netsh>interface (netsh命令的子命令)
  interface>ip (interface命令的子命令)
  interface ip>set (interface ip命令的子命令)

set address - 设置指定的接口的 IP 地址或默认网关。
  set dns - 设置 DNS 服务器模式和地址。
  set wins - 设置 WINS 服务器模式和地址。

set address "本地连接" static 192.168.0.2 255.255.255.0 192.168.0.1

posted @ 12:52 | Feedback (1)

2008年9月16日

IE8和Google Chrome的对比

上上周装了Google Chrome后,很快就删了,头脑一发热就又去把IE7给升级成IE8了。看了一下IE8好像除了界面的颜色更柔合了一点,没有啥大的变化。但是经过几个小时的试用后,我发现IE8很好很强大,偶还常向同事推荐它。

现在说说偶觉IE8的亮点吧:

1.有强大的开发者工具,第一次看到开发者工具是在FF上面看到的,不过这些工具和IE8带的比起来,简直就像是小孩的玩具了。

里面有颜色拾取器和标尺,想想以前作网页的时候,要抄别人的页面经常得抓下图来,在PS里面取颜色和取尺寸,现在这些内置工具解决了这个问题。

开发者工具里面还带了JS的调试器,而且启用简单,容易使用,而且功能强大,无论是单独的JS文件也好,还是内嵌在网页里的JS块也好,一切都可以调试。

DOM查看器也是我所希望的那样子,虽然FF里面也有DOM查看器,不过它的显示模式让人感觉使用不方便,特别是DOM很深的时候,FF里面简直就没有办法用了。它的CSS栏里面,可以轻松地看到哪些CSS 是有效的,哪些是无效的。

2.已经开始面向W3C的标准了。这个偶也说不上它是一个进步还是一个倒退。不过作为易用性来说,IE8作的还是很不错的,因为它提供了兼容模式,一步操作就可以实现IE7和IE8模式的切换。这个我相信会让用户很方便的。对于用户来说,网页的标准是次要的,最重要的是他手上的浏览器能无误地打开他所需要的网站,并进行他所需要的操作。

差点跑题了:)

现在比比这两个浏览器了:

1.易用性:我觉得IE8要得高分,因为它能很好地兼容不同的网站,而Chrome却只能无误地打开有限的网站,而且错了也不报,让用户无所适从。

2.性能:不用怀疑,IE8确实要吃更多的内存,速度上来说,我并没有觉得太大的差别。

3.稳定性:我觉得IE8要更胜一筹,用了一两个星期了,以前打开一些老容易把浏览器卡死的网站,现在不会出现这种性况了。

再说说Chrome所说的一些新特性,其实在IE8里面早就是这样去实现的了:比如说每个TAB页单独的进程,隐私模式等等。基本上Chrome所说的亮点,在IE8里面早就存在了。

posted @ 21:49 | Feedback (0)

2008年9月3日

Google Chrome(谷歌浏览器)试用感受

打开下载链接,发现只有四百多K ,心中在想是不是又一个基于IE内核的浏览器,不过,很快推翻了我的想法,因为旁边有一个完整版的下载,那个400K只是一个浏览器的下载器,最讨厌这种发布方式,让人白开心了一把,如果上网不方便的,下载回去装,结果发现还得接着下,那叫一个郁闷。

安装过程太傻瓜了,一点选择权都没有,不是很爽,偶的C盘没有空间了,这样装,只好玩完以后就删了。

用的时候,发现速度还可以,不过有时候CPU的使用还是比IE高,多TAB页关掉的时候,没有释放内存,这个和IE是一样的。发现里面有一个任务管理器,可以看到每个页面占用的CPU,内存等。

继续用下去,发现问题来了,兼容性是一个大问题,IE和FF都能正常跑的页面,它跑不下去了,而且没有错误控制台,也不知道是哪个JS 出的错,再试一下百度的地图,结果发现用不了。再用google的地图发现可以用,难道被屏蔽了?

最终的结论是:我一会儿就会把它删掉,再没有更好的更新前,我不会使用它。

posted @ 10:22 | Feedback (2)

京ICP备 05050892号