为什么这些DLL具有两个看似相同的入口点?

时间:2018-10-04 09:52:59

标签: .net vb.net dll dllimport

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

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

但是,当我尝试从代码中调用这些方法时,出现一个错误,指出该入口点不存在。因此,我进行了一些研究,发现操作系统中的DLL(Windows 7企业版32位)没有完全包含这些方法,而是得到了:

  • PathIsNetworkPath →PathIsNetworkPath A / PathIsNetworkPath W
  • WNetAddConnection2 →WNetAddConnection2 A / WNetAddConnection2 W
  • WNetCancelConnection2 →WNetCancelConnection2 A / WNetCancelConnection2 W

因此,我测试了他们的行为: *以“ A”结尾的方法按预期工作。 *以“ W”结尾的方法不能按预期方式工作,它们会引发错误或返回错误的结果(例如,应为“ true”的情况下为“ false”)。 但是,在帮助主题中,没有人提到存在类似问题。

因此,我做了一些研究,在MSDN documentation中,我发现DLL仅包含以“ A”和“ W”结尾的方法,并且在这三个方法中,就我使用的案例而言,据我所知,文档页面完全相同。实际上,在整个页面中,他们都不使用以A / W结尾但没有A / W的方法的名称。

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

1 个答案:

答案 0 :(得分:4)

自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中默认情况下),平台调用仅搜索您指定的名称。例如,如果指定MessageBox,则平台调用会搜索MessageBox并在找不到精确的拼写时失败。

鉴于C#和VB.NET的DllImportAttribute.CharSet字段的默认值为“ ANSI”,Visual Basic的规则确定运行时搜索 A < / strong>或 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”函数的变体。