2010-10-20 181 views
3

My C Win32应用程序应允许传递完整的命令行以便其他程序启动,例如,将argv []传递给CreateProcess()的方法

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

回答

5

这实际上比您想象的要容易。

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

编辑:现在我看到你已经考虑这一选项。如果您担心绩效处罚,那么与流程创建相比,它们并没有什么优势。

+0

'的strstr()'样部分是有问题的。这个用例怎么样:'myapp.exe/SetTitle C:\ app.exe/RunMinimized C:\ app.exe/ArgToApp.exe'。实际的命令从第二场比赛开始。 – 2010-10-20 22:13:53

+0

@Ilia我看到了,在这种情况下,你应该自己解析它。无论如何,不​​要在速度上放肆。 – ruslik 2010-10-20 22:25:58

0

我认为这实际上比你想象的更普遍。

http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx

这最终取决于个别方案如何记号化的命令行到argv阵列(甚至CommandLineToArgv在理论上(也许在实践中,如果有什么意见的一个说的是真的)当它将argv初始化为main()时,其行为可能与CRT不同),所以甚至没有一套可以遵循的标准规则。

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

0

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

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

1

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

根据微软的文档,你应该只考虑反斜杠和引号。

你可以找到他们的文件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; 
}