我正在尝试调用
GetLibStatistics
接口的 ITypeLib2
方法。我尝试了几种变体和技术,但它们都抛出System.AccessViolationException: Attempted to read or write protected memory
。
我能够在 C++ 的本机 COM 中成功执行该方法,因此我知道我正在使用的 .tlb 文件没有错误。
我目前的猜测是
GetLibStatistics
并未在 C# 中实现。请指教。
public static void GetLibStatisticsTest()
{
CoInitialize(IntPtr.Zero);
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;
// This always throws `System.AccessViolationException`. Why??
tlb2.GetLibStatistics(pcUniqueNames, out pcchUniqueNames);;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
CoUninitialize();
}
}
[DllImport("ole32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int CoInitialize(IntPtr pvReserved);
[DllImport("oleaut32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int LoadTypeLibEx(string szFile, RegKind regKind, out ITypeLib pptlib);
[DllImport("ole32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern void CoUninitialize();
ITypeLib2::GetLibStatistics 方法在 C/C++ 中定义如下:
HRESULT GetLibStatistics(
[out] ULONG *pcUniqueNames,
[out] ULONG *pcchUniqueNames
);
文档规定了这一点:
[出] pcUniqueNames
唯一名称的计数。如果呼叫者不需要此信息, 设置为 NULL。
这就是 .NET 如此定义它的原因:
void GetLibStatistics(IntPtr pcUniqueNames, out int pcchUniqueNames);
所以第一个参数是一个指向32位无符号整数的指针。但显然,实际实现(在 OleAut32.dll 中)不喜欢空(IntPtr.Zero)指针,因此必须分配给某些东西,如下所示:
public static void GetLibStatisticsTest()
{
LoadTypeLibEx(@"d:\temp\idl\machin.tlb", RegKind.None, out ITypeLib tlb);
var tlb2 = tlb as ITypeLib2;
if (tlb2 == null) return;
var pcUniqueNames = Marshal.AllocCoTaskMem(Marshal.SizeOf<int>());
try
{
tlb2.GetLibStatistics(pcUniqueNames, out int pcchUniqueNames); ;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Marshal.FreeCoTaskMem(pcUniqueNames);
}
}
PS:你不需要 CoInitialize,.NET 会为你调用它,并且它不应该由方法调用,而应该在线程(任何使用 COM 的线程)初始化时调用。