我正在从托管代码调用 native 代码,但在弄清楚如何正确编码我的代码时遇到了一些麻烦。在 C 中,我有以下内容:
struct cHandle {
unsigned long handleLo;
unsigned long handleHi;
}
struct cBuffer {
unsigned long bufferSize;
unsigned long bufferType;
__field_bcount(bufferSize) void *bufferPtr;
}
struct cBufferDesc {
unsigned long bufferVersion;
unsigned long bufferCount;
_field_ecount(bufferCount) cBuffer *buffers;
}
uint __stdcall CMethod(
__in_opt cHandle* handle1,
__in_opt cHandle* handle2,
__in_opt wchar_t* wstr,
__in unsigned long long1,
__in unsigned long resevered1, // Reserved, always 0
__in unsigned long long2,
__in_opt cBufferDesc* inputBufferPtr,
__in unsigned long reserved2, // Reserved, always 0
__inout_opt cHandle* outHandle,
__inout_opt cBufferDesc* outputBufferPtr,
__out unsigned long * outLong,
__out_opt TimeStampStruct* timeStamp);
CMethod
的行为如下。 outputBufferPtr
将始终输出一个值。如果 inputBufferPtr
为 NULL
,则 handle2
也应为 null,并且 CMethod
应遵循不同的逻辑来提供初始输出缓冲区,如果不是,CMethod
应根据输入缓冲区中的数据计算输出缓冲区。我在第一次接到工作电话时遇到了麻烦。此外,我不关心时间戳,因此我不会详细说明该结构,也不会制作 C# 等效结构。我在 C# 中尝试了以下编码:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Handle {
private IntPtr HandleLo;
private IntPtr HandleHi;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Buffer {
public uint Size; // Possibly unknown
public uint Type; // Always set.
public IntPtr Buffer; // Possibly unknown
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BufferDesc {
public uint Count; // Always 1 for my purposes
public uint Version; // Always set
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public Buffer[] BufferArray; // Will always be a size 1 array.
}
// Used for calling when we have an existing input buffer
[DllImport("mylib.dll", ExactSpelling = "true", CharSet = CharSet.Unicode, SetLastError = true)]
uint CMethod(
[In] ref Handle handle1,
[In] ref Handle handle2,
[In] IntPtr wstr,
[In] uint long1, // C# uint == C ulong
[In] uint reserved1,
[In] uint long2,
[In] ref BufferDesc inputBufferPtr,
[In] uint reserved2,
[In, Out] ref Handle outHandle,
[In, Out] ref BufferDesc outputBufferPtr,
[Out] out IntPtr outLong,
[Out] out IntPtr timestamp);
// Used for calling when we do not have an existing input buffer
// Here IntPtr.Zero will be passed in for handle2 and inputBufferPtr
[DllImport("mylib.dll", ExactSpelling = "true", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint CMethod(
[In] ref Handle handle1,
[In] IntPtr handle2,
[In] IntPtr wstr,
[In] uint long1, // C# uint == C ulong
[In] uint reserved1,
[In] uint long2,
[In] IntPtr inputBufferPtr,
[In] uint reserved2,
[In, Out] ref Handle outHandle,
[In, Out] ref BufferDesc outputBufferPtr,
[Out] out IntPtr outLong,
[Out] out IntPtr timestamp);
public static void WrapperMethod(
ref Handle handle1,
ref Handle handle2,
string wstr,
byte[] inputBuffer,
ref Handle outHandle,
out byte[] outputBuffer)
{
BufferDesc inputBufferDesc;
BufferDesc outputBufferDesc;
outputBufferDesc.Count = 1;
outputBufferDesc.Version = 0; // Real data not shown
outputBufferDesc.BufferArray = new Buffer[0];
outputBufferDesc.BufferArray[0].Count = 0;
outputBufferDesc.BufferArray[0].Type = 2; // Real data not shown
outputBufferDesc.BufferArray[0].Buffer = IntPtr.Zero;
IntPtr wstrPtr = Marshal.StringToCoTaskMemUni(wstr);
IntPtr ignoredOutLong;
IntPtr ignoredTimestamp;
if (null != inputBuffer)
{
inputBufferDesc.Count = 1;
inputBufferDesc.Version = 0; // Real data not shown
inputBufferDesc.BufferArray = new Buffer[1];
inputBufferDesc.BufferArray[0].Size = inputBuffer.Length;
inputBufferDesc.BufferArray[0].Type = 2; // Real data not shown
inputBufferDesc.BufferArray[0].Buffer = GCHandle.Alloc(inputBuffer, GCHandleType.Pinned).AddrOfPinnedObject();
CMethod(
ref handle1,
ref handle2,
wstrPtr,
0, // Real data not shown
0,
0, // Real data not shown
ref inputBufferDesc,
0,
ref outHandle,
ref outputBufferDesc,
out ignoreOutLong,
out ignoreTimestamp);
}
else
{ ///////////////////////////////////////////////////////////////////////
// This is the call I am taking and also where the code is crashing. //
CMethod( //
ref handle1, //
IntPtr.Zero, //
wstrPtr, //
0, // Real data not shown //
0, //
0, // Real data not shown //
IntPtr.Zero, //
0, //
ref outHandle, //
ref outputBufferDesc, //
out ignoreOutLong, //
out ignoreTimestamp); //
///////////////////////////////////////////////////////////////////////
}
// Do Cleanup. Not reached at this point.
}
我收到的错误是我正在尝试访问受读或写保护的内存。如果您可以看到任何明显与我的编码方式错误的内容,或者如果我固定错误,或者只是没有固定我应该固定的位置,或者如果您看到任何其他问题,请告诉我。
最佳答案
问题出在我的输出缓冲区上。我没有将大小为 1 的空数组分配给 outputBufferDesc.Buffers,并且 native 代码尝试写入未为此目的分配的内存。我也无法将其编码为按值数组。相反,我的结构如下所示:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BufferDesc
{
public uint Version;
public uint Count;
public IntPtr Buffers;
}
我固定了一个大小为 1 的空 SecurityBuffer
数组,并将地址提供给 Buffers
。
关于c# - 来自 C# 的 native 调用尝试读取无效内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26770432/