C++封装C#的DLL

下面就用一个完整的实例来说明怎样在C++中使用C#编写的类。
比如说,现在有一个用C#编写的DLL工程CsharpDll里面有一个Person类:
  1. //Person.cs
  2. usingSystem;
  3. namespaceCsharpDll
  4. {
  5. publicclassPerson
  6. {
  7. publicPerson()
  8. {
  9. Name="NoName";
  10. Sex='N';
  11. Age=0;
  12. m_strLastError="NoError";
  13. }
  14. publicPerson(stringstrName,charcSex,intiAge)
  15. {
  16. m_strLastError="NoError";
  17. Name=strName;
  18. Sex=cSex;
  19. Age=iAge;
  20. }
  21. publicstringName
  22. {
  23. get
  24. {
  25. returnm_strName;
  26. }
  27. set
  28. {
  29. if((String.IsNullOrEmpty(value))||(value.Length>127))
  30. {
  31. m_strName="NoName";
  32. m_strLastError="Thelengthoftheinputnameisoutofrange.";
  33. return;
  34. }
  35. m_strName=value;
  36. }
  37. }
  38. publiccharSex
  39. {
  40. get
  41. {
  42. returnm_cSex;
  43. }
  44. set
  45. {
  46. if((value!='F')&&(value!='M')&&(value!='m')&&(value!='f'))
  47. {
  48. m_cSex='N';
  49. m_strLastError="Theinputsexisoutof[F/M].";
  50. return;
  51. }
  52. m_cSex=value;
  53. }
  54. }
  55. publicintAge
  56. {
  57. get
  58. {
  59. returnm_iAge;
  60. }
  61. set
  62. {
  63. if((value<0)||(value>150))
  64. {
  65. m_iAge=0;
  66. m_strLastError="Theinputageisoutofrange.";
  67. return;
  68. }
  69. m_iAge=value;
  70. }
  71. }
  72. publicstringLastError
  73. {
  74. get
  75. {
  76. returnm_strLastError;
  77. }
  78. }
  79. privatestringm_strName;
  80. privatecharm_cSex;
  81. privateintm_iAge;
  82. privatestringm_strLastError;
  83. }
  84. }

如果需要在C++中使用这个C#编写的Person类,就需要用托管C++来对这个C#进行包装,将它包装成一个C++能用的类。
首先,要创建一个托管C++的DLL工程ManageCppDll。并且,要添加对CsharpDll.dll的引用。然后对C#类所有的公有属性和方法进行包装。下面是具体的代码:

  1. //ManageCppDll.h
  2. #pragmaonce
  3. #ifndefLX_DLL_CLASS_EXPORTS
  4. #defineLX_DLL_CLASS__declspec(dllexport)
  5. #else
  6. #defineLX_DLL_CLASS__declspec(dllimport)
  7. #endif
  8. classLX_DLL_CLASSCPerson
  9. {
  10. public:
  11. CPerson();
  12. CPerson(constwchar_t*pName,constwchar_tcSex,intiAge);
  13. ~CPerson();
  14. voidSetName(constwchar_t*pName);
  15. wchar_t*GetName();
  16. voidSetSex(constwchar_tcSex);
  17. wchar_tGetSex();
  18. voidSetAge(intiAge);
  19. intGetAge();
  20. wchar_t*GetLastError();
  21. private:
  22. //用一个void指针指向Person的对象
  23. //所有公有成员函数的实现都是通过这个对象来实现
  24. void*m_pImp;
  25. wchar_tm_szName[128];
  26. wchar_tm_szLastError[128];
  27. };
  28. //ManageCppDll.cpp
  29. #include"stdafx.h"
  30. #include"ManageCppDll.h"
  31. #include<vcclr.h>
  32. #include<string.h>
  33. #include<stdlib.h>
  34. usingnamespaceSystem;
  35. usingnamespaceSystem::Runtime::InteropServices;
  36. usingnamespaceCsharpDll;
  37. //将GCHandle转换成为void指针
  38. #define__GCHANDLE_TO_VOIDPTR(x)((GCHandle::operatorSystem::IntPtr(x)).ToPointer())
  39. //将void指针转换为GCHandle
  40. #define__VOIDPTR_TO_GCHANDLE(x)(GCHandle::operatorGCHandle(System::IntPtr(x)))
  41. //辅助函数
  42. //将void指针指向的对象转换成为Person对象
  43. inlinePerson^GetImpObj(void*pHandle)
  44. {
  45. Person^person=nullptr;
  46. if(pHandle!=NULL)
  47. {
  48. person=static_cast<Person^>(__VOIDPTR_TO_GCHANDLE(pHandle).Target);
  49. }
  50. returnperson;
  51. }
  52. CPerson::CPerson()
  53. {
  54. m_pImp=NULL;
  55. Person^person=gcnewPerson();
  56. //创建GCHandle并将它转换成void指针保存到成员变量中
  57. GCHandlehandle=GCHandle::Alloc(person);
  58. m_pImp=__GCHANDLE_TO_VOIDPTR(handle);
  59. }
  60. CPerson::CPerson(constwchar_t*pName,constwchar_tcSex,intiAge)
  61. {
  62. m_pImp=NULL;
  63. Person^person=gcnewPerson();
  64. person->Name=gcnewString(pName);
  65. person->Sex=cSex;
  66. person->Age=iAge;
  67. GCHandlehandle=GCHandle::Alloc(person);
  68. m_pImp=__GCHANDLE_TO_VOIDPTR(handle);
  69. }
  70. CPerson::~CPerson()
  71. {
  72. if(m_pImp==NULL)
  73. return;
  74. //释放GCHandle
  75. GCHandlehandle=__VOIDPTR_TO_GCHANDLE(m_pImp);
  76. handle.Free();
  77. m_pImp=NULL;
  78. }
  79. voidCPerson::SetName(constwchar_t*pName)
  80. {
  81. //将void指针转换成Person指针
  82. //并用该指针调用相应的公有属性或方法
  83. Person^person=GetImpObj(m_pImp);
  84. person->Name=gcnewString(pName);
  85. }
  86. wchar_t*CPerson::GetName()
  87. {
  88. Person^person=GetImpObj(m_pImp);
  89. //将C#返回的字符串转换为wchat_t*指针能指向的地址
  90. wchar_t*pName=static_cast<wchar_t*>(Marshal::StringToHGlobalUni(person->Name).ToPointer());
  91. wcscpy_s(m_szName,pName);
  92. Marshal::FreeHGlobal(System::IntPtr(pName));//释放内存
  93. returnm_szName;
  94. }
  95. voidCPerson::SetSex(constwchar_tcSex)
  96. {
  97. Person^person=GetImpObj(m_pImp);
  98. person->Sex=cSex;
  99. }
  100. wchar_tCPerson::GetSex()
  101. {
  102. Person^person=GetImpObj(m_pImp);
  103. returnperson->Sex;
  104. }
  105. voidCPerson::SetAge(intiAge)
  106. {
  107. Person^person=GetImpObj(m_pImp);
  108. person->Age=iAge;
  109. }
  110. intCPerson::GetAge()
  111. {
  112. Person^person=GetImpObj(m_pImp);
  113. returnperson->Age;
  114. }
  115. wchar_t*CPerson::GetLastError()
  116. {
  117. Person^person=GetImpObj(m_pImp);
  118. wchar_t*pLastError=static_cast<wchar_t*>(Marshal::StringToHGlobalUni(person->LastError).ToPointer());
  119. wcscpy_s(m_szLastError,pLastError);
  120. Marshal::FreeHGlobal(System::IntPtr(pLastError));
  121. returnm_szLastError;
  122. }

现在对上面代码中所用到的一些相关背景知识进行一下介绍。
GCHandle结构提供从非托管内存访问托管对象的方法。
GCHandle.Alloc方法(Object)为指定的对象分配Normal句柄。它保护对象不被垃圾回收。当不再需要GCHandle时,必须通过Free将其释放。Normal句柄类型表示不透明句柄,这意味着无法通过此句柄解析固定对象的地址。可以使用此类型跟踪对象,并防止它被垃圾回收器回收。当非托管客户端持有对托管对象的唯一引用(从垃圾回收器检测不到该引用)时,此枚举成员很有用。
上面的代码中,在类CPerson的构造函数中用GCHandle为C#类Person的对象分配一个句柄,并将该句柄转换为void指针存放在成员变量中,以保证这个对象不会被垃圾回收器回收。然后在类CPerson的析构函数中释放这个句柄,将C#类Person的对象的回收权交给系统。
Marshal类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
Marshal..::.StringToHGlobalUni方法向非托管内存复制托管String的内容。StringToHGlobalUni对于自定义封送处理或者在混合托管和非托管代码时很有用。由于该方法分配字符串所需的非托管内存,因此应始终通过调用FreeHGlobal释放内存。
(更多关于上面介绍的背景知识可以搜索MSDN。说实在的MSDN真是一个宝库!VS能在Windows平台开发中取得绝大多数的份额,除了因为它和Windows都是微软开发的之外,MSDN的完备性功不可没!)
通过上面的方法,就把一个C#编写的类Person用托管C++给封装成了一个C++可以使用的CPerson类。我们可以在C++的工程中像使用一般的C++类一样使用类CPerson。比如下面的代码。

  1. CPersonperson(_T("StarLee"),'M',28);
  2. person.SetName(_T("StarLee"));
  3. person.SetSex('M');
  4. person.SetAge(28);
  5. wcout<<"Name:"<<person.GetName()<<"Sex:"<<person.GetSex()<<"Age:"<<person.GetAge()<<endl;
  6. wcout<<"Error:"<<person.GetLastError()<<endl;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值