API-7 WinMainをD&Dで起動

WinMain関数の第3引数はコマンドライン引数です。
ただし、main関数のように引数の数(argc)とそれぞれの引数(argv)のように分けられず、渡された引数全てがひとつの文字列として代入されています。
また、実行ファイル自信のパスは含まれていません。

GetCommandLine関数を使えば、渡された引数だけでなく自身のパスも得ることができます。
でも、こっちもひとつの文字列として。
http://www.wisdomsoft.jp/dev/api/windows/005

それぞれの引数はダブルクォーテーションとスペースで分けられているので、自分で分解する必要があります。
[技術者向] コンピューター > プログラミング > C&C++ ">http://oshiete1.goo.ne.jp/qa2889140.html

main関数みたいにできないかと探してみたところ、__argcと__argvを使えば、main関数と同じように引数を使うことができるみたいです。
ただし、コンパイラに依存してしまうみたいです。
http://win32lab.com/tips/tips3.html


WinMainでもmainでも、ファイル名が長いとダブルクォーテーションが追加されないようです。
そのとき、ファイル名に半角スペースが含まれていると、スペースで分けられてそれぞれ別の引数としてargvに代入されてしまうみたいです。

サンプル
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
// Borland C++ Compiler 5.5 にてコンパイル

#include <windows.h>

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                        LPSTR lpsCmdLine, int nCmdShow)
{
        TCHAR    ss[256];
        INT        i;
        
        MessageBox(NULL,lpsCmdLine,TEXT("lpsCmdLine"),MB_OK);
        MessageBox(NULL,GetCommandLine(),TEXT("GetCommandLine"),MB_OK);
        wsprintf(ss,"%d",__argc);
        MessageBox(NULL,ss,TEXT("__argc"),MB_OK);
        for(i=0; i<__argc; i++)
        {
        wsprintf(ss,"%d\n%s",i,__argv[i]);
        MessageBox(NULL,ss,TEXT("__argv"),MB_OK);
        }
        return 0;
}

さあ、実験だ、とこれまで何度も言っている

言ってるのですが、そのうち、実際にすぐ実験に入ったのはどれほどでしょうか?
大抵、言ってから数日後に実験が始まっているような気がするのですが、気のせいでしょうか?

金曜に試料作りを終え、今日から実験に入る予定だったのですが、機械のメンテや、装置やら何やらのセッティングで、1日が過ぎてゆきました。
こんなことばかりな気がします。


今日の心に残った一言
「フレキシブル平行四辺形変形」

フレキシブルという単語から感じる、なんだかすごそうな響き。
その次にいきなり平行四辺形。
フレキシブル平行四辺形、合わさると、なんだか怪しそうな感じ。
そして変形が続くことで、繰り返される、へんけい。

すごそうで、なぜかチープな感じの響き。
なんだか、印象に残ったのです。

API 6- 5日かかってツールバー

 メニュー、アイコン、カーソルと来て、次はツールバーです。
 このツールバー、問題だらけでした。

 ツールバーを使うためには、"commctrl.h" をincludeし、"ComCtl32.lib" をlinkする必要があります。
 ComCtl32.libはコンパイラが自動的にlinkしてくれるので、ソースでは、commctrl.hをincludeするだけです。
 いつものように本を見ながらソース入力。

 まず、「未解決の外部シンボル」エラー。

 ツールバー関連の関数本体が行方不明らしいです。ちゃんとincludeしてlinkできているはずなのに。
 ComCtl32.libをプロジェクトに入れて、コンパイルしてみたら解決。力ずく
 linkの設定、間違ってたんでしょうか?


 次、「"○○"の命令が"△△"のメモリを参照しました。メモリが"read"になることはできませんでした。」エラー。

 ここの解決に時間がかかりました。
 CreateToolbarEx関数を実行するところでエラーが出て停止してしまうのです。
 引数がまずいのか、リソースがまずいのか、bmpがまずいのか、原因がまったく不明。
 本を見てもよくわからない引数があります。ひとまず、無視。
 リソースはほぼ手書き状態。本についてきたリソースファイルはエディタ製で比較できない。
 とりあえず、本についてきたお手本のbmpを使ってみる。
 すると、動いた!
 どうやら、ここに原因があるようです。

 ならば、自作bmpとお手本bmpとの違いは何か?
  • 絵柄(違って当然といえば当然)
  • 画像のサイズ(幅と高さ)
  • ファイルのサイズ(○○バイト)
  • ビットの深さ(プロパティにて発見)
・・・・・・、ん?ビットの深さ?何でしょう?
 それに、幅・高さはそんなに違わないのにファイルサイズがけっこう違う。
 どうやら、これは色数の違いみたいです。
 ペイントで開いて名前を付けて保存を選ぶと、bmpでも24bit、256色、16色、モノクロの4種があります。ここで気付きました。
 いつも使っているペイントソフトのbmp形式保存は単にbmpのみ。
 自作bmpは24bit、お手本bmpは16色。
 カーソルのときと同じような原因でしたね。

 いろいろ試してみると、24bitのときにエラーが出ます。

API 5- オリジナルなアイコン・カーソル

 リソースつながりで、今回はアイコンとカーソル。
 どちらも、リソースに画像を登録して、LoadImage関数で読み込んで、WNDCLASSC構造体に渡すだけ。
 やることは簡単です。

 が、いつものごとく、問題が出てくるのですな。


 LoadImage関数の引数に少々迷ったものの、アイコンは無事実装。

 問題はカーソル。
「resource file ○○ is not in 3.00 format」
てなエラーが出ます。
 用意したカーソル画像が3.00formatじゃないということです。

 アイコン画像(ico)とカーソル画像(cur)は、それぞれ専用のファイル形式なのです。
 ただbmpの拡張子を変えただけのものではありません。
 私が使っているペイントソフトは、ico保存はできるのですが、cur保存はできません。できませんが、無理やり拡張子をcurにして保存。完全にこれが原因です!

 Windows付属のペイントでもcur保存できないようなので、カーソルファイル作成ソフトを探してみるものの、いいソフトは見つからず。
 とりあえず、適当なソフトでcurファイルを作りました。

API-4 メニューを作ろう

 本を飛ばし飛ばしで進めて、今回はメニューを作ってみることに。
 さあ、ここでいきなり問題です。

 リソースの作り方がわかりません。

 リソースというのは、そのプログラムに使うメニューやアイコン、画像ファイルなんかをひとまとめにしたファイルで、開発環境があればリソースエディタというリソース作成ソフトで作れるものです。
 が、Expressにはないんです、リソースエディタ。

 本には、エディタに任せましょう、と書いてあり、エディタでの作り方しか書いてありません。
 自分で書こうにも、書式が不明なので無理

 探しましたよ。リソースエディタ。

 フリーのリソースエディタを探して、いくつか見つかったのですが、ソフト開発用というよりはリソース改造用といった感じで私の望むものではなかったです。

 そして、ようやく見つけたのが"BCCForm"。
 リソースエディタはこれしか知らないので比較できませんが、なかなかに使いやすいです。
 ただ、Borland C++ 用のリソースエディターなので、作ったリソースをそのまま Visual C++ 系で使うことができません。
 まあ、簡単なものならそのまま使えるし、そうでなくても少し手を加えるだけでいいんですが。

ディレクトリ関連

ディレクトリ関連の関数を使用するにはdirect.hincludeすればいい。

ディレクトリの作成
1.
2.
#include <direct.h>
int mkdir(const char *pathname);
ディレクトリを作るのは、すでに存在するディレクトリ内でなければならない。
成功すると0、失敗すると非0を返す。

ディレクトリの削除
1.
2.
#include <direct.h>
int rmdir(const char *pathname);
空のディレクトリでなければ削除できない。
成功すると0、失敗すると非0を返す。


2010/05/23追記
mkdir関数は_mkdir関数に置き換わっている。

作成に成功すれば0を返し、失敗すると-1を返す。
 また、失敗した場合errnoに値が格納され、すでに存在するディレクトリ名を指定していた場合EXISTとなるのでディレクトリの存在確認にも使用できるはず。

また、ワイド文字バージョンも存在する。
教えて!goo「C ファイル出力で、フォルダがない場合でも作成する方法」
msdn「_mkdir、_wmkdir」

カレントディレクトリの変更
1.
2.
#include <direct.h>
int chdir(const char *pathname);
成功すると0、失敗すると非0を返す。

以前、実行時のカレントディレクトリの違いによって悩まされたことから、ファイル入出力をするときのカレントディレクトリを意識するようになった。
 その結果、main関数の引数からexeファイルのパスを取得し、そのパスを使って絶対パスでファイル名を指定するようになった。
 でも、chdirを使えばこれまで通りファイル名だけでも意図した通りのディレクトリにファイルを作ることができる。

カレントディレクトリの変更
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
#include <stdio.h>
#include <direct.h>
#include <windows.h>    // GetCurrentDirectory

int main(void)
{
    char s[256];

    GetCurrentDirectory(255,s);
    printf("current : %s\n", s);

    // ディレクトリ削除
    printf("del dir\n");
    if( rmdir("dir") != 0 )
    {
        perror("rmdir");
    }

    // ディレクトリ作成
    printf("make dir\n");
    if( mkdir("dir") != 0 )
    {
        perror("mkdir");
    }

    // カレントを作成したディレクトリに変更
    printf("chenge dir\n");
    chdir("dir");

    GetCurrentDirectory(255,s);
    printf("current : %s\n", s);

    // 1つ上の階層(元のカレントディレクトリ)へ
    printf("chenge dir\n");
    chdir("../");

    GetCurrentDirectory(255,s);
    printf("current : %s\n", s);

    {
        char c;
        c = getchar();
    }
    return 0;
}

ファイルの情報を調べる

ファイルのサイズ、更新日時を調べるには関数statを使えばいい。
関数stat
1.
2.
#include <sys/stat.h>
int stat(const char *pathname, struct stat *sbuf)
第2引数のstat構造体sys/stat.h内で定義されている。
stat構造体のメンバの型はsys/types.h内で typedefされている。
stat構造体メンバの一部
1.
2.
3.
4.
5.
6.
7.
8.
9.
struct  stat
{
        /*stat構造体メンバの一部*/
        mode_t  st_mode;    // short    ファイルモード
        off_t   st_size;    // long     ファイルのサイズ
        _TIME_T  st_atime;  // time_t   最後にアクセスされた時刻
        _TIME_T  st_mtime;  // time_t   最後に修正された時刻
        _TIME_T  st_ctime;  // time_t   最後に属性変更された時刻
};
st_modeS_IFMTでマスクすることで、それがファイルなのかディレクトリなのかを判断することができる。
ファイルとディレクトリとで処理を分岐する例
1.
2.
3.
4.
5.
6.
7.
8.
9.
switch( s.st_mode & S_IFMT )
{
        case S_IFREG: 
        ファイルの時の処理;
        break;
        case S_IFDIR:
        ディレクトリの時の処理;
        break;
}
なお、次のようなマクロも用意されている。
ディレクトリ・ファイル判定用のマクロ
1.
2.
#define S_ISDIR(mode)   ((mode&S_IFMT) == S_IFDIR)
#define S_ISREG(mode)   ((mode&S_IFMT) == S_IFREG)

サンプル
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>

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

        if(argc == 1)
        {
        printf("don't file inputed\n");
        return 0;
        }

        for(i=1; i<argc; i++)
        {
        struct stat  fileStat;
        time_t       t;

        // 入力ファイルの情報を取得
        if( stat(argv[i],&fileStat) != 0 )
                perror("argv[i]");

        // ファイルのモード
        switch( fileStat.st_mode & S_IFMT )
        {
        case S_IFREG:        // ファイル
                printf("this is file\n");
                break;
        case S_IFDIR:        // ディレクトリ
                printf("this is directry\n");
                break;
        }

        // ファイルのサイズ
        printf("file size : %d\n",fileStat.st_size);

        // 最後にアクセスされた時刻
        t = fileStat.st_atime;
        printf("acsess time\t\t: %s",ctime(&t));

        // 最後に修正された時刻
        t = fileStat.st_mtime;
        printf("modification time\t: %s",ctime(&t));
        
        // 最後に属性変更された時刻
        t = fileStat.st_ctime;
        printf("status change time\t: %s",ctime(&t));
        }

        {
        char c;
        c = getchar();
        }
        return 0;
}

ファイルの存在確認

任意のファイルが存在するかしないかを判定するには、
リードモードでファイルを開きエラーなら存在しない、とすればいい。

その他に、ファイルが存在するかを調べるための関数を使用する方法もある。
ファイルが存在するかを調べる
1.
2.
#include <io.h>
int access(const char *pathname, int mode)
第2引数に0を指定すれば存在確認に使うことができる。
戻り値が0なら存在する、非0なら存在しない。

サンプル
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
#include stdio.h>
#include <io.h>

#define FILENAME "access.txt"

int main(void)
{
        FILE *fp;

        // とりあえず、ファイルを削除
        printf("remove file\n");
        if( remove(FILENAME) != 0 )
        perror("remove");

        // ファイル有無の確認
        if( access(FILENAME,00) == 0)
        {
        printf(FILENAME" is exist\n");
        }
        else
        {
        perror("access1");
        }

        // ファイルを作成
        printf("make file\n");
        if( (fp = fopen(FILENAME,"w")) == NULL )
        {
        perror("fopen");
        }
        fclose(fp);

        // ファイル有無の確認
        if( access(FILENAME,00) == 0)
        {
        printf(FILENAME" is exist\n");
        }
        else
        {
        perror("access2");
        }

        {
        char c;
        c = getchar();
        }
        return 0;
}

main関数の引数argc,argvをほかの関数に渡す

main関数には2つの引数がある。
引数の数int argc
argv[]の添え字とargcの値は1つずれていることに注意
引数無しで実行した場合argc=1、引数1つの場合はargc=2となる。
渡された文字列char *argv[]
argv[0]は自分自身のパスが入る。
渡された引数はargv[1]から順に格納されている。

ここで、argvchar型ポインタのポインタなので、
char *argv[]char **argv と同じことになる。

なので、main関数の引数をほかの関数に渡すには次のようにすればいい。

サンプル
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
#include <stdio.h>

void printArgv(int argc,char** argv)
{
        int i;
        for(i=0; i<argc; i++)
        printf("%d\t%s\n", i, argv[i]);
}

int main(int argc, char *argv[])
{
        printArgv(argc,argv);
        {
        char ss[80];
        gets(ss);
        }
        return 0;
}