【问题标题】:Why do these DLLs have two apparently identical entry points?为什么这些 DLL 有两个明显相同的入口点?
【发布时间】:2018-10-04 09:52:59
【问题描述】:

今天,在编写一些 VB.NET 代码时,我必须访问两个外部 DLL 才能使用某些方法。我发现的帮助主题告诉我使用以下外部方法:

  • shlwapi.dll → PathIsNetworkPath (Reference 1)
  • mpr.dll → WNetAddConnection2 和 WNetCancelConnection2 (Reference 2)

但是,当我尝试从我的代码中调用这些方法时,我收到一条错误消息,指出入口点不存在。所以我做了一些研究,发现我的操作系统(Windows 7 企业版 32 位)中的 DLL 不完全包含这些方法,但是,我得到了:

  • PathIsNetworkPath → PathIsNetworkPathA / PathIsNetworkPathW
  • WNetAddConnection2 → WNetAddConnection2A / WNetAddConnection2W
  • WNetCancelConnection2 → WNetCancelConnection2A / WNetCancelConnection2W

所以,我测试了他们的行为: * 以“A”结尾的方法按预期工作。 * 以“W”结尾的方法不能按预期工作,它们会引发错误或返回错误结果(例如,当它应该为“真”时为“假”)。 然而,帮助主题中没有人提到有类似的问题。

所以我做了一些研究,在 MSDN documentation 我发现 DLL 只包含以“A”和“W”结尾的方法,并且在我使用的三种情况下,它们都包含,据我所知,文档页面是相同的。事实上,在整个页面中,他们不使用以 A/W 结尾但没有它的方法的名称。

所以我的问题是:*为什么我的 DLL 中有方法“A”和“W”而不是没有 A/W 的方法?他们俩有什么区别?为什么方法“A”对我有效,而方法“W”无效?

【问题讨论】:

  • pinvoke marshaller 已经知道这个细节。您在 [DllImport] 声明中使用的 CharSet 属性很重要,CharSet.Ansi(默认)选择 A 版本,其他选择 W。我们真正知道的是您的声明不正确,我们看不到。
  • 我之所以选择重新打开,是因为问题更多是关于 Visual Basic 方面的问题,而不仅仅是 WinAPI 函数的 A 和 W 变体之间的区别,@Damien。

标签: .net vb.net dll dllimport


【解决方案1】:

自 Windows NT 4 / 2000 / XP 起,WinAPI 函数有 ANSI (A) 和 Unicode (W) 变体。另见What is the difference between the `A` and `W` functions in the Win32 API?

使用 P/Invoke 时,C# 和 VB.NET 之间存在差异。请参阅Specifying a Character Set on MS Docs,特别是“字符串编组和名称匹配”和“在 Visual Basic 中指定字符集”:

当 DllImportAttribute.ExactSpelling 字段为 true(在 Visual Basic 2005 中默认为 true)时,平台调用仅搜索您指定的名称。例如,如果您指定 MessageBox,则平台调用会搜索 MessageBox,并在找不到确切拼写时失败。

鉴于 C# 和 VB.NET 的 DllImportAttribute.CharSet 字段的默认值为“ANSI”,Visual Basic 的规则确定运行时搜索 A 或 W 入口点,请参阅Docs: DllImportAttribute.ExactSpelling Field。我猜这是为了向后兼容 Visual Basic。

所以你有三个选择:

  1. 显式指定“W”入口点和CharSet.Unicode:

    <DllImport("shlwapi.dll", EntryPoint:="PathIsNetworkPathW", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Public Function PathIsNetworkPath(<MarshalAs(UnmanagedType.LPTStr)>pszPath As String) As <MarshalAs(UnmanagedType.Bool)>Boolean
    End Function
    
  2. 禁用精确拼写,导致运行时搜索“A”入口点(给定默认字符集,如果未指定,则为 ANSI):

    <DllImport("shlwapi.dll", EntryPoint:="PathIsNetworkPath", SetLastError:=True, ExactSpelling:=False)> _
    Public Function PathIsNetworkPath(<MarshalAs(UnmanagedType.LPTStr)>pszPath As String) As <MarshalAs(UnmanagedType.Bool)>Boolean
    End Function
    
  3. 将 CharSet 设置为 Auto,表示 platform-specific character set (Unicode for most OSes) 和 ExactSpelling False:

    <DllImport("shlwapi.dll", EntryPoint:="PathIsNetworkPath", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Public Function PathIsNetworkPath(<MarshalAs(UnmanagedType.LPTStr)>pszPath As String) As <MarshalAs(UnmanagedType.Bool)>Boolean
    End Function
    

我更喜欢选项 3,因为它消除了无用(甚至危险,因为它可能导致数据丢失)Unicode -> ANSI -> Unicode 转换字符串,并且不需要您明确指定 " W" 函数的变体。

【讨论】:

  • 感谢@CodeCaster 的详细回复。我进行了搜索,但没有找到您在帖子开头提到的主题,它已经给了我一些回复,但您的答案要完整得多。
猜你喜欢
  • 1970-01-01
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
  • 2023-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多