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
常见做法:
- 设置 DLL 自动编译到目标可执行程序所在路径.
- 在项目中配置外部启动程序
exe. - 让调试器以宿主程序启动, 再进入 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 失败, 先检查架构, 依赖库和输出路径.
- 若回调崩溃, 优先检查签名, 生命周期和内存所有权边界.