c# - 来自 C# 的 native 调用尝试读取无效内存

标签 c# c marshalling

我正在从托管代码调用 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 将始终输出一个值。如果 inputBufferPtrNULL,则 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/

相关文章:

c - 我无法为更大的矩阵分配内存,然后〜200x200 Lapack Visual Studio in C 这是我的代码

java - JAX-RS 消耗资源对象

json - 结构中的 json 标记真的需要编码吗?

C# mono - 传递 stringbuilder 以填充 C++ 中的 unicode 内容

c# - 同名 'upload'的属性路由必须有相同的模板

c# - 如何制作xna排行榜?

c# - HttpClient 不使用 ServicePointManager 服务点

c# - 如何返回一个带有我事先不知道的通用参数的函数?

c - 如何重新排列二维数组

c - 套接字编程中的段错误