欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#程序如何調(diào)用C++?dll詳細教程

 更新時間:2024年04月30日 16:47:12   作者:令狐掌門  
C#和C++形成的DLL有一層天然的屏障,并不能簡單地互相調(diào)用,想要C#工程調(diào)用c++dll,需要先在其外部包裹上clr?c++的外殼,本篇文章給大家分享了C#調(diào)用C++?dll的詳細步驟和方法,有興趣的朋友可以參考學(xué)習(xí)下

在使用C#開發(fā)客戶端時,有時需要調(diào)用C++ dll,本篇博客來介紹C#程序如何調(diào)用C++ dll。

一、創(chuàng)建C++ dll項目

首先使用VS2022創(chuàng)建C++ dll項目,具體步驟如下:

(1)選擇Windows桌面向?qū)?,點擊下一步, 取項目名,例如我的dll項目名是libMath

(2)選擇動態(tài)項目,勾選導(dǎo)出符號

(3)編寫動態(tài)代碼,代碼如下:

libMath.h

// 下列 ifdef 塊是創(chuàng)建使從 DLL 導(dǎo)出更簡單的
// 宏的標(biāo)準(zhǔn)方法。此 DLL 中的所有文件都是用命令行上定義的 LIBMATH_EXPORTS
// 符號編譯的。在使用此 DLL 的
// 任何項目上不應(yīng)定義此符號。這樣,源文件中包含此文件的任何其他項目都會將
// LIBMATH_API 函數(shù)視為是從 DLL 導(dǎo)入的,而此 DLL 則將用此宏定義的
// 符號視為是被導(dǎo)出的。
#ifdef LIBMATH_EXPORTS
#define LIBMATH_API __declspec(dllexport)
#else
#define LIBMATH_API __declspec(dllimport)
#endif

// 此類是從 dll 導(dǎo)出的
class LIBMATH_API ClibMath {
public:
	ClibMath();
	int Add(int a, int b);
	int Sub(int a, int b);
};

// 由于需要給C#調(diào)用,為了方便導(dǎo)出類,添加了函數(shù)進行導(dǎo)出,并且需要加extern "C"
extern "C" {
    LIBMATH_API ClibMath* CreateMyClass();

    LIBMATH_API void DeleteMyClass(ClibMath* obj);

    LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);

    LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}

注意: 如果想導(dǎo)出C++類在C#中使用,由于語言語法差異,C++類在C#中無法使用,因為C++類通常包含成員函數(shù)、構(gòu)造函數(shù)、析構(gòu)函數(shù)等,而C#與C++在處理這些方面存在差異。一種可行的方法是在C++類中添加一些導(dǎo)出函數(shù),這樣它們可以通過C#調(diào)用。這些函數(shù)可以執(zhí)行類的實例化、調(diào)用成員函數(shù)等操作。確保使用 extern “C” 以避免名稱修飾, 因為C++函數(shù)在編譯時,會在原有的函數(shù)名前后添加一些符號,例如add函數(shù)在編譯后可能變成了@xxasd_sfdf_add_xxx之類的,但是使用extern "C" 后就是按照C語言的方式導(dǎo)出,函數(shù)名不變,例如我添加的一些類導(dǎo)出方法:

// 由于需要給C#調(diào)用,為了方便導(dǎo)出類,添加了函數(shù)進行導(dǎo)出,并且需要加extern "C"
extern "C" {
    LIBMATH_API ClibMath* CreateMyClass();

    LIBMATH_API void DeleteMyClass(ClibMath* obj);

    LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2);

    LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2);
}

libMath.cpp

// libMath.cpp : 定義 DLL 的導(dǎo)出函數(shù)。
//

#include "framework.h"
#include "libMath.h"

// 這是已導(dǎo)出類的構(gòu)造函數(shù)。
ClibMath::ClibMath()
{
    return;
}

int ClibMath::Add(int a, int b)
{
    return a + b;
}

int ClibMath::Sub(int a, int b)
{
    return a - b;
}

LIBMATH_API ClibMath* CreateMyClass() {
    return new ClibMath();
}

LIBMATH_API void DeleteMyClass(ClibMath* obj) {
    delete obj;
}

LIBMATH_API int CallAdd(ClibMath* obj, int num1, int num2) {
    return obj->Add(num1, num2);
}

LIBMATH_API int CallSub(ClibMath* obj, int num1, int num2) {
    return obj->Sub(num1, num2);
}

二、C#程序員調(diào)用C++ dll

生成dll后可以用命令拷貝到C#項目的exe目錄,或者手動拷貝,然后在C#代碼中使用import導(dǎo)入即可,如下圖:

代碼如下:

/*

C# 調(diào)用 C++ dll 

將libMath.dll放到CSharpCallCppDLL/bin/Debug目錄下

*/

using System.Runtime.InteropServices;

namespace CSharpCallCppDLL
{
    internal class Program
    {
        private static IntPtr myClassInstance;  // 定義C++類的實例,用于后面的調(diào)用

        [DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr CreateMyClass();

        [DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern void DeleteMyClass(IntPtr obj);

        [DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int CallAdd(IntPtr obj, int num1, int num2);

        [DllImport("libMath.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern int CallSub(IntPtr obj, int num1, int num2);

        static void Main(string[] args)
        {
            Console.WriteLine("測試C#調(diào)用C++");

            myClassInstance = CreateMyClass();
            int nRet = CallAdd(myClassInstance, 1, 2);
            Console.WriteLine($"1 + 2 = {nRet}");

            // 清理C++內(nèi)存
            DeleteMyClass(myClassInstance);
        }
    }
}

注意:由于C++的編譯特點,C++項目有Debug、Release、x86、x64之分,特別需要注意dll的放置位置,放錯了可能就鏈接失敗了,以及C++在x64于x86下的指針4字節(jié)與8字節(jié)的區(qū)分,這些都會導(dǎo)致在C#調(diào)用C++ dll代碼失敗或者crash。此外,確保你的應(yīng)用程序具有訪問和加載DLL所需的適當(dāng)權(quán)限。

運行結(jié)果如下:

在C#中可以創(chuàng)建一個對應(yīng)C++類的C#包裝類,使用 DllImport 屬性聲明導(dǎo)出函數(shù),例如下面的代碼:

public class MyClassWrapper {
    private IntPtr myClassInstance;

    [DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr CreateMyClass();

    [DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void DeleteMyClass(IntPtr obj);

    [DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void CallMyMethod(IntPtr obj);

    public MyClassWrapper() {
        myClassInstance = CreateMyClass();
    }

    ~MyClassWrapper() {
        DeleteMyClass(myClassInstance);
    }

    public void MyMethod() {
        CallMyMethod(myClassInstance);
    }
}

使用C#包裝類:

在C#中,可以實例化MyClassWrapper類,并調(diào)用其中的方法,這將轉(zhuǎn)發(fā)調(diào)用到C++類的實例。

MyClassWrapper myInstance = new MyClassWrapper();
myInstance.MyMethod();

這是一個簡單的例子,實際情況可能更為復(fù)雜,特別是在處理類的繼承、虛函數(shù)等方面。確保在進行實際應(yīng)用時進行充分的測試,以確?;ゲ僮餍哉_\作。

三、C++與C#數(shù)據(jù)類型對應(yīng)

C#在調(diào)用C++ DLL時,需要通過P/Invoke技術(shù)來完成。P/Invoke是.NET Framework用于調(diào)用非托管代碼庫的一種方式。在這個過程中,我們需要處理兩種語言之間的數(shù)據(jù)類型轉(zhuǎn)換,因為它們的數(shù)據(jù)類型不完全一致。

基本數(shù)據(jù)類型對應(yīng)表

以下是C++和C#之間的一些常見數(shù)據(jù)類型的對應(yīng)表(請注意,這并不是一個完全的列表,只是一些常見類型的示例):

C++C#
boolbool
char / BYTEbyte
shortshort
intint
longint
floatfloat
doubledouble
char* (C-style string)string
wchar_t* (Unicode string)string

在C#中,我們使用DllImport特性來聲明對C++ DLL的函數(shù)調(diào)用。例如,如果我們有一個C++函數(shù)如下:

extern "C" __declspec(dllexport) int Add(int a, int b);

在C#中,我們可以這樣聲明和使用它:

[DllImport("MyLibrary.dll")]
public static extern int Add(int a, int b);

public void Main()
{
    int result = Add(2, 3);
}

對于更復(fù)雜的數(shù)據(jù)類型,如結(jié)構(gòu)體和類,我們需要在C#中創(chuàng)建對應(yīng)的類或結(jié)構(gòu)體來匹配。例如,如果我們有一個C++結(jié)構(gòu)體:

struct Person
{
    char* name;
    int age;
};

我們可以在C#中創(chuàng)建一個類來匹配它:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Person
{
    public string name;
    public int age;
}

注意我們使用了StructLayout特性并設(shè)置CharSetCharSet.Ansi,因為C++中的char*類型對應(yīng)于ANSI字符串。

在處理C++ DLL中的函數(shù)時,也需要注意函數(shù)調(diào)用的約定(cdecl,stdcall等)。默認(rèn)情況下,C#假定被調(diào)用的函數(shù)使用stdcall調(diào)用約定。如果函數(shù)使用不同的調(diào)用約定,你需要在DllImport特性中指定它,例如:

[DllImport("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl)]

在處理更復(fù)雜的情況,如回調(diào)函數(shù),復(fù)雜的數(shù)據(jù)結(jié)構(gòu),和C++類時,可能需要更多的處理。處理這些情況通常需要對兩種語言都有深入的理解,并且可能需要手動管理內(nèi)存。

C++指針類型與C#類型

C++ 指針在C#中的相應(yīng)類型取決于指針指向的數(shù)據(jù)類型和你如何打算使用它。這里有幾種常見的情況:

  • 指向簡單類型的指針:如果指針指向一個簡單的數(shù)值類型(如int*float*等),你可以在C#中使用IntPtr類型來表示它。你可以使用Marshal類中的方法(如 Marshal.ReadInt32、Marshal.WriteInt32等)來讀取或?qū)懭胫羔樦赶虻臄?shù)據(jù)。

  • 指向字符串的指針:如果指針指向一個字符串(如char*wchar_t*),你可以在C#中使用string類型來表示它。在DllImport特性中,你可以使用MarshalAs特性來指定字符串的編碼方式。

  • 指向結(jié)構(gòu)體的指針:如果指針指向一個結(jié)構(gòu)體,你可以在C#中創(chuàng)建一個對應(yīng)的類或結(jié)構(gòu)體,并使用ref關(guān)鍵字或者IntPtr類型來表示指針。使用ref關(guān)鍵字時,.NET運行時會自動處理內(nèi)存管理。使用IntPtr時,你需要手動管理內(nèi)存。

例如,假設(shè)你有一個C++函數(shù)如下:

extern "C" __declspec(dllexport) void ModifyPerson(Person* person);

在C#中,你可以這樣使用它:

[DllImport("MyLibrary.dll")]
public static extern void ModifyPerson(ref Person person);

// 或者
[DllImport("MyLibrary.dll")]
public static extern void ModifyPerson(IntPtr personPtr);
  • 指向數(shù)組的指針:如果指針指向一個數(shù)組,你可以在C#中使用數(shù)組類型來表示它。你也可以使用MarshalAs特性來指定數(shù)組的大小。

對于更復(fù)雜的情況,例如指向函數(shù)的指針(函數(shù)指針),你可能需要使用Marshal.GetDelegateForFunctionPointer方法將其轉(zhuǎn)換為C#委托。

需要注意的是,當(dāng)你使用IntPtr來管理指針時,你需要自己負責(zé)內(nèi)存的分配和釋放。你可以使用Marshal.AllocHGlobalMarshal.FreeHGlobal方法來分配和釋放非托管內(nèi)存。

總結(jié)

到此這篇關(guān)于C#程序如何調(diào)用C++ dll的文章就介紹到這了,更多相關(guān)C#調(diào)用C++ dll內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論