C# 来自C的本机调用尝试读取无效内存
我正在从托管代码中调用本机代码,并且在弄清楚如何正确封送我的代码时遇到了一些麻烦。在C中,我有以下内容:C# 来自C的本机调用尝试读取无效内存,c#,c,marshalling,C#,C,Marshalling,我正在从托管代码中调用本机代码,并且在弄清楚如何正确封送我的代码时遇到了一些麻烦。在C中,我有以下内容: struct cHandle { unsigned long handleLo; unsigned long handleHi; } struct cBuffer { unsigned long bufferSize; unsigned long bufferType; __field_bcount(bufferSize) void *buffe
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,而本机代码试图写入未为此目的分配的内存。我也无法将其作为byvalue数组封送。相反,我的结构如下所示:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BufferDesc
{
public uint Version;
public uint Count;
public IntPtr Buffers;
}
我锁定一个大小为1的空SecurityBuffer数组,并将地址提供给缓冲区。是否有可能尝试在64位操作系统上使用32位dll,并将visual studio中的目标平台设置为任何CPU?如果是这样,intptr的大小将是8,而不是4,并且将失败。但是您应该会遇到一个错误,即无法在64位进程中加载32位dll。不,所讨论的dll是windows dll,因此它们的32位和64位名称相同,但我可以使用以前的本机调用调用相同的dll,所以我一定是编错了什么。事实上,我确信这一定是缓冲区或BufferDesc结构有问题,因为我以前正确地使用了句柄,自动正确地字符串marhsal,我看不出uint或IntPtr是如何导致问题的。