在C++中使用C#編寫的類

來源:互聯網
上載者:User

     在那篇《在C#中使用C++編寫的類》中我介紹了如何在C#中使用C++編寫的類。可是由於C#在使用者介面設計、資料庫儲存和XML檔案讀取等方面的優勢,有時候也會出現要在C++中使用C#編寫的類的情況。下面就用一個完整的執行個體來說明怎樣在C++中使用C#編寫的類。
    比如說,現在有一個用C#編寫的DLL工程CsharpDll裡面有一個Person類:

  1. // Person.cs
  2. using System;
  3. namespace CsharpDll
  4. {
  5.     public class Person
  6.     {
  7.         public Person()
  8.         {
  9.             Name = "No Name";
  10.             Sex = 'N';
  11.             Age = 0;
  12.             m_strLastError = "No Error";
  13.         }
  14.         public Person(string strName, char cSex, int iAge)
  15.         {
  16.             m_strLastError = "No Error";
  17.             Name = strName;
  18.             Sex = cSex;
  19.             Age = iAge;
  20.         }
  21.         public string Name
  22.         {
  23.             get
  24.             {
  25.                 return m_strName;
  26.             }
  27.             set
  28.             {
  29.                 if ((String.IsNullOrEmpty(value)) || (value.Length > 127))
  30.                 {
  31.                     m_strName = "No Name";
  32.                     m_strLastError = "The length of the input name is out of range.";
  33.                     return;
  34.                 }
  35.                 m_strName = value;
  36.             }
  37.         }
  38.         public char Sex
  39.         {
  40.             get
  41.             {
  42.                 return m_cSex;
  43.             }
  44.             set
  45.             {
  46.                 if ((value != 'F') && (value != 'M') && (value != 'm') && (value != 'f'))
  47.                 {
  48.                     m_cSex = 'N';
  49.                     m_strLastError = "The input sex is out of [F/M].";
  50.                     return;
  51.                 }
  52.                 m_cSex = value;
  53.             }
  54.         }
  55.         public int Age
  56.         {
  57.             get
  58.             {
  59.                 return m_iAge;
  60.             }
  61.             set
  62.             {
  63.                 if ((value < 0) || (value > 150))
  64.                 {
  65.                     m_iAge = 0;
  66.                     m_strLastError = "The input age is out of range.";
  67.                     return;
  68.                 }
  69.                 m_iAge = value;
  70.             }
  71.         }
  72.         public string LastError
  73.         {
  74.             get
  75.             {
  76.                 return m_strLastError;
  77.             }
  78.         }
  79.         private string m_strName;
  80.         private char m_cSex;
  81.         private int m_iAge;
  82.         private string m_strLastError;
  83.     }
  84. }

    如果需要在C++中使用這個C#編寫的Person類,就需要用託管C++來對這個C#進行封裝,將它封裝成一個C++能用的類。
    首先,要建立一個託管C++的DLL工程ManageCppDll。並且,要添加對CsharpDll.dll的引用。然後對C#類所有的公有屬性和方法進行封裝。下面是具體的代碼:

  1. // ManageCppDll.h
  2. #pragma once
  3. #ifndef LX_DLL_CLASS_EXPORTS
  4.     #define LX_DLL_CLASS __declspec(dllexport)
  5. #else
  6.     #define LX_DLL_CLASS __declspec(dllimport)
  7. #endif
  8. class LX_DLL_CLASS CPerson
  9. {
  10. public:
  11.     CPerson();
  12.     CPerson(const wchar_t *pName, const wchar_t cSex, int iAge);
  13.     ~CPerson();
  14.     void SetName(const wchar_t *pName);
  15.     wchar_t * GetName();
  16.     void SetSex(const wchar_t cSex);
  17.     wchar_t GetSex();
  18.     void SetAge(int iAge);
  19.     int GetAge();
  20.     wchar_t * GetLastError();
  21. private:
  22.     // 用一個void指標指向Person的對象
  23.     // 所有公有成員函數的實現都是通過這個對象來實現
  24.     void *m_pImp;
  25.     wchar_t m_szName[128];
  26.     wchar_t m_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. using namespace System;
  35. using namespace System::Runtime::InteropServices;
  36. using namespace CsharpDll;
  37. // 將GCHandle轉換成為void指標
  38. #define __GCHANDLE_TO_VOIDPTR(x) ((GCHandle::operator System::IntPtr(x)).ToPointer())
  39. // 將void指標轉換為GCHandle
  40. #define __VOIDPTR_TO_GCHANDLE(x) (GCHandle::operator GCHandle(System::IntPtr(x)))
  41. // 輔助函數
  42. // 將void指標指向的對象轉換成為Person對象
  43. inline Person ^ 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.     return person;
  51. }
  52. CPerson::CPerson()
  53. {
  54.     m_pImp = NULL;
  55.     Person ^ person = gcnew Person();
  56.     // 建立GCHandle並將它轉換成void指標儲存到成員變數中
  57.     GCHandle handle = GCHandle::Alloc(person);
  58.     m_pImp = __GCHANDLE_TO_VOIDPTR(handle); 
  59. }
  60. CPerson::CPerson(const wchar_t *pName, const wchar_t cSex, int iAge)
  61. {
  62.     m_pImp = NULL;
  63.     Person ^ person = gcnew Person();
  64.     person->Name = gcnew String(pName);
  65.     person->Sex = cSex;
  66.     person->Age = iAge;
  67.     GCHandle handle = 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.     GCHandle handle = __VOIDPTR_TO_GCHANDLE(m_pImp);
  76.     handle.Free();
  77.     m_pImp = NULL;
  78. }
  79. void CPerson::SetName(const wchar_t *pName)
  80. {
  81.     // 將void指標轉換成Person指標
  82.     // 並用該指標調用相應的公有屬性或方法
  83.     Person ^ person = GetImpObj(m_pImp);
  84.     person->Name = gcnew String(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.     return m_szName;
  94. }
  95. void CPerson::SetSex(const wchar_t cSex)
  96. {
  97.     Person ^ person = GetImpObj(m_pImp);
  98.     person->Sex = cSex;
  99. }
  100. wchar_t CPerson::GetSex()
  101. {
  102.     Person ^ person = GetImpObj(m_pImp);
  103.     return person->Sex;
  104. }
  105. void CPerson::SetAge(int iAge)
  106. {
  107.     Person ^ person = GetImpObj(m_pImp);
  108.     person->Age = iAge;
  109. }
  110. int CPerson::GetAge()
  111. {
  112.     Person ^ person = GetImpObj(m_pImp);
  113.     return person->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.     return m_szLastError;
  122. }

    現在對上面代碼中所用到的一些相關背景知識進行一下介紹。
    GCHandle結構提供從非託管記憶體訪問託管對象的方法。
    GCHandle.Alloc方法(Object)為指定的對象分配Normal控制代碼。它保護對象不被記憶體回收。當不再需要GCHandle時,必須通過Free將其釋放。Normal控制代碼類型表示不透明控制代碼,這意味著無法通過此控制代碼解析固定對象的地址。可以使用此類型跟蹤對象,並防止它被記憶體回收行程回收。當非託管用戶端持有對託管對象的唯一引用(從記憶體回收行程檢測不到該引用)時,此枚舉成員很有用。
    上面的代碼中,在類CPerson的建構函式中用GCHandle為C#類Person的對象分配一個控制代碼,並將該控制代碼轉換為void指標存放在成員變數中,以保證這個對象不會被記憶體回收行程回收。然後在類CPerson的解構函式中釋放這個控制代碼,將C#類Person的對象的回收權交給系統。
    Marshal類提供了一個方法集,這些方法用於分配非託管記憶體、複製非託管記憶體塊、將託管類型轉換為非託管類型,此外還提供了在與Unmanaged 程式碼互動時使用的其他雜項方法。
    Marshal..::.StringToHGlobalUni方法向非託管記憶體複製託管String的內容。StringToHGlobalUni對於自訂封送處理或者在混合託管和Unmanaged 程式碼時很有用。由於該方法分配字串所需的非託管記憶體,因此應始終通過調用FreeHGlobal釋放記憶體。
    (更多關於上面介紹的背景知識可以搜尋MSDN。說實在的MSDN真是一個寶庫!VS能在Windows平台開發中取得絕大多數的份額,除了因為它和Windows都是微軟開發的之外,MSDN的完備性功不可沒!)
    通過上面的方法,就把一個C#編寫的類Person用託管C++給封裝成了一個C++可以使用的CPerson類。我們可以在C++的工程中像使用一般的C++類一樣使用類CPerson。比如下面的代碼。

  1. CPerson person(_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;

    這裡的方法跟《在C#中使用C++編寫的類》一樣,都是借用託管C++這個橋樑來溝通C++編寫的類和C#編寫的類,使在C++中使用C#編寫的類和在C#中使用C++編寫的類成為現實。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.