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

VB.NET Core调用C#.NET Core DLL非托管函数程序闪退问题排查

问题核心原因

程序无征兆闪退是以下几个问题叠加导致的,和非托管调用的边界规则直接相关:

  • 普通.NET Core编译产出的C#类库是托管DLL,不支持直接被DllImport调用。如果你没有用NativeAOT将C#项目发布为原生DLL,或者没有通过DNNE(.NET Native Exports)生成原生导出层,强行P/Invoke托管DLL会直接触发进程级崩溃,且不会抛出可捕获的托管异常。
  • [UnmanagedCallersOnly]特性默认不会按C#方法名导出原生入口点,未显式指定EntryPoint时,导出的函数名会被编译器修饰,VB侧DllImport找不到匹配的入口点时,若调用约定不匹配触发栈错位,会直接终止进程,无法进入你写的EntryPointNotFoundException捕获块。
  • 两侧调用约定未显式对齐,32位模式下栈布局不一致会直接导致内存访问违规闪退。
  • 你写的VB代码存在明显问题:Catch块中定义的异常变量是ex,块内却调用了不存在的e.ToString();另外代码末尾的End语句会强制直接终止进程,不会执行任何资源回收逻辑;你通过Marshal.StringToHGlobalAnsi分配的非托管内存从未释放,存在内存泄漏问题。
  • 标记[UnmanagedCallersOnly]的方法不允许托管异常穿越非托管边界,一旦异常漏出方法边界,进程会被立即终止,你在C#方法内写的try/catch只能捕获方法内部处理的异常,无法拦截非托管层面的栈损坏、内存访问错误。
排查定位步骤

按以下顺序排查可以快速定位根因:

  • 验证DLL导出函数:打开VS开发者命令提示符,执行dumpbin /exports TestLibrary1NE.dll查看导出表,如果没有名为AddNE的导出项,说明C#侧未正确配置导出。
  • 启用混合模式调试:在VB项目属性-调试页勾选「启用本机代码调试」,再次触发调用时VS会停在具体的崩溃位置,给出访问冲突、栈损坏、入口点找不到的明确错误信息,不会直接无征兆退出。
  • 检查平台目标一致性:确认C# DLL和VB项目的平台目标完全统一,不能一个是x86一个是x64,禁止使用Any CPU配置(NativeAOT/DNNE生成的原生DLL是架构绑定的)。
  • 确认DLL部署路径:确保TestLibrary1NE.dll放在VB编译输出的EXE同级目录下,不要放在系统目录或其他不被P/Invoke搜索的路径。
修复方案

第一步:修正C#导出函数代码

注意必须将C#项目配置为NativeAOT发布,或引入DNNE包生成原生导出层,普通编译产出的托管DLL无法被直接P/Invoke调用。

using System;
using System.Runtime.InteropServices;

namespace TestLibrary1
{
  public class Class1
  {
    // 显式指定导出入口点名、调用约定,确保和VB侧完全对齐
    [UnmanagedCallersOnly(EntryPoint = nameof(AddNE), CallingConvention = CallingConvention.StdCall)]
    public static IntPtr AddNE(IntPtr a)
    {
      // 提前做参数校验,禁止异常漏出非托管边界
      if (a == IntPtr.Zero) return IntPtr.Zero;
      return a;
    }
  }
}

C#项目配置(csproj文件)需要添加以下配置才能正确生成原生导出:

<PropertyGroup>
  <OutputType>Library</OutputType>
  <PublishAot>true</PublishAot> <!-- NativeAOT发布原生DLL -->
  <TargetFramework>net8.0</TargetFramework> <!-- 使用7.0以上版本的.NET才支持NativeAOT -->
  <Platforms>x64;x86</Platforms>
</PropertyGroup>

第二步:修正VB侧调用代码

Imports System.Runtime.InteropServices

Public Class Form1
  Private Const DllName As String = "TestLibrary1NE.dll"

  ' 显式指定入口点、调用约定、字符集,和C#侧完全对齐
  <DllImport(DllName, 
             EntryPoint:="AddNE", 
             CallingConvention:=CallingConvention.StdCall, 
             CharSet:=CharSet.Ansi)>
  Private Shared Function AddNE(ByVal a As IntPtr) As IntPtr
  End Function

  Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
    Dim ptrAddress As IntPtr = IntPtr.Zero
    Try
      ptrAddress = Marshal.StringToHGlobalAnsi(txtAddress.Text.Trim())
      Dim ptrResponse As IntPtr = AddNE(ptrAddress)
      
      If ptrResponse = IntPtr.Zero Then
        MessageBox.Show("函数调用返回空指针")
        Return
      End If

      Dim strResponse As String = Marshal.PtrToStringAnsi(ptrResponse)
      MessageBox.Show(strResponse)
    Catch ex As Exception
      ' 捕获所有异常方便调试,不要只捕获入口点找不到
      MessageBox.Show($"调用失败:{ex.Message}")
    Finally
      ' 释放手动分配的非托管内存,避免泄漏
      If ptrAddress <> IntPtr.Zero Then
        Marshal.FreeHGlobal(ptrAddress)
      End If
    End Try
    ' 删除原代码中粗暴终止进程的End语句
  End Sub
End Class
额外注意事项
  • 如果后续你修改C#函数逻辑返回DLL内部分配的非托管内存,必须在C#侧同步提供对应的内存释放函数,不要在VB侧释放非当前进程分配的内存,否则会触发堆损坏。
  • 不要在标记[UnmanagedCallersOnly]的方法内传递托管对象引用,所有跨边界传递的参数必须是可 blittable 的值类型或者非托管指针。
  • 发布时如果用SelfContained模式部署NativeAOT生成的DLL,不需要目标机器安装.NET运行时;如果用框架依赖模式,需要确保目标机器安装了对应版本的.NET运行时。

内容的提问来源于stack exchange,提问作者Raj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 18:27:31