WinMainのコマンドライン引数から mainに引数を変換するコード

C言語のでのWindowsプログラミングでは,コマンドライン引数が空白で配列要素に区切られていない べたな文字列で渡されている。
そのため,いちいち コマンドライン引数の文字を解析しないといけない。
strtok関数を使って空白で区切ってやることも出来るが,"" で囲まれている場合は,空白をトークン(区切り文字)として認識してはいけないという,問題点がある。しかも めったにない条件ではない。具体的には,以下の様な場合だ。

 ファイルの関連付けをしたら,ファイル名は "%1" という風に関連付けするため,"C:\Samlple Folder\a.txt" とかいう風に ""で囲まれた文字でわたされる。
当然 " "が付いた状態では fopenやifstreamやCreateFileにファイル名を渡してもファイルを開くことが出来ない。 ""を取り除く作業も必要。


 解決策としては,コンソールアプリケーションで作るという方法があるが,コンソールウインドウが開きかっこ悪い。

コンソールウインドウを出さないで,コンソールアプリケーションと同じよ様にかけないか?
また,プログラムをWindowsに移植したいのだが,コンソールアプリケーションとして作成したくなく,WinMainに修正したくない場合,または,コンソールを出したくない場合など,応用は様々!

というわけで,コードを書いてみた。
WinMainからmainに変換するプログラム。まだまだ改良の余地があるが,とりあえず記録。
UNICODEには対応していないが,修正は難しくないだろう。



#include <windows.h>
#include <malloc.h>

int main( int argc, char *argv[] );


// WinMainのコマンドライン引数をC言語通常のmain関数へ変換

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPervInstance,
                 LPSTR lpszCmdLine, int nCmdShow)
{
    // mainに渡すコマンドライン引数の変数
    int ac;        // 件数
    char **av;    // 文字列配列(char *av[]; と同じ)

    char cmd[] = "program.exe";    // プログラムファイル名
    int r;    // mainからの戻り値格納先
    int len; // コマンドライン引数の文字列の長さ
    int pos; // コマンドライン引数の文字列要素アクセス用

    len = lstrlen(lpszCmdLine);    // コマンドライン引数の文字列長

    // コマンドライン引数区切りの件数を数える
    pos = 0;
    for(ac = 1; pos < len; ac++)
    {
        // 先頭や途中や最後に使われている空白をスキップする
        while(lpszCmdLine[pos] == ' ')
            pos++;

        // コマンドライン最後の空白だった場合,ループから抜ける
        if(pos < len)
            ;
        else
            break;    // 終了

        if(lpszCmdLine[pos] =='\"') {    // ""で囲まれた文字列を処理
            pos++;    // " をスキップ
            while(pos < len && lpszCmdLine[pos] !='\"')    // 終わりの " を探す
                pos++;
        } else {                    // スペースで囲まれた文字列を処理
            while(pos < len && lpszCmdLine[pos] !=' ')    // 次の空白まで移動
                pos++;
        }
        pos++;
    }

    // ココからが,本処理
    av = (char**)malloc(sizeof(char*)*ac);
    av[0] = cmd;    // 先頭はプログラム名
    pos = 0;
    for(ac = 1; pos < len; ac++)
    {
        // 先頭や途中や最後に使われている空白をスキップする
        while(lpszCmdLine[pos] == ' ')
            pos++;

        // コマンドライン最後の空白だった場合,ループから抜ける
        if(pos < len)
            ;
        else
            break;    // 終了

        if(lpszCmdLine[pos] =='\"') {// ""で囲まれた文字列を処理
            pos++;    // " をスキップ
            av[ac] = lpszCmdLine + pos;
            while(pos < len && lpszCmdLine[pos] !='\"')    // 終わりの " を探す
                pos++;
        } else {                    // スペースで囲まれた文字列を処理
            av[ac] = lpszCmdLine + pos;
            while(pos < len && lpszCmdLine[pos] !=' ')    // 次の空白まで移動
                pos++;
        }
        lpszCmdLine[pos] = '\0';    // 文字列を区切る
        pos++;
    }

    r = main(ac,av);

    free(av);

    return r;
}

/*

// 動作確認用コード
int main( int argc, char *argv[] )
{
    int i;
    for(i = 0; i < argc; i++)
    {
        MessageBox(NULL, argv[i], "", MB_OK);
    }
    return 0;
}

*/

この記事へのコメント

とおりすがり
2008年08月27日 14:19
Windowsプログラミングでは__argc __argvという値がプログラム上で使用できます。その値の内容はコンソールのmain関数でのコマンドライン引数と同じように取得できます。

この記事へのトラックバック