You need to enable JavaScript to run this app. C#如何将非托管类型转为托管类型 LDAP结构体转译内存报错排查
优惠活动
大模型
产品
解决方案
定价
更多

C#如何将非托管类型转为托管类型 LDAP结构体转译内存报错排查

你的映射表存在3处错误:

  • UCHAR* 映射为byte[]不准确:如果UCHAR指向的是Null结尾的字符串,在你调用Unicode版本API的场景下,应该映射为带[MarshalAs(UnmanagedType.LPWStr)]的string,只有当UCHAR指向固定长度的字节数组时才用byte[]。
  • 你实际代码中把ULONG_PTR类型的ld_msgid映射为ulong是错误的:ULONG_PTR是平台相关类型,32位系统下占4字节、64位下占8字节,而ulong固定为8字节,32位进程运行时会直接导致内存布局错位,正确的映射是UIntPtr。
  • PCHAR映射为string缺少字符集标注:你调用的是W后缀的Unicode版本API,所有字符串指针都是宽字符类型,必须给string字段加上[MarshalAs(UnmanagedType.LPWStr)]显式指定序列化方式,否则默认按ANSI处理会导致内存读取错误。

除了映射表的问题,你的代码还有几个致命错误导致内存异常:

  1. 自定义的LDAP结构体漏了第一个成员:你定义了ld_sb子结构体,但是没有把它作为第一个字段放到LDAP结构体中,导致整个结构体的内存布局从开头就和非托管定义不匹配,这是触发保护内存异常的核心原因。
  2. 结构体没有指定对齐规则:Win32 API的结构体默认Pack值为8,你没有显式指定的话,C#的Sequential布局可能使用不同的对齐规则,导致字段偏移错位。
  3. 没有判断ldap_sslinit的返回值是否为空:如果host不可达、参数错误,函数会返回IntPtr.Zero,直接传入PtrToStructure自然会触发内存访问错误。

你之前直接把返回值声明为LDAP结构体报错是正常的:ldap_sslinit的返回值本身就是指向LDAP结构体的指针,不是值传递的结构体实例,PInvoke不支持直接将非托管返回的指针映射为托管值类型,所以会提示签名不兼容。

修正后的代码示例

[DllImport("wldap32.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ldap_sslinitW",
    SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr ldap_sslinit(string hostName, uint portNumber, int secure);

[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct LdapSb
{
    public UIntPtr sb_sd;
    public byte Reserved1;
    public UIntPtr sb_naddr;
    public byte Reserved2;
}

[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
public struct LDAP
{
    public LdapSb ld_sb; // 补上漏写的第一个字段
    [MarshalAs(UnmanagedType.LPWStr)] public string ld_host;
    public uint ld_version;
    public byte ld_lberoptions;
    public uint ld_deref;
    public uint ld_timelimit;
    public uint ld_sizelimit;
    public uint ld_errno;
    [MarshalAs(UnmanagedType.LPWStr)] public string ld_matched;
    [MarshalAs(UnmanagedType.LPWStr)] public string ld_error;
    public UIntPtr ld_msgid; // 替换ulong为UIntPtr
    [MarshalAs(UnmanagedType.LPWStr)] public string Reserved3;
    public uint ld_cldaptries;
    public uint ld_cldaptimeout;
    public uint ld_refhoplimit;
    public uint ld_options;
}

private const uint LDAP_SSL_PORT = 636;

static void Main(string[] args)
{
    IntPtr ld = ldap_sslinit("test", LDAP_SSL_PORT, 1);
    if (ld == IntPtr.Zero)
    {
        Console.WriteLine("ldap_sslinit调用失败");
        return;
    }
    var ldap = Marshal.PtrToStructure<LDAP>(ld); 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 17:27:00