C# 如果结构中的字符串比所使用的p/Invoked签名长或短,会发生什么情况?
举个例子,我想使用from C#。但是,我不确定如何声明该结构,因为C# 如果结构中的字符串比所使用的p/Invoked签名长或短,会发生什么情况?,c#,c++,c,.net,pinvoke,C#,C++,C,.net,Pinvoke,举个例子,我想使用from C#。但是,我不确定如何声明该结构,因为dbcc\u name的大小取决于dbcc\u size(正式声明为char dbcc\u name[1]) 据我所知,似乎我需要补充 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)] 超过dbcc\u name 但是为什么要使用SizeConst=255?我们不知道尺寸。(从我看到的其他答案来看,这似乎是一种逐案指定大小的方法。) 那么,如果我在链接答案中设置一个静态长度
dbcc\u name
的大小取决于dbcc\u size
(正式声明为char dbcc\u name[1]
)
据我所知,似乎我需要补充
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
超过dbcc\u name
但是为什么要使用SizeConst=255
?我们不知道尺寸。(从我看到的其他答案来看,这似乎是一种逐案指定大小的方法。)
那么,如果我在链接答案中设置一个静态长度,会发生什么呢。如果字符串较短或较长,会发生什么情况
测试表明,如果长一点,我会得到正确的字符串,如果短一点,我会得到一个截断的字符串(例如,如果我将SizeConst设置为2,而真正的字符串是“abc”,我会得到“ab”)。但是我能确定它是如何工作的吗,或者它依赖于某种在这种特定情况下恰好是好的东西吗?你不能完全声明结构,你能做的是这样的:
[StructLayout(LayoutKind.Sequential)]
private struct _DEV_BROADCAST_DEVICEINTERFACE_A
{
public int dbcc_size;
public uint dbcc_devicetype;
public uint dbcc_reserved;
public Guid dbcc_classguid;
public char dbcc_name; // just for offset; don't use!
}
// get ptr to structure from somewhere (lParam from WM_DEVICECHANGE ...)
IntPtr ptr = ...
// read structure
var iface = Marshal.PtrToStructure<_DEV_BROADCAST_DEVICEINTERFACE_A>(ptr);
// get name pointer
var namePtr = ptr + Marshal.OffsetOf<_DEV_BROADCAST_DEVICEINTERFACE_A>(nameof(_DEV_BROADCAST_DEVICEINTERFACE_A.dbcc_name)).ToInt32();
// get name
var name = Marshal.PtrToStringAnsi(namePtr);
然后像这样使用它:
[StructLayout(LayoutKind.Sequential)]
private struct _DEV_BROADCAST_DEVICEINTERFACE_A
{
public int dbcc_size;
public uint dbcc_devicetype;
public uint dbcc_reserved;
public Guid dbcc_classguid;
public char dbcc_name; // just for offset; don't use!
}
// get ptr to structure from somewhere (lParam from WM_DEVICECHANGE ...)
IntPtr ptr = ...
// read structure
var iface = Marshal.PtrToStructure<_DEV_BROADCAST_DEVICEINTERFACE_A>(ptr);
// get name pointer
var namePtr = ptr + Marshal.OffsetOf<_DEV_BROADCAST_DEVICEINTERFACE_A>(nameof(_DEV_BROADCAST_DEVICEINTERFACE_A.dbcc_name)).ToInt32();
// get name
var name = Marshal.PtrToStringAnsi(namePtr);
//从某处获取ptr到结构(来自WM_DEVICECHANGE的LPRAM…)
IntPtr ptr=。。。
//读结构
var iface=Marshal.ptr结构(ptr);
//获取名称指针
var namePtr=ptr+Marshal.OffsetOf(nameof(_DEV_BROADCAST_DEVICEINTERFACE_A.dbcc_name)).ToInt32();
//得名
var name=Marshal.PtrToStringAnsi(namePtr);
注意:如果名称可以包含零,则应使用
Marshal.PtrToStringAnsi(namePtr,len)
和len=dbcc_size-dbcc_name的偏移量
即使使用较少的内存,也需要保留最大内存大小。c#中有一个属性,即结构的分配大小,如果超过该大小,则在运行代码时会出现异常。如果不添加行,则不会出现编译器错误。@jdweng请参阅我的编辑(-问题的最后一段)-似乎设置一个较大的大小就可以了。但我不知道它是否总是能正常工作。这取决于一个字符是一个字节还是两个字节。如果是两个字节,则需要511而不是255。看起来是一个c#双字节字符,所以需要511。见平沃克:@jdweng见我的另一个问题dbcc_name
既不是字符,也不是指针。它只是字符串的第一个字符。它是一个窗口结构,必须满足windows的要求。谢谢。我会调查的。(我更新了我的问题,就在你发布答案的时候。请看我问题的最后一段-似乎我可以简单地设置一个高的数字,而且效果很好。但我不知道它是否总是很好。)你似乎在考虑dbcc\u name
一个指针。从文档(在我的问题中链接)和它似乎不是。@ispiro-这不是我正在考虑的。如果您在_DEV_BROADCAST_DEVICEINTERFACE_a结构上有一个指针,那么您可以计算dbcc_name(=>namePtr)上的指针,因此我们计算指向字符串开头的指针,然后读取所有指针(直到一个空字符)。现在我明白了。谢谢你的毅力。我假设我们也可以使用另一个重载并使用dbcc_size
来计算要读取多少。再次感谢。我知道你的代码应该是有效的。但是现在尝试一下,我只得到了dbcc\u name
的第一个字符。我想我会试试另一种超负荷。。。