科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件从桌面移动到设备:多线程和用户界面(4)

从桌面移动到设备:多线程和用户界面(4)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文帮助开发人员克服他们在使用后台线程与用户界面进行交互时Microsoft .NET Compact Framework的局限性,具体内容包括:多线程和用户界面基础知识和构建更好的类。

作者:Jim Wilson 来源:51CTO.com 2007年9月1日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

在线程之间传递数据

UISafeInvoker 实现的一个复杂方面就是将数据从 Invoke 与 BeginInvoke 方法传递到 WndProc 方法。为了使用 SendMessage 或者 PostMessage 将数据发送给隐藏窗口,我们首先需要定义一个类,该类包含将引用存储到 EventHandler 委托以及对象和 EventHandler 委托期望的 EventArgs 参数等必需信息。

class InvokerData{ public EventHandler eventHandler; public object obj; public EventArgs eventArgs; }

然后,Invoke 和 BeginInvoke 实现可以填充 InvokerData 类的一个实例,并使用 SendMessage 或者 PostMessage 将这些信息发送给 WndProc 方法。

问题是,InvokerData 是一个存储在 .NET Compact Framework 托管内存空间中的 .NET 类。SendMessage 和 PostMessage 的实现以及窗口消息队列由底层的 Windows 操作系统来处理,操作系统位于 .NET Compact Framework 托管内存空间之外。

请记住,.NET Compact Framework 主动地跟踪和管理着所有应用程序对象的内存。它可以将对象在内存中从一个位置移动到另一个位置,并且它删除没有活动 .NET 引用指向的任何对象。

我们使用 SendMessage 和 PostMessage 的复杂性在于,如果我们将 InvokerData 对象引用作为参数传递给这些函数中的一个,那么该对象的当前地址将被直接复制到隐藏窗口消息队列中。Windows 操作系统实现的窗口消息队列位于 .NET Compact Framework 托管内存空间之外;因此,.NET Compact Framework 不知道应用程序仍旧保存着 InvokerData 对象地址的事实,并计划再次使用它。

当隐藏窗口最终处理消息时,地址从窗口消息队列中复制到由 UISafeInvokerWndProc 函数接收的 .NET Compact Framework Message 结构中。在地址被复制到消息队列中并传递回 WndProc 函数期间,.NET Compact Framework 垃圾回收器可能会移动甚至可能会删除 InvokerData 对象。

为了避免这种潜在的危险情况,我们可以只要求 .NET Compact Framework 为我们提供一种表示 InvokerData 对象安全传输到 .NET 环境之外并且我们返回时仍旧有效的标记。为此,我们可以使用 GCHandle 结构。

GCHandle 结构可以方便地创建一种表示 .NET 对象被安全传递到 .NET 环境之外并且稍后可以用来找到同一个对象的标记。拥有 GCHandle 结构的 .NET 对象可以防止该对象被作为垃圾回收。GCHandle 结构还可以安全地来回转换成 IntPtr,使得使用 SendMessage 和 PostMessage 非常简单。

既然我们有了 GCHandle,因此这里是完整的 UISafeInvoker.Invoke 方法;UISafeInvoker.BeginInvoke 除了使用 PostMessage 之外,基本上是相同的。

public void Invoke(EventHandler eh, object obj, EventArgs e){ InvokerData d = new InvokerData() ; d.eventHandler eh; d.obj = sender; d.eventArgs = e; GCHandle dataHandle = GCHandle.Alloc(d); // Get token to InvokerData IntPtr iPtr = (IntPtr) dataHandle; // Cast to IntPtr Message m = Message.Create(this.Hwnd, WM_INVOKEMETHOD, IntPtr.Zero, iPtr); MessageWindow.SendMessage(ref m); }

我们的 WndProc 实现然后就可以使用包含在接收消息内的 GCHandle 结构来检索 InvokerData 实例。

注在不再需要该结构时,WndProc 函数必须调用 GCHandle 结构上的 Free 方法。如果没有调用 Free 方法,.NET Compact Framework 内存管理器就无法知道我们不再需要 GCHandle,并且将不能清除相关的对象。

protected override void WndProc(ref Message m)
{
  base.WndProc (ref m);
  if (m.Msg == WM_INVOKEMETHOD)  {
    GCHandle h = (GCHandle) m.LParam;// Cast IntPtr back to GCHandle
    InvokerData d = (InvokerData) h.Target; // Get the InvokerData instance
    h.Free(); // Indicate that we are finished with GCHandle

    d.eventHandler(d.obj, d.eventArgs); // Call the delegate passing the parameters
  }
}

小结

.NET Framework 与 .NET Compact Framework 之间有许多不同点。大多数差别都是细微的;有些更为复杂,另外还需要一点创造性。虽然从后台线程与 UI 进行交互是一种更为复杂的情况,我们的 UISafeInvoker 类为我们提供了在线程之间传递参数并异步运行委托的能力,正如 .NET Framework 一样。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章