在如何在C#中使用Unmanaged dll 文章中曾介紹C#中使用C++ 函式庫(DLL)的方法, 不過此方法僅適用於C-Style export 出來的函式, 不行用在類別(Class)中的成員函式.
但其實很多API都是設計成C++的類別, 因此本文將介紹如何在C# 使用C++ Class的函式.
但其實很多API都是設計成C++的類別, 因此本文將介紹如何在C# 使用C++ Class的函式.
其步驟如下:
- 設計一個C-Style function 介面, 透過指標存取物件的實體 – 由於C#中並沒有提供包裝C++ Class的方法, 所以我們必須將類別中public function重新包裝成C-Style function, 而一個物件的實體, 其實可以透過指標(*)作存取, C#中指標的使用也可透過IntPtr.
- 利用C# PInvoke機制呼叫C-Style function– 作法可參考如何在C#中使用Unmanaged dll
- 釋放unmanaged object記憶體 –由於C#的回收機制,不會自動回收unmanaged object記憶體, 所以我們必須透過Dispose/Finalize手動回收, 其作法可參考C#記憶體管理
範例如下:
原始C++類別程式碼
- #ifdef CSHAPEAPI_EXPORTS
- #define CSHAPE_API __declspec(dllexport)
- #else
- #define CSHAPE_API __declspec(dllimport)
- #endif
- class CSHAPE_API CShape
- {
- public:
- CShape(){};
- virtual ~CShape(){};
- public:
- float GetRectangleArea(float w ,float h) { return w*h; };
- float GetTriangleArea(float b ,float h) { return 0.5*b*h;};
- };
另外設計一個C-Style function 介面, 包裝要使用的C++ 類別
CShapeBridge.h
- #include "CShapeAPI.h"
- #ifdef __cplusplus
- extern "C" {
- #endif
- extern CSHAPE_API CShape* CreateInstance();
- extern CSHAPE_API void DisposeInstance(CShape* pShapeInstance);
- extern CSHAPE_API float GetRectangleArea(CShape* pShapeInstance,
- float w ,float h);
- extern CSHAPE_API float GetTriangleArea(CShape* pShapeInstance,
- float b ,float h);
- #ifdef __cplusplus
- }
- #endif
CShapeBridge.cpp
- #include "stdafx.h"
- #include "CShapeBridge.h"
- extern "C" CSHAPE_API CShape* CreateInstance()
- {
- return new CShape();
- }
- extern "C" CSHAPE_API void DisposeInstance(CShape* pShapeInstance)
- {
- if(pShapeInstance != NULL)
- {
- delete pShapeInstance;
- pShapeInstance = NULL;
- }
- }
- extern "C" CSHAPE_API float GetRectangleArea(CShape* pShapeInstance,
- float w ,float h)
- {
- return pShapeInstance->GetRectangleArea(w, h);
- }
- extern "C" CSHAPE_API float GetTriangleArea(CShape* pShapeInstance,
- float b ,float h)
- {
- return pShapeInstance->GetTriangleArea(b, h);
- }
利用PInvoke機制呼叫C-Style function , 並釋放unmanaged object記憶體
- ShapeWrapper.cs
- using System;
- using System.Runtime.InteropServices;
- namespace ShapeTest
- {
- public class ShapeWrapper : IDisposable
- {
- #region PInvokes
- [DllImport("CShapeAPI.dll")]
- private static extern IntPtr CreateInstance();
- [DllImport("CShapeAPI.dll")]
- private static extern void DisposeInstance(IntPtr pShapeInstance);
- [DllImport("CShapeAPI.dll")]
- private static extern float GetRectangleArea(IntPtr pShapeInstance,
- float w, float h);
- [DllImport("CShapeAPI.dll")]
- private static extern float GetTriangleArea(IntPtr pShapeInstance,
- float b, float h);
- #endregion
- #region Members
- // Variable to hold the C++ class's this pointer
- private IntPtr m_pNativeObject;
- #endregion Members
- public ShapeWrapper()
- {
- // We have to Create an instance of
- // this class through an exported function
- this.m_pNativeObject = CreateInstance();
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool bDisposing)
- {
- if (this.m_pNativeObject != IntPtr.Zero)
- {
- // Call the DLL Export to dispose this class
- DisposeInstance(this.m_pNativeObject);
- this.m_pNativeObject = IntPtr.Zero;
- }
- if (bDisposing)
- {
- // No need to call the finalizer since we've now cleaned
- // up the unmanaged memory
- GC.SuppressFinalize(this);
- }
- }
- // This finalizer is called when Garbage collection occurs, but only if
- // the IDisposable.Dispose method wasn't already called.
- ~ShapeWrapper()
- {
- Dispose(false);
- }
- #region Wrapper methods
- public float GetRectangleArea(float w, float h)
- {
- return GetRectangleArea(m_pNativeObject, w, h);
- }
- public float GetTriangleArea(float b, float h)
- {
- return GetTriangleArea(m_pNativeObject, b, h);
- }
- #endregion
- }
- }
測試C#程式使用C++ Class DLL
- using System;
- namespace ShapeTest
- {
- class Program
- {
- static void Main(string[] args)
- {
- ShapeWrapper shape = new ShapeWrapper();
- float w= 25.0f;
- float h= 10.0f;
- Console.WriteLine("Rectangle area = "+shape.GetRectangleArea(w, h));
- float b = 30.0f;
- float h1 = 20.0f;
- Console.WriteLine("Triangle area = "+ shape.GetTriangleArea(b, h1));
- }
- }
- }
測試結果:
Rectangle area = 250
Triangle area = 300
另外, MSDN也有提供使用CLR的方法, 請參考包裝原生類別以便讓 C# 使用
沒有留言:
張貼留言