You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor WASM中能否通过反向P/Invoke从C/C++调用C#方法?

Blazor WASM 原生代码反向P/Invoke问题

项目背景

  • 仅具备C/C++编程基础,观看Steve Sanderson与Matthew Leibowitz在ASP.NET Community Standup的两次技术分享后,计划搭建实验项目验证相关技术概念
  • 项目目标:将字节数组格式的视频文件传入原生代码,在原生层完成所有视频帧提取后,将结果回传至C#层,供SkiaSharp画布渲染使用
  • 当前进展:已经实现字节数组向原生代码的传递,但无法完成原生C代码对C#方法的调用,尝试反向P/Invoke方案时出现运行时断言错误

现有实现代码

原生C/C++侧代码

测试用回调代码:

extern "C"
{
void test_class_callmycallback(
      void callback(int,int))
  {
    callback(3,5);
  }
}

实际业务逻辑需要实现的效果:

void test_class_renderpicture(void *obj, void (*func)(uint8_t *, int))
  {
    ((TestClass *)obj)->getBytesWithCallback(func);
  }
}

C#侧代码

[DllImport("Test")]
static extern void test_class_callmycallback([MarshalAs(UnmanagedType.FunctionPtr)] CallbackDelegate func);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackDelegate(int arg1, int arg2);

public void RenderPicture()
{
    var managedDel = new CallbackDelegate(Multiply);

     test_class_callmycallback(Multiply);
    //RenderPictureDelegate managedDelegate = new RenderPictureDelegate(RenderPictureCallback);
    //test_class_renderpicture(this.handle, managedDelegate);
}
public void Multiply(int a,int b)
{
    var c=a * b; 
}

编译与引入配置

  • 原生代码编译命令:emcc myfile.cpp -shared -o Test.o
  • Blazor项目引入配置:
<ItemGroup> <NativeFileReference Include="Test.o" /> </ItemGroup>

运行时错误

项目整体编译无异常,运行时抛出Mono loader.c断言错误,错误栈如下:

Assertion at /__w/1/s/src/mono/mono/metadata/loader.c:1806, condition
`' not met 573846 @ dotnet..bnoeqi2kuy.js:1430
_emscripten_asm_const_int @ dotnet..bnoeqi2kuy.js:5673 $wasm_trace_logger @ 02e96b9a:0x228b7a
$eglib_log_adapter @ 02e96b9a:0xc102f
$monoeg_g_logstr @ 02e96b9a:0x20754f
$monoeg_g_logv_nofree @ 02e96b9a:0x2074f3
$mono_assertion_message @ 02e96b9a:0x2075d0
$mono_assertion_message @ 02e96b9a:0x207619

待解决问题

  1. Blazor WASM项目通过NativeFileReference引入emscripten编译的原生文件时,是否支持从C/C++原生代码反向P/Invoke调用C#方法?如果支持具体实现方式是什么?
  2. 该场景下[UnmanagedFunctionPointer(CallingConvention.Cdecl)]与[MarshalAs(UnmanagedType.FunctionPtr)]特性是否为必需配置?emscripten使用__stdcall或__declspec(dllexport)时会抛出警告,与传统本地DllImport场景的配置逻辑是否存在差异?

问题解答

关于反向P/Invoke的支持性与实现方式

Blazor WASM完全支持从emscripten编译的原生代码反向调用C#委托,你遇到的断言错误核心原因有两个:

  • 传入委托时没有保持委托的根引用,GC在原生代码触发回调前就回收了委托对象,导致原生侧拿到的是野指针
  • emscripten编译WASM目标时参数使用错误,默认不支持裸函数指针跨边界传递,不能用-shared参数编译供Blazor NativeFileReference引用的目标文件。

正确实现步骤如下:

  1. 修正emscripten编译命令,去掉-shared参数,添加导出符号配置和WASM函数表支持参数,正确命令参考:
    emcc myfile.cpp -o Test.o -sEXPORTED_FUNCTIONS=_test_class_callmycallback,_test_class_renderpicture -sALLOW_TABLE_GROWTH
    
    其中-sALLOW_TABLE_GROWTH是必须添加的参数,反向P/Invoke需要动态往WASM函数表插入委托对应的thunk函数,不开这个参数会直接导致函数表操作失败触发断言。
  2. C#侧需要把传入原生层的委托存为类级别的字段,保证回调触发前委托不会被GC回收,不要像现有代码那样只在方法内声明临时变量。参考修正后的C#代码:
    // 把委托存为实例字段,保持根引用避免被GC回收
    private CallbackDelegate _callbackInstance;
    
    [DllImport("Test")]
    static extern void test_class_callmycallback(CallbackDelegate func);
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void CallbackDelegate(int arg1, int arg2);
    
    public void RenderPicture()
    {
        _callbackInstance = Multiply;
        test_class_callmycallback(_callbackInstance);
    }
    
    public void Multiply(int a,int b)
    {
        var c = a * b; 
    }
    
  3. 后续实现帧数据回传时,原生侧的uint8_t*指针在C#侧对应IntPtr,不要直接映射为byte[],需要在回调方法里用Marshal.Copy把数据从非托管内存拷贝到托管字节数组中,避免内存访问违规。

关于特性配置与调用约定的差异

  • [UnmanagedFunctionPointer(CallingConvention.Cdecl)]是必须配置的,WASM平台原生代码默认全部使用cdecl调用约定,不存在stdcall的概念,这也是加__stdcall会出警告的核心原因——WASM没有x86平台上的多种调用约定区分,所有原生函数统一用cdecl规则。
  • [MarshalAs(UnmanagedType.FunctionPtr)]在WASM场景下不是必需配置,委托类型默认就会被封送为函数指针,加了不会出错但属于冗余代码。
  • __declspec(dllexport)在emscripten编译WASM目标时不需要加,emscripten是通过编译参数里的EXPORTED_FUNCTIONS列表控制导出符号的,加这个修饰符没有实际作用还会触发警告,和Windows本地DLL的导出逻辑完全不同。
  • 传统桌面DllImport场景下需要考虑不同调用约定的栈平衡、符号修饰名差异,在Blazor WASM场景下这些问题都不存在,所有原生函数统一用cdecl,符号名就是C函数名加下划线前缀,不需要额外配置EntryPoint。

内容的提问来源于stack exchange,提问作者Can Günaydın

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:45:58