科技行者

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

知识库

知识库 安全导航

至顶网软件频道基础软件Visual C++中删除托管对象、包装库及其他

Visual C++中删除托管对象、包装库及其他

  • 扫一扫
    分享文章到微信

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

本文将给大家讲解一下Visual C++中如何删除托管对象、包装库及其他,大家在实际开发中可以做个参考。

作者:Paul DiLascia 来源:msdn中国 2007年11月20日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
答:哦,当然可以。您不能通过简单的 dllimport 语句将本机列表转换成托管类型的集合。interop 服务不错,但不是非常 好!您需要编写一个包装,它将您的列表显式转换成托管集合(例如 ArrayLis)。我编写了一个具有三个模块的程序 (ListWrap) 来说明如何实现。第一个模块是 ListLib.cpp,它实现了一个具有两个函数(AllocateList 和 FreeList)的本机 C++ 库 (DLL),它们的作用是分配和释放本机 C++ 结构的链接列表。这两个函数模拟应用程序中的 getmystruct 和 freemystruct 函数。第二个模块是一个托管 C++ 文件 — ListWrap.cpp,它实现了一个包装本机 C++ 实现的托管类 ManagedNode(请参见图 3)。第三个模块是一个 C# 测试程序,它调用包装来显示如何工作。您可以从 MSDN Magazine Web 站点下载 ListLib.cpp 和 C# 测试程序的完整源代码。

  ListLib.cpp 实现了两个本机函数 — AllocateList 和 FreeList,它们用于分配和释放 NativeNode 结构的列表:

以下是引用片段:
  // from ListLib.h
  struct NativeNode {
  int a, b;
  TCHAR *str;
  struct NativeNode *next;
  };

  ListWrap.cpp 中的包装类 ManagedNode 模拟 NativeNode 的定义,不过有几个细微区别:本机 char* 被替换为托管 String,并且由于我使用 ArrayList 来实现列表结构,所以没有 next 指针。在代码中,它应该如下所示:

以下是引用片段:
  // managed equivalent of NativeNodepublic __gc class ManagedNode {public: int a, b; String* str;};

  定义好 ManagedNode 之后,下一步就是编写一些代码将 NativeNode 转换成 ManagedNode。但在开始编写之前,请稍微考虑一下转换器函数应该是什么样子的,它应该有什么样的参数和返回值。一种方式是编写一个函数,将 NativeNode 的本机列表作为参数并返回 ManagedNode 的托管列表,它可能销毁进程中的本机列表。.NET 客户端应用程序会直接调用 ListLib DLL(或者您的 getmystruct)来获取本机列表,将其作为 IntPtr,然后将这个 IntPtr 传递给转换函数,如下所示:

以下是引用片段:
  // call DLL directly through interop
  IntPtr nativeList = AllocateList(7);
  // call wrapper to convert
  ArrayList amanagedList = ListWrap.Convert(nativeList);

  在大多数情况下,客户端要负责调用 DLL 来释放本机列表,或者由 Convert 函数自动完成释放过程。

  另一种方法是完全隐藏 DLL,其做法是:将本机函数 AllocateList 包装在一个包装器中,由它分配列表、转换并释放原始本机列表,再将托管列表作为 ArrayList 返回。哪种方法更好呢?第一种策略的优点是您只需编写一个简单的转换函数,在任何具有本机列表的地方都可以使用它。第二种策略需要包装创建列表的每个函数。如果您有多个创建列表的函数,这种方式会有些麻烦,但是它的优点是对 .NET 客户端完全隐藏了所有本机内容。客户端不需要处理 IntPtrs,甚至不需要导入 DLL;ListWrap 将这一切都隐藏了。我采取的是这种方式,我也鼓励您在自己的应用程序中使用这种方式。它要对库进行完全包装还需要做很多工作,但结果会比较可靠,而且完全封装。

  有了 ManagedNode 之后,剩下的工作就是包装 AllocateList 了。这个过程十分简单。首先,调用 AllocateList 来分配本机列表,然后创建一个空的 ArrayList。接下来将所有 NativeNode 复制到 ManagedNode,并将它们添加到托管列表中,您在进行这些操作时会将它们删除。图 3 显示了完整的细节。托管 C++ 的好处之一就是所有代码看起来很简单、整洁,即使您处理的是混合对象。将本机 char* 复制到托管 String 只是一个简单的赋值,如以下的代码行所示:

以下是引用片段:
  mn->str = nn->str; // String = char*: it just works!

  不需要调用转换函数,编译器知道如何去做。CreateList 在运行时会删除本机节点。这比在最后删除节省存储空间。

  通过将整个列表转换成托管对象(而不是通过 interop 和 StructLayout 将它导出),您可以为托管客户端营造一种托管环境。这叫入乡随俗!毕竟,一些程序员选择 .NET 的主要原因之一就是它具有自动垃圾回收的功能。如果您直接通过 interop 导出列表,您还必须导出 FreeList,并要求使用其他基于 .NET 语言的程序员要记得调用它。

  一般情况下,如果您导出到托管环境,最好是将尽可能多的数据转换成托管对象。什么情况下例外呢?您的客户端也是采用 C++ 编写的。当然,这条规则并不总是适用。有时更好的方式是直接导出结构,并要求客户端释放它们 — 例如,如果复制对性能或内存造成的影响太大而不可接受,就需要这样做。您必须使用判断来决定是进入托管环境还是进入本机环境。

  问:我正在使用 C++ 托管扩展来包装现有的 C++ 库,使基于 .NET 的语言可以访问它。在托管 C++ 中,我可以写为

以下是引用片段:
  String* s = new String();
  s = _T("Hello, world");

  但如何将托管 String 再次转换为本机 TCHAR*?

  Matthew Brady

  答:一旦您了解了神奇的 voodoo,这会变得很简单。您必须调用 PtrToStringChars 并 pin 结果。代码如下所示:

以下是引用片段:
  String __gc* s = S"Hello";
  const wchar_t __pin* p = PtrToStringChars(s);

  不要忘记对从 PtrToStringChars 返回的指针进行 __pin 操作。Pin 是必须的,因为 PtrToStringChars 将一个托管 (__gc) 指针返回给托管内存中的 String 对象的第一个字符,而垃圾回收器随时都会回收托管内存,除非您显式对它进行 __pin。一般情况下,每次将 __gc 指针传递给本机(非托管)函数时都必须使用 __pin。

  图 4 显示了一个小程序,它将托管 String 转换成宽字符和 ANSI 字符串。要转换成 ANSI,可以使用自己喜欢的转换函数,如 wcstombs 或 ATL W2A 宏。如果您使用 MFC Cstrings,则不必进行任何操作,因为 CString 对 char* 和 wchar_t 都有赋值运算符:

以下是引用片段:
  // both will work
  CString s1 = "hello, world";
  CString s2 = L"Hello, world";

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

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

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