代码之家  ›  专栏  ›  技术社区  ›  PanJanek

如何调用非托管C++函数来分配输出缓冲区以返回C中的数据?

  •  3
  • PanJanek  · 技术社区  · 15 年前

    对于C返回函数数组的输出参数,我有问题。

    这里是C++声明:

    #define DLL_API __declspec(dllexport)
    
    typedef TPARAMETER_DATA
    {
        char        *parameter;
        int     size;
    } PARAMETER_DATA;
    
    int DLL_API GetParameters(PARAMETER_DATA *outputData);
    

    函数为char数组分配内存,将数据放在那里,并返回“size”字段中分配的字节数。 这是我的C声明:

    [StructLayout(LayoutKind.Sequential)]
    public struct PARAMETER_DATA
    {        
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 50000)]        
        public byte[] data;   // tried also SizeParamIndex = 1 instead of SizeConst
    
        [MarshalAs(UnmanagedType.I4)]                      
        public int size;
    }
    
    [DllImport("thedll.dll", SetLastError = true, ExactSpelling = true)]
    public extern static uint GetParameters(ref PARAMETER_DATA outputData); // tried also  'out' parameter
    

    在C中调用函数时,我得到空结构(大小=0,空数组)。我尝试传递数据feld初始化为新字节[50000]的outputData参数,但仍然没有返回任何数据。

    这个dll中的其他每个函数(有些具有复杂的输入结构)工作正常,但这是唯一分配内存以返回数据的函数。 我尝试了许多其他C封送声明(使用lparray、lpstring),但没有成功——返回总是空的数据结构或抛出内存访问异常。 我错过了简单的东西吗?

    编辑:

    我不能改变C++代码——它是外部库。

    4 回复  |  直到 11 年前
        1
  •  1
  •   Ondergetekende    15 年前

    您面临的问题是返回了一个指针——实际上不是字符串或数组。封送拆收器无法将指针转换为数组或字符串,因为长度未知。

    解决方案可能是在C中进行指针处理。您还应该弄清楚您是否负责释放指针,或者库是否会为您这样做。

    [StructLayout(LayoutKind.Sequential)]
    public struct PARAMETER_DATA
    {        
        public IntPtr data;   // tried also SizeParamIndex = 1 instead of SizeConst
    
        [MarshalAs(UnmanagedType.I4)]                      
        public int size;
    }
    
    [DllImport("thedll.dll", SetLastError = true, ExactSpelling = true)]
    private extern static uint GetParameters(ref PARAMETER_DATA outputData);
    
    public static uint GetParameters(out String result)
    {
        PARAMETER_DATA outputData = new PARAMETER_DATA();
        result= Marshal.PtrToStringAnsi(outputData.data, outputData.size );
        Marshal.FreeHGlobal(outputData.data); // not sure about this
    }
    
        2
  •  0
  •   John Knoeller    15 年前

    C结构的封送声明与C++结构不匹配,它们对应于C++结构。

    typedef TPARAMETER_DATA
    {
        char    parameter[50000];
        int     size;
    } PARAMETER_DATA;
    

    您可能可以使用一些特殊封送处理代码,但我认为更简单的方法是更改C++侧的分配方式。

    我确信还有其他方法可以做到这一点,但我要做的一个方法是使用安全阵列。SafeArray是标准COM API的一部分,最初是为与VB的互操作而创建的(我认为)。 http://msdn.microsoft.com/en-us/library/ms221145.aspx

    safearray知道它的大小和数据类型,所以封送拆收器很容易处理。我必须被锁定之前,你可以写进它在C++中,然后解锁之前尝试和封送它。

    所以你的新参数结构是这样的(我认为不需要大小,safearray已经知道了)

    typedef TPARAMETER_DATA
    {
        SAFEARRAY * parameter;
        int         size; // I think this is redundant.
    }
    

    在C++代码中,使用数组来分配数组。

    SAFEARRAY * psa = SafeArrayCreateVector(VT_UI1, 0, 50000);
    if ( ! psa)
        return E_OUTOFMEMORY;
    
    HRESULT hr = SafeArrayLock(psa);
    if (FAILED(hr))
    {
        SafeArrayDestroy(psa);
        return hr;
    }
    
    CopyMemory(psa->pvData, mydataptr, 50000);
    SafeArrayUnlock(psa);
    
    PARAMETER_DATA pda = {psa, 50000};
    

    那么结构的C声明是

    [StructLayout(LayoutKind.Sequential)]
    public struct PARAMETER_DATA
    {        
        [MarshalAs(UnmanagedType.SafeArray)]        
        public byte[] data; // I used System.Array here, but I think byte[] is OK
    
        [MarshalAs(UnmanagedType.I4)]                      
        public int size;
    }
    
        3
  •  0
  •   John Knoeller    15 年前

    这是你试过的吗?lparray和sizeParamIndex=1

    [StructLayout (LayoutKind.Sequential)]
    public struct PARAMETER_DATA
    {
      [MarshalAs (UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 1)]
      public byte [] data; 
    
      [MarshalAs (UnmanagedType.I4)]
      public int size;
    }
    
        4
  •  0
  •   DS.    11 年前
    为了供您参考,我在我的项目中使用了layoutKind.Explicit和FieldOffset,并忽略了arraysubtype。
    
    [结构布局(layoutkind.explicit,charset=charset.ansi,size=50004)]
    公共结构参数_data
    {
    [字段偏移量(0)]
    [MarshalAs(UnmanagedType.ByValArray,SizeConst=50000)]
    公共字节[]数据;
    
    [场偏移(50000)]
    [封送处理(UnmanagedType.i4)]
    公共int大小;
    }