9

我的 C Win32 应用程序应该允许传递完整的命令行以启动另一个程序,例如

myapp.exe /foo /bar "C:\Program Files\Some\App.exe" arg1 "arg 2"

myapp.exe可能看起来像

int main(int argc, char**argv)
{
  int i;

  for (i=1; i<argc; ++i) {
     if (!strcmp(argv[i], "/foo") {
        // handle /foo
     } else if (!strcmp(argv[i], "/bar") {
        // handle /bar
     } else {
        // not an option => start of a child command line
        break;
     }
  }

  // run the command
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  // customize the above...

  // I want this, but there is no such API! :(
  CreateProcessFromArgv(argv+i, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

  // use startup info si for some operations on a process
  // ...
}

我可以考虑一些解决方法:

它们都冗长并且重新实现了繁琐的 windows 命令行解析逻辑,这已经是CommandLineToArgvW().

我的问题有“标准”解决方案吗?变通办法的标准(Win32、CRT 等)实现算作一种解决方案。

4

5 回答 5

6

它实际上比你想象的要容易。

1)有一个API,GetCommandLine()它将返回整个字符串

myapp.exe /foo /bar "C:\Program Files\Some\App.exe" arg1 "arg 2"

2)CreateProcess()允许指定命令行,因此将其用作

CreateProcess(NULL, "c:\\hello.exe arg1 arg2 etc", ....) 

会做你需要的。

3) 通过解析命令行,您可以找到 exe 名称的开始位置,并将该地址传递给CreateProcess(). 它可以很容易地完成

char* cmd_pos = strstr(GetCommandLine(), argv[3]);

最后:CreateProcess(NULL, strstr(GetCommandLine(), argv[i]), ...);

编辑:现在我看到您已经考虑过这个选项。如果您担心性能损失,那么它们与流程创建相比毫无意义。

于 2010-10-20T21:56:38.290 回答
2

您的问题中尚未包含的唯一标准功能是PathGetArgs,但它没有那么多。函数PathQuoteSpacesPathUnquoteSpaces也很有帮助。在我看来,将CommandLineToArgvWGetCommandLineW结合使用才是您真正需要的。如果您想要一个通用的解决方案,我认为在命令行解析期间使用 UNICODE 是强制性的。

于 2010-10-21T00:24:03.057 回答
1

我按如下方式解决了它:安装 Visual Studio 后,您可以找到一些用于创建 C 库的标准代码的副本。特别是,如果您查看 VC\crt\src\stdargv.c,您会发现“wparse_cmdline”函数的实现,它根据 GetCommandLineW API 的结果创建 argc 和 argv。我创建了此代码的增强版本,它还创建了一个“cmdv”指针数组,该数组在每个 argv 指针开始的位置指向原始字符串。然后,您可以根据需要对 argv 参数进行操作,并且当您想将“rest”传递给 CreateProcess 时,您只需传入 cmdv[i] 即可。

该解决方案的优点是使用完全相同的解析代码,仍然像往常一样提供 argv,并允许您传递原始内容而无需重新引用或重新转义它。

于 2012-08-07T13:03:44.570 回答
1

我和你遇到了同样的问题。问题是,我们不需要解析整个字符串,如果我们可以将 的结果分开GetCommandLine(),然后您可以将它们放在一起。

根据 Microsoft 的文档,您应该只考虑反斜杠和引号。

你可以在这里找到他们的文件。

然后,您可以调用Solve以获取下一个参数起点。

E.g. 
     "a b c" d e

First Part: "a b c"
Next Parameter Start: d e

我解决了Microsoft 文档中的示例,所以请担心兼容性。通过Solve递归调用函数,可以得到整个argv数组。

这是文件test.c

#include <stdio.h>

extern char* Solve(char* p);

void showString(char *str)
{
    char *end = Solve(str);

    char *p = str;

    printf("First Part: ");
    while(p < end){
        fputc(*p, stdout); 
        p++;
    }

    printf("\nNext Parameter Start: %s\n", p + 1);
}

int main(){
    char str[] = "\"a b c\" d e";
    char str2[] = "a\\\\b d\"e f\"g h";
    char str3[] = "a\\\\\\\"b c d";
    char str4[] = "a\\\\\\\\\"b c\" d e";

    showString(str);
    showString(str2);
    showString(str3);
    showString(str4);

    return 0;
}

运行结果为:

First Part: "a b c"
Next Parameter Start: d e
First Part: a\\b
Next Parameter Start: d"e f"g h
First Part: a\\\"b
Next Parameter Start: c d
First Part: a\\\\"b c"
Next Parameter Start: d e

这是函数的所有源代码Solve,文件findarg.c

/**

This is a FSM for quote recognization.

Status will be 
    1. Quoted. (STATUS_QUOTE)
    2. Normal. (STATUS_NORMAL)
    3. End.    (STATUS_END)

    Quoted can be ended with a " or \0
    Normal can be ended with a " or space( ) or \0

    Slashes
*/

#ifndef TRUE
#define TRUE 1
#endif

#define STATUS_END    0
#define STATUS_NORMAL 1
#define STATUS_QUOTE  2

typedef char * Pointer;
typedef int STATUS;

static void MoveSlashes(Pointer *p){

    /*According to Microsoft's note, http://msdn.microsoft.com/en-us/library/17w5ykft.aspx */
    /*Backslashes are interpreted literally, unless they immediately precede a double quotation mark.*/

    /*Here we skip every backslashes, and those linked with quotes. because we don't need to parse it.*/
    while (**p == '\\'){

        (*p)++;

        //You need always check the next element
        //Skip \" as well.
        if (**p == '\\' || **p == '"')
            (*p)++;

    }
}

/*    Quoted can be ended with a " or \0  */
static STATUS SolveQuote(Pointer *p){
    while (TRUE){
        MoveSlashes(p);
        if (**p == 0)
            return STATUS_END;

        if (**p == '"')
            return STATUS_NORMAL;

        (*p)++;
    }
}

/* Normal can be ended with a " or space( ) or \0 */
static STATUS SolveNormal(Pointer *p){
    while (TRUE){
        MoveSlashes(p);
        if (**p == 0)
            return STATUS_END;

        if (**p == '"')
            return STATUS_QUOTE;

        if (**p == ' ')
            return STATUS_END;

        (*p)++;
    }
}

/*
    Solve the problem and return the end pointer.

    @param p The start pointer

    @return The target pointer.
*/
Pointer Solve(Pointer p){

    STATUS status = STATUS_NORMAL;

    while (status != STATUS_END){
        switch (status)
        {
        case STATUS_NORMAL:
            status = SolveNormal(&p); break;

        case STATUS_QUOTE:
            status = SolveQuote(&p); break;

        case STATUS_END:
        default:
            break;
        }

        //Move pointer to the next place.
        if (status != STATUS_END)
            p++;
    }

    return p;
}
于 2014-05-31T06:25:42.730 回答
0

我认为对于一般情况,这实际上比您想象的要难。

请参阅CommandLineToArgvW 对引号和反斜杠的奇怪处理是怎么回事

最终取决于各个程序如何将命令行标记为argv数组(甚至CommandLineToArgv在理论上(甚至在实践中,如果其中一个评论所说的是真的)在初始化argv为时的行为可能与 CRT 不同main()),所以甚至没有一套标准的深奥规则可以遵循。

但无论如何,简短的回答是:不,不幸的是没有简单/标准的解决方案。您必须滚动自己的函数来处理引号和反斜杠等。

于 2010-10-20T23:31:21.837 回答