python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python调用C# dll

Python调用C# dll的两种主要方法

作者:学亮编程手记

这篇文章主要介绍了两种方法让Python调用C# DLL:推荐的pythonnet方法和使用COM组件的方法,每种方法都有详细的步骤,包括创建和编译C#项目、安装Python依赖、运行测试等,文章还讨论了注意事项,如架构匹配、依赖项、异常处理和性能优化,需要的朋友可以参考下

Python 调用 C# DLL 的完整案例

下面提供两种主要方法:使用 pythonnet (推荐) 和使用 COM 组件。

方法一:使用 pythonnet (clr) - 推荐

1. 创建 C# 类库项目

MathOperations.cs

using System;
using System.Runtime.InteropServices;

namespace CSharpDLL
{
    [ComVisible(true)]  // 使类对 COM 可见
    [Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]  // 唯一标识符
    public interface IMathOperations
    {
        int Add(int a, int b);
        double Multiply(double a, double b);
        string Greet(string name);
        double[] ProcessArray(double[] numbers);
        Person ProcessPerson(Person person);
    }

    [ComVisible(true)]
    [Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71")]
    [ClassInterface(ClassInterfaceType.None)]
    public class MathOperations : IMathOperations
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public double Multiply(double a, double b)
        {
            return a * b;
        }

        public string Greet(string name)
        {
            return $"Hello, {name}! from C# DLL";
        }

        public double[] ProcessArray(double[] numbers)
        {
            if (numbers == null)
                return null;

            double[] result = new double[numbers.Length];
            for (int i = 0; i < numbers.Length; i++)
            {
                result[i] = numbers[i] * 2 + 1;  // 简单的处理:2*x + 1
            }
            return result;
        }

        public Person ProcessPerson(Person person)
        {
            if (person == null)
                return null;

            return new Person
            {
                Name = person.Name.ToUpper(),
                Age = person.Age + 1,
                Score = person.Score * 1.1
            };
        }

        // 静态方法
        public static string GetVersion()
        {
            return "CSharpDLL v1.0.0";
        }

        // 重载方法示例
        public string ProcessData(string data)
        {
            return $"Processed string: {data}";
        }

        public string ProcessData(int data)
        {
            return $"Processed int: {data}";
        }

        public string ProcessData(double data)
        {
            return $"Processed double: {data:F2}";
        }
    }

    [ComVisible(true)]
    [Guid("F54F8C0A-69B5-4BC5-9E2A-123456789ABC")]
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public double Score { get; set; }

        public override string ToString()
        {
            return $"Person: {Name}, Age: {Age}, Score: {Score}";
        }
    }

    // 高级功能示例
    [ComVisible(true)]
    [Guid("12345678-1234-1234-1234-123456789DEF")]
    public class AdvancedOperations
    {
        // 回调函数示例
        public delegate void ProgressCallback(int progress, string message);
        
        public void LongRunningOperation(ProgressCallback callback)
        {
            for (int i = 0; i <= 100; i += 10)
            {
                System.Threading.Thread.Sleep(500);
                callback?.Invoke(i, $"Progress: {i}%");
            }
        }

        // 事件示例
        public event EventHandler<string> OperationCompleted;

        public void StartOperation()
        {
            System.Threading.Thread.Sleep(1000);
            OperationCompleted?.Invoke(this, "Operation completed successfully!");
        }

        // 异常处理示例
        public double SafeDivide(double a, double b)
        {
            if (b == 0)
                throw new DivideByZeroException("Division by zero is not allowed");
            return a / b;
        }
    }
}

AssemblyInfo.cs (重要:添加强名和COM注册信息)

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("CSharpDLL")]
[assembly: AssemblyDescription("C# DLL for Python interop")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSharpDLL")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

[assembly: ComVisible(true)]
[assembly: Guid("a13cc3d7-6c1e-4b5a-93a7-2e4c073d7a9a")]

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

2. 编译 C# 项目

使用 Visual Studio 或命令行编译:

csc /target:library /out:CSharpDLL.dll /reference:System.Runtime.InteropServices.dll *.cs

3. Python 端代码

install_requirements.py

#!/usr/bin/env python3
"""
安装必要的 Python 包
"""
import subprocess
import sys

def install_package(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

if __name__ == "__main__":
    packages = ["pythonnet", "clr-loader"]
    
    for package in packages:
        try:
            print(f"正在安装 {package}...")
            install_package(package)
            print(f"{package} 安装成功!")
        except Exception as e:
            print(f"安装 {package} 失败: {e}")

main.py

#!/usr/bin/env python3
"""
Python 调用 C# DLL 的主程序
"""
import os
import sys
import clr
from typing import List, Dict, Any
import logging

# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class CSharpDLLManager:
    """C# DLL 管理器"""
    
    def __init__(self, dll_path: str):
        """
        初始化 DLL 管理器
        
        Args:
            dll_path: C# DLL 文件路径
        """
        self.dll_path = dll_path
        self._loaded = False
        self._math_ops = None
        self._advanced_ops = None
        
    def load_dll(self) -> bool:
        """加载 C# DLL"""
        try:
            # 添加 DLL 所在目录到 CLR 路径
            dll_dir = os.path.dirname(os.path.abspath(self.dll_path))
            clr.AddReference(dll_dir)
            
            # 加载 DLL
            clr.AddReference(os.path.basename(self.dll_path).replace('.dll', ''))
            
            # 导入命名空间
            from CSharpDLL import MathOperations, Person, AdvancedOperations
            
            self._math_ops = MathOperations()
            self._advanced_ops = AdvancedOperations()
            self._loaded = True
            
            logger.info("C# DLL 加载成功")
            return True
            
        except Exception as e:
            logger.error(f"加载 C# DLL 失败: {e}")
            return False
    
    def test_basic_operations(self) -> Dict[str, Any]:
        """测试基本操作"""
        if not self._loaded or not self._math_ops:
            raise RuntimeError("DLL 未加载")
        
        results = {}
        
        # 测试整数运算
        results['add'] = self._math_ops.Add(10, 20)
        
        # 测试浮点数运算
        results['multiply'] = self._math_ops.Multiply(3.14, 2.5)
        
        # 测试字符串处理
        results['greet'] = self._math_ops.Greet("Python")
        
        # 测试数组处理
        input_array = [1.0, 2.0, 3.0, 4.0, 5.0]
        results['array_input'] = input_array
        results['array_output'] = list(self._math_ops.ProcessArray(input_array))
        
        # 测试对象处理
        person = Person()
        person.Name = "Alice"
        person.Age = 25
        person.Score = 95.5
        
        processed_person = self._math_ops.ProcessPerson(person)
        results['person_input'] = str(person)
        results['person_output'] = str(processed_person)
        
        # 测试重载方法
        results['overload_string'] = self._math_ops.ProcessData("test")
        results['overload_int'] = self._math_ops.ProcessData(42)
        results['overload_double'] = self._math_ops.ProcessData(3.14159)
        
        return results
    
    def test_advanced_operations(self) -> Dict[str, Any]:
        """测试高级操作"""
        if not self._loaded or not self._advanced_ops:
            raise RuntimeError("DLL 未加载")
        
        results = {}
        
        # 测试回调函数
        def progress_callback(progress, message):
            logger.info(f"进度回调: {progress}% - {message}")
        
        logger.info("开始测试长时运行操作...")
        self._advanced_ops.LongRunningOperation(progress_callback)
        
        # 测试事件
        def operation_completed(sender, args):
            logger.info(f"操作完成事件: {args}")
            results['event_message'] = args
        
        self._advanced_ops.OperationCompleted += operation_completed
        self._advanced_ops.StartOperation()
        
        # 测试异常处理
        try:
            results['safe_divide_success'] = self._advanced_ops.SafeDivide(10.0, 2.0)
            results['safe_divide_error'] = "No error"
        except Exception as e:
            results['safe_divide_error'] = f"捕获到异常: {e}"
        
        return results
    
    def run_benchmark(self, iterations: int = 10000) -> Dict[str, Any]:
        """性能测试"""
        if not self._loaded or not self._math_ops:
            raise RuntimeError("DLL 未加载")
        
        import time
        
        start_time = time.time()
        
        for i in range(iterations):
            result = self._math_ops.Add(i, i + 1)
        
        end_time = time.time()
        
        return {
            'iterations': iterations,
            'total_time': end_time - start_time,
            'average_time': (end_time - start_time) / iterations * 1000,  # 毫秒
            'operations_per_second': iterations / (end_time - start_time)
        }

def main():
    """主函数"""
    # DLL 路径 - 请根据实际情况修改
    dll_path = r"CSharpDLL.dll"  # 或者完整路径如 r"C:\path\to\CSharpDLL.dll"
    
    if not os.path.exists(dll_path):
        logger.error(f"找不到 DLL 文件: {dll_path}")
        logger.info("请先编译 C# 项目生成 DLL 文件")
        return
    
    # 创建管理器
    manager = CSharpDLLManager(dll_path)
    
    # 加载 DLL
    if not manager.load_dll():
        return
    
    try:
        # 测试基本操作
        logger.info("=== 测试基本操作 ===")
        basic_results = manager.test_basic_operations()
        
        for key, value in basic_results.items():
            logger.info(f"{key}: {value}")
        
        print("\n" + "="*50 + "\n")
        
        # 测试高级操作
        logger.info("=== 测试高级操作 ===")
        advanced_results = manager.test_advanced_operations()
        
        for key, value in advanced_results.items():
            logger.info(f"{key}: {value}")
        
        print("\n" + "="*50 + "\n")
        
        # 性能测试
        logger.info("=== 性能测试 ===")
        benchmark_results = manager.run_benchmark(10000)
        
        for key, value in benchmark_results.items():
            logger.info(f"{key}: {value}")
            
    except Exception as e:
        logger.error(f"测试过程中发生错误: {e}")

if __name__ == "__main__":
    main()

方法二:使用 COM 组件

1. C# COM 组件注册

修改 C# 项目属性:

regasm CSharpDLL.dll /tlb /codebase

2. Python 使用 COM 组件

com_interop.py

#!/usr/bin/env python3
"""
使用 COM 组件方式调用 C# DLL
"""
import pythoncom
import win32com.client
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def test_com_interop():
    """测试 COM 互操作"""
    try:
        # 创建 COM 对象
        math_ops = win32com.client.Dispatch("CSharpDLL.MathOperations")
        
        # 测试方法调用
        result = math_ops.Add(15, 25)
        logger.info(f"15 + 25 = {result}")
        
        result = math_ops.Greet("COM Client")
        logger.info(f"Greeting: {result}")
        
        # 测试数组处理
        input_array = [1.0, 2.0, 3.0]
        result = math_ops.ProcessArray(input_array)
        logger.info(f"Array input: {input_array}")
        logger.info(f"Array output: {list(result)}")
        
    except Exception as e:
        logger.error(f"COM 互操作失败: {e}")

if __name__ == "__main__":
    test_com_interop()

完整项目结构

CSharpPythonInterop/
├── CSharpDLL/                    # C# 类库项目
│   ├── MathOperations.cs
│   ├── Person.cs
│   ├── AdvancedOperations.cs
│   ├── AssemblyInfo.cs
│   └── CSharpDLL.csproj
├── PythonClient/                 # Python 客户端
│   ├── main.py
│   ├── com_interop.py
│   ├── install_requirements.py
│   └── requirements.txt
└── README.md

使用说明

环境要求

安装步骤

编译 C# DLL:

cd CSharpDLL
dotnet build --configuration Release

安装 Python 依赖:

cd PythonClient
pip install pythonnet
# 或者
python install_requirements.py

运行测试:

python main.py

注意事项

  1. 架构匹配: 确保 Python 和 C# DLL 的架构一致 (x86/x64)
  2. 依赖项: 如果 C# DLL 有依赖项,确保它们在同一目录或 GAC 中
  3. 异常处理: C# 异常会作为 Python 异常抛出,需要适当处理
  4. 性能: 对于高频调用,考虑批量操作减少互操作开销

常见问题解决

  1. DLL 加载失败: 检查路径和依赖项
  2. 类型转换错误: 确保参数类型匹配
  3. 内存泄漏: 及时释放 COM 对象
  4. 线程安全: 在多线程环境中注意同步

这个完整案例展示了 Python 调用 C# DLL 的各种场景,包括基本类型、数组、对象、回调、事件和异常处理等。

以上就是Python调用C# dll的两种主要方法的详细内容,更多关于Python调用C# dll的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文