Способ передачи argv [] в CreateProcess () - PullRequest
5 голосов
/ 21 октября 2010

Приложение My 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 и т. Д.) Обходных путей считается решением.

Ответы [ 5 ]

6 голосов
/ 21 октября 2010

Это на самом деле проще, чем вы думаете.

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

РЕДАКТИРОВАТЬ: теперь я вижу, что вы уже рассмотрели этот вариант. Если вас беспокоят потери производительности, они ничто по сравнению с созданием процесса.

1 голос
/ 31 мая 2014

Я столкнулся с такими же проблемами с вами. Дело в том, что нам не нужно анализировать всю строку, если мы можем отделить результат 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;
}
1 голос
/ 07 августа 2012

Я решил это следующим образом: с вашей установкой Visual Studio вы можете найти копию некоторого стандартного кода, использованного для создания библиотеки C.В частности, если вы посмотрите в VC \ crt \ src \ stdargv.c, вы найдете реализацию функции "wparse_cmdline", которая создает argc и argv из результата API GetCommandLineW.Я создал расширенную версию этого кода, которая также создала массив указателей «cmdv», которые указывали обратно на оригинальную строку в том месте, где начинается каждый указатель argv.Затем вы можете работать с аргументами argv по своему усмотрению, а когда вы хотите передать "rest" в CreateProcess, вы можете просто вместо этого передать cmdv [i].

Это решение имеет преимущества, заключающиеся в использованииточно такой же код синтаксического анализа, по-прежнему обеспечивает argv как обычно и позволяет передавать оригинал без необходимости повторного цитирования или повторного экранирования.

0 голосов
/ 21 октября 2010

Единственная стандартная функция, которую вы еще не включили в свой вопрос, это PathGetArgs , но это не так уж много. Функции PathQuoteSpaces и PathUnquoteSpaces также могут быть полезны. По моему мнению, использование CommandLineToArgvW в сочетании с GetCommandLineW - это то, что вам действительно нужно. Использование UNICODE при разборе командной строки, на мой взгляд, обязательно, если вы хотите иметь общее решение.

0 голосов
/ 21 октября 2010

Я думаю, что на самом деле это сложнее, чем вы думаете в общем случае.

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

В конечном итоге это зависит от отдельных программ, как они токенизируют командную строку в массив argv (и даже CommandLineToArgv в теории (и, возможно, на практике, если то, что в одном из комментариев было верно), могут вести себя по-разному чем CRT, когда он инициализирует argv в main()), так что нет даже стандартного набора эзотерических правил, которым вы можете следовать.

Но в любом случае, краткий ответ: нет, к сожалению, нет простого / стандартного решения. Вам придется развернуть свою собственную функцию для работы с кавычками и обратными слешами и тому подобным.

...