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



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

我会解释的。 我有一个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;
}

因此,尝试不同的东西,并且在阅读了很多之后,我得出了一个"部分"有效的解决方案:

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);

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

谢谢。

编辑:

感谢用户阿德里亚诺·雷佩蒂,现在我有了这个:

C# 代码 [StructLayout(LayoutKind.Sequential(] public struct stChannelInfo { [MarshalAs(UnmanagedType.LPStr, SizeConst = 17(] 公共字符串通道标签; 公共字节通道启用; };

    [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));
        }

现在的问题是我在这里得到一个访问违规异常:

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

但我真的不知道为什么,我将不胜感激任何帮助。

不幸的是,

您的数组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

您可以将其声明为使用"UnmanagedType.LPStr"修饰该字段的字符串: [MarshalAs(UnmanagedType.LPStr, SizeConst=17(] 公共字符串通道标签;

编辑:我错了,String不能在返回值中使用,因为它不是可 blitable,当然IntPtr是错误的,因为你有一个固定长度的数组,我建议使用:

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

您只需使用 Encoding.ASCII.GetString(yourStruct.ChannelTag); 解码此数组即可获得原始字符串。作为替代方案,您可以遵循JaredPar在这篇文章中的建议。

最新更新