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

标签 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;
}

所以尝试了不同的东西,在阅读了很多之后,我找到了一个“部分”有效的解决方案:

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)结构 stChannelInfo { [MarshalAs(UnmanagedType.LPStr, SizeConst = 17)] 公共(public)字符串 ChannelTag; 公共(public)字节 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);

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

最佳答案

不幸的是,您的数组 ChannelInfo 没有固定大小,因此使用 [MarshalAs(UnamangedType.LPArray)] 进行的自动编码(marshal)处理不起作用。手动进行编码意味着 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);

您没有明确询问,但请注意您不能手动编码unamanged char* string with IntPtr 固定长度的字符数组

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

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

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

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

关于c# - 结构指针作为c++ dll函数的返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36419396/

相关文章:

c# - 通过 Windows 服务将 pdf 文件发送到打印机

c# - 需要合并 Dll 并创建单个程序集

c# - 为 Windows Phone 8 枢轴应用程序设计 ViewModel 很热门

c# - 如何使自定义标题栏上的无边框表单可拖动?

c# - 如何在不关闭套接字的情况下多次调用套接字发送和接收?

c++ - TCP 和 UDP 干扰

c++ - Visual C++ 数组大小崩溃

c++ - 通过复制传递 std::string 会被优化吗?

c++ - 将 DLL(和关联的 VBA)转换为 XLL

c++ - 使用 C++ 在 DLL 中进行子类化