PathGetArgs/PathRemoveArgs与CommandLineToArgvW有区别吗

PathGetArgs/PathRemoveArgs vs. CommandLineToArgvW - is there a difference?

本文关键字:有区别 CommandLineToArgvW PathRemoveArgs PathGetArgs      更新时间:2023-10-16

我正在进行一些路径解析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表现不同的情况。

函数相似但并不完全相同,主要与如何处理带引号的字符串有关。

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

PathRemoveArgs调用PathGetArgs,然后使用返回的指针终止字符串。如果遇到的第一个空间恰好在行的末尾,它也会剥离尾部空间。

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

 "c:program filesmy appmy 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文档。

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

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

    lpCmdLine = ""
    argv[0] = C:Program
    argv[1] = FilesVendorMyProgram.exe
    
  2. 使用包含参数的lpCmdLine调用CommandLineToArgvW,不包括程序路径和名称,因此工作正常(只要参数中没有其他空格…):

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

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

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

    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);