亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

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

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

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

(3)編寫(xiě)動(dòng)態(tài)代碼,代碼如下:

libMath.h

// 下列 ifdef 塊是創(chuàng)建使從 DLL 導(dǎo)出更簡(jiǎn)單的
// 宏的標(biāo)準(zhǔn)方法。此 DLL 中的所有文件都是用命令行上定義的 LIBMATH_EXPORTS
// 符號(hào)編譯的。在使用此 DLL 的
// 任何項(xiàng)目上不應(yīng)定義此符號(hào)。這樣,源文件中包含此文件的任何其他項(xiàng)目都會(huì)將
// LIBMATH_API 函數(shù)視為是從 DLL 導(dǎo)入的,而此 DLL 則將用此宏定義的
// 符號(hào)視為是被導(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ù)進(jìn)行導(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#中使用,由于語(yǔ)言語(yǔ)法差異,C++類在C#中無(wú)法使用,因?yàn)镃++類通常包含成員函數(shù)、構(gòu)造函數(shù)、析構(gòu)函數(shù)等,而C#與C++在處理這些方面存在差異。一種可行的方法是在C++類中添加一些導(dǎo)出函數(shù),這樣它們可以通過(guò)C#調(diào)用。這些函數(shù)可以執(zhí)行類的實(shí)例化、調(diào)用成員函數(shù)等操作。確保使用 extern “C” 以避免名稱修飾, 因?yàn)镃++函數(shù)在編譯時(shí),會(huì)在原有的函數(shù)名前后添加一些符號(hào),例如add函數(shù)在編譯后可能變成了@xxasd_sfdf_add_xxx之類的,但是使用extern "C" 后就是按照C語(yǔ)言的方式導(dǎo)出,函數(shù)名不變,例如我添加的一些類導(dǎo)出方法:

// 由于需要給C#調(diào)用,為了方便導(dǎo)出類,添加了函數(shù)進(jìn)行導(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#項(xiàng)目的exe目錄,或者手動(dòng)拷貝,然后在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++類的實(shí)例,用于后面的調(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è)試C#調(diào)用C++");

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

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

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

運(yùn)行結(jié)果如下:

在C#中可以創(chuàng)建一個(gè)對(duì)應(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#中,可以實(shí)例化MyClassWrapper類,并調(diào)用其中的方法,這將轉(zhuǎn)發(fā)調(diào)用到C++類的實(shí)例。

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

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

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

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

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

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

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

在C#中,我們使用DllImport特性來(lái)聲明對(duì)C++ DLL的函數(shù)調(diào)用。例如,如果我們有一個(gè)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);
}

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

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

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

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

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

在處理C++ DLL中的函數(shù)時(shí),也需要注意函數(shù)調(diào)用的約定(cdeclstdcall等)。默認(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++類時(shí),可能需要更多的處理。處理這些情況通常需要對(duì)兩種語(yǔ)言都有深入的理解,并且可能需要手動(dòng)管理內(nèi)存。

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

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

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

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

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

例如,假設(shè)你有一個(gè)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ù)組的指針:如果指針指向一個(gè)數(shù)組,你可以在C#中使用數(shù)組類型來(lái)表示它。你也可以使用MarshalAs特性來(lái)指定數(shù)組的大小。

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

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

總結(jié)

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

相關(guān)文章

最新評(píng)論