B3log Solo  当前在线人数:12 登录 注册

奔放的胸毛。

天行健 胸毛以自强不息

标签: C# (11)

Visual Studio调试闪退——金山作祟

2015-05-13 11:42:39 奔放的胸毛。
1  评论    1,050  浏览

症状:

F5调试一个控制台应用程序,按下F5开始编译,然后在“加载符号”的时候就闪退掉了。当时解决方案里的其它控制台应用程序正常。

 

解决:

解决之前艰辛蛋疼的坎坷就不多说了,直接说重点:

关闭一切跟金山有关的进程之后就好了。

 

缘由:

在操作系统的事件查看器里找到报错日志,发现了如下信息:

 

错误应用程序路径: E:\xxxxxx\Solution\xxxxxx\bin\Debug\xxxxxx.vshost.exe
错误模块路径: C:\Program Files (x86)\kingsoft\ksdef\ksdefdll64.dll

 

“错误应用程序路径”是我需要调试的程序,下面这个是罪魁祸首,金山。

我特么干毛要装WPS。

这个日志表示金山注入了我这个程序的进程,发生故障,让我的程序崩掉,所以调试闪退,也不知道金山要干嘛……

,

windows控制台应用程序窗口一拖动就退出

2013-06-17 16:18:40 奔放的胸毛。
0  评论    2,221  浏览

症状:

远程桌面登入服务器,启动里面的控制台应用程序,一拖动其窗口,程序就退出。

解决:

关闭本机上的“有道桌面词典”。

原因:

这种翻译软件会检测鼠标动作,以达到划词翻译的功能,在划词之后会自动按下Ctrl - C以复制划选的文本。

然而Ctrl - C正好是控制台退出的快捷键......

, ,

C#调用Python文件实现【砍价算法】

2013-04-12 15:25:30 奔放的胸毛。
0  评论    3,593  浏览

砍价算法

客户做个商城,要求可以砍价。这个功能的实用性与意义暂且不谈。

对于要做一件没做过的事情之前,我通常会搜索一下有没有现成的,毕竟开源社区的开源项目那么多,大部分的社会需求所应对的代码都被别人写过好几遍了,没必要自己从头走——站在巨人的肩膀上。

搜索了一阵子发现并没有现成的这种小玩意,倒是意外的发现人世间竟有“砍价公司”这种事物的存在,唏嘘不已。

考虑到这种机器智能的算法大都不会精明得跟人一样,所以肯定会频繁修改,所以尝试了一下,将砍价的逻辑用Python写在了脚本里,然后再C#里调用,如果发现算法不够合用,只需要把新的python脚本替换一下就好了。

砍价机器人

代码在:这里

, ,

Visual Studio 好用的拓展插件推荐——(二)Web Essentials

2012-12-04 18:09:17 奔放的胸毛。
0  评论    4,729  浏览

Web Essentials

很多句话概括效用:

    • 可快速将某样式定义为IE6/7/8专用样式。
    • 可快速将某样式定义为某浏览器内核专用
    • 可将background-image样式对应的图片转码为base64,使样式脱离图片文件。
    • 压缩选中样式
    • 可格式化样式表代码
    • 可查看选定样式的详细文档
    • 便捷化CSS编写方式,自动回车、自动分号等。
    • 极大地优化CSS显示方式
    • ——————分隔——————
    • 压缩js代码
    • 为js代码添加#region块标签
    • 为js代码块(for、function等包含大括号的代码块)添加代码折起显示支持
    • ——————分隔——————
    • 当从解决方中拖拽文件到html代码中时,正确地引入css、js、WebUserControl 等文件(VS默认的拖拽会添加一个A标签指向拖入的文件)。
    • ——————分隔——————
    • 其它

插件下载地址:猛戳这里(内含英文版官方介绍,英语好的过去就不用过来了。)

支持的VS版本:Visual Studio 2010

详细解说:

这真的是一款十分难得的插件,搞ASP.NET开发的杀人越货居家旅行必备。

看图说话之图一

Web Essentials

安装后,在编辑CSS文件时,会自动出现此菜单(Shift + Alt + F10 展开)。

第一项:Add vendor specific properties

为选择的样式添加Firefox和webkit内核的单独写法,效果如下图:

Web Essentials

第二/三项:Make visible to IE6/IE7/IE8

将选择的样式改写成指定IE版本的特殊写法。

第四项:Info and examples

当对某个样式的意思不太清楚的时候,可以选择此项,直接打开此样式在W3School上的详解文档(此站有对应的中文版网站)。

看图说话之图二

Web Essentials

将选择的样式格式化,缺少大括号的会自动补全。

看图说话之图三

Web Essentials

Web Essentials

将样式表中指向的图片资源转换为base64,使样式脱离图片文件。过长的base64代码会被收缩显示。

看图说话之图四

Web Essentials

样式中,颜色表现形式的转换。十六进制、RGB、英文单词 间互转。

看图说话之图五

Web Essentials

鼠标移动到字体名称上时,提供预览。

 

Web Essentials

鼠标移动到颜色上时,提供预览。

 

Web Essentials

鼠标移动到图片URL上时,提供预览。

 

Web Essentials

自动为javascript代码增加左侧的边线收缩按钮。

 

Web Essentials

CSS全文所有颜色增加色块标注。

 

看图说话之图六

Web Essentials

第一项:Surround with region

在编辑js文件或css文件时,对选中的代码块快速添加region标签,使选中的代码块可折叠。下图所示:

Web Essentials

第二项:Minify selection

在编辑js文件或css文件时,对选中的代码块进行压缩。

看图说话之图七

Web Essentials

在编辑js文件或css文件时,可使用快捷键Ctrl + 上/下 快速调整数值。

看图说话之八

Web Essentials

装插件前,从解决方案中往aspx页面拖入一个用户控件,会以a标签方式引入。

装插件后,妥了,正确引入。拖拽js、css、image各种文件后毫无压力了。

妥妥的

 

其它功能

英文不太好,原文有几行没看明白……

 

解说完毕,觉得好使的顶一顶,谢谢合作。

, ,

Visual Studio 好用的拓展插件推荐——(一)Image Optimizer

2012-11-30 18:34:44 奔放的胸毛。
1  评论    4,695  浏览

Image Optimizer

一句话概括效用:在Visual Studio的解决方案中,为图片或包含图片的文件夹添加右键菜单,可对图片进行压缩,无损压缩。

插件下载地址:猛戳这里(内含英文版官方介绍,英语好的过去就不用过来了。)

支持的VS版本:Visual Studio 2010/11

详细解说:

Image Optimizer

安装后,解决方案的右键菜单会出现图中红色图片的菜单项(选择文件夹或多个文件时没有下面那个菜单项)。

第一项:Optimize image

优化(压缩)图片,直接对选中的图片或文件夹中的图片进行优化,优化的过程中会在输出窗口显示相关信息,如下图所示:

Image Optimizer

输出的信息为,某图片、压缩前的大小、压缩后的大小、比例。

妥妥的

无损压缩,妥妥的。

第二项:Extract data URI to clipboard

导出图片的Base64数据到剪贴板。

 

解说完毕,觉得好使的顶一顶,谢谢合作。

, ,

为什么StringBuilder比string更高效?

2012-10-31 16:47:39 奔放的胸毛。
0  评论    2,510  浏览

这里说的是C#中的两种字符串对象。

以前只是知道StringBuilder比string快,但是为什么快,快在哪?说不清楚,一直到前几天,聊天的时候跟头说到这事,头给我详细讲解了下,这才通透。

StringBuilder / string

string

string其本质是一个char数组,在内存中是一个连续的空间。众所周知数组的长度是固定的,那么在下面一行代码中,string对象是如何处理的呢?

string str = "a";
str = "b";

这是一个字符串被修改的例子,从肉眼上看,修改前后的字符串长度也没有变。那么,修改前与修改后str在内存中指向的地址还是同一个么?也就是说,后者是在前者的基础上修改,还是重新开辟的一块空间呢?

我曾经就有这样的疑问。

头告诉我,string对象在内存里是只读的数组(另一说不是数组,是一条死的数据。但是已经不重要了,只要知道它是只读的就够了)。

重新赋值的话,是指向了新的地址,尽管重新赋值后的长度没变。

 

再看一段字符串拼接的例子:

string str = "a";
str += "b";

首先,str指向数组char[]{'a'};然后,内存中再新建数组char[]{'b'};最后,做拼接操作,生成数组char[]{'a','b'},然后str再指向最后这个数组。

在这个过程中,char[]{'a'}和char[]{'b'}都变成了废品,等待被回收。

做一次拼接就产生了三个数组,其中两个将要被GC,浪费了资源,所以不够高效。

当然,一次操作两个垃圾这不算什么,但是想象一下做Replace操作的话,每此需要修改一个位置就开辟一个新的……立马就蛋疼了。

 

StringBuilder

StringBuilder是string的封装,用来处理字符串,那么它是如何来存储字符串的呢?为什么说它比string更高效呢?

因为StringBuilder储存的不是跟srting一样的只读char数组,而是可读写的char数组。这样的设计会带来什么好处呢?同样做拼接操作:

StringBuilder str = new StringBuilder();
str.Append("a");
str.Append("b");
str.Append("12345678901234567890");

首先,开辟一个长度为16的char数组(为什么是16?反编译.NET查看源码所得),然后给第一位赋值为"a",然后给第二位赋值为"b"。

在第三次Append的时候,显然之前的16个长度不够了,这个时候会怎么办呢?

这时候会重新开辟一个新的char数组,这个数组的长度为十六的倍数,且刚好大于需要的空间。

有了新开辟的char数组,之前的地盘就没用了,会被GC,产生一个垃圾。

然后再说StringBuilder做Replace,因为StringBuilder是可读写的char数组,在结果长度超出目前可用长度前,是不会产生垃圾的。

 

最后引用一段MSDN上的描述: 

String 对象串联操作总是用现有字符串和新数据创建新的对象。StringBuilder 对象维护一个缓冲区,以便容纳新数据的串联。如果有足够的空间,新数据将被追加到缓冲区的末尾;否则,将分配一个新的、更大的缓冲区,原始缓冲区中的数据被复制到新的缓冲区,然后将新数据追加到新的缓冲区。

 

String 或 StringBuilder 对象的串联操作的性能取决于内存分配的发生频率。String 串联操作每次都分配内存,而 StringBuilder 串联操作仅当StringBuilder 对象缓冲区太小而无法容纳新数据时才分配内存。因此,如果串联固定数量的 String 对象,则 String 类更适合串联操作。这种情况下,编译器甚至会将各个串联操作组合到一个操作中。如果串联任意数量的字符串,则 StringBuilder 对象更适合串联操作;例如,某个循环对用户输入的任意数量的字符串进行串联。

 

此实现的默认容量是 16,默认的最大容量是 Int32.MaxValue。 当实例值增大时,StringBuilder 可按存储字符的需要分配更多的内存,同时对容量进行相应的调整。分配的内存量是特定于实现的,而且如果所需内存量大于最大容量,会引发 ArgumentOutOfRangeException

,

Winform操作注册表,锁屏小工具的拓展(三)

2010-08-17 13:34:00 奔放的胸毛。
0  评论    5,812  浏览

在上一章中我们已经完成了对密码的加密,然后今天我们将把我们加密后的密码储存在注册表中,并且能修改。也就是对注册表的增删改查。

注册表概述

首先介绍下Windows操作系统中的注册表是什么?有什么作用?。

很早以前的注册表咱就不管了,只说说目前主流的几个Windows操作系统中的注册表。按照我个人的理解,注册表在操作系统中的角色是一个巨大的配置文件数据库。这个特殊的“数据库”里面储存着大量的操作系统配置信息。

比如是否使用屏保、使用的屏保文件的路径、是否开启来宾用户、两次鼠标点击间隔在多少毫秒内才算双击、是否自动排列图标、按照什么顺序排列图标、鼠标的指针移动速速、使用什么连接连接到网络、是否开启系统防火墙、是否开启自动更新、“我的文档”的路径、“我的电脑”的图标路径、腾讯QQ可执行文件路径等等等等……

为什么可以把原本在C盘的“我的文档”移动到D盘,就是因为修改了注册表里标示“我的文档”的路径的键的值。

为什么有时在网页上点击类似于“QQ会话”图片的图片可以启动QQ聊天软件?网页怎么知道我的QQ安装在何处?因为每次安装QQ的时候QQ安装程序都会在注册表中添加一条记录,这个记录便是QQ的安装路径,在网页上点击图片后被执行的程序只要读取这个位置的路径便可以知道“QQ.exe”在哪,然后启动它。

这就是注册表的作用——它是操作系统中的配置信息数据库(个人通俗描述)。

所以,我们可以用这个“数据库”来储存我们锁屏工具的密码,虽然注册表所有人都可以打开看,但是估计一般人看到注册表里面的数据都会晕的,更不会去找你的密码放在哪了,更何况就算找到了它也是经过加密的。安全性方面我觉得这样已经比较可靠了,我知道世上什么高手都有,但是我觉得没有几个人会有兴趣来研究我的锁屏工具的密码的。

在命令行输入命令“regedit”打开注册表编辑器,如图:

2010-08-17_110715 2

2010-08-17_111128

根据这后两张图我们可以看到注册表是以树形结构来存储数据的,这个树的根节点叫做“我的电脑”,然后下面有5个主节点。这5个主节点下分别对应储存这些配置信息(个人描述不专业,下列介绍来自百度百科):

HKEY_CLASSES_ROOT

在注册表中HKEY_CLASSES_ROOT是系统中控制所有数据文件的项。这个在Win95和Winnt中是相通的。HKEY_CLASSES_ROOT控制键包括了所有文件扩展和所有和执行文件相关的文件。它同样也决定了当一个文件被双击时起反应的相关应用程序。

HKEY_CURRENT_USER

 

       HKEY_CURRENT_USER包含着在HKEY_USERS安全辨别里列出的同样信息。任何在HKEY_CURRENT_USER里的改动也都会立即HKEY_USERS改动。相反也是这样。

 

HKEY_CURRENT_USER允许程序员和开发者易于存取目前登陆用户的设置。通过建立这个键,微软很容易在不涉及到用户的SID下改变,添加和设置。

也就是说,所有当前的操作改变只是针对当前用户而改变,并不影响其他用户。

HKEY_CURRENT_CONFIG

win95一般只使用一个硬件配置文件。如果有多个硬件配置文件。HKEY_LOCAL_MACHINE\Config中就会添加一个键。HKEY_LOCAL_MACHINE\Config包含了HKEY_LOCAL_MACHINE中相同的数据

在启动时,你可以选择你愿意使用的配置文件。如果有多个安装,每次系统重新启动时,你就必须选择.HKEY_CURRENT_CONFIG是在启动时控制目前硬件配置的键

在系统启动以后,任何地方的变化都会自动影响到它。程序员经常使用HKEY_CURRENT_CONFIG方便的来存取配置信息。

HKEY_CURRENT_CONFIG包括了系统中现有的所有配置文件的细节。你的选择影响了哪一个硬件配置文件成为现在的。举例来说,如果配置0002被选择了,所有0002的配置信息会被映射到这些键上

HKEY_CURRENT_CONFIG允许软件和设备驱动程序员很方便的更新注册表,而不涉及到多个配置文件信息。 HKEY_LOCAL_MACHINE中同样的数据和任何注册表的变化都会同时的变化。

HKEY_USERS

HKEY_USERS将缺省用户和目前登陆用户的信息输入到注册表编辑器,在win95中,它仅被那些配置文件激活的登陆用户使用,同样在nt下,它也是这样。

win95从user.dat中取得他们的信息,winnt从ntuser.dat中取得信息。.dat文件包含了所有基于用户的注册表设置并且允许你取配置这些用户的环境。如果你改变了缺省用户的设置,所有新用户会继承同样的设置。而且,那些已经被建立的用户变的失效。

HKEY_LOCAL_MACHINE

HKEY_LOCAL_MACHINE是一个显示控制系统和软件的处理键。HKLM键保存着计算机的系统信息。它包括网络和硬件上所有的软件设置。(比如文件的位置,注册和未注册的状态,版本号等等)这些设置和用户无关,因为这些设置是针对使用这个系统的所有用户的。

HKEY_LOCAL_MACHINE节点是我们今天的重点,软件设置信息一般都存放在这里,为什么存放在这个节点下而不存放在其它地方呢?我们把信息藏在一个很少有人知道的地方岂不更加安全么?这是因为,注册表中的每个节点都有权限分配,当然,我们以Administrators的身份进入系统那绝对是哪都能操作的,但是,并不是所有的人都是以管理员的身份进入的系统,所以并不是所有的当前用户都对注册表的所有节点有操作权限。

所以,微软当初在做这个操作系统的时候就安排好了,HKEY_LOCAL_MACHINE节点下的SOFTWARE节点用于各种软件存储信息。

2010-08-17_114355

如何称呼注册表中的各“部件”:

2010-08-17_113255

项:左半边带有文件夹图标的节点全部都称为项。

键:右半边里面的一行数据就是一个键。

键的名称:如图。

键的类型:最前面说道,注册表是一个“配置信息数据库”,既然是“数据库”,那么,很显然“数据库”是不光能够存储字符串类型数据的。能储存什么数据看图:

2010-08-17_115526

我们今天的需求只需要存储字符串类型的数据,所以其它的就不管了。

键的数据:如图。

分析需求并编程实现

说到底,今天我们的需求就是要在注册表中储存锁屏工具的密码,然后实现登陆验证和修改密码的功能。

经过前面对注册表的分析,我们需要在HKEY_LOCAL_MACHINE\SOFTWARE下建立一个自定义项,然后在我们建立的项中插入一个字符串类型的键,并在建中储存我们的密码。

操作注册表引用命名空间:using Microsoft.Win32;

编程思路及代码

· 增

由于注册表中没有专门给锁屏工具存东西的地方,所以,我们每次打开锁屏工具的时候就要看看注册表中的HKEY_LOCAL_MACHINE\SOFTWARE下建立了锁屏工具的数据项和键没有,如果已经建立了,不管。如果没有建立,说明这是第一次运行锁屏工具,在HKEY_LOCAL_MACHINE\SOFTWARE下建立一个名为“Zane”(这是我自己随便起的名字)的项,然后在这个项下面建个字符串类型名为“zane”,值为DESEncrypt.Encrypt("")的键(DESEncrypt类中的Encrypt方法为加密方法,将空字符串加密后再放在注册表安全性更高),这个键的值就是我们第一次使用锁屏工具的默认密码。

关键代码,在Load时调用RegedieValidate()即可(注册表中的路径不分大小写):

        /// 
        /// 检查注册表中是否已经存在Zane项和zane键,如果没有,创建
        /// 
        private void RegedieValidate()
        {
            if (!IsRegeditItemExist())//首先判断是否存在Zane项,没有就建立
            {
                RegistryKey key = Registry.LocalMachine;//获取HKEY_LOCAL_MACHINE节点
                key.CreateSubKey("software\\Zane");//建立该节点,如果该节点已经存在,则不影响
            }
            if (!IsRegeditKeyExit())//然后判断Zane项中是否有zane键,没有就添加默认密码记录
            {
                RegistryKey key = Registry.LocalMachine;
                RegistryKey software = key.OpenSubKey("software\\Zane", true);  //该项必须已存在
                software.SetValue("zane", DESEncrypt.Encrypt(" "));
            }
        }

        /// 
        /// 判断是否存在Zane节点
        /// 
        /// 
        private bool IsRegeditItemExist()
        {
            string[] subkeyNames;
            RegistryKey hkml = Registry.LocalMachine;
            RegistryKey software = hkml.OpenSubKey("SOFTWARE");
            //RegistryKey software = hkml.OpenSubKey("SOFTWARE", true);  
            subkeyNames = software.GetSubKeyNames();
            //取得该项下所有子项的名称的序列,并传递给预定的数组中  
            foreach (string keyName in subkeyNames)   //遍历整个数组  
            {
                if (keyName == "Zane") //判断子项的名称  
                {
                    hkml.Close();
                    return true;
                }
            }
            hkml.Close();
            return false;
        }

        /// 
        /// 判断密码是否存在(判断是否存在zane键)
        /// 
        /// 
        private bool IsRegeditKeyExit()
        {
            string[] subkeyNames;
            RegistryKey hkml = Registry.LocalMachine;
            RegistryKey software = hkml.OpenSubKey("SOFTWARE\\Zane");
            subkeyNames = software.GetValueNames();
            //取得该项下所有键值的名称的序列,并传递给预定的数组中   
            foreach (string keyName in subkeyNames)
            {
                if (keyName == "zane")    //判断键值的名称   
                {
                    hkml.Close();
                    return true;
                }
            }
            hkml.Close();
            return false;
        }

· 查

这样一来,HKEY_LOCAL_MACHINE\SOFTWARE\Zane下的zane键就是我们储存密码的地方了。

密码储存在这里之后每次解锁就在这里取得密码,然后进行登录验证。

取得HKEY_LOCAL_MACHINE\SOFTWARE\Zane下的zane键的值,并进行登录验证的代码如下:

/// 
/// 密码验证
/// 
/// 
private bool PwdValidate()
{
    RegistryKey Key = Registry.LocalMachine;
    RegistryKey Zane = Key.OpenSubKey("software\\Zane", true);
    string pwd = Zane.GetValue("zane").ToString();
    Zane.Close();
    if (DESEncrypt.Encrypt(txtPassword.Text) != pwd)
    {
        count++;
        lblMsg.Text = "密码错误";
        if (count >= 4)
        {
            lblMsg.Text = "警告:\r\n你猥琐的面孔已被摄像头捕获\r\n并发送到主人邮箱中";
        }
        lblMsg.Visible = true;
        txtPassword.SelectAll();
        return false;
    }
    count = 0;
    txtPassword.Text = " ";
    return true;
}

上面的代码里面包含一些用于提示信息的label的控制,至于用摄像头捕捉猥琐人物的照片,并将照片发送到指定邮箱的功能在后续章节中添加。

· 改

有密码的地方就会有登录验证和修改密码。

private void btnOK_Click(object sender, EventArgs e)
{
    if (txtNew.Text.Trim() == " ")
    {
        MessageBox.Show("新密码不能为空!");
        return;
    }
    if (txtNew.Text != txtReset.Text)
    {
        MessageBox.Show("两次输入的密码不一致!");
        return;
    }
    RegistryKey Key = Registry.LocalMachine;
    RegistryKey Zane = Key.OpenSubKey("software\\Zane",true);
    string pwd = Zane.GetValue("zane").ToString();
    if (DESEncrypt.Encrypt(txtOld.Text) != pwd)
    {
        MessageBox.Show("密码错误!");
        return;
    }
    Zane.SetValue("zane", DESEncrypt.Encrypt(txtNew.Text));
    Zane.Close();
    this.Close();//修改成功后关闭本Form
}

· 删

虽然锁屏工具不需要删除注册表中的记录,但是我还是一并把删除注册表中的记录的代码贴出来,这是遍历清空某项的键的代码:

private void DeleteRegist(string name) 
{ 
   string[] keys; 
   RegistryKey hkml = Registry.LocalMachine; 
   RegistryKey software = hkml.OpenSubKey("SOFTWARE",true); 
   RegistryKey Zane = software.OpenSubKey("Zane",true); 
   keys = Zane.GetSubKeyNames(); 
   foreach(string key in keys) 
   { 
    if(key == name) 
     Zane.DeleteSubKeyTree(name); 
   } 
}

直接删除某项的代码就不贴了,只需要调用父节点的DeleteSubKey("子项的name");就可以删除叫name的子项了,当然,也包括子项中所有的键。

到此为止,一个完整的锁屏工具就完成了,不过我准备给这个锁屏工具添加更过的细节功能,使之更强大。当然,学习各种各样的技术手段才是重点。

,

Winform 字符串加密/解密,锁屏小工具的拓展(二)

2010-08-13 16:38:00 奔放的胸毛。
1  评论    8,811  浏览

这是我的舞台,虽然我很草根,写的东西没什么技术含量,也没有人鼓掌,但是有人看,这就足够了。

大家好才是真的好,我将一如既往的曝光我的草根Code。

上次我们完成了锁屏工具的雏形,但是没有实现用户自定义密码。今天将实现密码的加密与解密,为后面把密码保存在注册表中做准备。

我所使用的字符串加密方法并不是我写的,也不是很火的MD5等加密方法,仅仅是用.net中现有的类来处理字符串,实现加密的过程。先看代码,再讲解使用方法:

using System;
using System.Security.Cryptography;
using System.Text;
namespace LockScreen
{
	/// <summary>
	/// DES加密/解密类。
	/// </summary>
	public class DESEncrypt
	{
		public DESEncrypt()
		{			
		}

		#region ========加密======== 
 
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="Text"></param>
        /// <returns></returns>
		public static string Encrypt(string Text) 
		{
			return Encrypt(Text,"zane");
		}
		/// <summary> 
		/// 加密数据 
		/// </summary> 
		/// <param name="Text"></param> 
		/// <param name="sKey"></param> 
		/// <returns></returns> 
		public static string Encrypt(string Text,string sKey) 
		{
			DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 
			byte[] inputByteArray; 
			inputByteArray=Encoding.Default.GetBytes(Text); 
			des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); 
			des.IV = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); 
			System.IO.MemoryStream ms=new System.IO.MemoryStream(); 
			CryptoStream cs=new CryptoStream(ms,des.CreateEncryptor(),CryptoStreamMode.Write); 
			cs.Write(inputByteArray,0,inputByteArray.Length); 
			cs.FlushFinalBlock(); 
			StringBuilder ret=new StringBuilder(); 
			foreach( byte b in ms.ToArray()) 
			{ 
				ret.AppendFormat("{0:X2}",b); 
			} 
			return ret.ToString(); 
		} 

		#endregion
		
		#region ========解密======== 
   
 
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="Text"></param>
        /// <returns></returns>
		public static string Decrypt(string Text) 
		{
            return Decrypt(Text, "zane");
		}
		/// <summary> 
		/// 解密数据 
		/// </summary> 
		/// <param name="Text"></param> 
		/// <param name="sKey"></param> 
		/// <returns></returns> 
		public static string Decrypt(string Text,string sKey) 
		{ 
			DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 
			int len; 
			len=Text.Length/2; 
			byte[] inputByteArray = new byte[len]; 
			int x,i; 
			for(x=0;x<len;x++) 
			{ 
				i = Convert.ToInt32(Text.Substring(x * 2, 2), 16); 
				inputByteArray[x]=(byte)i; 
			} 
			des.Key = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); 
			des.IV = ASCIIEncoding.ASCII.GetBytes(System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8)); 
			System.IO.MemoryStream ms=new System.IO.MemoryStream(); 
			CryptoStream cs=new CryptoStream(ms,des.CreateDecryptor(),CryptoStreamMode.Write); 
			cs.Write(inputByteArray,0,inputByteArray.Length); 
			cs.FlushFinalBlock(); 
			return Encoding.Default.GetString(ms.ToArray()); 
		} 
 
		#endregion 


	}
}

加密和解密方法都有+2重载,因为这个算法中需要两个加密/解密“种子”,这两个种子用于参与加密/解密的运算过程,种子不同,加密/解密的结果也就不一样,所以就算别人知道你的算法,但是不知道你的种子参数,还是不能进行解密(我说的不能解密是狭义的,别老拿FBI级别的队伍说事)。

System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5"),这里有两个参数,这两个参数就是刚刚提到的加密/解密种子,为求简单,方法中只把第一个种子作为参数作为调用时输入,第二个参数直接写死,为“MD5”。虽然这里写了个md5,顺便也通过一个我们公司的笑话说说md5加密。

某天,BOSS不知道在哪搞到了md5加密的源码,然后召集队伍,下达了命令,要求一个星期之内研究出md5解密。因为BOSS知道:md5加密是让数据的每一个0和1参与运算,加密结果为一个长度为32的字符串。由于是让每一个字节都参与了运算,所以加密的结果跟每一个字节都有关。既然加密后的结果跟加密前的每个字节都有关,那么肯定能根据加密的结果反推出加密前的数据。

忽然间老板雄起了,因为一旦实现md5解密,那么就能根据一个长度为32的字符串反推出加密前的数据,加密前的数据,它可能是一部20G的高清大片。这个意义就非比寻常了,那将是世界上最先进的压缩技术——再大的数据也能压成32长度的字符串。解压即解密。

如果在U盘里面内置一个md5加解密程序,一个U盘的容量将达到理论上的无限大…BOSS越想越兴奋。

但是当我们告诉他不可能实现的时候老板显得很不解:“源代码都给你们了,这都搞不定!这么简单的事情!点一下就OK了嘛!……”。

事情最终不了了之,讲到这里也说明md5加密是不可逆的,是不能解密的,它是一种有损加密。也就是加密后的数据比加密前要少。上面贴出来的加密算法是可以被解密的,它加密后的数据比加密前要多,属于无损加密。

今天就写到这里了,明天我们将会把加密后的密码储存在注册表中,同时讲解注册表的“增删改查”。

,

Winform 键盘钩子,锁屏小工具的实现(一)

2010-08-12 10:48:00 奔放的胸毛。
0  评论    6,863  浏览

闲来无聊,给自己做了个锁屏小工具,在这里写写步骤,感兴趣的看看。

新建Windws工程后将Form1的FormBorderStyle设置为None,使这个窗口没有边框;TopMost为True,使这个窗口始终在所有窗口的最上方;设置WindowState为Maximized,使窗口一开始就是最大化状态;设置ShowInTaskbar为False,使窗口不在任务栏出现。

然后给窗体设置一张自己喜欢的背景图片,再把BackGroundImageLayout设置为None,使背景图片不重复。

然后在窗口左下角放置一个密码框和一个按钮,按钮单击事件里面暂定密码为“123”就可以退出锁屏。

现在按下Ctrl + F5启动程序,初步效果就已经出来了。

但是这个时候按下Alt + F4就可以关闭锁屏工具,没有安全性可言,所以我们要使用键盘钩子来阻止这一行为。键盘钩子的原理就是通过Windows API实现我们的程序可以获取到所有的键盘事件,只要键盘稍有“响动”,锁屏工具就会第一时间获取到这一信息,然后我们就可以把这一信息拦截,拦截之后不告诉操作系统有按键事件发生,操作系统就不会做出反应。

但是由于我们需要用键盘输入密码来解锁,所以我们不能屏蔽所有的按键,我们也不需要屏蔽所有的按键,只需要把Ctrl、Alt、Win、Tab等功能键屏蔽掉就够了,没了这些功能键,就不能用键盘关闭锁屏工具了。

到底屏蔽哪些键,在下面这个类中的方法KeyboardHookProc中可以自行添加修改。

安装钩子:InstallHook()、卸载钩子:UnInstallHook()。

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace LockScreen
{
    public class Hook_Keyboard
    {
        #region 私有变量

        /// <summary>
        /// 键盘钩子句柄
        /// </summary>
        private IntPtr m_pKeyboardHook = IntPtr.Zero;

        /// <summary>
        /// 钩子委托声明
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

        /// <summary>
        /// 键盘钩子委托实例
        /// </summary>
        /// <remarks>
        /// 不要试图省略此变量,否则将会导致
        /// 激活 CallbackOnCollectedDelegate 托管调试助手 (MDA)。 
        /// 详细请参见MSDN中关于 CallbackOnCollectedDelegate 的描述
        /// </remarks>
        private HookProc m_KeyboardHookProcedure;

        // 底层键盘钩子
        public const int idHook = 13;

        /// <summary>
        /// 安装钩子
        /// </summary>
        /// <param name="idHook"></param>
        /// <param name="lpfn"></param>
        /// <param name="hInstance"></param>
        /// <param name="threadId"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn,
            IntPtr pInstance, int threadId);

        /// <summary>
        /// 卸载钩子
        /// </summary>
        /// <param name="idHook"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);

        /// <summary>
        /// 传递钩子
        /// </summary>
        /// <param name="pHookHandle">是您自己的钩子函数的句柄。用该句柄可以遍历钩子链</param>
        /// <param name="nCode">把传入的参数简单传给CallNextHookEx即可</param>
        /// <param name="wParam">把传入的参数简单传给CallNextHookEx即可</param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(IntPtr pHookHandle, int nCode,
            Int32 wParam, IntPtr lParam);

        #endregion 私有变量

        #region 私有方法


        /// <summary>
        /// 键盘钩子处理函数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        /// <remarks>此版本的键盘事件处理不是很好,还有待修正.</remarks>
        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            //return 1;
            KeyMSG m = (KeyMSG)Marshal.PtrToStructure(lParam, typeof(KeyMSG));
            if (m_pKeyboardHook != IntPtr.Zero)
            {
                switch (((Keys)m.vkCode))
                {
                    case Keys.LWin:
                    case Keys.RWin:
                    case Keys.Delete:
                    case Keys.Alt:
                    case Keys.Escape:
                    case Keys.F4:
                    case Keys.Control:
                    case Keys.Tab:
                        return 1;
                }
            }
            return 0;
        }

        #endregion 私有方法

        #region 公共方法

        /// <summary>
        /// 安装钩子
        /// </summary>
        /// <returns></returns>
        public bool InstallHook()
        {
            //IntPtr pInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);
            IntPtr pInstance = (IntPtr)4194304;
            if (this.m_pKeyboardHook == IntPtr.Zero)
            {
                this.m_KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                this.m_pKeyboardHook = SetWindowsHookEx(idHook, m_KeyboardHookProcedure, pInstance, 0);
                if (this.m_pKeyboardHook == IntPtr.Zero)
                {
                    this.UnInstallHook();
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 卸载钩子
        /// </summary>
        /// <returns></returns>
        public bool UnInstallHook()
        {
            bool result = true;
            if (this.m_pKeyboardHook != IntPtr.Zero)
            {
                result = (UnhookWindowsHookEx(this.m_pKeyboardHook) && result);
                this.m_pKeyboardHook = IntPtr.Zero;
            }
            return result;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct KeyMSG
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        #endregion 公共方法
    }
}

修改我们刚刚的锁屏工具,在窗体加载的时候安装钩子,在窗体关闭ing的时候卸载钩子即可。

启动程序观察效果,果然强大了。

但是还是有问题,理论上是屏蔽了这些功能键了,但是按下Ctrl + Alt + Del的时候居然还

是可以调出系统的任务管理器,并且任务管理器的窗口Top级别是最高的,所以可以通过任务管理器

关闭锁屏工具,这个问题可以这样解决:

        /// <summary>
        /// 用Timer杀死任务管理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            try
            {
                this.Activate();

                Process[] myProcess = Process.GetProcesses();
                foreach (Process p in myProcess)
                {
                    if (p.ProcessName == "taskmgr")
                    {
                        p.Kill();
                        return;
                    }
                }
            }
            catch (Exception)
            {

            }
        }

这是我在Form中添加一个Timer的Tick事件,用这个事件来“杀死”任务管理器进程就OK了。

到这里,我们的锁屏工具基本上已经刀枪不入了。锁屏之后除非有密码,别人是动不了被锁的电脑的。

    警告:卸载钩子这一步骤在程序被关闭时一定要执行,不然很麻烦,很可能出现锁屏工具已经关闭了,但是键盘还是被锁着在。然后建议在制作调试的过程中不启用Timer,以免一不小心出点小毛病输不了密码,键盘也被锁,任务管理器也打不开,那就只好重启电脑了。如果后期加了开机自启动功能,出现这毛病,重启都不顶事,哭去吧。

虽然这个锁屏工具实现是锁屏的功能,安全性也还不错,但是这个简易的锁屏工具离真正的“产品”的要求还很远,比如密码不能修改、锁屏功能不能被快捷键呼出、锁屏工具不能开机自启动等细节问题。我将在接下来的几章里继续完善。

,

公告

业精于勤,荒于嬉;行成于思,毁于随。

有些事现在不做,一辈子都不会做了
这一刻就计划
下一刻就实施
绝对不给自己找任何退却的理由
做,去做
做了才能有改变,有收获

最新评论

评论最多的文章

访问最多的文章

分类标签

友情链接

存档

TOP
Copyright (c) 2009-2017, b3log.org