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

C# 作为c+;返回值的结构指针+;dll函数,c#,c++,dll,struct,pinvoke,C#,C++,Dll,Struct,Pinvoke,这件事我已经谈了好几天了,我读了很多问题,这些问题帮助我达到了现在的水平。但我仍然需要一些帮助 我会解释的。我有一个C++ DLL,我想在C语言中用它包起来。我有DLL的文档,但我不能更改任何内容。许多函数与基本dllimport设置一起工作,但我有一些函数不能正常工作,以下是其中之一: DLL documentation struct stChannel LookForAvailableChannels (const char *dataBaseFolder, int serialNumber

这件事我已经谈了好几天了,我读了很多问题,这些问题帮助我达到了现在的水平。但我仍然需要一些帮助

我会解释的。我有一个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;
}
因此,我尝试了不同的方法,在阅读了很多之后,我找到了一个“部分”有效的解决方案:

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

Estructura = LookForAvailableChannels("C:\\Folder", 12345678, FechaInicio, FechaFinal);
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));
        }
然后我封送Esstructure a.ChannelInfo.ChannelTag:

string btListFile = Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag);
这实际上是可行的,它返回数据,我知道它是正确的。但我只接收数组的第一个元素,因为stChannel中的stChannelInfo struct是一个指针,我不知道如何在c#中处理它

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

Marshal.PtrToStringAnsi(Estructura.ChannelInfo.ChannelTag);
应该是

Marshal.PtrToStringAnsi(Estructura.ChannelInfo[i].ChannelTag);
但我现在用的东西都不管用。我将感谢任何帮助

多谢各位

编辑:

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

C#代码 [StructLayout(LayoutKind.Sequential)] 公共结构stChannelInfo { [Marshallas(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);
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));
        }

但是我真的不知道为什么,我非常感谢您的帮助。

不幸的是,您的数组
ChannelInfo
没有固定大小,然后使用
[MarshalAs(unangedtype.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));
}

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

不幸的是,您的数组
ChannelInfo
没有固定大小,然后使用
[MarshalAs(unangedtype.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));
}

您只需使用
Encoding.ASCII.GetString(yourStruct.ChannelTag)对该数组进行解码即可获得原始字符串。作为替代方案,您可以按照JaredPar在中提出的建议进行操作。

谢谢您的快速回答,这确实有助于我了解正在发生的事情。一切正常,但这部分“Marshal.SizeOf(typeof(T)*index))”没有编译。此运算符不能应用于这两种类型,我不知道如何解决它。如果您执行手动封送,则它也应该可以工作。有点烦人,但有一些辅助功能,这可能是合理的。我的建议是围绕它们编写一个完整的托管包装。编辑了我以前的评论:DMissing括号,已修复。与其发布新代码,不如让新问题只是一个新问题。期望有人也调试你所有的后续bug是不现实的。谢谢你的快速回答,它真的帮助我理解发生了什么。一切正常,但这部分“Marshal.SizeOf(typeof(T)*index))”没有编译。此运算符不能应用于这两种类型,我不知道如何解决它。如果您执行手动封送,则它也应该可以工作。有点烦人,但有一些辅助功能,这可能是合理的。我的建议是围绕它们编写一个完整的托管包装。编辑了我以前的评论:DMissing括号,已修复。与其发布新代码,不如让新问题只是一个新问题。期望有人也调试您所有的后续bug是不现实的。