在工作中,我们有一个本机 C 代码,负责读取和写入专有的平面文件数据库。我有一个用 C# 编写的包装器,它将 P/Invoke 调用封装到一个 OO 模型中。自项目启动以来,P/Invoke 调用的托管包装器的复杂性显着增加。有趣的是,当前的包装器做得很好,但是,我认为我实际上需要做更多的事情来确保正确的操作。
答案带来了一些注意事项:
- 可能不需要 KeepAlive
- 可能不需要 GCHandle 固定
- 如果您确实使用 GCHandle,请尝试...最后是该业务(尽管 CER 问题未解决)
以下是修改后的代码示例:
[DllImport(@"somedll", EntryPoint="ADD", CharSet=CharSet.Ansi,
ThrowOnUnmappableChar=true, BestFitMapping=false,
SetLastError=false)]
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
internal static extern void ADD(
[In] ref Int32 id,
[In] [MarshalAs(UnmanagedType.LPStr)] string key,
[In] byte[] data, // formerly IntPtr
[In] [MarshalAs(UnmanagedType.LPArray, SizeConst=10)] Int32[] details,
[In] [MarshalAs(UnmanagedType.LPArray, SizeConst=2)] Int32[] status);
public void Add(FileId file, string key, TypedBuffer buffer)
{
// ...Arguments get checked
int[] status = new int[2] { 0, 0 };
int[] details = new int[10];
// ...Make the details array
lock (OPERATION_LOCK)
{
ADD(file.Id, key, buffer.GetBytes(), details, status);
// the byte[], details, and status should be auto
// pinned/keepalive'd
if ((status[0] != 0) || (status[1] != 0))
throw new OurDatabaseException(file, key, status);
// we no longer KeepAlive the data because it should be auto
// pinned we DO however KeepAlive our 'file' object since
// we're passing it the Id property which will not preserve
// a reference to 'file' the exception getting thrown
// kinda preserves it, but being explicit won't hurt us
GC.KeepAlive(file);
}
}
我的(修改后的)问题是:
- 数据、详细信息和状态是否会自动固定/保持活动状态?
- 我是否错过了正常运行所需的其他任何内容?
编辑:我最近发现了一个图表,它激发了我的好奇心。它基本上表明,一旦您调用 P/Invoke 方法,GC 就可以抢占您的本机代码。因此,虽然本地调用可能是同步进行的,但 GC可以选择运行和移动/删除我的内存。我想现在我想知道自动固定是否足够(或者它是否可以运行)。