首页产品库评测行情新闻|手机数码笔记本台式机DIY硬件数字家庭数码相机办公外设|软件下载游戏开发|社区

更多

数码相机
MP4
LCD
机箱
音箱

软件资讯设计 工具 系统 开发 安全 办公 陶吧 IT教育 Vista频道 | 下载中心酷我音乐盒 腾讯QQ
天极网 > 开发频道>C#匿名方法引起的异常

C#匿名方法引起的异常

2008-05-10 09:00作者:信息加油站义工出处:天极网责任编辑:dizzarz

  问题:最近编写一个低级的键盘钩子,用c#制作,于是用到了win32 api。但是运行大概不久后就会莫名其妙地发生异常,是非法访问内存导致的异常。

  调试发现,异常的地方是不可捕获的。

 

以下是引用片段:
 static class Program
  {
  private static HookLog hookLog;
  /// 
  /// The main entry point for the application.
  /// 
  [STAThread]
  static void Main()
  {
  hookLog = new HookLog(Form1.appPath);
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
  Application.Run(new Form1());
  }
  static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
  {
  hookLog.LogExceptionInfo(e.Exception);
  }
  }
  异常发生在Application.Run(new Form1())的代码行。提示是HookProc回调函数被释放了。于是明白是怎么一回事了。其中,HookProc是声明的一个委托回调类型:

  

以下是引用片段:
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
  这个类型是与windows钩子的回调函输原型对应一致的。

  钩子安装函数:

  

以下是引用片段:
public void Setup()
  {
  if (handle != IntPtr.Zero)
  return;
  using(Process process = Process.GetCurrentProcess())
  {
  using(ProcessModule module = process.MainModule)
  {
  //handle = Win32Api.SetWindowsHookEx(WindowsHookTypes.WH_KEYBOARD_LL,
  // new HookProc(ProcessKeyEvent), Win32Api.GetModuleHandle(module.ModuleName), // 0);
  handle = Win32Api.SetWindowsHookEx(WindowsHookTypes.WH_KEYBOARD_LL,
  kbdHookProc, Win32Api.GetModuleHandle(module.ModuleName), 0);
  if (handle != IntPtr.Zero)
  Win32Api.MessageBeep(MessageBeepTypes.IconExclamation);
  }
  }
  }
  代码中绿色注释掉的是引起异常的代码。因为直接使用了匿名委托。也就是生成的对象是个临时对象,new HookProc(ProcessKeyEvent),这个对象没有被保存。因此,对于托管代码的部分,这个委托回调函输对象将作为临时变量而存在。

  但是通过调用win32 api --- SetWindowsHookEx,这个对象被传递给了非托管代码。这样就出现了问题。托管代码部分将把对象最为临时对象进行垃圾回收,因为发现没有被托管代码中的任何地方所引用。而非托管代码将仍然会调用这个回调函输,因为它无法知道托管代码已经释放了这个指针。这就是为什么运行不久后就发生一个无法捕获的越界访问异常的原因。

  修改为下面一行代码,程序就正常了。

  代码中kbdHookProc是在类中声明的成员变量:

  

以下是引用片段:
/// 
  /// 键盘钩子回调函数
  /// 
  private HookProc kbdHookProc;
  /// 
  /// 构造函数
  /// 
  public LowLevelKeboardHook()
  {
  disposed = false;
  handle = IntPtr.Zero;
  kbdHookProc = new HookProc(ProcessKeyEvent);
  }
  /// 
  /// 键盘钩子处理函数
  /// 
  private IntPtr ProcessKeyEvent(int nCode, IntPtr wParam, IntPtr lParam)
  {
  KBDLLHOOKSTRUCT? rfs = Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)) as KBDLLHOOKSTRUCT?;
  if (KeyboardEvent != null && rfs.HasValue)
  {
  int tag = wParam.ToInt32();
  if(tag == (int)WindowsMessageTypes.WM_KEYDOWN || tag == (int)WindowsMessageTypes.WM_SYSKEYDOWN)
  KeyboardEvent(nCode, rfs.Value.vkCode, rfs.Value.flags);
  }
  return Win32Api.CallNextHookEx(handle, nCode, wParam, lParam);
  }
  这样,看起来没多少变化。但是用类的成员变量记住了这个回调委托对象,因此就不会被垃圾回收所回收了。

  所以使用匿名函数、匿名委托、匿名临时对象等等,当涉及到非托管代码时需要万分小心。

原文连接:http://www.cnblogs.com/worldreason/archive/2008/05/09/1190358.html

共1页。 1
进入 最权威的Windows 7论坛 查看网友讨论

软件频道最新更新

热点推荐

IT嘉年华

编辑推荐

软件下载

热门
推荐

网友关注

软件
资料
游戏

装机推荐

文章排行

本周
本月
最新更新
天极服务|关于我们|About us|网站律师|RSS订阅|友情合作|加入我们|天极动态|网站地图|意见反馈|MSN/QQ上看天极
Copyright (C) 1999-2012 Yesky.com, All Rights Reserved 版权所有 天极网络