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

C#调用ITypeLib2.GetLibStatistics始终抛出AccessViolationException求助

在C#中调用ITypeLib2.GetLibStatistics触发AccessViolationException的解决办法

问题描述

在C#中调用ITypeLib2接口的GetLibStatistics方法时,始终抛出System.AccessViolationException: Attempted to read or write protected memory异常。已在C++原生COM环境验证该方法可正常执行,确认.tlb文件无问题,怀疑C#中的接口实现不正确,寻求技术建议。

原代码示例

public static void GetLibStatisticsTest()
{
    ITypeLib tlb;
    LoadTypeLibEx(@"C:\Sample.tlb", RegKind.None, out tlb);

    var tlb2 = tlb as ITypeLib2;
    if (tlb2 == null ) { return; }

    try
    {
        IntPtr pcUniqueNames = IntPtr.Zero;
        int pcchUniqueNames;

        // 此处总是抛出System.AccessViolationException
        tlb2.GetLibStatistics(pcUniqueNames, out pcchUniqueNames);;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

[DllImport("oleaut32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int LoadTypeLibEx(string szFile, RegKind regKind, out ITypeLib pptlib);

问题根源

  1. 参数类型错误:GetLibStatistics的第一个参数pcUniqueNames是COM输出参数(对应C++的ULONG*),原代码传入IntPtr.Zero,COM方法尝试向空指针写入数据,直接触发内存访问违规。
  2. 默认互操作接口定义可能不正确:系统自带的ITypeLib2互操作定义可能将参数类型错误映射,导致调用时内存布局不匹配。

解决步骤

1. 手动修正ITypeLib2接口定义

必须确保接口方法签名与COM原生接口完全一致,重新定义ITypeLib2接口(包含继承的ITypeLib方法):

using System;
using System.Runtime.InteropServices;

[ComImport, Guid("00020402-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITypeLib
{
    // IUnknown方法
    [PreserveSig]
    int QueryInterface(ref Guid riid, out IntPtr ppvObject);
    [PreserveSig]
    uint AddRef();
    [PreserveSig]
    uint Release();

    // ITypeLib方法(按COM接口顺序实现)
    void GetDocumentation(int index, out string name, out string docString, out int helpContext, out string helpFile);
    [PreserveSig]
    int GetTypeInfoCount();
    void GetTypeInfo(int index, out ITypeInfo ppTI);
    void GetTypeInfoType(int index, out TYPEKIND pTKind);
    void GetTypeInfoOfGuid(ref Guid guid, out ITypeInfo ppTI);
    void GetLibAttr(out IntPtr ppTLibAttr);
    void GetTypeComp(out ITypeComp ppTComp);
    [PreserveSig]
    int IsName([MarshalAs(UnmanagedType.LPWStr)] string szNameBuf, int lHashVal);
    void FindName([MarshalAs(UnmanagedType.LPWStr)] string szNameBuf, int lHashVal, [Out, MarshalAs(UnmanagedType.LPArray)] ITypeInfo[] ppTInfo, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgMemId, ref short pcFound);
    void ReleaseTLibAttr(IntPtr pTLibAttr);
}

[ComImport, Guid("00020411-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITypeLib2 : ITypeLib
{
    // 继承ITypeLib的方法(确保顺序一致)
    new int QueryInterface(ref Guid riid, out IntPtr ppvObject);
    new uint AddRef();
    new uint Release();
    new void GetDocumentation(int index, out string name, out string docString, out int helpContext, out string helpFile);
    new int GetTypeInfoCount();
    new void GetTypeInfo(int index, out ITypeInfo ppTI);
    new void GetTypeInfoType(int index, out TYPEKIND pTKind);
    new void GetTypeInfoOfGuid(ref Guid guid, out ITypeInfo ppTI);
    new void GetLibAttr(out IntPtr ppTLibAttr);
    new void GetTypeComp(out ITypeComp ppTComp);
    new int IsName([MarshalAs(UnmanagedType.LPWStr)] string szNameBuf, int lHashVal);
    new void FindName([MarshalAs(UnmanagedType.LPWStr)] string szNameBuf, int lHashVal, [Out, MarshalAs(UnmanagedType.LPArray)] ITypeInfo[] ppTInfo, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgMemId, ref short pcFound);
    new void ReleaseTLibAttr(IntPtr pTLibAttr);

    // ITypeLib2新增方法
    void GetLibStatistics(out uint pcUniqueNames, out int pcchUniqueNames);
    void GetCustData(ref Guid guid, out object pVarVal);
    void GetLibId(out Guid pGuid);
    void SetLibId(ref Guid guid);
    void GetDocumentation2(int index, out string pbstrHelpString, out int pdwHelpStringContext, out string pbstrHelpStringDll);
    void GetAllCustData(IntPtr pCustData);
}

public enum TYPEKIND
{
    TKIND_ENUM = 0,
    TKIND_RECORD = 1,
    TKIND_MODULE = 2,
    TKIND_INTERFACE = 3,
    TKIND_DISPATCH = 4,
    TKIND_COCLASS = 5,
    TKIND_ALIAS = 6,
    TKIND_UNION = 7,
    TKIND_MAX = 8
}

[ComImport, Guid("00020401-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITypeInfo
{
    void GetTypeAttr(out IntPtr ppTypeAttr);
    void ReleaseTypeAttr(IntPtr pTypeAttr);
}

[ComImport, Guid("00020403-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITypeComp
{
    // 按需补充完整方法定义
}

2. 正确调用GetLibStatistics方法

直接使用out修饰符接收输出参数,无需手动分配内存:

public static void GetLibStatisticsTest()
{
    ITypeLib tlb;
    LoadTypeLibEx(@"C:\Sample.tlb", RegKind.None, out tlb);

    var tlb2 = tlb as ITypeLib2;
    if (tlb2 == null) { return; }

    try
    {
        uint uniqueNameCount;
        int totalCharCount;

        tlb2.GetLibStatistics(out uniqueNameCount, out totalCharCount);
        Console.WriteLine($"唯一名称数量:{uniqueNameCount},总字符数:{totalCharCount}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"调用失败:{ex.Message}");
    }
}

[DllImport("oleaut32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int LoadTypeLibEx(string szFile, RegKind regKind, out ITypeLib pptlib);

public enum RegKind
{
    None = 0,
    Register = 1,
    RegAsConfig = 2
}

3. 关键注意事项

  • COM接口的方法顺序必须严格与原生定义一致,否则会导致调用时内存偏移错误。
  • 使用[PreserveSig]标记非HRESULT返回值的方法,确保互操作层正确处理返回值。
  • 可通过LoadTypeLibEx的返回值判断.tlb文件加载是否成功(返回0表示加载正常)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:03:12