为什么这些 DLL 有两个明显相同的入口点?
Why do these DLLs have two apparently identical entry points?
今天,在编写一些 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" 结尾的方法无法按预期工作,它们会引发错误或 return 错误结果(g.i。"false" 而应为 "true")。
然而,帮助主题中没有人提到有类似的问题。
所以我做了一些研究,在 MSDN documentation 中我发现 DLL 只包含以 "A" 和 "W" 结束的方法,并且它们都在我使用的三个案例,据我所知,文档页面是相同的。事实上,整个页面上,他们都没有使用以 A/W 结尾的方法名称,而是没有使用它。
所以我的问题是:*为什么我的 DLL 中有方法 "A" 和 "W" 而不是没有 A/W 的方法?两者有什么区别?为什么方法 "A" 对我有用,而方法 "W" 却不行?
自 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,特别是 "String Marshaling and Name Matching" 和 "Specifying a Character Set in Visual Basic":
When the DllImportAttribute.ExactSpelling field is true, as it is by default in Visual Basic 2005, platform invoke searches only for the name you specify. For example, if you specify MessageBox, platform invoke searches for MessageBox and fails when it cannot locate the exact spelling.
鉴于 DllImportAttribute.CharSet 字段的默认值对于 C# 是 "ANSI" 和 VB.NET,Visual Basic 的规则确定运行时 不会 搜索 A 或 W 入口点,参见 Docs: DllImportAttribute.ExactSpelling Field。我猜这是为了向后兼容 Visual Basic。
所以你有三个选择:
明确指定 "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
禁用精确拼写,导致运行时搜索 "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
将 CharSet 设置为自动,意味着 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" 函数变体。
今天,在编写一些 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" 结尾的方法无法按预期工作,它们会引发错误或 return 错误结果(g.i。"false" 而应为 "true")。 然而,帮助主题中没有人提到有类似的问题。
所以我做了一些研究,在 MSDN documentation 中我发现 DLL 只包含以 "A" 和 "W" 结束的方法,并且它们都在我使用的三个案例,据我所知,文档页面是相同的。事实上,整个页面上,他们都没有使用以 A/W 结尾的方法名称,而是没有使用它。
所以我的问题是:*为什么我的 DLL 中有方法 "A" 和 "W" 而不是没有 A/W 的方法?两者有什么区别?为什么方法 "A" 对我有用,而方法 "W" 却不行?
自 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,特别是 "String Marshaling and Name Matching" 和 "Specifying a Character Set in Visual Basic":
When the DllImportAttribute.ExactSpelling field is true, as it is by default in Visual Basic 2005, platform invoke searches only for the name you specify. For example, if you specify MessageBox, platform invoke searches for MessageBox and fails when it cannot locate the exact spelling.
鉴于 DllImportAttribute.CharSet 字段的默认值对于 C# 是 "ANSI" 和 VB.NET,Visual Basic 的规则确定运行时 不会 搜索 A 或 W 入口点,参见 Docs: DllImportAttribute.ExactSpelling Field。我猜这是为了向后兼容 Visual Basic。
所以你有三个选择:
明确指定 "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
禁用精确拼写,导致运行时搜索 "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
将 CharSet 设置为自动,意味着 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" 函数变体。