在如何在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# 使用
參考文章:

