【问题标题】:LoadLibraryW and POSIX path separatorLoadLibraryW 和 POSIX 路径分隔符
【发布时间】:2016-01-10 16:52:13
【问题描述】:

LoadLibrary 的 MSDN 文档警告不要使用普通的 Unix 斜杠“/”:

指定路径时,请务必使用反斜杠 (\),而不是正斜杠 (/)。

在调用此 API 时,使用正斜杠或反斜杠(或两者)都没有发现任何问题。我尝试了以下路径名:

c:/foo/bar/baz.dll
/foo/bar/baz.dll
c:/foo/bar\\baz.dll
/foo/bar\\baz.dll
./bar/baz.dll

所有组合都按预期工作。为什么 MSDN 建议不要使用正斜杠作为路径分隔符?

编辑:

关于 UNC 名称,它也可以与“//127.0.0.1/c$/foo/bar/baz.dll”完美配合。

是的,如果您添加“\?\”,它不会加载库,但 _wfopen 也将无法打开文件。 LoadLibraryW 与任何其他接受/不接受正斜杠的 Windows API 有何不同?为什么有关于 LoadLibraryW 而不是 CreateFileW 的明确警告。

创建文件:https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx

要创建或打开的文件或设备的名称。您可以在此名称中使用正斜杠 (/) 或反斜杠 ()。

加载库:https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx

指定路径时,请务必使用反斜杠 (),而不是正斜杠 (/)。

在使用正斜杠 (/) 时,是否存在其他可能导致 LoadLibrary 失败的缺陷,而其他 Windows API 可以毫无问题地接受正斜杠?

【问题讨论】:

  • 您使用的是普通的 Windows API 路径名。一旦您有意或偶然地开始使用 NT 风格的名称(通过添加“\\?\”前缀),将不再有任何路径分隔符的翻译。 Naming Files, Paths, and Namespaces 拥有您需要的所有信息。本质上:始终使用本机路径分隔符。
  • 你是不是不小心删了一半的问题?我不明白问题是什么;听起来你没有任何问题??
  • 它被弄乱了太多,例如任何反恶意软件修补它。您只是违反了保修条款,如果您致电 Microsoft 支持,他们发现您使用了正斜杠,然后他们就会挂断电话。
  • 您仅引用了CreateFile 文档的一部分。以下是相关的剩余部分:"[...] 在路径前添加 "\\?\"。有关详细信息,请参阅 Naming Files, Paths, and Namespaces。" 最后一个链接将您带到以下内容:“Windows API 中的文件 I/O 函数将“/”转换为“\”作为将名称转换为 NT 样式名称的一部分,使用“\\?\”前缀时除外。”没有 Windows API 调用将转换以“\\?\”为前缀的路径名。唯一的区别:LoadLibrary 附带更准确的文档。

标签: winapi dll posix loadlibrary


【解决方案1】:

Windows API 中的文件 I/O 将正斜杠 ("/") 转换为反斜杠 ("\"),然后将请求传递给本机 API。本机 API 需要 NT 风格的名称。当路径名以“\\?\”为前缀时,不执行任何转换。有时您无法控制此前缀是否存在(例如,在使用 Windows API 检索基本路径时)。

简而言之:使用本机路径分隔符是安全的解决方案。违反合同并使用正斜杠可能会突然停止工作。

完整的信息记录在 MSDN 中Naming Files, Paths, and Namespaces。

【讨论】:

  • 但这通常是 Windows API。 _wfopen 也是如此。它不接受带有“\\?\”前缀的正斜杠。 LoadLibraryW 有何不同?同时我没有接受这个问题
  • @Artyom: _wfopen 最终会调用CreateFile,因此同样的规则适用于 CRT 的文件 I/O 函数。两者都与LoadLibraryW 不同:如果您在路径名前加上“\\?\”,则不会执行参数翻译。
  • 是的,我知道一旦添加了“\\?\”,_wfopen/CreateFile 将无法处理 Unix 斜杠 (/)。如果在 CreateFile 和其他 Windows API 上的 LoadLibrary 不仅仅是“\\?\”路径前缀,我试图了解什么。
  • @Artyom:“\\?\”-前缀用于所有处理路径名的 Windows API。其目的是将未经修改的参数传递给本机 API。某些 Windows API(例如CreateFile)允许您在路径中使用正斜杠,除非它是“\\?\”前缀。其他 API(例如 LoadLibrary)要求您使用反斜杠。询问为什么违反合同有时似乎起作用并不是很有用,而且我无法告诉您何时停止工作。毕竟,它根本没有记录在案。
  • @Artyom:什么样的跨平台代码会调用LoadLibrary?这个 API 调用是特定于平台的,如果你想使用它,你需要遵守它的规则。如果要编写跨平台代码,可以定义一个符号常量来指定路径分隔符,并为其分配一个与目标平台匹配的值。没有充分的理由违反合同。
【解决方案2】:

原因是程序员以后不会遇到奇怪的问题。 Windows API 非常庞大,预计会有很多组合可以工作。通过取缔某些案件,他们使他们的生活更轻松。

但我猜你想要这种特殊情况。就这样吧。 Windows 加载器需要维护自己的模块和路径数据库,其中一些实际上暴露在 API 中,因此在某种程度上他们必须选择规范表示,然后他们可以忘记系统如何获取 DLL 的细节首先。这在加载器中更是如此,它实际上是一个 NT 层组件,而不是 Win32 层组件。因此,从 Win32 进入的路径会得到您注意到的兼容处理,但从 NT-native 到 Win32 的路径会通过规范转换。

举个例子

#include "stdafx.h"

const wchar_t name[] = L"c:/windows/system32/atl.dll";

int main() {
  HMODULE mod1 = ::LoadLibrary(name);

  wchar_t lo_name[512];
  GetModuleFileName(mod1, lo_name, 512);
  wprintf(L"%s - %s", name, lo_name);

  return 0;
}

如您所见,它会打印不同的字符串。现在想象一下,开发人员可能会决定他们需要基于name 相等或一些奇怪的方案来释放模块。它不起作用。

bool FreeLibByName(HMODULE mod, const wchar_t* name) {
  wchar_t lo_name[512];
  GetModuleFileName(mod, lo_name, 512);
  if (wcscmp(name, lo_name) != 0) {
    return false;
  }
  FreeLibrary(mod);
  return true;
}

如果你没有长时间编程 Windows,那么这个功能甚至不会看起来那么奇怪。

但还有更多:https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx

要在当前进程中检索模块的基本名称,请使用 GetModuleFileName 函数检索完整的模块名称,然后 使用 strrchr(szmodulename, '\') 之类的函数调用扫描到 模块名称字符串中基本名称的开头。这更 比调用 GetModuleBaseName 更高效、更可靠 当前进程的句柄。

MSDN 告诉程序员“最佳实践”假设反斜杠,想象一下LoadLibrary() 中的原始路径表示是否已存储?这里会造成一些混乱的代码。

【讨论】:

  • 虽然此答案包含大量信息,但并未解决问题。您为LoadLibrary 描述的内容对于CreateFile 同样适用。使用CreateFile 打开一个文件(包含正斜杠)并使用GetFileInformationByHandleEx 请求FileNameInfo。您会观察到,返回的信息现在包含反斜杠。
猜你喜欢
  • 1970-01-01
  • 2014-07-29
  • 2018-04-25
  • 2011-06-03
  • 1970-01-01
  • 2011-01-23
  • 2015-02-09
  • 2017-03-10
  • 2018-11-17
相关资源
最近更新 更多