現在在Windows下的應用程式開發,VS.Net占據了絕大多數的份額。因此很多以前搞VC++開發的人都轉向用更強大的VS.Net。在這種情況下,有很多開發人員就面臨瞭如何在C#中使用C++開發好的類的問題。下麵就用一個完整的實例來詳細說明怎樣用托管C++封裝一個C++類以提供給C#使用。 比如,現 ...
現在在Windows下的應用程式開發,VS.Net占據了絕大多數的份額。因此很多以前搞VC++開發的人都轉向用更強大的VS.Net。在這種情況下,有很多開發人員就面臨瞭如何在C#中使用C++開發好的類的問題。下麵就用一個完整的實例來詳細說明怎樣用托管C++封裝一個C++類以提供給C#使用。
比如,現在有一個工程名為NativeCppDll的由C++編寫的DLL,裡面輸出了一個CPerson類。下麵是具體的代碼:
// NativeCppDll.h #pragma once #ifndef LX_DLL_CLASS_EXPORTS #define LX_DLL_CLASS __declspec(dllexport) #else #define LX_DLL_CLASS __declspec(dllimport) #endif // hovertree.com class LX_DLL_CLASS CPerson { public: CPerson(); CPerson(const wchar_t *pName, const wchar_t cSex, int iAge); void SetName(const wchar_t *pName); wchar_t * GetName(); void SetSex(const wchar_t cSex); wchar_t GetSex(); void SetAge(int iAge); int GetAge(); wchar_t * GetLastError(); private: wchar_t m_szName[128]; wchar_t m_cSex; int m_iAge; wchar_t m_szLastError[128]; void ShowError(); }; // NativeCppDll.cpp #include "stdafx.h" #include "NativeCppDll.h" #include <iostream> #include <tchar.h> using namespace std; CPerson::CPerson() { wcscpy_s(m_szName, _T("No Name")); m_cSex = 'N'; m_iAge = 0; wcscpy_s(m_szLastError, _T("No Error")); } CPerson::CPerson(const wchar_t *pName, const wchar_t cSex, int iAge) { wcscpy_s(m_szLastError, _T("No Error")); SetName(pName); SetSex(cSex); SetAge(iAge); } void CPerson::SetName(const wchar_t *pName) { if ((pName == NULL) || (wcslen(pName) == 0) || (wcslen(pName) > 127)) { wcscpy_s(m_szName, _T("No Name")); wcscpy_s(m_szLastError, _T("The length of the input name is out of range.")); ShowError(); return; } wcscpy_s(m_szName, pName); } wchar_t * CPerson::GetName() { return m_szName; } void CPerson::SetSex(const wchar_t cSex) { if ((cSex != 'F') && (cSex != 'M') && (cSex != 'm') && (cSex != 'f')) { m_cSex = 'N'; wcscpy_s(m_szLastError, _T("The input sex is out of [F/M].")); ShowError(); return; } m_cSex = cSex; } wchar_t CPerson::GetSex() { return m_cSex; } void CPerson::SetAge(int iAge) { if ((iAge < 0) || (iAge > 150)) { m_iAge = 0; wcscpy_s(m_szLastError, _T("The input age is out of range.")); ShowError(); return; } m_iAge = iAge; } int CPerson::GetAge() { return m_iAge; } wchar_t * CPerson::GetLastError() { return m_szLastError; } void CPerson::ShowError() { cerr << m_szLastError << endl; }
這是一個很典型的由C++開發的DLL,輸出一個完整的C++類。如果現在要求開發一個C#工程,需要用到這個DLL中輸出的C++類CPerson,該怎麼辦呢?針對這個例子來說,類CPerson非常小,可以用C#重新寫一個跟這個C++類一樣的類。可是,如果需要的C++類很大,或者很多的時候,重寫工程將非常龐大。而且這樣沒有對現有的代碼進行重用,浪費了現有資源,開發起來費時費力。
當然,還是有方法解決這個問題的。那就是用托管C++將C++類給封裝一下,然後再提供給C#來使用。下麵就用代碼來詳細說明怎樣用托管C++來封裝上面的那個C++類。
首先,要創建一個托管C++的DLL工程ManageCppDll,然後在裡面添加下麵的代碼:
// ManageCppDll.h #pragma once #define LX_DLL_CLASS_EXPORTS #include "../NativeCppDll/NativeCppDll.h" using namespace System; namespace ManageCppDll { public ref class Person { // 包裝所有類CPerson的公有成員函數 public: Person(); Person(String ^ strName, Char cSex, int iAge); ~Person(); property String ^ Name { void set(String ^ strName); String ^ get(); }// hovertree.com property Char Sex { void set(Char cSex); Char get(); } property int Age { void set(int iAge); int get(); } String ^ GetLastError(); private: // 類CPerson的指針,用來調用類CPerson的成員函數 CPerson *m_pImp; }; };
從這個頭文件就能看出來,這是對C++類CPerson的包裝。類Person的所有公有成員函數都跟C++類CPerson一樣,只不過成員函數的參數和返回值就改成了托管C++的類型,這也是讓類Person能在C#中使用的首要條件。當然只需要對公有成員函數進行封裝,對於保護成員函數和私有成員函數則不必做任何封裝。
類Person僅有一個私有的成員變數:一個類CPerson的指針。而類Person的所有成員函數的實現都是靠這個CPerson指針來調用類CPerson的相應成員函數來實現。
下麵是具體的實現代碼:
// ManageCppDll.cpp #include "stdafx.h" #include "ManageCppDll.h" #include <vcclr.h> namespace ManageCppDll { // 在構造函數中創建類CPerson的對象併在析構函數中將該對象銷毀 // 所有的成員函數實現都是通過指針m_pImp調用類CPerson的相應成員函數實現 Person::Person() { m_pImp = new CPerson(); } Person::Person(String ^ strName, Char cSex, int iAge) { // 將string轉換成C++能識別的指針 hovertree.com pin_ptr<const wchar_t> wcName = PtrToStringChars(strName); m_pImp = new CPerson(wcName, cSex, iAge); } Person::~Person() { // 在析構函數中刪除CPerson對象 delete m_pImp; } void Person::Name::set(String ^ strName) { pin_ptr<const wchar_t> wcName = PtrToStringChars(strName); m_pImp->SetName(wcName); } String ^ Person::Name::get() { return gcnew String(m_pImp->GetName()); } void Person::Sex::set(Char cSex) { m_pImp->SetSex(cSex); } Char Person::Sex::get() { return m_pImp->GetSex(); } void Person::Age::set(int iAge) { m_pImp->SetAge(iAge); } int Person::Age::get() { return m_pImp->GetAge(); } String ^ Person::GetLastError() { return gcnew String(m_pImp->GetLastError()); } };
如果要在C#中使用類Person,首先要添加對ManageCppDll.dll的引用,然後就可以像用普通的C#類一樣的使用類Person了。比如下麵這樣的代碼:
using ManageCppDll; Person person = new Person(); person.Name = "StarLee"; person.Sex = 'M'; person.Age = 28; // 何問起
熟悉設計模式的看了上面的代碼肯定會發現,這樣的設計跟BRIDGE模式如出一轍。其實,上面的方法也算是一種BRIDGE模式,由托管C++充當了C#中使用用C++開發的類的橋梁。另外,這種形式也可以理解為ADAPTER模式,托管C++類Person就是C++類CPerson的一個適配器。通過這個橋梁,可以很容易的重用以前用C++開發的類,讓這些C++類繼續在C#中發揮它們的效用,讓開發變得事半功倍。
http://www.cnblogs.com/roucheng/
http://www.cnblogs.com/roucheng/p/3521864.html