[MVC]ASP.NET MVC实用集锦(2)

mikel阅读(774)

继续和大家分享一些MVC使用技巧与实例。
首先想再补充一下集锦(1)中 的扩展htmlhelper问题,其实在C#3.0,就已经提供了扩展方法这个新特性,我们可以在程序中扩展一些基本类型,也可以扩展一些自定义类型。例 如:String,Datetime ,Enumerable等,具体步骤与扩展htmlhelper没什么区别(还是提醒一下别忘记引用命名空间)。
继续(1)中的顺序往下进行吧.
5.在view与Mvc view user control中使用强类型。

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
    Inherits="System.Web.Mvc.ViewPage<Account>" %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Account>>" %>

在view 或 Mvc view user control中的代码如下

<% if (Model != null) { %>
      <div><%= Model.Name %></div>
      <div><%= Model.Age %></div>
<% } %>

在controller中需要将Model赋值

public ActionResult ShowAccount()
{
      var account = new Account { Name = "Name" , Age = 18 };
      return View(account);
}

顺便一提:
当我需要呈现View的时候我会选择 return View()
当我需要ajax异步请求到controller做一些事情时,处理结束后,我会通过 return Json() 返回操作是否成功或返回的数据。
当我需要调用其他action时我会使用 RedirectToAction 或 RedirectToRoute。
当我什么都不需要做的时候我会使用 EmptyResult()。例如数据导出操作等。
6.UpdateModel与TryUpdateModel
虽然在MVC 中可以直接在Controller的方法中定义操作对象,然后通过表单提交装入数据到对象,如下:

public ActionResult Register(Account account)
{ //TODO }

但 我会使用UpdateModel和TryUpdateModel去做这些事情,还是简单说一下这两个方法的功能。如果用户操作的view是完成注册用户功 能,此时表单数据已经填写完整,提交表单后转入上面的方法( Register() , 去掉参数),在方法中使用如下代码:

public ActionResult Register()
{
      var account = new Account();
      UpdateModel(account);
      //TODO: Register
      return ……
}

这样表单中的数据可以装入 account 对象,后来发现有时总是不成功,程序报错,遂发现TryUpdateModel(),虽然执行后也会有错误,但是部分数据已经正确填入程序也并不会报错, 然后将没有填入的数据(问题就发生在这些数据上)单独处理。不能装入的数据通常都是由规律的,例如ID,view中通过DropDownList选择的数 据等等,所以可以使用重载方法将可能会出错的数据排除掉, string[] excludeProperties 参数,这样TryUpdateModel就不会报错了,虽然已经找到装入对象时出错的规律,但还是想找到更准确的判断方法,不想这样偷懒下去。偶然间注意 到 ModelState.IsValid 这句经常出现的代码,所以就试了一下,在TryUpdateModel语句执行后,在快速监视中添加了ModelState,果然可以看到出错的字段(里 面有error属性)。后面的操作就是一样的了,排除这些字段,单独处理!

7.在Action中使用FormCollection,6中使用 string[] excludeProperties 参数排除掉的字段可以通过下面的方法赋值

public ActionResult Register(FormCollection form)
{
      var account = new Account();
      // city在表单中是下拉框选择的(DropDownList)
      if (TryUpdateModel(account , null ,  null , new string[] { "City" } ))   // UpdateModel也可以重载
      {
            account.City = form["City"];
      }
      return ……
}

[MVC]在 ASP.NET MVC 中使用 HTTPS (SSL/TLS)

mikel阅读(805)

某些安全性较高的网页,如网上支付或用户登陆页面,可能会使用到https(SSL/TLS)来提高安全性。本文介绍了如何在ASP.NET MVC中强制某action使用https和如何进行向https页面的跳转。
我们先实现强制一个action使用https。这里写了一个RequireHttpsAttribute,它的作用是将非https连接转换成https连接,这样所有使用了RequireHttps这个filter的controller都会强制使用https连接。
 

 1 using System.Web.Mvc;
 2 
 3 namespace Snowdream.Demo.RequireHttps
 4 {
 5     public class RequireHttpsAttribute:AuthorizeAttribute
 6     {
 7         /// <summary>
 8         /// 重写OnAuthorization方法
 9         /// </summary>
10         /// <param name="filterContext"></param>
11         public override void OnAuthorization(AuthorizationContext filterContext)
12         {
13             // 如果已经是https连接则不处理,否则重定向到https连接
14             if (!filterContext.HttpContext.Request.IsSecureConnection)
15             {
16                 // 获取当前请求的Path
17                 string path = filterContext.HttpContext.Request.Path;
18 
19                 // 从web.config中获取host,也可以直接从httpContext中获取
20                 string host = System.Configuration.ConfigurationManager.AppSettings["HostName"];
21 
22                 // 从web.config中获取https的端口
23                 string port = System.Configuration.ConfigurationManager.AppSettings["HttpsPort"];
24 
25                 // 如果端口号为空表示使用默认端口,否则将host写成host:port的形式
26                 if (port != null)
27                 {
28                     host = string.Format("{0}:{1}", host, port);
29                 }
30 
31                 // 重定向到https连接
32                 filterContext.HttpContext.Response.Redirect(string.Format("https://{0}{1}", host, path));
33             }
34         }
35     }
36 }
37 

由于https和https服务使用不同的端口号,而且https不能绑定主机头,只能通过不同端口的方式来区分各个站点,所以这里将host和port信息写到了web.config里,以方便配置。在web.config的appsettings节加入如下信息即可

1 <appSettings>
2     <add key="HostName" value="localhost"/>
3     <add key="httpsPort" value="443"/>
4 </appSettings>
5 

HttpsPort可以不写,将使用默认的443。
然后在要使用https连接的controller或action前加上[RequireHttps],如

1 [RequireHttps]
2 public ActionResult About()
3 {
4     return View();
5 }
6 

这样,当我们用http://localhost/Home/About访问该页面时会自动跳到https://localhost/Home/About。 但是这样还有一个问题,网页中的链接都是http的,当点击进入需要使用https连接的网页时都要进行一次Redirect。所以我们要将网页中的链接 也改成https。这步不难,只需要将view中所有链接到https页面的Html.Action()使用适当的重载方法来写即可。ASP.NET MVC 1.0 RTM中提供了2种重载可以将protocol设置为https。在新建ASP.NET MVC Web Application后默认生成的站点中,shared文件夹下有site.master文件中有个指向/Home/About的 ActionLink。原来是

1 Html.ActionLink("Home""Index""Home")

我们对其进行改写

1 Html.ActionLink("About""About""Home""https""localhost""",nullnull)

这样,生成出来的链接就是https的了,点击以后直接会使用https连接而无需再进行一次Redirect,之后新的要到https页面的链接也可仿照次写法。
这 里又要用到hostName信息,我们之前已经将它写在web.config里了,所以可以专门写一个方法来获取web.config中的这部分信息并拼 接成这里需要的hostName字符串,或者还可以对HtmlHelper写一个扩展方法专门用于处理https的链接,这些可以在实际使用时做适当的优 化。
示例代码下载

[JQuery]如何在多个页面使用同一个HTML片段

mikel阅读(755)

下载源代码
问题描述
有一个比较复杂的HTML片段(A),如果把这个HTML片段嵌入到其他页面中(B,C,D….)。
问题的关键是在HTML片段中有大量的JavaScript逻辑需要处理,比如说分页,点击事件响应等。
对于这个问题,我们用一个简单的例子来说明:
“页面上有一个按钮,点击此按钮引入一个HTML片段,此HTML片段中有分页按钮。”
1. 使用IFrame
主页面,点击一个按钮向页面引入一个IFrame:

    <script type="text/javascript">
$(function() {
$("#clickToInsert").click(function() {
$("#placeholder").html('<iframe src="iframe.htm"></iframe>');
});
});
</script>
<input type="button" id="clickToInsert" value="Insert HTML" />
<div id="placeholder">
</div>

IFrame页面,模拟分页的情况:

    <script type="text/javascript">
$(function() {
var parent = $("#complex_page_segment");
$(".previous", parent).click(function() {
$(".content", parent).html("Previous Page Content");
});
$(".next", parent).click(function() {
$(".content", parent).html("Next Page Content");
});
});
</script>
<div id="complex_page_segment">
<input type="button" value="Previous Page" class="previous" />
<input type="button" value="Next Page" class="next" />
<div class="content">
Page Content</div>
</div>

2. AJAX返回页面片段,并注册事件
注:我们通过textarea来模拟返回的HTML片段。

    <script type="text/javascript">
$(function() {
$("#clickToInsert").click(function() {
$("#placeholder").html($("#clone").val());
var parent = $("#complex_page_segment");
$(".previous", parent).click(function() {
$(".content", parent).html("Previous Page Content");
});
$(".next", parent).click(function() {
$(".content", parent).html("Next Page Content");
});
});
});
</script>
<input type="button" id="clickToInsert" value="Insert HTML" />
<div id="placeholder">
</div>
<textarea id="clone" style="display: none;">
<div id="complex_page_segment">
<input type="button" value="Previous Page" class="previous" />
<input type="button" value="Next Page" class="next" />
<div class="content">Page Content</div>
</div>
</textarea>

由于我们需要在多个页面引用同一个HTML片段,这种方法导致大量事情处理被重复性的拷贝粘贴,明显我们需要将公共的方法提取出来。
3. AJAX返回页面片段,并调用页面片段中的函数注册事件

    <script type="text/javascript">
$(function() {
$("#clickToInsert").click(function() {
$("#placeholder").html($("#clone").val());
init_complex_page_segment();
});
});
</script>
<input type="button" id="clickToInsert" value="Insert HTML" />
<div id="placeholder">
</div>
<textarea id="clone" style="display: none;">
<script type="text/javascript">
function init_complex_page_segment() {
var parent = $("#complex_page_segment");
$(".previous", parent).click(function() {
$(".content", parent).html("Previous Page Content");
});
$(".next", parent).click(function() {
$(".content", parent).html("Next Page Content");
});
}
</script>
<div id="complex_page_segment">
<input type="button" value="Previous Page" class="previous" />
<input type="button" value="Next Page" class="next" />
<div class="content">Page Content</div>
</div>
</textarea>

其实我们可以更进一步,完全没必要手工调用这个函数,而是可以在返回的HTML片段中让其自动执行。
4. AJAX返回页面片段,其事件自动注册

    <script type="text/javascript">
$(function() {
$("#clickToInsert").click(function() {
$("#placeholder").html($("#clone").val());
});
});
</script>
<input type="button" id="clickToInsert" value="Insert HTML" />
<div id="placeholder">
</div>
<textarea id="clone" style="display: none;">
<script type="text/javascript">
$(function() {
var parent = $("#complex_page_segment");
$(".previous", parent).click(function() {
$(".content", parent).html("Previous Page Content");
});
$(".next", parent).click(function() {
$(".content", parent).html("Next Page Content");
});
});
</script>
<div id="complex_page_segment">
<input type="button" value="Previous Page" class="previous" />
<input type="button" value="Next Page" class="next" />
<div class="content">Page Content</div>
</div>
</textarea>

最后一种方法和第一种IFrame的方式是我们所推荐的。

[JQuery]遍历声明的Json数组

mikel阅读(799)

以前基本上都是在用AJAX从服务器获得Json格式数据,然后直接调用JQuery的getJson中的Data然后
$.each(data,function(){
//具体代码
});
就行了,今天做模板,需要声明好json数组然后遍历显示,代码如下:
var json=[{"Identifier":"1","Name":"lele"},{"Identifier":"2","Name":"fifi"},{"Identifier":"3","Name":"mikel"}];
$.each(data,function(){
alert(this.Name);
});
OR
$(json).each(function(){
alert(this.Name);
});

[Hook]Windows Hook 学习笔记(1)——基本概念

mikel阅读(690)

基本概念
      钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定 窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息 或特定事件。
      钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目 的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制 结束消息的传递。
运行机制
1、钩子链表和钩子子程:
      每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指 定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到 Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子 放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。
      Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
      钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。
   钩子子程必须按照以下的语法:
LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);
  • HookProc是应用程序定义的名字。
  • nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。
  • wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。
2、钩子的安装与释放:
      使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表 中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关 联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个 Hook子程需要调用CallNextHookEx函数。
HHOOK SetWindowsHookEx(
     int idHook,      // 钩子的类型,即它处理的消息类型
     HOOKPROC lpfn,   // 钩子子程的地址指针。如果dwThreadId参数为0
// 或是一个由别的进程创建的线程的标识,
// lpfn必须指向DLL中的钩子子程。
// 除此以外,lpfn可以指向当前进程的一段钩子子程代码。
// 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。
     HINSTANCE hMod,  // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。
// 如果dwThreadId 标识当前进程创建的一个线程,
// 而且子程代码位于当前进程,hMod必须为NULL。
// 可以很简单的设定其为本应用程序的实例句柄。
     DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。
// 如果为0,钩子子程与所有的线程关联,即为全局钩子。
                 );   
      函数成功则返回钩子子程的句柄,失败返回NULL。
  以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。
在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个 SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值, 返回值的类型依赖于钩子的类型。这个函数的原型如下:
LRESULT CallNextHookEx
(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
); 
  • hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。
  • NCode为传给钩子过程的事件代码。
  • wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。
      钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。
      钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:
UnHookWindowsHookEx
(
HHOOK hhk;
);
      函数成功返回TRUE,否则返回FALSE。
 
3、一些运行机制:
      在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情 况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该 进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它 们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。
      因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各 进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给 这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。
      #pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
 
      在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws"),那么这个数据节中的数据可以在所有DLL的实例之间共 享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。
      当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。
 
4、系统钩子与线程钩子:
      SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。
      线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。
      系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。
几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。
(2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。
(3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。
 
钩子类型
      每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。
WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。
CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。
2、WH_CBT Hook
      在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:
      1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;
      2. 完成系统指令;
      3. 来自系统消息队列中的移动鼠标,键盘事件;
      4. 设置输入焦点事件;
      5. 同步系统消息队列事件。
      Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。
3、WH_Debug Hook
      在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_Debug Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。
4、WH_FOREGROUNDIDLE Hook
      当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。
5、WH_GETMESSAGE Hook
      应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。
6、WH_JOURNALPLAYBACK Hook
      WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。
      WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。
      WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。
      WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。
7、WH_JOURNALRECORD Hook
      WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。
      WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。
      WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。
8、WH_KEYBOARD Hook
      在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。
9、WH_KEYBOARD_LL Hook
      WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。
10、WH_MOUSE Hook
      WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。
11、WH_MOUSE_LL Hook
      WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。
12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
      WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。
      WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。
      通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。
13、WH_SHELL Hook
      外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。
      WH_SHELL 共有5钟情況:
      1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;
      2. 当Taskbar需要重画某个按钮;
      3. 当系统需要显示关于Taskbar的一个程序的最小化形式;
      4. 当目前的键盘布局状态改变;
      5. 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。
      按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。
 

[Hook]Winform:关于钩子的基础知识

mikel阅读(867)

基本概念
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

 

http://blog.csdn.net/hejinjiang/archive/2008/03/19/2197066.aspx有对勾子作了非常详细的介绍

 

尽管在 .NET 框架中不支持全局挂钩

MSDN

您无法在 Microsoft .NET 框架中实现全局挂钩。若要安装全局挂钩,挂钩必须有一个本机动态链接库 (DLL) 导出以便将其本身插入到另一个需要调入一个有效而且一致的函数的进程中。这需要一个 DLL 导出,而 .NET 框架不支持这一点。托管代码没有让函数指针具有统一的值这一概念,因为这些函数是动态构建的代理。

 

但我们通过API函数的调用,还是可以照样实现十年前很热门的技术

 

勾子到底能干嘛呢?

提示: 如果要设置系统级钩子, 钩子函数必须在 DLL .


SetWindowsHookEx(

 idHook: Integer;   {钩子类型}

 lpfn: TFNHookProc; {函数指针}

 hmod: HINST;       {包含钩子函数的模块(EXEDLL)句柄; 一般是 HInstance; 如果是当前线程这里可以是 0}

 dwThreadId: DWORD {关联的线程; 可用 GetCurrentThreadId 获取当前线程; 0 表示是系统级钩子}

): HHOOK;            {返回钩子的句柄; 0 表示失败}

 

//钩子类型 idHook 选项:

WH_MSGFILTER       = -1; {线程级; 截获用户与控件交互的消息}

WH_JOURNALRECORD   = 0; {系统级; 记录所有消息队列从消息队列送出的输入消息, 在消息从队列中清除时发生; 可用于宏记录}

WH_JOURNALPLAYBACK = 1; {系统级; 回放由 WH_JOURNALRECORD 记录的消息, 也就是将这些消息重新送入消息队列}

WH_KEYBOARD        = 2; {系统级或线程级; 截获键盘消息}

WH_GETMESSAGE      = 3; {系统级或线程级; 截获从消息队列送出的消息}

WH_CALLWNDPROC     = 4; {系统级或线程级; 截获发送到目标窗口的消息, SendMessage 调用时发生}

WH_CBT             = 5; {系统级或线程级; 截获系统基本消息, 譬如: 窗口的创建、激活、关闭、最大最小化、移动等等}

WH_SYSMSGFILTER    = 6; {系统级; 截获系统范围内用户与控件交互的消息}

WH_MOUSE           = 7; {系统级或线程级; 截获鼠标消息}

WH_HARDWARE        = 8; {系统级或线程级; 截获非标准硬件(非鼠标、键盘)的消息}

WH_Debug           = 9; {系统级或线程级; 在其他钩子调用前调用, 用于调试钩子}

WH_SHELL           = 10; {系统级或线程级; 截获发向外壳应用程序的消息}

WH_FOREGROUNDIDLE = 11; {系统级或线程级; 在程序前台线程空闲时调用}

WH_CALLWNDPROCRET = 12; {系统级或线程级; 截获目标窗口处理完毕的消息, SendMessage 调用后发生}

 

从这个结构体与其枚举参数,这是仁者见仁的事情,我们同事一直都在做用钩子控制别人软件,如QQ的事情,嘿嘿,其实也即相当于木马了,比如某某监听健盘而获取你密码的软件。而我们经常用的ctrl+alt+z调出QQ,也可以通过做一个全局的钩子来应用(热健的下面再介绍)

 

这里介绍了一篇勾住MOUSE的方法

http://support.microsoft.com/kb/318804/

而这里也介绍了一篇控制KEYBOARD的方法

http://www.cnblogs.com/zagelover/articles/1137331.html

 

而其最主要的就是下面这么几个API函数,而委托作为函数指针也在这里有个比较完美的体现吧,其实就充当回调函数的指针。

 

 

 //Declare wrapper managed MouseHookStruct class.

        [StructLayout(LayoutKind.Sequential)]

        public class MouseHookStruct

        {

            public POINT pt;

            public int hwnd;

            public int wHitTestCode;

            public int dwExtraInfo;

        }

//声明键盘钩子的封送结构类型
        [StructLayout(LayoutKind.Sequential)]
        public class KeyboardHookStruct
        {
            public int vkCode; //表示一个在1254间的虚似键盘码
            public int scanCode; //表示硬件扫描码
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        //装置钩子的函数
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //卸下钩子的函数
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        //下一个钩挂的函数
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        [DllImport("user32")]
        public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);

注:

ToAscii Function


The ToAscii function translates the specified virtual-key code and keyboard state to the corresponding character or characters. The function translates the code using the input language and physical keyboard layout identified by the keyboard layout handle.

To specify a handle to the keyboard layout to use to translate the specified code, use the ToAsciiEx function.

Return Value

If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.

Value

Meaning

0

The specified virtual key has no translation for the current state of the keyboard.

1

One character was copied to the buffer.

2

Two characters were copied to the buffer. This usually happens when a dead-key character (accent or diacritic) stored in the keyboard layout cannot be composed with the specified virtual key to form a single character.

通过这个函数,有时也可以实现某些热健功能,特别是返回值为1    

到这里也顺便复习下KEYPRESS

在控件有焦点的情况下按下键时发生

键事件按下列顺序发生:

KeyDown

KeyPress

KeyUp

非字符键不会引发 KeyPress 事件;但非字符键却可以引发 KeyDown KeyUp 事件。

使用 KeyChar 属性在运行时对键击进行取样,并且使用或修改公共键击的子集。

若要仅在窗体级别处理键盘事件而不允许其他控件接收键盘事件,请将窗体的 KeyPress 事件处理方法中的 KeyPressEventArgs.Handled 属性设置为 true

   说明  
   
  具有焦点的对象接收该事件。一个窗体仅在它没有可视和有效的控件或   KeyPreview   属性被设置为   True   时才能接收该事件。一个   KeyPress   事件可以引用任何可打印的键盘字符,一个来自标准字母表的字符或少数几个特殊字符之一的字符与   CTRL   键的组合,以及   ENTER   或   BACKSPACE   键。KeyPress   事件过程在截取   TextBox   或   ComboBox   控件所输入的击键时是非常有用的。它可立即测试击键的有效性或在字符输入时对其进行格式处理。改变   keyascii   参数的值会改变所显示的字符。  
   
  可使用下列表达式将   keyascii   参数转变为一个字符:  
   
  Chr(KeyAscii)  
   
  然后执行字符串操作,并将该字符反译成一个控件可通过该表达式解释的   ANSI   数字:  
   
  KeyAscii   =   Asc(char)  
   
  应当使用   KeyDown   和   KeyUP   事件过程来处理任何不被   KeyPress   识别的击键,诸如:功能键、编辑键、定位键以及任何这些键和键盘换档键的组合等。与   KeyDown   和   KeyUp   事件不同的是,KeyPress   不显示键盘的物理状态,而只是传递一个字符。  
   
  KeyPress   将每个字符的大、小写形式作为不同的键代码解释,即作为两种不同的字符。而   KeyDown   和   KeyUp   用两种参数解释每个字符的大写形式和小写形式:keycode   —   显示物理的键(将   A   和   a   作为同一个键返回)和   shift   —指示   shift   +   key   键的状态而且返回   A   或   a   其中之一。  
   
  如果   KeyPreview   属性被设置为   True,窗体将先于该窗体上的控件接收此事件。可用   KeyPreview   属性来创建全局键盘处理例程。  
   

  注意…CTRL+@   的键盘组合的   ANSI   编号是   0。因为   Visual   Basic   将一个零值的   keyascii   识别为一个长度为零的字符串   (""),在应用程序中应避免使用   CTRL+@   的组合。

 

 

在这里下面的委托将充当函数指针用

public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

[Hook]C#强化系列文章二:在C#中使用钩子

mikel阅读(870)

相信以前用过VB、Delphi,特别是VC的程序员应该对钩子程序都不陌生。在C#中我们同样可以使用钩子程序来实现特殊效果,比如当用户按下某个特殊键时提示,比如关闭应用程序前提示等。
当然使用方法相对VC来说要稍微复杂一点,有的地方还不太方便,下面的例子中实现两个基本功能:
1、按下Alt+F4时使窗口最小化
2、关闭应用程序前提示
不过目前只能捕获消息,不能屏蔽消息,我正在实验,也希望知道的高手能多多指教
一、加入winuser.h中的定义
因为钩子程序一般情况下都是在vc下使用的,在C#里面并没有对应的方法、结构等的定义,我们首先需要把winuser.h中的相关定义加入自己的类

钩子类型的枚举

具体的说明在msdn中都可以查到,主要的比如WH_KEYBOARD是监控按键事件,WH_CALLWNDPROC是在消息触发时执行

虚键值的定义

这个不用说明了,对应ALT、CTRL等键

消息结构体

这个是windows内部传递过来的消息的结构
二、加入自己定义的委托和事件参数

钩子委托

HokkProc是SetWindowsHookEx调用时的委托事件,HookEventHandler是自己的委托事件

钩子事件参数

是自己的委托事件中接受的事件参数
三、实现自己的钩子类
这一步是最重要的,要使用钩子,我们需要引用user32.dll中的相应方法:

        [DllImport("user32.dll")]
        
static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);

        [DllImport(
"user32.dll")]
        
static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport(
"user32.dll")]
        
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport(
"user32.dll")]
        
static extern short GetKeyState(VirtualKeys nVirtKey);

SetWindowsHookEx是注册一个钩子程序,UnhookWindowsHookEx是释放钩子程序,CallNextHookEx调用钩子的后续事件处理,GetKeyState得到所按的虚键
然后就可以调用这些方法来实现钩子程序,比如注册一个钩子可以调用:

            m_hook = SetWindowsHookEx(m_hooktype, m_hookproc, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());

其中m_hooktype就是HookType中定义的类型,m_hookproc就是实际的钩子处理程序:

m_hookproc = new HookProc(KeyHookProcedure);

最关键的就是KeyHookProcedure等钩子处理程序:

        protected int KeyHookProcedure(int code, IntPtr wParam, IntPtr lParam)
        
{
            
if (code != 0)
            
{
                
return CallNextHookEx(m_hook, code, wParam, lParam);
            }


            
if (HookInvoked != null)
            
{
                Keys key 
= (Keys)wParam.ToInt32();
                HookEventArgs eventArgs 
= new HookEventArgs();
                eventArgs.key 
= key;
                eventArgs.lParam 
= lParam;
                eventArgs.wParam 
= wParam;
                eventArgs.HookCode 
= code;
                eventArgs.bAltKey 
= GetKeyState(VirtualKeys.VK_MENU) <= 127;
                eventArgs.bCtrlKey 
= GetKeyState(VirtualKeys.VK_CONTROL) <= 127;
                HookInvoked(this, eventArgs);
            }


            
return CallNextHookEx(m_hook, code, wParam, lParam);
        }

在这个事件中可以取得消息的参数,特别是按键的值,然后通过HookInvoked委托调用事件实际的处理程序
四、在应用程序中调用钩子类
我们可以在自己的form中声明两个钩子对象

        private MyHook callProcHook = new MyHook(HookType.WH_CALLWNDPROC);
        
private MyHook keyHook = new MyHook(HookType.WH_KEYBOARD);

然后在初始化时注册钩子:

        private void Form1_Load(object sender, EventArgs e)
        
{
            keyHook.HookInvoked 
+= new HookEventHandler(keyHook_HookInvoked);
            keyHook.Install();

            callProcHook.HookInvoked 
+= new HookEventHandler(callProcHook_HookInvoked);
            callProcHook.Install();
        }

然后就是实际的钩子事件:

        private void keyHook_HookInvoked(object sender, HookEventArgs e)
        
{
            
if (e.key == Keys.F4 && e.bAltKey) //Alt + F4
            {
                
this.WindowState = FormWindowState.Minimized;
            }

        }


        
private void callProcHook_HookInvoked(object sender, HookEventArgs e)
        
{
            
unsafe
            
{
                CWPSTRUCT
* message = (CWPSTRUCT*)e.lParam;
                
if (message != null)
                
{
                    
if (message->message == WM_CLOSE)
                    
{
                        (sender 
as MyHook).CallNextProc = false;
                        MessageBox.Show(
"程序即将关闭!");
                    }

                }

            }

        }

这样我们就可以通过钩子实现一些相对底层的应用。
代码说的有点乱,我就把最主要的代码直接列在下面供大家参考:

例子代码

以上的钩子只对当前应用程序起作用,如果想控制其他的所有程序,需要使用全局钩子。原则上全局钩子在C#中是不支持的,在http://www.codeproject.com/csharp/globalhook.asp 中的代码可以参照来实现全局钩子

[Hook]API 通过HOOK OpenProcess() 实现进程防杀

mikel阅读(1610)

在WINDOWS操作系统下,当我们无法结束或者不知道怎样结束一个程序的时候,或者是懒得去找“退出”按钮的时候,通常会按 “CTRL+ALT+DEL”呼出任务管理器,找到想结束的程序,点一下“结束任务”就了事了,呵呵,虽然有点粗鲁,但大多数情况下都很有效,不是吗?
                 
    设想一下,如果有这么一种软件,它所要做的工作就是对某个使用者在某台电脑上的活动 作一定的限制,而又不能被使用者通过“结束任务”这种方式轻易地解除限制,那该怎么做?无非有这么三种方法:1.屏蔽“CTRL+ALT+DEL”这个热 键的组合;2.让程序不出现在任务管理器的列表之中;3.让任务管理器无法杀掉这个任务。对于第一种方法,这样未免也太残酷了,用惯了“结束任务”这种方 法的人会很不习惯的;对于第二种方法,在WINDOWS 9X下可以很轻易地使用注册服务进程的方法实现,但是对于WINDOWS  NT架构的操作系统没有这个方法了,进程很难藏身,虽然仍然可以实现隐藏,但 实现机制较为复杂;对于第三种方法,实现起来比较简单,我的作品:IPGate网址过滤器 就是采用的这种方式防杀的,接下来我就来介绍这种方法。
    任务管理器的“结束任务”实际上就是强制终止进程,它所使用的杀手锏是一个叫做TerminateProcess()的Win32 API函数,我们来看看它的定义:
            BOOL TerminateProcess(
              HANDLE      hProcess; // 将被结束进程的句柄
              UINT        uExitCode; // 指定进程的退出码
            );
    看 到这里,是不是觉得不必往下看都知道接下来要做什么:Hook TerminateProcess()函数,每次TerminateProcess()被调用的时候先判断企图结束的进程是否是我的进程,如果是的话就简 单地返回一个错误码就可以了。真的是这么简单吗?先提出一个问题,如何根据hProcess判断它是否是我的进程的句柄?答案是:在我的进程当中先获得我 的进程的句柄,然后通过进程间通讯机制传递给钩子函数,与hProcess进行比较不就行了?错!因为句柄是一个进程相关的值,不同进程中得到的我的进程 的句柄的值在进程间进行比较是无意义的。
    怎么办?我们来考察一下我的hProcess它是如何得到的。一个进程只有它的进程 ID是独一无二的,操作系统通过进程ID来标识一个进程,当某个程序要对这个进程进行访问的话,它首先得用OpenProcess这个函数并传入要访问的 进程ID来获得进程的句柄,来看看它的参数:
            HANDLE OpenProcess(
            DWORD      dwDesiredAccess, // 希望获得的访问权限
            BOOL       bInheritHandle, // 指明是否希望所获得的句柄可以继承
            DWORD      dwProcessId // 要访问的进程ID
            );
     脉 络渐渐显现:在调用TerminateProcess()之前,必先调用OpenProcess(),而OpenProcess()的参数表中的 dwProcessId是在系统范围内唯一确定的。得出结论:要Hook的函数不是TerminateProcess()而是 OpenProcess(),在每次调用OpenProcess()的时候,我们先检查dwProcessId是否为我的进程的ID(利用进程间通讯机 制),如果是的话就简单地返回一个错误码就可以了,任务管理器拿不到我的进程的句柄,它如何结束我的进程呢?
    至此,疑团全部 揭开了。由Hook TerminateProcess()到Hook OpenProcess()的这个过程,体现了一个逆向思维的思想。其实我当初钻进了TerminateProcess()的死胡同里半天出也不来,但最 终还是蹦出了灵感的火花,注意力转移到了OpenProcess()上面,实现了进程防杀。喜悦之余,将这心得体会拿出来与大家分享。

[QQ]让电脑只能登录指定QQ号码,限制其他QQ登陆方法

mikel阅读(605)

这里有一个方法可以电脑只能登录指定QQ号码,供大家参考。

  1、首先登录允许登录的QQ号码,(如果已经在电脑上登录过了,这步可以省略)主要目的就是把自己的QQ号码记录下来,这样在QQ的安装文件夹里就会出来你自己的QQ号码文件夹。如图1

  让电脑只能登录指定QQ号码

  2、进入到你安装QQ的目录中,把其它人的QQ号码文件夹进行删除,只留下你需要登录的QQ号码文件夹即可。

  3、进入到你安装QQ的目录中,找到“WizardCtrl.dll”(动态链接库文件),将该文件删除或者移动到其他的目录中。这里我建议大家改名就可以了,把WizardCtrl最后一个字母改为1,哈哈,这样不容易被人发现。如果删除,以后想恢复就有点麻烦。

  让电脑只能登录指定QQ号码

  当完成以上三步的时候,你的电脑就只认你的QQ号了,其它人的QQ号进行登录的时候,一律无反应。

[QQ]ws2_32.dll 禁止使用某个软件

mikel阅读(676)

一般在自己电脑上,不会不想使用某个软件,但却有些软件不想让别人使用。怎么办?
方法:
在该软件安装目录下新建一个文件名为 ws2_32.dll 的文件(建立一个空的文本文件,然后将文件名改为ws2_32.dll,注意扩展名。) 此后在运行该软件应用程序时,系统就会有出错提示:“应用程序或 DLL X:\XXX\XXX\ws2_32.dll 为无效的  Windows 映像。请再检测一遍您的安装盘。”该软件就不能被使用了。本方法适用基于NT系统的WinXP, Win2000, Win2003。
当然,这只是一个小技巧。如果你想在别人的电脑上玩游戏,却发现不能运行程序,那就赶快到该游戏的安装目录下看看,有没有一个 ws2_32.dll 的文件。
简单原理:
ws2_32.dll 是 Windows Sockets应用程序接口,用于支持 Internet 和网络应用程序,是个动态链接库文件。程序运行时会自动调用 ws2_32.dll 文件。而 Windows 在查找动态链接库文件时会先在应用程序当前目录搜索,但此时该目录下的文件是重建的一个空文件,所以程序就不能被正常运行了。
一些知识:
ws2_32.dll 一般位于系统文件夹中。Windows 在应用程序当前目录没有找到然后会去搜索 Windows 所在目录,如果还是没有会搜索 system32 和 system 目录。一些病毒利用此原理在杀毒软件目录中建立伪 "ws2_32.dll" 的文件或文件夹,在杀毒软件看来这是程序运行需要的文件而调用,这个所谓的“文件”又不具备系统 "ws2_32.dll" 文件的功能,所以杀毒软件等就无法运行了而提示:应用程序正常初始化 (0xc00000ba) 失败!
伪 "ws2_32.dll" 文件夹其实重命名之后程序就可以运行的,但是有个没用的又无法删除的文件夹看着也不舒服,因此这个内部含有非法文件名的文件夹就用下面方法删除:
1. 解压附件里面的文件(修复“显示所有文件和文件夹”.reg),双击它修复显示所有文件和文件夹。然后在资源管理器上点击工具>>选项 >>查看,将隐藏受保护的操作系系统(推荐)的勾去掉,勾选显示所有文件和文件夹之后一路的确认,好了现在隐藏的文件全部显示出来。(临时解 决办法可以将 ws2_32.dll 文件夹改个名称,要彻底删除请往下看。)
2. 到杀毒软件的安装目录下找到以伪 "ws2_32.dll" 的文件夹(一般隐藏的文件夹颜色略白)。
3. 解压附件里面的“将畸形文件托到我上面.bat”到任意目录,用鼠标左键点击将要删除的 ws2_32.dll 文件或者文件夹(一般这些目录下的隐藏文件都需要删除)按住不松然后拖放到该文件图标上(就像把文件拖到文件夹里的操作一样,此工具双击是不能运行的), 一个 CMD 窗口闪烁之后伪 "ws2_32.dll" 文件夹就被删除了。你需要的软件就可以启动了!此批处理文件也可以删除其他文件名怪怪的文件。