【问题标题】:Way to pass argv[] to CreateProcess()将 argv[] 传递给 CreateProcess() 的方法
【发布时间】:2011-04-28 07:27:58
【问题描述】:

我的 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 等)实现算作一种解决方案。

【问题讨论】:

    标签: c winapi command-line-arguments createprocess


    【解决方案1】:

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

    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]), ...);

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

    【讨论】:

    • strstr()-like 部分有问题。这个用例怎么样:myapp.exe /SetTitle C:\app.exe /RunMinimized C:\app.exe /ArgToApp.exe。实际的命令从第二个匹配开始。
    • @Ilia 我明白了,在这种情况下你应该自己解析它。无论如何,不​​要为速度而烦恼。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

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

        你可以找到他们的文件here

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

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

        我解决了Microsoft documentation 中的示例,所以请注意兼容性。 通过递归调用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;
        }
        

        【讨论】:

          【解决方案5】:

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

          What's up with the strange treatment of quotation marks and backslashes by CommandLineToArgvW

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-09
            • 1970-01-01
            • 2011-12-14
            • 1970-01-01
            • 2019-07-19
            • 2014-05-28
            相关资源
            最近更新 更多