1. 程式人生 > 其它 >C# 呼叫 C++ dll的兩種方式

C# 呼叫 C++ dll的兩種方式

https://www.cnblogs.com/xuqp/p/11987707.html

目錄:

1.非託管方式

2.託管方式

3.介紹 extern "C"

4.介紹 DllImport

1.非託管方式

第一種,非託管方式:呼叫類和方法https://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class

  非託管方式,只能呼叫函式,並且函式在extern "C"的體裡面

  有一下幾種方式,把函式都寫了,把介面寫了。

//建立物件的方法
extern "C" EXAMPLEUNMANAGEDDLL_API CUnmanagedTestClass* CreateTestClass()
{
    return new CUnmanagedTestClass();
}
//釋放物件
extern "C" EXAMPLEUNMANAGEDDLL_API void DisposeTestClass(CUnmanagedTestClass* pObject)
{
    if(pObject != NULL)
    {
        delete pObject;
        pObject = NULL;
    }
}
//呼叫函式
extern "C" EXAMPLEUNMANAGEDDLL_API void CallPassInt(CUnmanagedTestClass* pObject, int nValue)
{
    if(pObject != NULL)
    {
        pObject->PassInt(nValue);
    }
}
//呼叫函式
extern "C" EXAMPLEUNMANAGEDDLL_API void CallPassString(CUnmanagedTestClass* pObject, char* pchValue);
extern "C"{
 EXAMPLEUNMANAGEDDLL_API char* CallReturnString(CUnmanagedTestClass* pObject)

}

C++的呼叫

  把生成的TestClassDLL.dll放到 bin/Debug/下,然後在C#中呼叫

public class CSUnmanagedTestClass : IDisposable
{
    #region PInvokes
    [DllImport("TestClassDLL.dll")]
    static private extern IntPtr CreateTestClass();

    [DllImport("TestClassDLL.dll")]
    static private extern void DisposeTestClass(IntPtr pTestClassObject);

    [DllImport("TestClassDLL.dll")]
    static private extern void CallPassInt(IntPtr pTestClassObject, int nValue);
    .
    .
    .
    #endregion PInvokes

    #region Members
    private IntPtr m_pNativeObject; 
    // Variable to hold the C++ class's this pointer
    #endregion Members

    public CSUnmanagedTestClass()
    {
        // We have to Create an instance of this class through an exported 
        // function
        this.m_pNativeObject = CreateTestClass();
    }

    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
            DisposeTestClass(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.
    ~CSUnmanagedTestClass()
    {
        Dispose(false);
    }

    #region Wrapper methods
    public void PassInt(int nValue)
    {
        CallPassInt(this.m_pNativeObject, nValue);
    }
    .
    .
    .
    #endregion Wrapper methods
}

2.託管方式

我手上有一個C++寫的類(NativeClass),想在C#下呼叫這個類,可是C#是沒有簡單的像Dllimport這樣的方法獲取非託管C++ dll裡的類。我的解決方法是,生成一個託管C++的dll,然後在C#下引用這個dll。因為託管程式碼與非託管程式碼是不能在一個檔案裡混編的,所以我必須將非託管C++寫的NativeClass用託管C++的手段封裝一下,然後生成一個dll,以供C#呼叫。

https://www.cnblogs.com/stemon/p/4246165.html

3.介紹 extern "C"

:https://www.cnblogs.com/xiangtingshen/p/10980055.html

extern "C"包含雙重含義

  • 被extern "C"修飾的變數和函式是按照C語言方式進行編譯和連結的:這點很重要!!!!
  • extern "C"的使用要點總結

    1,可以是如下的單一語句:

    extern "C" double sqrt(double);

    2,可以是複合語句, 相當於複合語句中的宣告都加了extern "C"

    extern "C"
    {
          double sqrt(double);
          int min(int, int);
    }

    3,可以包含標頭檔案,相當於標頭檔案中的宣告都加了extern "C" 

1 2 3 4 extern"C" { #include <cmath> }

  

4.介紹 DllImport

  https://www.cnblogs.com/fer-team/archive/2017/12/13/8033413.html

要使用DllImport需要引用名稱空間: System.Runtime.InteropServices;
DllImport 屬性定義
如下:

namespace System.Runtime.InteropServices
{
  [AttributeUsage(AttributeTargets.Method)]
  public class DllImportAttribute: System.Attribute
  {
public DllImportAttribute(string dllName){...} //定位引數為dllName
public CallingConvention CallingConvention; //入口點呼叫約定
public CharSet CharSet; //入口點採用的字元接
public string EntryPoint; //入口點名稱
public bool ExactSpelling; //是否必須與指示的入口點拼寫完全一致,預設false
public bool PreserveSig; //方法的簽名是被保留還是被轉換
public bool SetLastError; //FindLastError方法的返回值儲存在這裡
public string Value {get {...}}
  }
}

說明:
1、DllImport只能放置在方法宣告上。
2、DllImport具有單個定位引數:指定包含被匯入方法的 dll 名稱的 dllName 引數。
3、DllImport具有五個命名引數:
a、CallingConvention 引數指示入口點的呼叫約定。如果未指定CallingConvention,則使用預設值CallingConvention.Winapi。
b、CharSet引數指定用在入口點的字符集。如果未指定CharSet,則使用預設值CharSet.Auto。
c、EntryPoint引數給出dll中入口點的名稱。如果未指定EntryPoint,則使用方法本身的名稱。
d、ExactSpelling引數指示EntryPoint是否必須與指示的入口點的拼寫完全匹配。如果未指定ExactSpelling,則使用預設值false。
e、PreserveSig引數指示方法的簽名被保留還是被轉換。當簽名被轉換時,它被轉換為一個具有HRESULT返回值和該返回值的一個名為retval的附加輸出引數的簽名。如果未指定PreserveSig,則使用預設值true。
f、SetLastError引數指示方法是否保留Win32“上一錯誤”。如果未指定SetLastError,則使用預設值false。
4、它是一次性屬性類。
5、用DllImport屬性修飾的方法必須具有extern修飾符。

標籤:c#和C++呼叫