当前位置:天才代写 > tutorial > C语言/C++ 教程 > 如何删除托管工具及包装一个库

如何删除托管工具及包装一个库

2017-11-05 08:00 星期日 所属: C语言/C++ 教程 浏览:440

副标题#e#

在托管 C++ 中,请汇报我利用 delete 操纵符销毁托管工具是否安详?

是的,在托管 C++ 中,你可以删除( delete )托管工具,只要你领略删除只不外是挪用工具的析构函数,但析构函数必需显示界说。挪用 delete 不会释放工具的存储区。只有垃圾收集器才行。Figure 1 展示了一个简朴的措施,该措施界说了一个带析构函数的托管类,当它运行的时候会显示一条信息。TESTDTOR 分派两个 ManagedClass 实例。它显式删除第一个实例,但第二个则否则。假如运行 TESTDTOR,你会获得象下面这样的功效:

Begin main
ManagedClass(04A712D4)::ctor
ManagedClass(04A712D4)::dtor
ManagedClass(04A712E0)::ctor
End main
ManagedClass(04A712E0)::dtor

它说明白当 delete 语句执行时,第一个工具的析构函数当即执行;而第二个工具(at 04A712E0)则没有被销毁,直到节制分开 main 而且系统终止代码挪用垃圾收集器释放停留工具。

如何删除托管东西及包装一个库

Figure 2 Testdtor 的出色输出

不管什么时候,假如你不能确定 .NET 情况中产生了什么,你老是可以编写一些代码,编译它并查抄微软中间语言(MSIL)发生的对象。正如 Figure 2 所展示的,界说析构函数导致编译器发生两个要领:一个是 Finalize 要领,它包括你的实现(这里是挪用 printf),一个是 __dtor 要领,它挪用 System.GC::SuppressFinalize,然后再挪用 Finalize。当你删除工具时,编译器发生一个对此 __dtor 要领的掉用。假如你用 /FAs 编译 TESTDTOR 来发生有源码的措施集清单,你将看到 delete 语句以如下的方法编译:

; delete pmc;
ldloc.0 ; _pmc$
call ??1ManagedClass@@$$FQ$AAM@XZ

精悍的 C++ 措施员大概会弄不大白,假如挪用 delete 都无法释放工具,那挪用它有干什么?好问题。挪用 delete 的独一来由是收回任何你的类所利用的非托管资源。譬喻,假如你的工具打开数个文件或建设了数据库毗连,你可以写一个封锁其资源的析构函数,然后在用完该工具时利用 delete 释放它。在托管类中释放资源的一个更好的要领是通过实现 Dispose 模式,IDisposable——假如你在写托管 C++ 代码——由 auto_dispose 来挪用它。(更多的信息拜见 Tomas Restrepo 在 MSDN 杂志 2002 二月刊上的文章:“Tips and Tricks to Bolster Your Managed C++ Code in Visual Studio .NET”)。

假如你实现 dispose 模式,其他的 .NET 利用者也可以利用它。假如你本身在析构函数中举办清理,其它语言便没有步伐显式挪用你的清理代码。因为在 C# 和 Visual Basic 中没有 delete 操纵符。

所以功效是你能挪用 delete 来触发你的析构函数,可是将清理代码放在析构函数中大概不是一个好主意。最好是实现 IDisposable,这样所有人都能利用。留意,在 Visual C++ 2005 中,这个行为有所改变。更多信息拜见 Andy Rich 对这个问题的接头:“Deterministic Finalization IV – Benefits, part II”,以及当前的 C++/CLI 语言类型尺度:“C++/CLI Language Specification Standard”

我有一个返回链表的非托管函数,个中有 char* 字符串:

struct blah {
int a, b;
char *a, *b;
struct blah *next;
};
struct blah *getmystruct();

因为 getmystruct() 分派内存,当用完之后,我需要挪用 freemystruct(struct blah *b)。我实验做一个包装器,用它来将之转换成托管范例的荟萃,但我不知道当需要释放所有这些指针的时候,该如何来处理惩罚。你可否见教?

为什么,简直。你不能用 dllimport 语句将你的当地链表转换成托管范例荟萃。interop 处事当然不错,但处理惩罚此问题也不是那么好!你需要编写一个包装器,显式地将你的链表转换为托管荟萃,象 ArrayList。我写了一个带有三个模块的措施,ListWrap,它示范了详细做法。第一个模块,ListLib.cpp,实现一个当地 C++ 库(DLL),个中有两个函数,AllocateList 和 FreeList。别离用来分派和释放当地 C++ 布局链表。它们仿照你措施中的 getmystruct 和 freemystruct 函数。第二个模块是一个托管 C++ 文件,ListWrap.cpp,它实现托管类 ManagedNode,该类包装当地 C++ 实现(拜见 Figure 3)。第三个模块是 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 取代,另外它没有 next 指针,因为我将用 ArrayList 实现链表布局。代码如下:

// managed equivalent of NativeNode
public __gc class ManagedNode {
public:
int a, b;
String* str;
};

#p#分页标题#e#

有了 ManagedNode 的界说,下一步是编写代码将 NativeNodes 转换到 ManagedNodes。但在开始之前,先停下来思量一下转换函数应该是什么样子,他应该有什么样的参数,返回什么值。一种要领是编写一个函数,参数是当地 NativeNodes 链表并返回托管的 ManagedNodes 链表,在这个进程中大概销毁当地链表。.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 函数自动完成。

一种差异的要领是通过在某个包装器中包装分派链表的当地函数 AllocateList 来完全埋没这个 DLL,转换并在作为 ArrayList 返回托管链表之前释放本来的当地链表。哪种要领更好的呢?第一种计策的利益是你只需要编写单一的转换函数,它便可以在任何有当地链表的处所利用。第二个要领需要对每个建设链表的函数举办包装。假如有多个建设链表的函数,则事情量稍大一些。可是其利益是它向 .NET 客户端完全埋没了所有的当地处理惩罚逻辑和细节。客户端不再需要去处理惩罚 IntPtrs 或甚至是导入此 DLL,因为 ListWrap 埋没了一切。这是我将要回收的要领,同时也是我勉励你在本身的措施中利用的要领。尽量对库举办完全的包装需要更多的尽力,可是功效却越发靠得住和彻底的封装。

有了 ManagedNode,剩下的工作即是包装 AllocateList。这个进程很是简朴直白。首先,挪用 AllocateList 分派当地链表,然后建设一个空的 ArrayList,接着将所有 NativeNodes 拷贝到 ManagedNodes 并将它们添加到托管链表中,最有分开时删除它们。Figure 3 展示了所有的细节。托管 C++ 的美妙之处在于即即是在处理惩罚殽杂工具时,所有的代码看起来都很简单优雅。将当地 char* 拷贝到托管 String 用一个赋值即可,就像下面这行代码:

mn->str = nn->str; // String = char*: it just works!

不需要挪用转换函数;编译器知道该怎么做。分开 CreateList 时删除当地节点。这样做比在末端删除它们存储更有效。


#p#副标题#e#

通过将整个链表转换到托管工具(而不是用 interop 和 StructLayout 将它导出),使托管客户端不消分开托管世界,此谓入乡随俗也!究竟,某些措施员选择 .NET 的一个主要来由是其自动的垃圾收集。假如你用 interop 直接导出链表,那么你也必需导出 FreeList,从而必需让利用基于 .NET 语言的其他措施员记得挪用它。

一般来说,假如你要导出到托管世界,最好将数据尽大概多地转换成托管工具。不然你的客户端也必需用 C++ 编写代码。虽然,这个法则并不老是合用。有时直接导出布局并让客户端释放它们更好——譬喻,假如拷贝行动会激发焦点不行接管的机能问题或内存斗嘴。那么你必需做出判定以抉择是走托管之路照旧利用当地机制。

我正在利用 C++ 托管扩展(Managed Extension for C++)包装现存的 C++ 库,以便基于 .NET 的语言能会见它。在 托管 C++ 中,我可以写如下代码:

String* s = new String();
s = _T("Hello, world");

但我如何才气将一个托管 String 转换回当地的 TCHAR*?

一旦你知道了这个神奇的要领,它便很简朴。你必需挪用 PtrToStringChars 并对功效举办 pin (销毗连)操纵。代码可以这样你写:

String __gc* s = S"Hello";
const wchar_t __pin* p = PtrToStringChars(s);

不要忘了对 PtrToStringChars 返回的指针举办 __pin 操纵。销毗连是必不行少的,因为 PtrToStringChars 返回指向托管内存中 String 工具第一个字符的托管(__gc)指针,垃圾收集器大概在任何兴奋的时候移走托管内存,除非你显示地对之举办 __pin 操纵。一般来讲,你必需在将 __gc 指针通报给某个当地(非托管)函数的任何时候利用用 __pin。

Figure 4 展示了一个简短的措施,它将托管 String 转换为宽字符和 ANSI 字符串两者。为了转换到 ANSI,要用到你痛爱的转换函数,象 wcstombs 或 ATL W2A 宏。假如你利用 MFC CString,你不必任何工作,因为 CString 具备针对 char* 和 wchar_t 的赋值操纵:

// both will work
CString s1 = "hello, world";
CString s2 = L"Hello, world";

#p#分页标题#e#

我想在本身的应用措施中改变标签控件的配景颜色,将它从灰色改成白色。我实验成立一个 CTabCtrl 的派生类并利用其全部成果,但没有乐成,你能帮我一把吗?

改变标签控件中标签的颜色十分简朴,但要想让属性页布满某种颜色,这个改革涉及相当大的事情量,对付一个胆小的人来说,是不敢轻举妄动的。对付标签来说,根基思路让该控件是自绘控件,然后处理惩罚 WM_DRAWITEM 动静。假如利用 MFC,你可以改写虚拟函数 DrawItem。

在 1998 年三月坎的 Microsoft Systems Journal 中,我示范了如何实现一个标签控件类 CTabCtrlWithDisable,这个类支持标签禁用。作为禁用标签的一部份,当标签被禁用时, CTabCtrlWithDisable 将标签文本颜色改成了浅灰色,本文我借用了 CTabCtrlWithDisable 中的一些代码实现了一个新类 CColorTablCtrl,使你能改变标签的颜色。(拜见 Figure 5)

为了利用 CColorTablCtrl, 在你的属性页中建设一个实例:

class CMyPropSheet : public CPropertySheet {
protected:
CColorTabCtrl m_tabCtrl;
};

你必需在属性页的 OnInitDialog 处理惩罚例程(这样 MFC 将利用它)中子类化标签控件,然后凭据本身的意愿配置前景致和配景致:

// in CMyPropSheet::OnInitDialog()
HWND hWndTab = (HWND)SendMessage(PSM_GETTABCONTROL);
m_tabCtrl.SubclassDlgItem(::GetDlgCtrlID(hWndTab), this);
m_tabCtrl.SetColor(WHITE, RED);

这里 WHITE 和 RED 是尺度的 COLORREF 值,也就是 RGB(255, 255, 255) 和 RGB(255,0,0)。一旦你实例化并初始化 CColorTabCtrl,颜色标签控件便认真其余的工作。(拜见 Figure 6)

如何删除托管东西及包装一个库

Figure 6 带颜色的标签控件

CColorTabCtrl 有一个改写的 SubclassDlgItem,它挪用 ModifyStyle 将式样改变为 TCS_OWNERDRAWFIXED。较量适合这项事情的处所是 PreSubclassWindow 函数中,因为岂论控件被子类化,照旧用 CreateWindow 建设(但在此杂志中,我得收缩代码,所以我回收的是简版),它都要被挪用。留意 SubclassDlgItem 只是简朴地改写,不是虚拟函数。为了配置颜色,SetColor 生存在两个成员变量 m_clrBackground 和 m_clrForeground.中通报的颜色。

一旦 将式样配置为自绘方法,只要到了绘制该标签时,Windows 便会发送 WM_DRAWITEM 动静。MFC 捕捉这个动静并挪用标签控件的虚拟 DrawItem 函数,它由 CColorTabCtrl 实现,用新的颜色绘制文本:

// in CColorTabCtrl::DrawItem
dc.FillSolidRect(rc, m_clrBackground);
dc.SetBkColor(m_clrBackground);
dc.SetTextColor(m_clrForeground);
dc.DrawText(...);

就这么简朴直白,详细细节请看源代码。由于你大概在页面颜色没有改变的环境下也不想改变标签颜色,所以我还实现了一个 CColorPropertyPage 类,使你能改变属性页的配景致以便和标签颜色匹配,假如 Figure 6 所示,对付属性页而言,改变配景致最容易的要领是处理惩罚 WM_ERASEBKGND:

BOOL CColorPropertyPage::OnEraseBkgnd(CDC* pDC)
{
  CRect rc;
  GetClientRect(&rc);
  pDC->FillSolidRect(rc, m_clrBackground);
  return TRUE;}

假如你只是实验,你会发明各类恼人的问题。首先,假如你改变页面颜色,所有控件的配景致都是错误的,所以你必需还要办理这个问题。为此,你不得不处理惩罚处理惩罚 WM_CTLCOLOR 和 WM_ERASEBKGND。详细细节请参考我在 MSJ May 1997 专栏的文章。

别的一个问题是标签控件仍然以系统3D颜色绘制出边沿和四个角。这样必定不可,为办理这个问题,除了处理惩罚 WM_PAINT 动静别无选择,而且要本身认真整个的绘制操纵。它包罗绘制选中时标签之间的偏移,以便使它看起来是在前面。至此,可以说你已开始从头发现标签控件,每一个利用 Windows 的措施员都知道,改变控件的颜色最疾苦的一件工作,而且一旦你开始走上这条路,便会以为要做完它真是遥遥无期。不久尺度的颜色看起来将比起初的颜色更好,可能你将会以为为什么不转到 .NET 框架上来,在那上面改变颜色实在简朴,用如下代码即可:

ctl.BackColor = Color.Aquamarine;

编程愉快!

 

    关键字:

天才代写-代写联系方式