从本机结构复制字符串的说明

本文关键字:说明 字符串 复制 本机 结构 | 更新日期: 2023-09-27 18:19:53

我使用PInvoke的东西是为了使用C++中的SetupAPI函数。我用它来获取符合HID规范的USB设备的路径。我已经做好了一切工作,但我不明白的事情让我感到困惑。从SetupAPI:使用此结构

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
    DWORD cbSize;
    TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;

我没有得到与我正在使用的示例代码相同的结果。首先,我使用IntPtr,并使用Marshal.AllocHGlobal()分配内存来来回传递此信息。我调用SetupDiGetDeviceInterfaceDetail()两次,第一次是为了获得我需要的缓冲区的大小,第二次是为了实际获得我感兴趣的数据。我想获得这个设备的Path,它存储在这个结构中。

我要离开的代码是这样的:

IntPtr pDevPath = new IntPtr(pDevInfoDetail.ToInt32() + 4);
string path = Marshal.PtrToStringAuto(pDevPath);

这很好用。我这样做了,我得到的绳子是胡言乱语。我不得不把它改成

IntPtr pDevPath = new IntPtr(pDevInfoDetail.ToInt32() + 4);
string path = Marshal.PtrToStringAnsi(pDevPath);

让它发挥作用。为什么会这样?我是不是错过了项目/解决方案的一些设置,这些设置告诉这个野兽如何处理字符串和字符?到目前为止,MSDN上关于PtrToStringAuto()的文章并没有告诉我太多关于它的信息。事实上,看起来这个方法应该做出适当的决定,根据我的需要称为Unicode或Ansi版本,一切都会好起来的。

请解释。

从本机结构复制字符串的说明

首先,+10000使用真正的p/Invoke互操作类型,而不是手动编组数据。但既然你问了,下面是你的琴弦发生了什么。

运行时根据应用于互操作声明的属性、使用互操作的上下文、调用的方法等,根据具体情况决定如何处理字符串和字符。每种类型的p/Invoke声明(外部方法、委托或结构)都允许您为该定义的范围指定默认字符大小。有三种选择:

  • 使用CharSet.Ansi,它将托管的Unicode字符串转换为8位字符
  • 使用CharSet.Unicode,它将字符串数据作为16位字符传递
  • 使用CharSet.Auto,它在运行时根据主机操作系统决定使用哪一个操作系统

总的来说,我讨厌CharSet.Auto,因为它大多毫无意义。由于该框架甚至不支持Windows95,唯一一次"自动"并不意味着"Unicode"是在Windows98上运行时。但这里还有一个更大的问题,那就是运行时关于如何封送字符串的决定发生在"错误的时间"。

您正在调用的非托管代码在编译时做出了该决定,因为编译器必须决定TCHAR是指char还是wchar——该决定是基于_UNICODE预处理器宏的存在。这意味着,对于大多数库来说,它总是会使用其中一个,让CLR"选择一个"是没有意义的。

对于Windows系统组件来说,情况要好一点,因为支持Unicode的构建实际上包括大多数系统功能的两个版本。例如,设置API有两种方法:SetupDiGetDeviceInterfaceDetailASetupDiGetDeviceInterfaceDetailW。*A版本使用8位"ANSI"字符串,*W版本使用16位宽的"Unicode"字符串。它类似地具有ANSI和Wide版本的任何具有字符串的结构。

这就是CharSet.Auto发光的情况,假设你使用得当。将DllImport应用于函数时,可以指定字符集。如果您为字符集指定了Ansi,如果运行时找不到与您的函数名完全匹配的函数,它会附加A并重试。(奇怪的是,如果指定Unicode,它将首先调用*W函数,并且只有在失败时才尝试完全匹配。)

问题是:如果您没有在DllImport上指定字符集,则默认值为CharSet.Ansi。这意味着您将获得该函数的ANSI版本,除非您特别重写字符集。这很可能是这里发生的情况:默认情况下,您调用的是SetupDiGetDeviceInterfaceDetail的ANSI版本,因此会返回ANSI字符串,但PtrToStringAuto希望使用Unicode,因为您可能至少运行Windows XP。

假设我们可以忽略Windows 98,最佳选项是在所有位置指定CharSet.Unicode,因为SetupAPI支持它,但至少,您需要在所有位置都指定相同的CharSet值。