struct的指针作为c ++ dll函数的返回值

时间:2016-04-05 07:06:44

标签: c# c++ dll struct pinvoke

我已经好几天了,我已经阅读了很多问题,这些问题帮助我到达了我现在的位置。但我仍然需要一些帮助。

我会解释。我有一个C ++ DLL,我想包装在c#中使用它。我有DLL的文档,但我无法改变它的任何内容。很多函数都适用于基本的dllimport设置,但我有一些功能不能正常工作,这是其中之一:

DLL documentation
struct stChannel LookForAvailableChannels (const char *dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime)

我也有这些结构:

struct stChannelInfo
{
 char ChannelTag[17];
 char ChannelEnabled;
}

struct stChannel
{
 int ChannelNumber;
 struct stChannelInfo *ChannelInfo;
}

所以尝试不同的东西,经过大量阅读后,我已经找到了一个解决方案,而且#34; partialy"工作原理:

C# Code
    [StructLayout(LayoutKind.Sequential)]
    public struct stChannelInfo
    {
        public IntPtr ChannelTag;
        public byte ChannelEnabled;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct stChannel {
        public int ChannelNumber;      
        public stChannelInfo ChannelInfo;
    };

[DllImport("NG.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern stChannel LookForAvailableChannels(string dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime);

stChannel Estructura = new stChannel();

我有一个按钮调用此代码:

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal);

然后我编组Estructura.ChannelInfo.ChannelTag:

string btListFile = Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag);

这实际上有效,它返回我知道它正确的数据。但是我只接收数组的第一个元素,因为stChannel中的stChannelInfo结构是一个指针,我不知道如何在c#中处理它。

应该以我现在使用的代码的方式完成:

Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag);

应该是

Marshal.PtrToStringAnsi(Estructura.ChannelInfo[i].ChannelTag);

但我现在使用的所有东西都不起作用。我将不胜感激任何帮助。

谢谢。

编辑:

感谢用户Adriano Repetti现在我有了这个:

C#代码         [StructLayout(LayoutKind.Sequential)]         public struct stChannelInfo         {             [MarshalAs(UnmanagedType.LPStr,SizeConst = 17)]             公共字符串ChannelTag;             public byte ChannelEnabled;         };

    [StructLayout(LayoutKind.Sequential)]
    public struct stChannel {
        public int ChannelNumber;
        public IntPtr ChannelInfo;
    };

[DllImport("NG.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern stChannel LookForAvailableChannels(string dataBaseFolder, int serialNumber, double firstLogTime, double lastLogTime);

stChannel Estructura = new stChannel();

我有一个按钮调用此代码:

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal);

var channelinf = (stChannelInfo)Marshal.PtrToStructure(Estructura.ChannelInfo, typeof(stChannelInfo));


        for (int i = 0; i < 4; i++)
        {
            var ptr = IntPtr.Add(Estructura.ChannelInfo, Marshal.SizeOf(typeof(stChannelInfo)) * i);
            var channelll =  (stChannelInfo)Marshal.PtrToStructure(ptr, typeof(stChannelInfo));
        }

现在的问题是我在这里得到了一个AccessViolationException:

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal);

但我不知道为什么,我会感激任何帮助。

1 个答案:

答案 0 :(得分:0)

不幸的是,您的数组ChannelInfo没有固定大小,然后自动封送[MarshalAs(UnamangedType.LPArray)]无法正常工作。手动编组意味着必须将ChannelInfo声明为IntPtr

[StructLayout(LayoutKind.Sequential)]
public struct stChannel {
    public int ChannelNumber;      
    public IntPtr ChannelInfo;
};

当您需要它时,您需要转换为结构:

var channelInfo = (stChannelInfo)Marshal.PtrToStructure(
    Estructura.ChannelInfo,
    typeof(stChannelInfo));

现在您正在访问第一个元素,以访问您需要偏移的数组项:

var ptr = IntPtr.Add(Estructura.ChannelInfo,
    Marshal.SizeOf(typeof(stChannelInfo)) * itemIndex);

然后使用Marshal.PtrToStructure()。您可能想要编写辅助方法:

static GetUnmanagedArrayItem<T>(IntPtr baseAddress, int index) {
    var ptr = IntPtr.Add(baseAddress, Marshal.SizeOf(typeof(T)) * index);
    return (T)Marshal.PtrToStructure(ptr, typeof(T));
}

像这样使用:

var channelInfo = GetUnamangedArrayItem<stChannelInfo>(Estructura.ChannelInfo, 1);

您没有明确要求,但请注意,您无法手动使用char*封送无法管理的 IntPtr字符串固定长度的char数组

<德尔> 您可以将其声明为使用`UnmanagedType.LPStr`装饰该字段的字符串:     [MarshalAs(UnmanagedType.LPStr,SizeConst = 17)]     公共字符串ChannelTag;

编辑:我错了,String无法用于返回值,因为它不是blittable,当然IntPtr 1}}是错误的,因为你有一个固定长度的数组,我建议使用:

[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 17)]
public byte[] ChannelTag;

您可以使用Encoding.ASCII.GetString(yourStruct.ChannelTag);简单地解码此数组的原始字符串。作为替代方案,您可以遵循JaredPar在this post中建议的内容。