【问题标题】:PathGetArgs/PathRemoveArgs vs. CommandLineToArgvW - is there a difference?PathGetArgs/PathRemoveArgs 与 CommandLineToArgvW - 有区别吗?
【发布时间】:2013-12-04 21:36:02
【问题描述】:

我正在编写一些路径解析 C++ 代码,并且为此尝试了很多 Windows API。 PathGetArgs/PathRemoveArgs 和稍加按摩的CommandLineToArgvW 有区别吗?

换句话说,除了长度/清洁度之外,是这样的:

std::wstring StripFileArguments(std::wstring filePath)
{
  WCHAR tempPath[MAX_PATH];

  wcscpy(tempPath, filePath.c_str());
  PathRemoveArgs(tempPath);

  return tempPath;
}

与此不同:

std::wstring StripFileArguments(std::wstring filePath)
{
  LPWSTR* argList;
  int argCount;
  std::wstring tempPath;

  argList = CommandLineToArgvW(filePath.c_str(), &argCount);

  if (argCount > 0)
  {
    tempPath = argList[0]; //ignore any elements after the first because those are args, not the base app

    LocalFree(argList);

    return tempPath;
  }

  return filePath;
}

这是这个

std::wstring GetFileArguments(std::wstring filePath)
{
  WCHAR tempArgs[MAX_PATH];

  wcscpy(tempArgs, filePath.c_str());
  wcscpy(tempArgs, PathGetArgs(tempArgs));

  return tempArgs;
}

不同于

std::wstring GetFileArguments(std::wstring filePath)
{
  LPWSTR* argList;
  int argCount;
  std::wstring tempArgs;

  argList = CommandLineToArgvW(filePath.c_str(), &argCount);

  for (int counter = 1; counter < argCount; counter++) //ignore the first element (counter = 0) because that's the base app, not args
  {
    tempArgs = tempArgs + TEXT(" ") + argList[counter];
  }

  LocalFree(argList);

  return tempArgs;
}

?在我看来,PathGetArgs/PathRemoveArgs 只是提供了CommandLineToArgvW 解析的更清洁、更简单的特殊情况实现,但我想知道是否存在 API 行为不同的极端情况。

【问题讨论】:

  • 似乎 SHLW-API (Path*) 除了找到第一个空格字符之外没有做任何特别的事情。这可能是文档的全部内容:“此函数不应用于通用命令路径模板。”heuristics used by CommandLineToArgvW 似乎是a bit more involved
  • 旧新事物链接+1。

标签: c++ parsing winapi path


【解决方案1】:

功能相似但不完全相同 - 主要与如何处理带引号的字符串有关。

PathGetArgs 返回指向输入字符串中第一个空格之后的第一个字符的指针。如果在第一个空格之前遇到引号字符,则在函数再次开始查找空格之前需要另一个引号。如果没有找到空格,函数返回一个指向字符串结尾的指针。

PathRemoveArgs 调用PathGetArgs 然后使用返回的指针终止字符串。如果遇到的第一个空格恰好在行尾,它也会去除尾随空格。

CommandLineToArgvW 获取提供的字符串并将其拆分为一个数组。它使用空格来描述数组中的每个项目。可以引用数组中的第一项以允许空格。第二个和后续项目也可以被引用,但它们支持稍微复杂的处理 - 参数还可以通过在它们前面加上反斜杠来包含嵌入的引号。例如:

 "c:\program files\my app\my app.exe" arg1 "argument 2" "arg \"number\" 3"

这将产生一个包含四个条目的数组:

  • argv[0] - c:\program files\my app\my app.exe
  • argv[1] - arg1
  • argv[2] - 参数 2
  • argv[3] - arg "number" 3

有关解析规则的完整说明,包括如何在参数中嵌入反斜杠和引号,请参阅 CommandLineToArgVW 文档。

【讨论】:

    【解决方案2】:

    是的,我观察到当前 SDK 的不同行为(VS2015 Update 3 + Windows 1607 Anniversary SDK,SDK 版本设置为 8.1):

    1. 使用空的 lpCmdLine 调用 CommandLineToArgvW(未传递参数时从 wWinMain 获得的内容)返回程序路径和文件名,它们将在每个空格上拆分。但这并没有在参数中指定,它一定是自己做的,但没有考虑忽略路径本身的间距:

      lpCmdLine = ""
      argv[0] = C:\Program
      argv[1] = Files\Vendor\MyProgram.exe
      
    2. 使用包含参数的 lpCmdLine 调用 CommandLineToArgvW,不包括程序路径和名称,因此按预期工作(只要参数中没有更多空格...):

      lpCmdLine = "One=1 Two=\"2\""
      argv[0] = One=1
      argv[1] = Two=2
      

    请注意,它还会在传递时去除参数内的任何其他引号。

    1. CommandLineToArgvW 不喜欢Text=\"Quoted spaces\" 格式的第一个参数,因此如果您尝试将 lpCmdLine 直接传递给它,它会错误地拆分 key=value 对(如果它们有空格):

      lpCmdLine = "One=\"Number One\" Two=\"Number Two\""
      argv[0] = One=\"Number
      argv[1] = One\"
      argv[2] = Two=\"Number
      argv[3] = Two\"
      

    这里有记录:

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

    但这种在程序路径中带有空格的行为是意料之中的。这对我来说似乎是一个错误。我希望在两种情况下都处理相同的数据。因为如果我真的想要可执行文件的路径,我会改为调用 GetCommandLineW()。

    在我看来,唯一明智的一致解决方案是完全忽略 lpCmdLine 并调用 GetCommandLineW(),将结果传递给 CommandLineToArgvW(),然后如果您对程序路径不感兴趣,则跳过第一个参数。这样,所有组合都受支持,即带和不带空格的路径、带和不带空格的嵌套引号的参数。

    int argumentCount;
    LPWSTR commandLine = GetCommandLineW();
    LPWSTR *arguments = CommandLineToArgvW(commandLine, &argumentCount);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-01
      • 2010-10-10
      • 1970-01-01
      • 2019-09-22
      • 2014-09-25
      • 2016-11-05
      • 1970-01-01
      相关资源
      最近更新 更多