VC++2008の最近のブログ記事

CPUがx64だと言っても、まだまだx86の呼出し規則が優勢と言うかWOW64の方が全盛の内に、x86呼出し規則を浚ってみました。

C・C++間や他言語からのDLL内関数呼出しを行う場合、大事なのはCリンケージと __stdcall です。

特に指定しない限り、C++コンパイラはC++リンケージであるC++のタイプセーフな名前付け規約(名前装飾)と、C++の呼出し規則を使用します。

Cコンパイラは既定で、Cの呼出し規則とCリンケージを使います。

★C・C++間でDLL内の関数を利用するには、Cリンケージである extern "C" を指定して関数を宣言し、コンパイラがC++の関数名を装飾するのを禁止する必要があります。

★他言語からDLL内の関数を利用するには、__stdcall 呼出し規則を使用する必要があります。


<注意点>

・C++が装飾名を必須とするのは、オーバーロードされた関数、クラスや名前空間のメンバー、コンストラクターやデストラクター、などなどを一意に特定する必要がある事に起因します。

・__stdcall で修飾さる名前には、関数名の前にアンダースコア '_' が、後にアットマーク '@' が、更にその後に引数リストのバイト数が付けられます。例えば、int __stdcall func(int a, double b) 関数は、_func@12 と名前修飾されます。


【Visual C/C++ コンパイラの呼出し規則】

Visual C/C++ コンパイラの呼出し規則一覧
規約 *1コンパイラ
オプション
スタック
一掃
引数渡しCリンケージ
修飾形式 *2
補足
__cdecl /Gd
x86 のみ
呼出し元 右から左の順で
スタックに積まれる
_fname C/C++標準
__clrcall なし N/A 左から右の順で
CLR式スタックに収容
N/A .NET専用
__stdcall /Gz
x86 のみ
呼出し先 右から左の順で
スタックに積まれる
_fname@num Win32API呼出し
他言語アプリからの
関数呼び出し
__fastcall /Gr
x86 のみ
呼出し先 レジスタで渡され
残りは右から左の順で
スタックに積まれる
@fname@num 通常、高速呼出し
__thiscall なし 呼出し先 右から左の順でスタック
に積まれ this ポインタは
ECX レジスタに格納
N/A C++メンバー関数
__vectorcall /Gv
x86, x64
呼出し先 レジスタで渡され
残りは右から左の順で
スタックに積まれる
fname@@num __fastcall より多くの
レジスタで渡すか
既定の x64 呼出し *3
を使う

 *1:Visual C/C++ コンパイラでサポートされている呼び出し規約
 *2:fname は関数名、num は引数リストのバイト数を表す
 *3:x64ABIの既定では4レジスタ高速呼出しの呼び出し規則が使用される

昨年末からの煽られたコロナ禍で、再度の非常事態宣言。暇が有り過ぎて、64ビット化擬きの真似事をしてみる事に。

だけど、ハードやOSはとっくに64ビット化して10年以上経つのに、64ビットアプリケーションってMSオフィス以外殆ど存在しない状況? だったりして。

なんたって仮想マシンWOW64(Windows 32 On Windows 64)の出来が良過ぎたのか? それとも一般には未だに32ビットアプリで十分なのか? いやいや特殊職以外64ビットアプリなんて抑々不必要?

そんなこんなで、ウィキペディアで64ビットの項目を読んでみると、何とも驚いた事に!

C言語およびC++では、char型を除く組み込みの整数型(short型やint型、long型)は、最低限のビット数や大小関係しか規定していない。C99規格およびC++11規格では、int32_t や int64_t などビット数を規定した固定幅整数型(stdint.h および cstdint にて定義される)を追加し、また64ビット以上の値を表現できることが保証されるlong long型を追加した。

まったくぅ、だからC++は愛想を尽かされるんだよ。基本データ型のサイズ位さっさと規定しろよ! って言いたくなりますよね?

更には、プログラマはしばしば、ポインタとint或いはlongが同じ大きさであるという前提でコードを書いてきた問題があります、と。
つまり、多くのコードが32ビット環境を前提として書かれてきたため、64ビット環境ではこの仮定が度々問題を引き起こす可能性が有りますよ。

そんな訳で先ずは、32ビットWindows環境のデータサイズを確認しようとしたところ、以前の「Windowsプログラムのいろはは、DOS窓でWin32API」で書いたように、素人には64ビット化は任せられないとマイクロソフトが想定したかどうかは分かりませんが、Windows環境に「固定精度整数型」「ポインター精度整数型」「固定精度ポインター型」という3つの新しいデータ型を追加していました。

これらのデータ型を使用すると、64ビット化の準備を整えることが出来るそうです。試しに、これらを含めたデータサイズをチェックするだけのサンプルを作ってみました。


<注意点>

・固定精度整数型は、32ビット用と64ビット用のプログラムで同じ長さ(サイズ)に成ります。

・ポインター精度整数型は、32ビット用にコンパイルされれば32ビット長に、64ビット用にコンパイルされれば64ビット長になります。

・固定精度ポインター型は、長さを32ビットまたは64ビットに明示的に指定します。

・ボイド型ポインタ(void *、別名汎用ポインタ)の参照先変数への読み書きは出来ません。⇒「error C2100: 間接指定演算子 (*) の使い方が正しくありません。」

・POINTER_32、POINTER_64 の型に long long や double なども指定可能だと思いますが、確かめていません。

・マイクロソフトのドキュメントには、新しいデータ型を採用する事で、コードの堅牢性が向上します。(コードを)安全にするには、新しいデータ型を使用します。とあります。

・ドキュメントでは、HWND、HINSTANCE、HDC の3つもボイド型ポインタだろうと思うのですが、コンパイルエラーはそうではないと言っているような(永遠にお手上げ?)。


【32ビットWindows環境のデータ型一覧】

基本型
タイプ定義データ幅ポインタ幅
char 1バイト 4バイト
short 2バイト 4バイト
int 4バイト 4バイト
long 4バイト 4バイト

 「long long」型は不使用が良いかと

固定精度整数型
タイプ定義データ幅ポインタ幅
DWORD32 32ビット符号無し整数 4バイト 4バイト
DWORD64 64ビット符号無し整数 8バイト 4バイト
INT32 32ビット符号付き整数 4バイト 4バイト
UINT32 32ビット符号無し整数 4バイト 4バイト
INT64 64ビット符号付き整数 8バイト 4バイト
UINT64 64ビット符号無し整数 8バイト 4バイト
LONG32 32ビット符号付き整数 4バイト 4バイト
ULONG32 32ビット符号無し整数 4バイト 4バイト
LONG64 64ビット符号付き整数 8バイト 4バイト
ULONG64 64ビット符号無し整数 8バイト 4バイト

 

ポインタ精度整数型
タイプ定義データ幅ポインタ幅
DWORD_PTR ポインタ有効ビット数の符号無し整数 4バイト 4バイト
INT_PTR ポインタ有効ビット数の符号付き整数 4バイト 4バイト
UINT_PTR ポインタ有効ビット数の符号無し整数 4バイト 4バイト
LONG_PTR ポインタ有効ビット数の符号付き整数 4バイト 4バイト
ULONG_PTR ポインタ有効ビット数の符号無し整数 4バイト 4バイト
SIZE_T ポインタが参照できる符号無し最大バイト数 *1 4バイト 4バイト
SSIZE_T ポインタが参照できる符号付き最大バイト数 *1 4バイト 4バイト
HALF_PTR ポインタ有効ビット数半分の符号付き整数 *2 2バイト 4バイト
UHALF_PTR ポインタ有効ビット数半分の符号無し整数 *2 2バイト 4バイト

 *1:ポインタの範囲全体にまたがる必要があるカウントに使用。
 *2:32ビットシステムでは16ビット、64ビットシステムでは32ビット。

文字列型
タイプ定義データ幅ポインタ幅
PSTR 1バイト文字列のポインタ 1バイト 4バイト
LPSTR 1バイト文字列のポインタ 1バイト 4バイト
PCSTR 1バイト文字列のポインタ 1バイト 4バイト
LPCSTR 1バイト文字列のポインタ 1バイト 4バイト
PWSTR 2バイト文字列のポインタ *1 2バイト 4バイト
LPWSTR 2バイト文字列のポインタ *1 2バイト 4バイト
PCWSTR 2バイト文字列のポインタ *1 2バイト 4バイト
LPCWSTR 2バイト文字列のポインタ *1 2バイト 4バイト
PTSTR 1バイト文字列のポインタ 1バイト 4バイト
LPTSTR 1バイト文字列のポインタ 1バイト 4バイト
PCTSTR 1バイト文字列のポインタ 1バイト 4バイト
LPCTSTR 1バイト文字列のポインタ 1バイト 4バイト

 *1:UNICODE文字列に対応。

ウインドウ型
タイプ定義データ幅ポインタ幅
LPARAM ポインタ有効ビット数の符号付き整数 4バイト 4バイト
WPARAM ポインタ有効ビット数の符号付き整数 4バイト 4バイト
LRESULT ポインタ有効ビット数の符号付き整数 4バイト 4バイト
LPVOID ボイドのポインタ 無効 *1 4バイト
HANDLE ボイドのポインタ 無効 *1 4バイト
HWND ‐?‐ *2 無効? 4バイト
HINSTANCE ‐?‐ *2 無効? 4バイト
HDC ‐?‐ *2 無効? 4バイト

 *1:アクセス不可を示す。
 *2:'HWND__' 'HINSTANCE__' 'HDC__' は、今のところ(永久に?)不明、アクセス不可?

固定精度ポインタ型
タイプ定義データ幅ポインタ幅
POINTER_32 32ビットポインタ *1 指定サイズ依存 4バイト
POINTER_64 64ビットポインタ *2 *3 指定サイズ依存 8バイト

 *1:32ビットシステムではネイティブポインタ、64ビットシステムでは切り捨てられた64ビットポインタ。
 *2:64ビットシステムではネイティブポインタ、32ビットシステムでは符号拡張32ビットポインタ。
 *3:32ビットシステムでは上位ポインタのビット状態を想定するのは安全ではありません。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- 32ビットデータ型確認 ---------------- コマンドライン --------------*/

D:\vc2008\x86x64>cl test.c

/*----------------------------------------------------------------------------*/


/*-------------------------------- お試し結果 ------------------------------*/

D:\vc2008\x86x64>test
基本型
sizeof(a) = 1, sizeof(char *) = 4, a = 0x12
sizeof(b) = 2, sizeof(short *) = 4, b = 0x1234
sizeof(c) = 4, sizeof(int *) = 4, c = 0x12345678
sizeof(d) = 4, sizeof(long *) = 4, d = 0x12345678

固定精度整数型
sizeof(f) = 4, sizeof(DWORD32 *) = 4, f = 0x12345678
sizeof(g) = 8, sizeof(DWORD64 *) = 4, g = 0x1234567890abcdef
sizeof(h) = 4, sizeof(INT32 *) = 4, h = 0x12345678
sizeof(i) = 4, sizeof(UINT32 *) = 4, i = 0x12345678
sizeof(j) = 8, sizeof(INT64 *) = 4, j = 0x1234567890abcd12
sizeof(k) = 8, sizeof(UINT64 *) = 4, k = 0x1234567812345678
sizeof(l) = 4, sizeof(LONG32 *) = 4, l = 0x12345678
sizeof(m) = 4, sizeof(ULONG32 *) = 4, m = 0x12345678
sizeof(n) = 8, sizeof(LONG64 *) = 4, n = 0x1234567890ab1234
sizeof(o) = 8, sizeof(ULONG64 *) = 4, o = 0x1234567812345678

ポインタ精度整数型
sizeof(p) = 4, sizeof(DWORD_PTR *) = 4, p = 0x12345678
sizeof(q) = 4, sizeof(INT_PTR *) = 4, q = 0x12345678
sizeof(r) = 4, sizeof(UINT_PTR *) = 4, r = 0x12345678
sizeof(s) = 4, sizeof(LONG_PTR *) = 4, s = 0x12345678
sizeof(t) = 4, sizeof(ULONG_PTR *) = 4, t = 0x12345678
sizeof(u) = 4, sizeof(SIZE_T *) = 4, u = 0x12345678
sizeof(v) = 4, sizeof(SSIZE_T *) = 4, v = 0x12345678
sizeof(w) = 2, sizeof(HALF_PTR *) = 4, w = 0x1234
sizeof(x) = 2, sizeof(UHALF_PTR *) = 4, x = 0x1234

文字列型
sizeof(*sa) = 1, sizeof(PSTR) = 4, *sa = 1
sizeof(*sb) = 1, sizeof(LPSTR) = 4, *sb = 2
sizeof(*sc) = 1, sizeof(PCSTR) = 4, *sc = A
sizeof(*sd) = 1, sizeof(LPCSTR) = 4, *sd = A
sizeof(*se) = 2, sizeof(PWSTR) = 4, *se = 3
sizeof(*sf) = 2, sizeof(LPWSTR) = 4, *sf = 4
sizeof(*sg) = 2, sizeof(PCWSTR) = 4, *sg = A
sizeof(*sh) = 2, sizeof(LPCWSTR) = 4, *sh = A
sizeof(*si) = 1, sizeof(PTSTR) = 4, *si = 5
sizeof(*sj) = 1, sizeof(LPTSTR) = 4, *sj = 6
sizeof(*sk) = 1, sizeof(PCTSTR) = 4, *sk = A
sizeof(*sl) = 1, sizeof(LPCTSTR) = 4, *sl = A

ウインドウ型
sizeof(wa) = 4, sizeof(LPARAM *) = 4, wa = 0x12345678
sizeof(wb) = 4, sizeof(WPARAM *) = 4, wb = 0x12345678
sizeof(wc) = 4, sizeof(LRESULT *) = 4, wc = 0x12345678
sizeof(wd) = 4, sizeof(LPVOID *) = 4
sizeof(we) = 4, sizeof(HANDLE *) = 4
sizeof(wf) = 4, sizeof(HWND *) = 4
sizeof(wg) = 4, sizeof(HINSTANCE *) = 4
sizeof(wh) = 4, sizeof(HDC *) = 4

固定精度ポインタ型
sizeof(ya) = 4, sizeof(char *) = 4, *ya = 0x12
sizeof(yb) = 4, sizeof(int *) = 4, *yb = 0x12345678
sizeof(yc) = 4, sizeof(void *) = 4
sizeof(za) = 8, sizeof(short *) = 4, *za = 0x1234
sizeof(zb) = 8, sizeof(long *) = 4, *zb = 0x12345678
sizeof(zc) = 8, sizeof(void *) = 4

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
// 基本型
    char a = 0x12;
    short b = 0x1234;
    int c = 0x12345678;
    long d = 0x12345678;

// 固定精度整数型
    DWORD32 f = 0x12345678;
    DWORD64 g = 0x1234567890abcdef;
    INT32 h = 0x12345678;
    UINT32 i = 0x12345678;
    INT64 j = 0x1234567890abcdef;
    UINT64 k = 0x1234567890abcdef;
    LONG32 l = 0x12345678;
    ULONG32 m = 0x12345678;
    LONG64 n = 0x1234567890abcdef;
    ULONG64 o = 0x1234567890abcdef;

// ポインタ精度整数型
    DWORD_PTR p = 0x12345678;
    INT_PTR q = 0x12345678;
    UINT_PTR r = 0x12345678;
    LONG_PTR s = 0x12345678;
    ULONG_PTR t = 0x12345678;
    SIZE_T u = 0x12345678;
    SSIZE_T v = 0x12345678;
    HALF_PTR w = 0x1234;
    UHALF_PTR x = 0x1234;

// 文字列型
    PSTR sa = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPSTR sb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    PCSTR sc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPCSTR sd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    PWSTR se = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPWSTR sf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    PCWSTR sg = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPCWSTR sh = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    PTSTR si = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPTSTR sj = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    PCTSTR sk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    LPCTSTR sl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

// ウインドウ型
    LPARAM wa = 0x12345678;
    WPARAM wb = 0x12345678;
    LRESULT wc = 0x12345678;
    LPVOID wd = (LPVOID)0x12345678;
    HANDLE we = (HANDLE)0x12345678;
    HWND wf = (HWND)0x12345678;
    HINSTANCE wg = (HINSTANCE)0x12345678;
    HDC wh = (HDC)0x12345678;

// 固定精度ポインタ型
    char *POINTER_32 ya = (char *)0x12345678;
    int *POINTER_32 yb = (int *)0x12345678;
    void *POINTER_32 yc = (void *)0x12345678;
    short *POINTER_64 za = (short *)0x12345678; // warning C4826: 'short *' から 'short *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。
    long *POINTER_64 zb = (long *)0x12345678; // warning C4826: 'long *' から 'long *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。
    void *POINTER_64 zc = (void *)0x12345678; // warning C4826: 'void *' から 'void *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。


// ウインドウ型チェック
    wd = &p;
    we = &q;
    wf = (HWND)&r;
    wg = (HINSTANCE)&s;
    wh = (HDC)&t;

//  *wd = 0x11; error C2100: 間接指定演算子 (*) の使い方が正しくありません。
//  *we = 0x22; error C2100: 間接指定演算子 (*) の使い方が正しくありません。
//  *wf = 0x33; error C2440: '=' : 'int' から 'HWND__' に変換できません。
//  *wg = 0x44; error C2440: '=' : 'int' から 'HINSTANCE__' に変換できません。
//  *wh = 0x55; error C2440: '=' : 'int' から 'HDC__' に変換できません。

// 固定精度ポインタ型チェック
    ya = (char *)&j;
    yb = (int *)&k;
    yc = &g;
    za = (short *)&n; // warning C4826: 'short *' から 'short *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。
    zb = (long *)&o; // warning C4826: 'long *' から 'long *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。
    zc = &g; // warning C4826: 'DWORD64 *' から 'void *' への変換は符号拡張されています。これは、予期しない実行時の動作を発生させる可能性があります。

    *ya = 0x12;
    *yb = 0x12345678;
//  *yc = 0x1234567890abcdef; error C2100: 間接指定演算子 (*) の使い方が正しくありません。
    *za = 0x1234;
    *zb = 0x12345678;
//  *zc = 0x1234567890abcdef; error C2100: 間接指定演算子 (*) の使い方が正しくありません。

// 文字列型チェック
    *sa = '1';
    *sb = '2';
//  *sc = '3'; error C2166: 左辺値は const オブジェクトに指定されています。
//  *sd = '4'; error C2166: 左辺値は const オブジェクトに指定されています。
    *se = '3';
    *sf = '4';
//  *sg = '7'; error C2166: 左辺値は const オブジェクトに指定されています。
//  *sh = '8'; error C2166: 左辺値は const オブジェクトに指定されています。
    *si = '5';
    *sj = '6';
//  *sk = '1'; error C2166: 左辺値は const オブジェクトに指定されています。
//  *sl = '2'; error C2166: 左辺値は const オブジェクトに指定されています。


// 基本型データ出力
    printf("基本型\n");
    printf("sizeof(a) = %d, sizeof(char *) = %d, a = %#x\n", sizeof(a), sizeof(char *), a);
    printf("sizeof(b) = %d, sizeof(short *) = %d, b = %#x\n", sizeof(b), sizeof(short *), b);
    printf("sizeof(c) = %d, sizeof(int *) = %d, c = %#x\n", sizeof(c), sizeof(int *), c);
    printf("sizeof(d) = %d, sizeof(long *) = %d, d = %#x\n", sizeof(d), sizeof(long *), d);

// 固定精度整数型データ出力
    printf("\n固定精度整数型\n");
    printf("sizeof(f) = %d, sizeof(DWORD32 *) = %d, f = %#x\n", sizeof(f), sizeof(DWORD32 *), f);
    printf("sizeof(g) = %d, sizeof(DWORD64 *) = %d, g = %#llx\n", sizeof(g), sizeof(DWORD64 *), g);
    printf("sizeof(h) = %d, sizeof(INT32 *) = %d, h = %#x\n", sizeof(h), sizeof(INT32 *), h);
    printf("sizeof(i) = %d, sizeof(UINT32 *) = %d, i = %#x\n", sizeof(i), sizeof(UINT32 *), i);
    printf("sizeof(j) = %d, sizeof(INT64 *) = %d, j = %#llx\n", sizeof(j), sizeof(INT64 *), j);
    printf("sizeof(k) = %d, sizeof(UINT64 *) = %d, k = %#llx\n", sizeof(k), sizeof(UINT64 *), k);
    printf("sizeof(l) = %d, sizeof(LONG32 *) = %d, l = %#x\n", sizeof(l), sizeof(LONG32 *), l);
    printf("sizeof(m) = %d, sizeof(ULONG32 *) = %d, m = %#x\n", sizeof(m), sizeof(ULONG32 *), m);
    printf("sizeof(n) = %d, sizeof(LONG64 *) = %d, n = %#llx\n", sizeof(n), sizeof(LONG64 *), n);
    printf("sizeof(o) = %d, sizeof(ULONG64 *) = %d, o = %#llx\n", sizeof(o), sizeof(ULONG64 *), o);

// ポインタ精度整数型データ出力
    printf("\nポインタ精度整数型\n");
    printf("sizeof(p) = %d, sizeof(DWORD_PTR *) = %d, p = %#x\n", sizeof(p), sizeof(DWORD_PTR *), p);
    printf("sizeof(q) = %d, sizeof(INT_PTR *) = %d, q = %#x\n", sizeof(q), sizeof(INT_PTR *), q);
    printf("sizeof(r) = %d, sizeof(UINT_PTR *) = %d, r = %#x\n", sizeof(r), sizeof(UINT_PTR *), r);
    printf("sizeof(s) = %d, sizeof(LONG_PTR *) = %d, s = %#x\n", sizeof(s), sizeof(LONG_PTR *), s);
    printf("sizeof(t) = %d, sizeof(ULONG_PTR *) = %d, t = %#x\n", sizeof(t), sizeof(ULONG_PTR *), t);
    printf("sizeof(u) = %d, sizeof(SIZE_T *) = %d, u = %#x\n", sizeof(u), sizeof(SIZE_T *), u);
    printf("sizeof(v) = %d, sizeof(SSIZE_T *) = %d, v = %#x\n", sizeof(v), sizeof(SSIZE_T *), v);
    printf("sizeof(w) = %d, sizeof(HALF_PTR *) = %d, w = %#x\n", sizeof(w), sizeof(HALF_PTR *), w);
    printf("sizeof(x) = %d, sizeof(UHALF_PTR *) = %d, x = %#x\n", sizeof(x), sizeof(UHALF_PTR *), x);

// 文字列型データ出力
    printf("\n文字列型\n");
    printf("sizeof(*sa) = %d, sizeof(PSTR) = %d, *sa = %c\n", sizeof(*sa), sizeof(PSTR), *sa);
    printf("sizeof(*sb) = %d, sizeof(LPSTR) = %d, *sb = %c\n", sizeof(*sb), sizeof(LPSTR), *sb);
    printf("sizeof(*sc) = %d, sizeof(PCSTR) = %d, *sc = %c\n", sizeof(*sc), sizeof(PCSTR), *sc);
    printf("sizeof(*sd) = %d, sizeof(LPCSTR) = %d, *sd = %c\n", sizeof(*sd), sizeof(LPCSTR), *sd);
    printf("sizeof(*se) = %d, sizeof(PWSTR) = %d, *se = %c\n", sizeof(*se), sizeof(PWSTR), *se);
    printf("sizeof(*sf) = %d, sizeof(LPWSTR) = %d, *sf = %c\n", sizeof(*sf), sizeof(LPWSTR), *sf);
    printf("sizeof(*sg) = %d, sizeof(PCWSTR) = %d, *sg = %c\n", sizeof(*sg), sizeof(PCWSTR), *sg);
    printf("sizeof(*sh) = %d, sizeof(LPCWSTR) = %d, *sh = %c\n", sizeof(*sh), sizeof(LPCWSTR), *sh);
    printf("sizeof(*si) = %d, sizeof(PTSTR) = %d, *si = %c\n", sizeof(*si), sizeof(PTSTR), *si);
    printf("sizeof(*sj) = %d, sizeof(LPTSTR) = %d, *sj = %c\n", sizeof(*sj), sizeof(LPTSTR), *sj);
    printf("sizeof(*sk) = %d, sizeof(PCTSTR) = %d, *sk = %c\n", sizeof(*sk), sizeof(PCTSTR), *sk);
    printf("sizeof(*sl) = %d, sizeof(LPCTSTR) = %d, *sl = %c\n", sizeof(*sl), sizeof(LPCTSTR), *sl);

// ウインドウ型データ出力
    printf("\nウインドウ型\n");
    printf("sizeof(wa) = %d, sizeof(LPARAM *) = %d, wa = %#x\n", sizeof(wa), sizeof(LPARAM *), wa);
    printf("sizeof(wb) = %d, sizeof(WPARAM *) = %d, wb = %#x\n", sizeof(wb), sizeof(WPARAM *), wb);
    printf("sizeof(wc) = %d, sizeof(LRESULT *) = %d, wc = %#x\n", sizeof(wc), sizeof(LRESULT *), wc);
    printf("sizeof(wd) = %d, sizeof(LPVOID *) = %d\n", sizeof(wd), sizeof(LPVOID *));
    printf("sizeof(we) = %d, sizeof(HANDLE *) = %d\n", sizeof(we), sizeof(HANDLE *));
    printf("sizeof(wf) = %d, sizeof(HWND *) = %d\n", sizeof(wf), sizeof(HWND *));
    printf("sizeof(wg) = %d, sizeof(HINSTANCE *) = %d\n", sizeof(wg), sizeof(HINSTANCE *));
    printf("sizeof(wh) = %d, sizeof(HDC *) = %d\n", sizeof(wh), sizeof(HDC *));

// 固定精度ポインタ型データ出力
    printf("\n固定精度ポインタ型\n");
    printf("sizeof(ya) = %d, sizeof(char *) = %d, *ya = %#x\n", sizeof(ya), sizeof(char *), *ya);
    printf("sizeof(yb) = %d, sizeof(int *) = %d, *yb = %#x\n", sizeof(yb), sizeof(int *), *yb);
//  printf("sizeof(yc) = %d, sizeof(void *) = %d, *yc = %d\n", sizeof(yc), sizeof(void *), *yc); error C2100: 間接指定演算子 (*) の使い方が正しくありません。
    printf("sizeof(yc) = %d, sizeof(void *) = %d\n", sizeof(yc), sizeof(void *));
    printf("sizeof(za) = %d, sizeof(short *) = %d, *za = %#x\n", sizeof(za), sizeof(short *), *za);
    printf("sizeof(zb) = %d, sizeof(long *) = %d, *zb = %#x\n", sizeof(zb), sizeof(long *), *zb);
//  printf("sizeof(zc) = %d, sizeof(void *) = %d, *zc = %d\n", sizeof(zc), sizeof(void *), *zc); error C2100: 間接指定演算子 (*) の使い方が正しくありません。
    printf("sizeof(zc) = %d, sizeof(void *) = %d\n", sizeof(zc), sizeof(void *));

    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

1回きりのデータ送受信では、TCP/IPによるソケット通信とは呼べない? という事で、ウィンドウ上で繰り返しデータの送受信を行ってみる事に。

試しに、ボタン1でWinSockの起動、ボタン3でエディットボックスに入力した文字列を相手に送信するだけのサンプルを作ってみました。


<注意点>

・エラー処理は手抜きで済ませています。

・接続要求待ちの accept で新たに生成されたソケットを、クライアントとのデータ送受信に使用します。

・accept に使用したソケットは、何度でも接続要求待ちに利用することが出来ます。(複数のクライアントに対応可能)

・send もブロッキング動作するようですが、送信完了する(TCPの送信バッファにデータを書き終わる)までなので、気付かなかった事に。


【WM_APPについて】

ウィンドウのメッセージループへユーザ独自のメッセージを送る際に、良く見かけるのが WM_USER + xyz の定義ですが WM_USER はユーザの為では無く、定義済みのウィンドウ(コントロール)クラスの為に用意されたものです。このプライベートウィンドウクラスの範囲で、ユーザアプリケーションは重要な独自メッセージを定義してはいけません。

WM_APP は、ユーザアプリケーションが独自メッセージを利用出来るよう用意されたもので、システム定義のメッセージと競合する事は有りません。


【Windowメッセージの範囲】

Windowメッセージコードの範囲
種類範囲説明
WM_XYZXYZ0~0x3FFシステムが使用するメッセージ
WM_USER0x400~0x7FFFプライベートウィンドウクラスが使用するメッセージ *1
WM_APP0x8000~0xBFFFアプリケーションが使用できるメッセージ *2
-- 0xC000~0xFFFFRegisterWindowMessage 関数が定義するメッセージ *3
RESERVED0x10000~将来のために予約

 *1:定義済みウィンドウ(コントロール)クラス用、*2:ユーザ定義用
 *3:アプリケーション実行中に関数を呼び出し、文字列のメッセージコード取得時に定義される


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

#define WM_WS_RECV (WM_APP + 1)

static SOCKET ss, cs;  // ss - server socket, cs - client socket

unsigned WINAPI subThread2(void *hCtr)
{
    int i, len;
    char str[64], buf[64], *datpt;

    if(send(cs, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    while(1) {
        memset(buf, 0, sizeof(buf));
        if((len = recv(cs, buf, sizeof(buf), 0)) <= 0) {
            printf("recv error : %d\n", WSAGetLastError());
            WSACleanup();
            return(1);
        }
        sprintf(str, "len = %d, data = %s", len, buf);
        MessageBox(NULL, str, "クライアント送信データ", MB_OK | MB_ICONINFORMATION);

        datpt = calloc(len + 1, sizeof(char));
        for(i = 0; i < len; i++) {
            datpt[i] = buf[i];
        }
        PostMessage((HWND)hCtr, WM_WS_RECV, (WPARAM)len, (LPARAM)datpt);
    }

    return(0);
}

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char str[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((ss = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(ss, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求待ち
    if(listen(ss, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((cs = accept(ss, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    } else {
        sprintf(str, "クライアントアドレス = %s、ポート = %d", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        MessageBox(NULL, str, "接続要求受付", MB_OK | MB_ICONINFORMATION);
    }

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1, hBtn3, hEdit;
    HINSTANCE hInstance;
    int len, wmid, wmevent;
    char str[64], buf[64];

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 100, 100, 210, 30, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, hWnd, 0, NULL);
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "押下3");
                memset(buf, 0, sizeof(buf));
                len = SendMessage((HWND)hEdit, WM_GETTEXT, (WPARAM)64, (LPARAM)buf);
                if(send(cs, buf, len, 0) < 1) {
                    printf("send error : %d\n", WSAGetLastError());
                    WSACleanup();
                    return(1);
                }
                SetWindowText(hBtn3, "ボタン3");
                EnableWindow(hBtn3, TRUE);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_WS_RECV :
        sprintf(str, "len = %d, data = %s", (int)wParam, (char *)lParam);
        MessageBox(NULL, str, "クライアントからデータが送られてきました。", MB_OK | MB_ICONINFORMATION);
        if((int)wParam == 4 && strncmp((char *)lParam, "exit", 4) == 0) {
            // WinSock 切断
            shutdown(ss, SD_BOTH);
            // ソケットの破棄
            closesocket(ss);
            closesocket(cs);
            // WinSock のリソース解放
            WSACleanup();
        }
        free((char *)lParam);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

#define WM_WS_RECV (WM_APP + 1)

static SOCKET cs;  // cs - client socket

unsigned WINAPI subThread2(void *hCtr)
{
    int i, len;
    char str[64], buf[64], *datpt;

    if(send(cs, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    while(1) {
        memset(buf, 0, sizeof(buf));
        if((len = recv(cs, buf, sizeof(buf), 0)) <= 0) {
            printf("recv error : %d\n", WSAGetLastError());
            WSACleanup();
            return(1);
        }
        sprintf(str, "len = %d, data = %s", len, buf);
        MessageBox(NULL, str, "サーバ送信データ", MB_OK | MB_ICONINFORMATION);

        datpt = calloc(len + 1, sizeof(char));
        for(i = 0; i < len; i++) {
            datpt[i] = buf[i];
        }
        PostMessage((HWND)hCtr, WM_WS_RECV, (WPARAM)len, (LPARAM)datpt);
    }

    return(0);
}

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKADDR_IN sa; // sa - server address
    int i;
    char str[64];
//  char *argv = "localhost";
    char *argv = "127.0.0.1";

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    // ソケットの作成
    if((cs = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv)) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv);
            WSACleanup();
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            sprintf(str, "ホスト名 = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
        MessageBox(NULL, "ホスト名", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            sprintf(str, "アドレス = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
        MessageBox(NULL, "アドレス", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    }

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1, hBtn3, hEdit;
    HINSTANCE hInstance;
    int len, wmid, wmevent;
    char str[64], buf[64];

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 100, 100, 210, 30, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, hWnd, 0, NULL);
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "押下3");
                memset(buf, 0, sizeof(buf));
                len = SendMessage((HWND)hEdit, WM_GETTEXT, (WPARAM)64, (LPARAM)buf);
                if(send(cs, buf, len, 0) < 1) {
                    printf("send error : %d\n", WSAGetLastError());
                    WSACleanup();
                    return(1);
                }
                SetWindowText(hBtn3, "ボタン3");
                EnableWindow(hBtn3, TRUE);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_WS_RECV :
        sprintf(str, "len = %d, data = %s", (int)wParam, (char *)lParam);
        MessageBox(NULL, str, "サーバからの送信データです。", MB_OK | MB_ICONINFORMATION);
        if((int)wParam == 3 && strncmp((char *)lParam, "bye", 3) == 0) {
            // ソケットの破棄
            closesocket(cs);
            // WinSock のリソース解放
            WSACleanup();
        }
        free((char *)lParam);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

WindowsのGUI(ウィンドウ)プログラムでTCP/IP通信を利用する際、アプリケーションが固まったように見えるブロッキング動作が問題となることが有ります。

ずぼらのめんどくさがり屋には、メインスレッドと並列処理可能なサブスレッドが単純明快で打って付けかと。

試しに、WinSockの通信(データの送受信)部分をそっくり其の儘、サブスレッドに突っ込んだだけのサンプルを作ってみました。

ブロッキング動作をする accept, send, recv がサブスレッド上で動作するので、ウィンドウの最低限の動作(メインスレッドが担当)は、滞り無く行われるはずです。


<注意点>

・WSACleanup APIが呼び出されると、プロセス内の全てのスレッドによって発行されたあらゆる保留中のブロッキングや非同期WinSock呼び出しがキャンセルされます。

・WSACleanup APIが呼び出された時点でオープンされていたソケットはリセットされ、closesocket を呼び出した場合と同じように自動的に解放されます。

・全ての保留中データが確実に送信されるようにするには、アプリケーションは shutdown を使用してコネクションをクローズし、クローズ完了まで待機してから closesocket および WSACleanup を呼び出します。

・サーバを終了した直後に再度サーバを起動しようとすると、bind エラーで終了する事が有ります。この現象は、サーバ側で先にクローズを実行すると発生する事が確認されています。先にクライアント側でクローズを実行する事で、このエラーを回避出来るようです。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c user32.lib ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    SOCKET ss, cs;  // ss - server socket, cs - client socket
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char str[64];
    char buf[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((ss = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(ss, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求待ち
    if(listen(ss, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((cs = accept(ss, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    } else {
//      printf("接続要求受付:クライアントアドレス = %s、ポート = %d\n", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        sprintf(str, "クライアントアドレス = %s、ポート = %d", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
        MessageBox(NULL, str, "接続要求受付", MB_OK | MB_ICONINFORMATION);
    }

    if(send(cs, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(cs, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }
//  printf("From Client : len = %d, data = %s\n", len, buf);
    sprintf(str, "len = %d, data = %s", len, buf);
    MessageBox(NULL, str, "クライアント送信データ", MB_OK | MB_ICONINFORMATION);

    // WinSock 切断
    shutdown(ss, SD_BOTH);

    // ソケットの破棄
    closesocket(ss);
    closesocket(cs);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1;
    HINSTANCE hInstance;
    int wmid, wmevent;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, NULL, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001

unsigned WINAPI subThread1(void *hCtr)
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKET cs;  // cs - client socket
    SOCKADDR_IN sa; // sa - server address
    int i, len;
    char str[64];
    char buf[64];
    char *argv = "localhost";
//  char *argv = "127.0.0.1";

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    // ソケットの作成
    if((cs = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv)) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv);
            WSACleanup();
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            printf("connect1 error : %d\n", WSAGetLastError());
            sprintf(str, "ホスト名 = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
//      printf("ホスト名でのサーバ接続完了\n");
        MessageBox(NULL, "ホスト名", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(cs, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            printf("connect2 error : %d\n", WSAGetLastError());
            sprintf(str, "アドレス = %s", argv);
            MessageBox(NULL, str, "接続要求エラー", MB_OK | MB_ICONWARNING);
            WSACleanup();
            return(1);
        }
//      printf("アドレスでのサーバ接続完了\n");
        MessageBox(NULL, "アドレス", "サーバ接続完了", MB_OK | MB_ICONINFORMATION);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(cs, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }
//  printf("From Server : len = %d, data = %s\n", len, buf);
    sprintf(str, "len = %d, data = %s", len, buf);
    MessageBox(NULL, str, "サーバ送信データ", MB_OK | MB_ICONINFORMATION);

    if(send(cs, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        WSACleanup();
        return(1);
    }

    // ソケットの破棄
    closesocket(cs);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hBtn1;
    HINSTANCE hInstance;
    int wmid, wmevent;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                _beginthreadex(NULL, 0, subThread1, NULL, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

TCP/IP通信と言うと、サーバ・クライアントシステムを思い浮かべますが、行き成り外部とデータの遣り取りをするのは危なっかしい気がしますので、テスト環境にも利用される(?)同一パソコン内のプロセス間通信(ソケット通信)を試してみる事に。

試しに、WinSockAPIを使って、内部プロセス間通信(データの送受信)を行うだけのサンプルを作ってみました。

sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;

sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;

上の2つの代入文は、どちらも同じ値を代入します。分かり易い(ピンときた)方を選ぶと良いかも。

因みに、非常に難解なTCP/IP通信をぎゅっと短く平たく(間違いを恐れず)大雑把に言うと、通信する相手<パソコン>をIPアドレスを使って特定する役割を担うのがIP、通信する相手の種類<アプリケーション>をポート番号を使って指定する役割を担うのがTCP、2つ合わせて正確無比な通信をインターネット上で実現しています。


<注意点>

・WinSockAPIの accept, send, recv は、通常ブロッキング動作をします。Windows ではノンブロッキング動作にすることも出来るようですが、select を使った監視処理の方がお勧め(?)のようです。

・ブロッキング動作は利用者にすれば、アプリケーションが固まってしまったように見えます。

・プログラムはWinSockの利用を終了する時、WSACleanup APIを呼び出して、WinSockがプログラムのために確保した全てのリソースを解放しなければなりません。

・プログラムは WSAStartup APIの呼び出し成功数に応じて、WSACleanup APIを呼び出さなければなりません。最後の WSACleanup API呼び出しのみが実際のリソース解放を行います。それ以外は単に、WS2_32.DLL が持つ内部参照カウント数を減算するだけです。

・重要:sa.sin_addr.s_addr = INADDR_ANY; の設定は外部に無造作に通信窓を開けるため、セキュリティーホールに成り得ます。実際、IPアドレス 204.205.206.207 という得体の知れない(多分偽装?)相手から、接続要求を受け取る羽目に。安易に INADDR_ANY を利用すると、きっと痛い目に合う事に・・・。


【SOCKADDR構造体】

SOCKADDR構造体のメンバー
メンバー名説明
sa_family unsigned short アドレスファミリを表す定数 *1
sa_data char [14] sa_family の固有情報を表すための空き領域

 *1:通常はAF_INET(IPv4インターネットプロトコル)


【SOCKADDR_IN構造体】

SOCKADDR_IN構造体のメンバー
メンバー名説明
sin_family short アドレスファミリを表す定数 *1
sin_port unsigned short ポート番号
sin_addr IN_ADDR IPアドレス
sin_zero char [8] 値0で初期化される空き領域

 *1:通常はAF_INET(IPv4インターネットプロトコル)


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- サーバの作成 --------------------- コマンドライン -------------------*/

D:\vc2008\dllchk>cl server.c ws2_32.lib

/*----------------------------------------------------------------------------*/

/*---- クライアントの作成 ------------------ コマンドライン ----------------*/

D:\vc2008\dllchk>cl client.c ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---- client の引数がホスト名----------------- お試し結果 -----------------*/

D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2401
From Client : len = 23, data = Let's try Communication

D:\vc2008\dllchk>client localhost
ホスト名でのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication

/*----------------------------------------------------------------------------*/

/*---- client の引数がアドレス----------------- お試し結果 -----------------*/

D:\vc2008\dllchk>server
接続要求受付:クライアントアドレス = 127.0.0.1、ポート = 2417
From Client : len = 23, data = Let's try Communication

D:\vc2008\dllchk>client 127.0.0.1
アドレスでのサーバ接続完了
From Server : len = 33, data = Wellcome to WinSock Communication

/*----------------------------------------------------------------------------*/


/*---- server.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET s, c;  // s - server socket, c - client socket
    SOCKADDR_IN sa, ca; // sa - server address, ca - client address
    int len;
    char buf[64];

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup error\n");
        return(1);
    }

    // ソケットの作成
    sa.sin_family = AF_INET;
    if((s = socket(sa.sin_family, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        return(1);
    }

    // ソケットに名前を付ける
    sa.sin_port = htons(54321);
//  sa.sin_addr.s_addr = INADDR_ANY;
    sa.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(bind(s, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) {
        printf("bind error : %d\n", WSAGetLastError());
        return(1);
    }

    // 接続要求待ち
    if(listen(s, 1) == SOCKET_ERROR) {
        printf("listen error : %d\n", WSAGetLastError());
        return(1);
    }

    // 接続要求受付
    len = sizeof(ca);
    if((c = accept(s, (SOCKADDR *)&ca, &len)) == INVALID_SOCKET) {
        printf("accept error : %d\n", WSAGetLastError());
        return(1);
    } else {
        printf("接続要求受付:クライアントアドレス = %s、ポート = %d\n", inet_ntoa(ca.sin_addr), ntohs(ca.sin_port));
    }

    if(send(c, "Wellcome to WinSock Communication", 33, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        return(1);
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        return(1);
    }
    printf("From Client : len = %d, data = %s\n", len, buf);

    // WinSock 切断
    shutdown(s, SD_BOTH);

    // ソケットの破棄
    closesocket(s);
    closesocket(c);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/

/*---- client.c ----------------------- お試しソース -----------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    LPHOSTENT host;
    SOCKET c;  // c - client socket
    SOCKADDR_IN sa; // sa - server address
    int i, len;
    char buf[64];

    if(argc != 2) {
        printf("Usage : %s dest\n", argv[0]);
        return(1);
    }

    // WinSock の初期化
    if(WSAStartup(WINSOCK_VERSION, &wsaData) != 0) {
        printf("WSAStartup Error\n");
        return(1);
    }

    if((c = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket error : %d\n", WSAGetLastError());
        return(1);
    }

    sa.sin_family = AF_INET;
    sa.sin_port = htons(54321);
    sa.sin_addr.s_addr = inet_addr(argv[1]);
    if(sa.sin_addr.s_addr == INADDR_NONE) { // 入力がホスト名なら
        if((host = gethostbyname(argv[1])) == NULL) {
            printf("ホスト名の取得に失敗しました : %s\n", argv[1]);
            return(1);
        }
        // サーバへの接続要求
        for(i = 0; (IN_ADDR *)host->h_addr_list[i] != NULL; i++) {
//          sa.sin_addr.s_addr = ((IN_ADDR *)host->h_addr_list[i])->s_addr;
            sa.sin_addr.s_addr = (*(IN_ADDR *)host->h_addr_list[i]).s_addr;
            if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) == 0) {
                break;
            }
        }
        if((IN_ADDR *)host->h_addr_list[i] == NULL) {
            printf("connect1 error : %d\n", WSAGetLastError());
            return(1);
        }
        printf("ホスト名でのサーバ接続完了\n");
    } else { // 入力がドット表記のアドレスなら
        // サーバへの接続要求
        if(connect(c, (SOCKADDR *)&sa, sizeof(sa)) != 0) {
            printf("connect2 error : %d\n", WSAGetLastError());
            return(1);
        }
        printf("アドレスでのサーバ接続完了\n");
    }

    memset(buf, 0, sizeof(buf));
    if((len = recv(c, buf, sizeof(buf), 0)) < 0) {
        printf("recv error : %d\n", WSAGetLastError());
        return(1);
    }
    printf("From Server : len = %d, data = %s\n", len, buf);

    if(send(c, "Let's try Communication", 23, 0) < 1) {
        printf("send error : %d\n", WSAGetLastError());
        return(1);
    }

    // ソケットの破棄
    closesocket(c);

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsアプリケーション間で、データの遣り取りをするにはどうすれば良いのか調べてみると、「クリップボード」や「パイプ」、「ソケット通信」、「共有メモリ」など、複数有ることが分かりました。

その中でも今回は、一番(?)取っつき易そうな「ソケット通信」を試してみる事に。

試しに、WinSockAPIでソケット通信の準備と整理を行うだけのサンプルを作ってみました。

printf("IP = %d.%d.%d.%d\n", (BYTE)*(host->h_addr_list[i]), (BYTE)*((host->h_addr_list[i]) + 1), (BYTE)*((host->h_addr_list[i]) + 2), (BYTE)*((host->h_addr_list[i]) + 3));

printf("IP = %s\n", inet_ntoa(*(IN_ADDR *)host->h_addr_list[i]));

上の2つの printf は、どちらも同じ結果を出力します。分かり易い(ピンときた)方を選ぶと良いかも。

・コマンドの引数にローカル指定だけでなく、ヤフーやグーグルなどの外部指定も動作する事を確認しています。


<注意点>

・プログラムは、WSAStartup API呼び出しが成功した後でのみ、他の Windows Sockets 関数を呼び出すことが出来ます。

・WSADATA構造体の wVersion メンバが呼出し元の条件(想定)に合わない場合、プログラムは WSACleanup 関数を呼び出しWinSockDLLリソースを解放して、WinSockAPIの初期化失敗とする事が望ましいようです。

・最新のWinSockの仕様はバージョン2.2です。現在、以下のバージョンをサポートしています。
  1.0、1.1、2.0、2.1、2.2

・WINSOCK_VERSION は、最新バージョンを指定します。


【WSADATA構造体】

WSADATA構造体のメンバー
メンバー名説明
wVersion WORD 呼出し元が想定する WinSock API のバージョン
wHighVersion WORD DLL がサポートする最新の WinSock バージョン
szDescription char [*1] DLL に関する説明
szSystemStatus char [*2] DLL の状態や構成情報
iMaxSockets unsigned short 利用可能な最大ソケット数 *3
iMaxUdpDg unsigned short UDP データグラムの送受信最大サイズ *3
lpVendorInfo char FAR * ベンダー固有の情報へのポインタ

 *1:WSADESCRIPTION_LEN + 1、*2:WSASYS_STATUS_LEN + 1
 *3:WinSock1.1 下位互換のため


【HOSTENT構造体】

HOSTENT構造体のメンバー
メンバー名説明
h_name char FAR * ホストの正式名称
h_aliases char FAR * FAR * ホストの別名のポインタの配列
h_addrype short 要求されたアドレスの種類を表す定数
h_length short アドレスのバイト単位のサイズ *1
h_addr_list char FAR * FAR * ネットワークアドレス情報のポインタの配列

 *1:IPv4は4バイト、IPv6は16バイト


【IN_ADDR構造体】

IN_ADDR構造体のメンバー
メンバー名説明
s_addrunsigned longアドレスを表す数値 *1

 *1:ドット表記 xxx.yyy.zzz.xyz のIPアドレスを数値形式に変換した値


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- WinSockAPI ソケット使用 -------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c ws2_32.lib

/*----------------------------------------------------------------------------*/


/*---------------------------------- お試し結果 ----------------------------*/

D:\vc2008\dllchk>test
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running

D:\vc2008\dllchk>test localhost
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running
正式名称 = biroku
IP = 127.0.0.1

D:\vc2008\dllchk>test 127.0.0.1
最新バージョン = 2.2
DLLの説明 = WinSock 2.0
現在の状態 = Running
正式名称 = localhost
IP = 127.0.0.1

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    LPHOSTENT host;
    IN_ADDR addr;
    int i, ret;

    // WinSock の初期化
    if((ret = WSAStartup(WINSOCK_VERSION, &wsaData)) != 0) {
        printf("WinSockの初期化に失敗しました:error = %d\n", ret);
        return(0);
    }
    printf("最新バージョン = %d.%d\nDLLの説明 = %s\n現在の状態 = %s\n", (BYTE)wsaData.wHighVersion, wsaData.wHighVersion >> 8, wsaData.szDescription, wsaData.szSystemStatus);

    if((BYTE)wsaData.wHighVersion < 2) {
        printf("WinSockの想定バージョンとサポートバージョンに相違があります。\n");
        return(0);
    }

    if(argc == 2) {
        addr.s_addr = inet_addr(argv[1]);
        if(addr.s_addr == INADDR_NONE) { // ホスト名なら
            host = gethostbyname(argv[1]);
        } else { // アドレスなら
            host = gethostbyaddr((const char *)&addr, sizeof(IN_ADDR), AF_INET);
        }
        if(host == NULL) {
            printf("ホスト名の取得に失敗しました:%s\n", argv[1]);
            return(0);
        }

        printf("正式名称 = %s\n", host->h_name);
        for(i = 0; host->h_aliases[i] != NULL; i++) {
            printf("別名 = %s\n", host->h_aliases[i]);
        }

        for(i = 0; host->h_addr_list[i] != NULL; i++) {
//          printf("IP = %d.%d.%d.%d\n", (BYTE)*(host->h_addr_list[i]), (BYTE)*((host->h_addr_list[i]) + 1), (BYTE)*((host->h_addr_list[i]) + 2), (BYTE)*((host->h_addr_list[i]) + 3));
            printf("IP = %s\n", inet_ntoa(*(IN_ADDR *)host->h_addr_list[i]));
        }
    }

    // WinSock のリソース解放
    WSACleanup();
    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

WindowsのGUIプログラムと呼ぶには何かが足りない? そうでした、ウィンドウを利用する際に代表的な操作方法である「メニュー」が抜け落ちていました。

メニューは拡張子が .rc のテキストファイルに、リソーススクリプトを所定の書式で定義してコンパイル、ウィンドウクラスに登録する事で表示されます。(リソースには、メニューやアイコン、ダイアログボックス、文字列などなどが該当します)

試しに、Win32APIでメニューを表示&動作させるだけのサンプルを作ってみました。


<注意点>

・前回のサンプルに、メニューの操作部分を追加しただけです。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API メニュー 追加 --------------- コマンドライン ---------------*/

D:\vc2008\dllchk>rc resource
D:\vc2008\dllchk>cl test.c user32.lib gdi32.lib resource.res

/*----------------------------------------------------------------------------*/


/*---- resource.h -------------------- お試しソース ------------------------*/

// Resource ID
#define IDR_MENU_MAIN   2000

// Menu Item ID
#define IDM_FILE_OPEN   2001
#define IDM_EXIT        2002
#define IDM_SYSINF_ARCH 2003
#define IDM_SYSINF_TYPE 2004
#define IDM_HELP_ABOUT  2005

/*----------------------------------------------------------------------------*/


/*---- resource.rc ------------------- お試しソース ------------------------*/

#include <windows.h>
#include "resource.h"

IDR_MENU_MAIN MENU
begin
    popup "ファイル(&F)"
    begin
        menuitem "開く(&O)...", IDM_FILE_OPEN
        menuitem separator
        menuitem "終了(&X)",    IDM_EXIT
    end
    popup "ヘルプ(&H)"
    begin
        popup "システム情報"
        begin
            menuitem "OS環境(&A)",    IDM_SYSINF_ARCH
            menuitem "CPU型(&T)",    IDM_SYSINF_TYPE
        end
        menuitem "バージョン情報(&A)",  IDM_HELP_ABOUT
    end
end

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>
#include <process.h>  // _beginthreadex 関数
#include "resource.h"

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

unsigned WINAPI subThread1(void *hCtr)
{
    int i, len;
    char s[] = ", i =   ";

    for(i = 0; i < 5; i++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", i = %d", i);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1000);
    }
    return(0);
}

unsigned WINAPI subThread2(void *hCtr)
{
    int j, len;
    char s[] = ", j =    ";

    for(j = 5; j < 15; j++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", j = %d", j);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(500);
    }
    return(0);
}

unsigned WINAPI subThread3(void *hCtr)
{
    int k, len;
    char s[] = ", k =    ";

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    for(k = 15; k < 19; k++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", k = %d", k);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1500);
    }
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hBtn1, hBtn2, hBtn3, hEdit;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";
    SYSTEM_INFO sysinfo;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "スレッド");
                _beginthreadex(NULL, 0, subThread1, hEdit, 0, NULL);
                _beginthreadex(NULL, 0, subThread3, hEdit, 0, NULL);
                break;
            case IDM_FILE_OPEN :
                // To Do List
                break;
            case IDM_EXIT :
                DestroyWindow(hWnd);
                break;
            case IDM_SYSINF_ARCH :
                GetNativeSystemInfo(&sysinfo);
                switch(sysinfo.wProcessorArchitecture) {
                case PROCESSOR_ARCHITECTURE_INTEL : // x86
                    MessageBox(NULL, "32ビットです。", "This OS's Version", MB_OK);
                    break;
                case PROCESSOR_ARCHITECTURE_ARM : // ARM
                    break;
                case PROCESSOR_ARCHITECTURE_IA64 : // Intel Itanium-based
                    break;
                case PROCESSOR_ARCHITECTURE_AMD64 : // x64(AMD or Intel)
                    MessageBox(NULL, "64ビットです。", "This OS's Version", MB_OK);
                    break;
//              case PROCESSOR_ARCHITECTURE_ARM64 : // ARM64
//                  break;
                case PROCESSOR_ARCHITECTURE_UNKNOWN :
                default :
                    break;
                }
                break;
            case IDM_SYSINF_TYPE :
                GetNativeSystemInfo(&sysinfo);
                switch(sysinfo.dwProcessorType) {
                case PROCESSOR_INTEL_386 :
                    break;
                case PROCESSOR_INTEL_486 :
                    break;
                case PROCESSOR_INTEL_PENTIUM :
                    MessageBox(NULL, "ペンティアム4です。", "This CPU's Version", MB_OK);
                    break;
                case PROCESSOR_INTEL_IA64 :
                    break;
                case PROCESSOR_AMD_X8664 :
                    MessageBox(NULL, "AMD Ryzen7です。", "This CPU's Version", MB_OK);
                    break;
//              case PROCESSOR_ARM :
                default :
                    break;
                }
                break;
            case IDM_HELP_ABOUT :
                // To Do List
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN);
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsは一時、マルチタスクが死に掛けましたが(高々携帯電話機、i何チャラに引き摺られて)、Ver.10に格下げ?されてマルチタスクが息を吹き返しました。

Windowsには「プロセス」と「スレッド」の2種類のタスクが有りますが、プログラム起動時にはプロセスで、プログラム内ではスレッドでと使い分けると簡単な気が・・・、プロセスは複数のスレッドを持てるようですし。

試しに、Win32APIでスレッドを呼び出すだけのサンプルを作ってみました。


<注意点>

・前回のサンプルに、スレッド部分を追加しただけです。

・通常プロセスを起動すると、その中に一つスレッドが自動的に生成されるので、メインスレッド、サブスレッド1、サブスレッド2、サブスレッド3とあります。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【_beginthreadexの引数】

_beginthreadex APIのパラメータ
パラメータ名説明
thrdAttr void * 新しいスレッドのセキュリティ属性。NULLの場合は実行元と同じになる
stackSize unsigned 新しいスレッドのスタックサイズ。0の場合は作成元と同じになる
thrdProc unsigned (*)(void *) スレッド・プロシージャのアドレス
param void * スレッド・プロシージャへのパラメータ
createFlag unsigned CREATE_SUSPENDED を指定すると、実行を開始せずに作成。0は作成して即実行 *1
thrdId unsigned * 新しいスレッドのスレッドIDを受け取る


 *1:CREATE_SUSPENDED を指定した場合、実行開始するには ResumeThread API を呼び出す必要があります。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API スレッド 追加 --------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib  gdi32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

unsigned WINAPI subThread1(void *hCtr)
{
    int i, len;
    char s[] = ", i =   ";

    for(i = 0; i < 5; i++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", i = %d", i);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1000);
    }
    return(0);
}

unsigned WINAPI subThread2(void *hCtr)
{
    int j, len;
    char s[] = ", j =    ";

    for(j = 5; j < 15; j++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", j = %d", j);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(500);
    }
    return(0);
}

unsigned WINAPI subThread3(void *hCtr)
{
    int k, len;
    char s[] = ", k =    ";

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    for(k = 15; k < 19; k++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", k = %d", k);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1500);
    }
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hBtn1, hBtn2, hBtn3, hEdit;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "スレッド");
                _beginthreadex(NULL, 0, subThread1, hEdit, 0, NULL);
                _beginthreadex(NULL, 0, subThread3, hEdit, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

ウィンドウを画面に表示しただけでは、UIと呼ぶには余りにもお粗末。

Windowsプログラムの処理動作には、「手続き型」と呼ばれるコンソールプログラムと「イベントドリブン型」と呼ばれるGUIプログラムの2種類があります。

イベントには、キー入力やマウス操作、メニュー選択などなどが有り、ウィンドウはこうしたイベントをシステムから受け取ります。

試しに、コントロール(部品)を2~3配置して、マウスに応答させるだけのサンプルを作ってみました。

前回のサンプルでは、ウィンドウが直ぐ閉じない様にメッセージボックス関数を return の前に入れていましたが、今回は本来のイベント待ち処理である「メッセージループ」(while 文)に置き換えています。

メッセージループの基本動作は前述したイベントがシステム(Windows)から通知されると、その処理を実行するウィンドウプロシージャを呼び出す、の繰り返しです。

ウィンドウには共通コントロールとして、「ボタン」や「エディットボックス」、「リストボックス」、「コンボボックス」、「スタティックコントロール」などなど、システムのウィンドウクラスが多数用意されています。

つまり、色々なアプリケーションで使われている「ボタン」コントロールは、システム側が提供するウィンドウクラスですので、CreateWindowEx API を使用して、ウィンドウ(=サブウィンドウ)を作成することになります。


<注意点>

・共通コントロールはサブウィンドウですので、ウィンドウ・スタイルには WS_CHILD を含めて指定することになります。メインウィンドウのスタイルは、WS_OVERLAPPEDWINDOW を指定するのが標準のようです。

・ウィンドウは、作成時に必ず、WM_CREATEメッセージの通知を受け取るので、そこでサブウィンドウなどを作成する事になります。

・メインとサブのウィンドウ名の表示効果に違いが有ります。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【MSGの構造体】

MSG構造体のメンバー
メンバー名説明
hwnd HWND メッセージの宛先ウィンドウハンドル
message UINT メッセージID
wParam WPARAM メッセージパラメータ1
lParam LPARAM メッセージパラメータ2
time DWORD メッセージがポストされた時刻
pt POINT メッセージがポストされた時のカーソル位置


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API ウィンドウ 作成 ------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib  gdi32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_EDITBOX 1011

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hEdit, hBtn1, hBtn2;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsアプリケーションの特徴はウィンドウ。GUIプログラムの基本もウィンドウ。って事は、全てのWindowsアプリケーションはGUIアプリケーション?

などと考えている内に、Win32APIでも触ってみるかという事に。

試しに、Windowsのウィンドウを表示させるだけのサンプルを作ってみました。

ただ、素人にはウィンドウ・プログラムの基本動作は任せられないとマイクロソフトが想定したかどうかは分かりませんが、ウィンドウクラスの登録(RegisterClassEx API)とウィンドウの生成(CreateWindowEx API)で、ほぼ最低限の機能を持つウィンドウが表示されたのには感心しました。


<注意点>

・サンプルを実行すると、プログラム終了と同時にウィンドウが閉じてしまうので、最低限の動作を確認できるように return の直前でメッセージボックス関数を呼び出し、何某かの入力待ちとなるようにしています。

・今回はウィンドウを表示するだけなので、Windowsメッセージを処理する関数は登録しません。代わりにWNDCLASSEXの lpfnWndProc には、デフォルトの処理を実行してくれる DefWindowProc API を登録します。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【WNDCLASSEXの構造体】

WNDCLASSEX構造体のメンバー
メンバー名説明
cbSize UINT WNDCLASSEX 構造体のサイズ
style UINT クラスのスタイル
lpfnWndProc WNDPROC ウィンドウ・プロシージャ(関数)のポインタ
cbClsExtra UINT ウィンドウ・クラスの追加データサイズ
cbWndExtra UINT ウィンドウ毎の追加データサイズ
hInstance HINSTANCE 実行プログラムのインスタンス・ハンドル
hIcon HICON ウィンドウを最小化した時に表示に使用されるアイコン
hCursor HCURSOR マウスカーソルがウィンドウ上にある時のカーソル形状
hbrBackground HBRUSH ウィンドウのクライアント領域を塗り潰すのに使うブラシ
lpszMenuName LPCTSTR メニュー名(デフォルトでリソースID)
lpszClassName LPCTSTR ウィンドウ・クラス名
hIconSm HICON システム・メニューに表示される小さなアイコン


【CreateWindowExの引数】

CreateWindowEx APIのパラメータ
パラメータ名説明
dwExStyle DWORD 拡張ウィンドウスタイル(CreateWindowEx API のみ)
lpClassName LPCTSTR ウィンドウ・クラス名
lpWindowName LPCTSTR ウィンドウ名(タイトルバーなどに表示)
dwStyle DWORD ウィンドウ・スタイル
x int ウィンドウの初期位置(左上隅、x座標)
y int ウィンドウの初期位置(左上隅、y座標)
nWidth int ウィンドウの初期サイズ、横幅
nHeight int ウィンドウの初期サイズ、高さ
hWndParent HWIND 子ウィンドウ、親ウィンドウのハンドル
上記以外、 所有ウィンドウのハンドル
hMenu HMENU 子ウィンドウ、子ウィンドウID
上記以外、 メニュー・ハンドル(デフォルトの場合NULL)
hInstance HINSTANCE ウィンドウ・クラスのインスタンス・ハンドル
lpParam LPVOID 任意のパラメータ


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API ウィンドウ 作成 ------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>

#define WINCLASS_NAME "Form.003"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = DefWindowProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    ret = MessageBox(NULL, "人為的地球温暖化はあやかしだと思うか?", "気候変動", MB_YESNOCANCEL | MB_ICONQUESTION);
    switch(ret) {
    case IDYES :
        MessageBox(NULL, "地球は訴えかけている\n命を護る行動を", "全てを救え、頼んだぞ", MB_OK | MB_ICONWARNING);
        break;
    case IDNO :
        MessageBox(NULL, "宇宙線は囁いている\n坐して死を待つのかと", "地球寒冷化だ", MB_OK | MB_ICONERROR);
        break;
    default :
        MessageBox(NULL, "防御態勢を整えながら\n次の衝撃波に備えよ", "総員配置に付け", MB_CANCELTRYCONTINUE | MB_ICONSTOP);
        break;
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

このアーカイブについて

このページには、過去に書かれたブログ記事のうちVC++2008カテゴリに属しているものが含まれています。

前のカテゴリはPHP5です。

次のカテゴリはWireless Lanです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

お気に入りリンク

NOP法人 アジアチャイルドサポート 最も大切なボランティアは、自分自身が一生懸命に生きること