Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

C# 笔记

说明

  • 本页记录 C# 在调试外部程序和与原生库交互时的常见做法.
  • 当前重点是 DLL 调试方式和 C# <-> Rust 的回调绑定.

从 DLL 项目调试外部 EXE

参考: https://learn.microsoft.com/zh-cn/visualstudio/debugger/how-to-debug-from-a-dll-project?view=vs-2022

常见做法:

  1. 设置 DLL 自动编译到目标可执行程序所在路径.
  2. 在项目中配置外部启动程序 exe.
  3. 让调试器以宿主程序启动, 再进入 DLL 断点排查.

适用场景:

  • 插件式程序开发.
  • 需要调试被宿主程序动态加载的类库.

C# 与 Rust 交互

让第三方 DLL 调用 C# 函数

C# 中创建 delegate, 再把函数指针传给原生侧:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DelegatTxHandler([MarshalAs(UnmanagedType.LPUTF8Str)] string msg, int id);

private IntPtr msg_handle_ptr;

void send_message([MarshalAs(UnmanagedType.LPUTF8Str)] string msg, int id) {
}

void Register() {
    msg_handle_ptr = Marshal.GetFunctionPointerForDelegate(new DelegatTxHandler(send_message));
    bind_msgHandler(msg_handle_ptr);
}

[DllImport("rust_dll", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void bind_msgHandler(IntPtr func);
#![allow(unused)]
fn main() {
pub type TxHandler = unsafe extern "C" fn(msg: *const c_char, id: i32);

#[no_mangle]
extern "C" fn bind_TxHandler(func: TxHandler) {
    unsafe {
        tx_handler = Some(func);
    }
}
}

联调注意点

  • CallingConvention 要与原生侧保持一致.
  • 字符串编码要提前约定, 例如统一 UTF-8.
  • 委托对象需要持有引用, 避免被 GC 提前回收.
  • 导出函数命名和实际绑定函数要完全一致, 避免入口找不到.

排错建议

  • 先验证最简单的数字或字符串回调, 再引入结构体和异步调用.
  • 若加载 DLL 失败, 先检查架构, 依赖库和输出路径.
  • 若回调崩溃, 优先检查签名, 生命周期和内存所有权边界.