我已经有几天了,我读了很多问题,这些问题帮助我到达了现在的位置。 但我仍然需要一些帮助。
我会解释的。 我有一个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
编辑:我错了,String
不能在返回值中使用,因为它不是可 blitable,当然IntPtr
是错误的,因为你有一个固定长度的数组,我建议使用:
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 17)]
public byte[] ChannelTag;
您只需使用 Encoding.ASCII.GetString(yourStruct.ChannelTag);
解码此数组即可获得原始字符串。作为替代方案,您可以遵循JaredPar在这篇文章中的建议。