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

実際は思ったより簡単です。

GetCommandLine()1)文字列全体を返すAPIがあります

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ですが、それほど多くはありません。関数PathQuoteSpacesおよびPathUnquoteSpacesも役立ちます。私の意見では、CommandLineToArgvWをGetCommandLineWと組み合わせて使用​​することが本当に必要です。一般的な解決策が必要な場合は、コマンドラインの解析中にUNICODEを使用することが必須であると私は考えています。

于 2010-10-21T00:24:03.057 に答える
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 に答える
1

私は次のように解決しました: Visual Studio をインストールすると、C ライブラリの作成に使用される標準コードのコピーを見つけることができます。特に、VC\crt\src\stdargv.c を見ると、GetCommandLineW API の結果から argc と argv を作成する "wparse_cmdline" 関数の実装が見つかります。このコードの拡張バージョンを作成し、各 argv ポインターが始まる場所で元の文字列を指すポインターの "cmdv" 配列も作成しました。その後、必要に応じてargv引数を操作できます。「残り」をCreateProcessに渡したい場合は、代わりにcmdv [i]を渡すだけです。

このソリューションには、まったく同じ解析コードを使用し、通常どおり argv を提供し、元のコードを再引用または再エスケープすることなく渡すことができるという利点があります。

于 2012-08-07T13:03:44.570 に答える
0

一般的な場合よりも実際には難しいと思います。

CommandLineToArgvWによる引用符と円記号の奇妙な扱いについてはどうなっているのかを参照してください。

最終的には、コマンドラインをargv配列にトークン化する方法は個々のプログラム次第です(理論的にも(そしておそらく実際には、コメントの1つが正しい場合)、CRTが初期化されCommandLineToArgvたときとは異なる動作をする可能性があります)、したがって、従うことができる難解なルールの標準セットすらありません。argvmain()

しかし、とにかく、簡単な答えは次のとおりです。いいえ、残念ながら、簡単で標準的な解決策はありません。引用符や円記号などを処理するには、独自の関数をロールする必要があります。

于 2010-10-20T23:31:21.837 に答える