第153章 再度DDEクライアントを作る


今回は前章で作ったサーバーのクライアントを作ってみます。 と、言っても第150章の知識があれば十分です。接続するサーバーのサービス名は 「dde03」トピックは「NEKO_DEMO_WAKARU」項目は「DATA_ITEM」とします。



まず、前章で作ったサーバーを起動しておきます。次に今回作るクライアントを 起動します。メニューの「DDE」「DDE開始」を選択します。うまくいけば その旨メッセージボックスが表示されます。

「データの取得」を選択すると現在のDATA_ITEM項目のデータをメッセージボックスで 表示します。最初はデータが何もありません。



「データの書きこみ」を選択すると左のようなダイアログボックスが出現します。 これに書きこみたいデータを入力します。再度「データの取得」を選択すると 今入力したデータを知ることができます。



では、プログラムを見てみましょう。

// ddecl01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "DDE(&D)" BEGIN MENUITEM "DDE開始(&S)", IDM_START MENUITEM "DDE終了(&E)", IDM_STOP MENUITEM "データの取得(&G)", IDM_GETDATA MENUITEM "データの書きこみ(&S)", IDM_SETDATA END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // SETDATA DIALOG DISCARDABLE 0, 0, 119, 63 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "データの書きこみ" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,42,50,14 PUSHBUTTON "キャンセル",IDCANCEL,63,42,50,14 LTEXT "書きこむデータ",IDC_STATIC,7,7,45,8 EDITTEXT IDC_EDIT1,7,19,96,14,ES_AUTOHSCROLL END

メニューとダイアログボックスのリソース・スクリプトです。 特に説明は不要ですね。

// ddecl01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HDDEDATA CALLBACK DdemlCallback(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HINSTANCE hInst; HWND hParent; int DDE_Start(HWND); int DDE_END(HWND); int DDE_GetData(HWND); int DDE_SetData(HWND); char szClassName[] = "ddecl01"; //ウィンドウクラス DWORD ddeInst; HSZ hszService; HSZ hszTopic; HCONV hConv; char szBuf[64];//バッファ char szData[256];//書きこむデータ int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

このへんは特に目新しいことはありません。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; hWnd = CreateWindow(szClassName, "猫でもわかるDDEクライアント",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInstance, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hParent = hWnd; return TRUE; }

ここも,いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_START: if (!hConv) DDE_Start(hWnd); else MessageBox(hWnd, "すでに開始されています", "OK", MB_OK); break; case IDM_GETDATA: DDE_GetData(hWnd); break; case IDM_SETDATA: DDE_SetData(hWnd); break; case IDM_STOP: DDE_END(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: DDE_END(hWnd); if(DdeUninitialize(ddeInst) != 0) MessageBox(hWnd, "正常にDDE終了", "ok", MB_OK); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メニューで選択された項目に対してそれぞれの関数を呼んでいます。 特に目新しいことはありません。

int DDE_Start(HWND hWnd) { if(DdeInitialize(&ddeInst, DdemlCallback, APPCMD_CLIENTONLY, 0) != DMLERR_NO_ERROR) { MessageBox(hWnd, "DdeInitialize失敗です", "Error", MB_OK); return -1; } hszService = DdeCreateStringHandle(ddeInst, "dde03", CP_WINANSI); hszTopic = DdeCreateStringHandle(ddeInst, "NEKO_DEMO_WAKARU", CP_WINANSI); hConv = DdeConnect(ddeInst, hszService, hszTopic, NULL); if (DdeGetLastError(ddeInst) != DMLERR_NO_ERROR) { MessageBox(hWnd, "サーバーに接続失敗", "ok", MB_OK); return -1; } return 0; }

DDEを初期化して「dde03」「NEKO_DEMO_WAKARU」「DATA_ITEM」に アクセスできるようにします。ここも第150章の知識のみで作れます。

int DDE_END(HWND hWnd) { if (hConv) { if(DdeFreeStringHandle(ddeInst, hszService) == 0) MessageBox(hWnd, "DdeFreeStringHandle失敗", "Error", MB_OK); if(DdeFreeStringHandle(ddeInst, hszTopic) == 0) MessageBox(hWnd, "DdeFreeStringHandle失敗", "Error", MB_OK); if(DdeDisconnect(hConv) != 0) MessageBox(hWnd, "DdeDisconnect成功", "OK", MB_OK); hConv = 0; } else { MessageBox(hWnd, "終了すべきDDEがありません", "OK", MB_OK); return -1; } return 0; }

DDE終了時の後始末です。

int DDE_GetData(HWND hWnd) { HDDEDATA hRet; HSZ hszMyItem; if (!hConv) { MessageBox(hWnd, "DDE開始されていません", "OK", MB_OK); return -1; } hszMyItem = DdeCreateStringHandle(ddeInst, "DATA_ITEM", CP_WINANSI); hRet = DdeClientTransaction( NULL, 0, hConv, hszMyItem, CF_TEXT, XTYP_REQUEST, 30000, NULL); if (!hRet && DdeGetLastError(ddeInst) != DMLERR_NO_ERROR) { MessageBox(hWnd, "失敗です", "DDECL01", MB_OK); return -2; } else if (hRet) { DdeGetData(hRet, (LPBYTE)szBuf, sizeof(szBuf), 0); DdeFreeStringHandle(ddeInst, hszMyItem); DdeFreeDataHandle(hRet); MessageBox(hWnd, szBuf, "DATA", MB_OK); } return 0; }

これは、DATA_ITEM項目よりデータを取得する関数です。これも特に どうということはないと思います。

HDDEDATA CALLBACK DdemlCallback(UINT uType, UINT uFmt, HCONV hconv, HSZ hsz1, HSZ hsz2, HDDEDATA hdata, DWORD dwData1, DWORD dwData2) { MessageBox(NULL, "DDEコールバック関数が呼ばれました", "DDECALLBACK", MB_OK); return (HDDEDATA)NULL; }

特に働きをしていないDDEコールバックです。 サーバーを先に起動しておき、終了時はクライアントから終了すると一度も呼ばれません。

int DDE_SetData(HWND hWnd) { HDDEDATA hRet; HSZ hszMyItem; if (!hConv) { MessageBox(hWnd, "DDE開始されていません", "OK", MB_OK); return -1; } DialogBox(hInst, "SETDATA", hWnd, (DLGPROC)MyDlgProc); hszMyItem = DdeCreateStringHandle(ddeInst, "DATA_ITEM", CP_WINANSI); hRet = DdeClientTransaction( (LPBYTE)szData, //クライアントデータ strlen(szData) + 1 , //データサイズ hConv, //通信ハンドル hszMyItem, //データ項目 CF_TEXT, //クリップボードフォーマット XTYP_POKE, //トランザクションタイプ 10000, // 最大待ち時間 TIMEOUT_ASYNCで非同期となる NULL); // トランザクションの結果 if (!hRet && DdeGetLastError(ddeInst) != DMLERR_NO_ERROR) { MessageBox(hWnd, "失敗です", "OK", MB_OK); return -2; } else if (hRet) { DdeFreeStringHandle(ddeInst, hszMyItem); DdeFreeDataHandle(hRet); } return 0; }

データを書きこむ関数です。XTYP_POKEトランザクションを使います。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), szData, sizeof(szData)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャです。OKボタンが押されたら IDC_EDIT1に書かれた内容をszDataにコピーしています。 第150章ではサーバーをエクセルにしていましたが、今回は 152章で作ったプログラムをサーバーにしているだけです。

これだけの簡単なプログラムですがいろいろ試していくと興味深いことがわかります。 たとえばdde03サーバーを1つ起動しておきます。次に今回作った クライアントを複数起動します。Aのクライアントで書きこみをして、 Bのクライアントでデータを読み出すとAで書きこんだデータを取得することができます。 つまり、サーバーを1つクライアントを複数起動することによってクライアント間での 通信もできます。

また、同じサーバーを複数起動しておいた場合はどのようなことが 起こるのでしょうか?いろいろ実験してみて下さい。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 06/Nov/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。