C#?DLL跨語言調(diào)用的兩種實(shí)現(xiàn)方法
你想知道如何編寫能被其他編程語言(如C++、Python、Java等)調(diào)用的C# DLL,這需要解決不同語言間的互操作(Interop)問題,核心是讓C# DLL符合跨語言調(diào)用的標(biāo)準(zhǔn)。
一、核心思路:讓C# DLL支持COM/非托管調(diào)用
普通C# DLL基于.NET托管代碼,非.NET語言(如C++原生、Python)無法直接調(diào)用,需要通過以下兩種主流方式實(shí)現(xiàn)跨語言調(diào)用:
- 方式1:將C# DLL注冊為COM組件(兼容絕大多數(shù)語言:C++、VB6、Python、Java等);
- 方式2:通過P/Invoke封裝非托管導(dǎo)出函數(shù)(主要適配C/C++等原生語言)。
下面優(yōu)先講解最通用的COM組件方式,再補(bǔ)充P/Invoke方式。
方式1:創(chuàng)建可注冊為COM的C# DLL(通用跨語言)
步驟1:創(chuàng)建類庫項目并配置COM兼容
- 打開Visual Studio,創(chuàng)建「類庫(.NET Framework)」項目(.NET Core/.NET 5+需額外配置,推薦先用.NET Framework做兼容),命名為
ComInteropDll。 - 右鍵項目→「屬性」→「應(yīng)用程序」→「程序集信息」,勾選「使程序集COM可見」。
- 右鍵項目→「屬性」→「生成」,勾選「為COM互操作注冊」(僅開發(fā)環(huán)境需要,發(fā)布時手動注冊)。
步驟2:編寫COM兼容的C#代碼
COM要求必須通過接口 暴露方法,且需要給接口/類添加Guid(唯一標(biāo)識),示例代碼如下:
using System;
using System.Runtime.InteropServices;
// 1. 為接口添加唯一Guid(可通過VS工具生成:工具→創(chuàng)建GUID→選注冊表格式,去掉{})
[Guid("12345678-1234-1234-1234-1234567890AB")]
// 2. 標(biāo)記為COM可見的接口
[ComVisible(true)]
public interface ICalculator
{
// COM接口方法不能有重載,參數(shù)/返回值盡量用基礎(chǔ)類型(int、string、double等)
int Add(int a, int b);
string ReverseString(string input);
}
// 3. 為實(shí)現(xiàn)類添加唯一Guid
[Guid("87654321-4321-4321-4321-BA0987654321")]
[ComVisible(true)]
public class Calculator : ICalculator
{
public int Add(int a, int b)
{
return a + b;
}
public string ReverseString(string input)
{
if (string.IsNullOrEmpty(input))
throw new ArgumentNullException(nameof(input));
char[] arr = input.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
}步驟3:編譯并注冊COM組件
按F6編譯項目,生成ComInteropDll.dll和ComInteropDll.tlb(類型庫文件,供其他語言識別)。
注冊COM組件(管理員權(quán)限運(yùn)行CMD):
# 替換為你的DLL路徑(Release版建議用Release目錄) regasm /codebase "C:\ComInteropDll\bin\Debug\ComInteropDll.dll"
卸載COM組件(如需):regasm /u "你的DLL路徑"
步驟4:不同語言調(diào)用該COM DLL示例
示例1:Python調(diào)用(使用pywin32庫)
# 先安裝依賴:pip install pywin32
import win32com.client
# 創(chuàng)建COM對象(參數(shù)是接口/類的ProgID,或直接用Guid)
# 方式1:用類名(需確保項目屬性中配置了ProgID,或手動指定)
calculator = win32com.client.Dispatch("ComInteropDll.Calculator")
# 調(diào)用方法
sum_result = calculator.Add(10, 25)
reverse_result = calculator.ReverseString("Python Call C# DLL")
print(f"兩數(shù)之和:{sum_result}") # 輸出:35
print(f"反轉(zhuǎn)字符串:{reverse_result}") # 輸出:LLD #C llaC nohtyP示例2:C++原生調(diào)用(使用COM接口)
#include <iostream>
#include <Windows.h>
// 導(dǎo)入類型庫(替換為你的tlb文件路徑)
#import "C:\ComInteropDll\bin\Debug\ComInteropDll.tlb" no_namespace
int main()
{
// 初始化COM庫
CoInitialize(NULL);
// 創(chuàng)建COM對象
ICalculator* pCalculator = NULL;
HRESULT hr = CoCreateInstance(
__uuidof(Calculator), // 類的Guid
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(ICalculator), // 接口的Guid
(void**)&pCalculator
);
if (SUCCEEDED(hr))
{
// 調(diào)用方法
int sum = 0;
pCalculator->Add(100, 200, &sum);
BSTR input = SysAllocString(L"C++ Call C# COM DLL");
BSTR reversed = NULL;
pCalculator->ReverseString(input, &reversed);
// 輸出結(jié)果
std::cout << "Sum: " << sum << std::endl; // 輸出:300
wprintf(L"Reversed: %s\n", reversed); // 輸出:LLD MOC #C llaC ++C
// 釋放資源
SysFreeString(input);
SysFreeString(reversed);
pCalculator->Release();
}
// 釋放COM庫
CoUninitialize();
return 0;
}方式2:P/Invoke導(dǎo)出非托管函數(shù)(適配C/C++)
如果僅需給C/C++調(diào)用,可通過DllExport庫直接導(dǎo)出非托管函數(shù)(C#原生不支持直接導(dǎo)出函數(shù),需第三方庫)。
步驟1:安裝DllExport庫
在NuGet包管理器中安裝:UnmanagedExports(注意:僅支持.NET Framework)。
步驟2:編寫導(dǎo)出函數(shù)的C#代碼
using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace PInvokeDll
{
public class MyExports
{
// 導(dǎo)出Add函數(shù),供C/C++調(diào)用
[DllExport("Add", CallingConvention = CallingConvention.Cdecl)]
public static int Add(int a, int b)
{
return a + b;
}
// 導(dǎo)出反轉(zhuǎn)字符串函數(shù)(注意字符編碼適配)
[DllExport("ReverseString", CallingConvention = CallingConvention.Cdecl)]
public static IntPtr ReverseString([MarshalAs(UnmanagedType.LPStr)] string input)
{
if (string.IsNullOrEmpty(input))
return IntPtr.Zero;
char[] arr = input.ToCharArray();
Array.Reverse(arr);
string result = new string(arr);
// 分配非托管內(nèi)存并返回(調(diào)用方需釋放)
return Marshal.StringToHGlobalAnsi(result);
}
// 提供釋放內(nèi)存的函數(shù),供調(diào)用方使用
[DllExport("FreeString", CallingConvention = CallingConvention.Cdecl)]
public static void FreeString(IntPtr ptr)
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
}步驟3:C++調(diào)用該DLL
#include <iostream>
#include <Windows.h>
// 聲明導(dǎo)入的函數(shù)
extern "C" __declspec(dllimport) int Add(int a, int b);
extern "C" __declspec(dllimport) char* ReverseString(const char* input);
extern "C" __declspec(dllimport) void FreeString(char* ptr);
int main()
{
// 調(diào)用Add函數(shù)
int sum = Add(5, 8);
std::cout << "Add result: " << sum << std::endl; // 輸出:13
// 調(diào)用ReverseString函數(shù)
char* reversed = ReverseString("Hello C++");
std::cout << "Reversed: " << reversed << std::endl; // 輸出:++C olleH
FreeString(reversed); // 釋放內(nèi)存
return 0;
}關(guān)鍵注意事項
- 數(shù)據(jù)類型兼容:跨語言調(diào)用時,優(yōu)先使用基礎(chǔ)數(shù)據(jù)類型(int、double、string),避免復(fù)雜自定義類型;字符串需注意編碼(ANSI/UTF-8/UTF-16)。
- 框架選擇:
- .NET Framework:對COM/P/Invoke支持最好,兼容老語言;
- .NET Core/.NET 5+:需用
ComHost或NativeAOT編譯為原生DLL,步驟更復(fù)雜。
- 錯誤處理:跨語言調(diào)用時,C#中的異常無法直接傳遞給非托管語言,建議返回錯誤碼而非拋出異常。
- 64位/32位適配:編譯DLL和調(diào)用程序時,需保持位數(shù)一致(均為x86或x64),否則會調(diào)用失敗。
總結(jié)
- 通用跨語言方案:將C# DLL注冊為COM組件,適配Python、Java、C++等絕大多數(shù)語言,核心是通過
ComVisible和Guid暴露接口,并注冊COM。 - C/C++專屬方案:使用
UnmanagedExports庫導(dǎo)出非托管函數(shù),通過P/Invoke調(diào)用,更輕量但僅適配C/C++。 - 核心要點(diǎn):保持?jǐn)?shù)據(jù)類型簡單、位數(shù)一致、處理好內(nèi)存釋放(尤其是字符串),是跨語言調(diào)用的關(guān)鍵。
以上就是C# DLL跨語言調(diào)用的兩種實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于C# DLL跨語言調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c#判斷字符是否為中文的三種方法分享(正則表達(dá)式判斷)
判斷一個字符是不是漢字通常有三種方法,第一種用 ASCII 碼判斷,第二種用漢字的UNICODE編碼范圍判斷,第三種用正則表達(dá)式判斷,以下是具體方法2014-01-01
C#基于數(shù)據(jù)庫存儲過程的AJAX分頁實(shí)例
這篇文章主要介紹了C#基于數(shù)據(jù)庫存儲過程的AJAX分頁實(shí)現(xiàn)方法,以實(shí)例形式詳細(xì)講述了數(shù)據(jù)庫存儲過程的定義、數(shù)據(jù)庫的訪問及Ajax的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-01-01
WinForm DataGridView控件隔行變色的小例子
WinForm的DataGridView控件設(shè)置行的顏色2013-03-03
C# ListView 點(diǎn)擊表頭對數(shù)據(jù)進(jìn)行排序功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了C# ListView 點(diǎn)擊表頭對數(shù)據(jù)進(jìn)行排序功能的實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-04-04

