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);
问题根源
- 参数类型错误:GetLibStatistics的第一个参数
pcUniqueNames是COM输出参数(对应C++的ULONG*),原代码传入IntPtr.Zero,COM方法尝试向空指针写入数据,直接触发内存访问违规。 - 默认互操作接口定义可能不正确:系统自带的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
相关产品推荐
相关产品推荐

