ラベル C の投稿を表示しています。 すべての投稿を表示
ラベル C の投稿を表示しています。 すべての投稿を表示

Windows 環境で GDB 起動時に .gdbinit ファイルが読込めない場合の対処法

Windwos 環境にて c 言語のソースコードを GCC でコンパイルして GDB でデバッグをしようとした際に、.gdbinit の自動読込が拒否されて意図したようにデバッグができない問題に遭遇した。

コンソールに表示されたメッセージの通りに対処しても問題を解決できなかったが、最終的に、それと同時に環境変数を追加することで解決できた。

構造体のソート

メモ書き
大きな構造体の配列をソートする場合、インデッックスとキーを代入した構造体配列をソートしたほうが軽くなる。

・ソートしたい構造体配列A

・補助配列B
 index
 key

1 配列Bに代入
for{
B(i).index = i
B(i).key = A(i).(ソートするメンバ)
}
2 配列Bをソート

3 ソートした配列Bのインデックスにしたがって
  Aを出力、代入など
for{
NewA(i).メンバ = A(B(i).index).メンバ
}


今回のマクロにてこのアルゴリズムを使用しました。
以前、どこかで見た記憶があるのですがどこだったか思い出せず
内容もうろ覚えなため正しくないかもしれません。
とりあえず、忘れないうちにメモ。




いったん記事更新してから思い出しました。

言語はC

・ソートしたい構造体配列A
・アドレスの配列B

1 配列Bに構造体配列Aのアドレスを代入
2 B(i).keyをキーにして配列Bをソート
3 NewA = B

他のアプリケーションが起動されているかを調べる

他のアプリケーションが起動されているかを調べる

FindWindow関数の第一引数(クラス名)か第2引数(ウィンドウ名)に起動を調べたいアプリケーションの値を代入する。
起動していればHWNDが返り、起動していなければNULLが返る。

ウィンドウ名を指定する方法が簡単。

例えば、電卓が起動しているのかを調べるなら、
hwnd = FindWindow(NULL, "電卓");

しかし、メモ帳の場合
hwnd = FindWindow(NULL, "無題 - メモ帳");
となり、ファイルに名前がつけられている場合「無題」がファイル名になるため汎用性がない。

そのような場合、クラス名を指定する。
Excelの場合、
hwnd = FindWindow("XLMAIN", NULL);

クラス名が分からない場合、FindWindowで取得したハンドルをGetClassName関数に渡せばよい。


参考
FindWindow関数(msdn)
GetClassName関数(msdn)
VB テクニック編2 - Windows API、ウィンドウハンドル取得、ウィンドウ制御(SAK 図書館)

ファイル一覧

io.h を include

long _findfirst(char *file, _finddata_t *info)
第1引数の条件でファイルを検索し、
第2引数で渡された構造体にファイル情報を入れます。

ワイルドカードを使うことができるので、
例えば "*.txt" ならカレントディレクトリ内の
すべてのテキストファイルが対象となります。

戻り値はファイルハンドル。
失敗したときは-1を返します。


int _findnext(long hFile, _finddata_t *info)
_findfirst関数を使うのは始めだけで、
2つ目のファイル検索からはこちらの関数を使います。

第1引数は _findfirst の戻り値を使います。
成功したときは0を、
失敗した(ファイルがない)ときは-1を返します。


int _findclose(long hFile)
ファイルハンドルを閉じます。


_finddata_t構造体
ファイルの属性や作成時間、サイズなどが代入されます。


こちらのページを参考にさせていただきました。
目指せプログラマー

ファイル・フォルダの名前変更・移動

ファイルの名前を変更する関数renameを使ってファイルの移動をすることができます。

stdio.h を include
int rename(const char *oldpath, const char *newpath);
oldpathをnewpathに変更します。
成功すればOを帰し、失敗すると-1を返します。

例えば、ファイルtest.txtをディレクトリdirに移動したいときは、
rename("test.txt", "dir¥¥test.txt");
と記述します。


久々のプログラミング、今回は関数renameの使い方を覚えるだけだったのですが、いろいろと忘れていたせいで無駄に時間がかかってしまいました。

何でできないんだ!
と1時間近く悩んでいたのですが、

用意したファイル test.txt
ソース中の記述 text.txt

実はファイル名を間違えていただけでした。
あと、¥(半角)を文字として記述するときは¥¥としなければいけないことを忘れていました。

API-11 エディットボックスで全選択

エディットボックスが選択されたときに全ての文字が選択されるようにしたくて、EM_SETSELメッセージを送ったけれどうまくいきません。

サブクラス化すればできるらしいのですが、エディットボックスの数が多くなると面倒そうです。
 EM_SETFOCUSが来たらPostMessageで自作メッセージを送る
 自作メッセージが来たらSetFocus
という方法を見つけました。
http://homepage1.nifty.com/MADIA/vc/vc_bbs/200606/200606_06060046.html

SendMessageだったのがいけなかったのかと思い、
EM_SETFOCUSが来たらPostMessage
1.
2.
3.
case EN_SETFOCUS:
        PostMessage(GetFocus(), EM_SETSEL, 0, -1);
        break;
で試してみたところ、期待通りに動いてくれました。

API-9 ダイアログのコントロール

Tabストップ
ダイアログウィンドウの中のコントロールに対してTab移動ができない。
普通にWindowを作って、そこからダイアログを作成した場合はTab移動できる。

ダイアログに来たメッセージかを判断するIsDialogMessageをメッセージループに書くとTab移動ができるようになった。


WM_COMMAND
なぜか、エディットコントロールだけ、選択時にWM_COMMANDメッセージが複数回やってくる。
ラジオボタンとか、プッシュボタンとかはちゃんと1回だけなのに。


ダイアログウィンドウの初期化
普通ダイアログを作ったときはWM_INITDIALOGメッセージがやってくる。
だが、ダイアログウィンドウの場合、やってくるのはWindowと同じWM_CREATEメッセージ。

ここで初期化のためにGetDlgItemでコントロ-ルのハンドルを取得しようとするが、できない。
Windowからダイアログを作った場合はちゃんと取得できている。
ならばと、ハンドルを取得しなくてもコントロールを操作できるSendDlgItemMessageを使ってみたが、反応しない。

コントロールのプッシュボタンを押したときに同じことをやってみると、GetDlgItemもSendDlgItemMessageもうまくいく。
が、これではエディットボックスの読込みはできても、ラジオボタンの初期化は結局できないまま。

ちなみに、
WM_CREATEはCreateWindowがreturnする前に届く
WM_INITDIALOGはダイアログが表示される前に届く
というタイミングの違いがある。

試しにCreateWindowがreturnした後に適当なメッセージ(まあWM_INITDIALOGが妥当だと思う。どうせシステムからは送られて来ないし)を送って、そこで初期化してみるとうまくいった。

考えてみると、この方法ならWM_INITDIALOGメッセージを処理しているので、そのまま通常のダイアログプロシージャにも使える。


なぜか、wsprintfが%fを正しく処理してくれない。
アクセラレータも動作しない。


wsprintfについて
そもそもwsprintfは小数点数を扱えないみたいです。
sprintfなどprintf系の関数を使うのがいいです。

MSDNライブラリ

API-8 ダイアログウィンドウを使ったアプリケーション

API関連の本やwebページを見ていると、一般にWindowsAPIでアプリケーションを作る場合、
メインとなるウィンドウを作り
そのウィンドウの上にコントロールなどのいろいろな部品を置く
という感じになるみたいです。


で、場合によってはメインウィンドウをダイアログにしたいことがあります。
ダイアログならリソースエディタでコントロールを簡単に配置できるからです。
それがダイアログウィンドウで、Windows付属の電卓なんかがそうらしいです。
http://wisdom.sakura.ne.jp/system/winapi/win32/win89.html

WNDCLASS構造体のメンバに値を代入するとき、
cbWndExtraメンバの値をDLGWINDOWEXTRAとし
lpszClassNameメンバで決めたクラス名をダイアログリソースのCLASSで指定します。

ウィンドウを作るとき、CreateWindowのかわりにCreateDialogを使います。
第3引数は0
通常は親ウィンドウを指定するのですが、親がない
第4引数はNULL
通常はダイアログプロシージャを指定するのですが、ダイアログウィンドウではWNDCLASSで指定されたプロシージャが使用される

ダイアログウィンドウはウィンドウと同じ扱いなので、作成時にはWM_INITDIALOGではなくWM_CREATEメッセージが送られてきます。



2009.3.25追記
BCC+BCCForm環境の注意点

BCCFormで作ったリソースファイルをそのまま使うことができません。
作ったリソースファイルをテキストエディタで開き、DIALOGの中にCLASSパラメータを追加します。

 IDD_DLG DIALOG ・・・・・・・
 EXSTYLE WS_EX_DLGMODALFRAME
 STYLE WS_POPUP | ・・・・・・
 CAPTION "ダイアログのキャプション"
 CLASS "DLG" // これを自分で入力
 FONT 10, "MS P明朝"
 ・・・・・・
今度は逆にBCCFormで編集することができなくなります。
編集する場合は、リソースファイルをテキストエディタで開いてCLASSパラメータを消します。

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;
}

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;
}

久々にプログラミング

以前、少しだけ記事に書いた実験データ処理用のプログラム、ようやく作業に入りました。

図書館で借りてきたこの本を読んで、いろいろと学びました。
  • main関数の引数について
  • 関数のポインタについて
  • マルチバイト文字の処理について
  • リダイレクトについて
  • makeについて
など。

で、この本ではbolandのコンパイラを使っていたので、
改めていつも使っているExpressと以前使っていたBCCエディタとを比べてみると、
Express
使いやすいデバッガ
オートコレクト機能
ポインティングで変数、関数の情報がホップアップ
BCCエディタ
軽い
コンパイル時にmakeファイルを使用
確かに、Expressは便利です。
でも、重たいし、デバッガは万能というわけでもありません。
以前、WinAPIかまったとき、まともに使えなかったです。(使い方が悪いせい?)

で、Expressじゃなくてもいいのではと思い、今回はBCCを使用。

API -3 お手本通りでエラー

 "Express" でWindowsプログラムを作る環境を整えたところで、本を見ながらAPIの勉強開始。

 いきなりでした。

 本に載っている、一番初めのプログラム。
 何もしない、ただ、ウィンドウを表示するだけのプログラム。

 なのに、エラー。

 本と見比べてみても、どこにも間違いはなし。

 CreateWindow関数の第1引数の型が違うとのこと。
 引数はLPCTSTR型。文字を入れたのはchar型変数。
 以前、ボーランドのコンパイラで作ったときは問題なかったのですが、Expressではエラーとなるようです。

 さて、LPCTSTR型って、なんでしょう?

 WinMain関数の第3引数、LPSTR型は文字列へのポインタと書いてあります。
 が、LPCTSTR型については、何も書いてありません。似た名前だし、文字列型の1種であるとは思うものの、違いは何なのか、まったくわかりません。

 調べてみると、
"LPSTR"→"char*"
"LPCTSTR"→"CONST TCHAR*"
とのこと。
 TCHARは、UNICODE対応の環境ではWCHARとして、そうでなければcharとして扱われる型。

 つまり、
"LPCTSTR"→"WCHAR"or"char"
となっているところを、
"char"→"WCHAR"or"char"
としてしまっていたわけです。

 そんなわけで、char型をTCHAR型に変更。
 これで、コンパイルできました。


 いまさらながら、exeファイル作成は"Debug"と"Release"のモードごとに設定されていることに気付く。
 つまり、"Debug"と"Release"を切り替えると、設定も切り替わるということ。

API-2 Expressをカスタマイズ

 APIに再挑戦し始めたときの話です。

 今回APIに再挑戦するにあたり、LSMと同様 "Visual C++ 2005 Express Edition" でプログラミングしようとしました。

 が、いきなり問題。
 Windowsプログラムのプロジェクトって、どうやって作るの?

 プロジェクトというのは、プログラムを作るときのソースの集まりみたいなものです。
 プロジェクト作る→ソース作る→プログラム完成、てな流れになります。
 一つのプロジェクトで一つのプログラム。たぶん。

 Express 使い始めたときからずっと気になってました。
 学校のPCにはVisualStudioが入っていて、新規作成からちゃんとWindowsプログラムのプロジェクトを作れます。
 が、Expressでは同じようにはできないのです。同じように操作していっても、選択肢に無いのですから。
 てか、Windowsプログラムだけでなく、DLLもないですよ!

 しかも、Express、そのままじゃあ "windows.h" をincludeできません。
 だって、Expressのincludeフォルダの中に無いんですから。

 "windows.h" というのは、Windowsプログラムを作るために絶対に必要なファイル。
 そのファイルが無いということは、Windowsプログラムを作れないということなのです。

 調べてみると、すっきり!! デフラグ非公式ヘルプのページにてプロジェクトの追加方法を発見。これで、WindowsプログラムとDLLを作れるようになりました。


 "MSVCR80.DLL"のことといい、今回の"windows.h"のことといい、初期のままじゃ問題ありすぎ!