第155章 DDEホットリンク・クライアントを作る


今回は前章で作ったDDEサーバに接続するクライアントを 作成します。特別難しいことはありません。

1.DdeInitialize関数でDDEを初期化する。 2.DdeConnect関数でサーバーに接続する。 3.DdeClientTransaction関数でサーバーにXTYP_ADVSTARTを送る。 4.DDEコールバック関数でXTYP_ADVDATAを待つ。 5.XTYP_ADVDATAが来たらDdeGetData関数でデータを取得する。 6.アドバイズループが不要になったらサーバーにXTYP_ADVSTOPを送る。 7.DdeDisconnect関数で接続を切る(通信ハンドルを無効にする)。 8.DdeUninitialize関数で通信を終了する。

今までと,ほとんど変わりはありません。アドバイズループを開始するのに XTYP_ADVSTARTを送ります。あとは、DDEコールバック関数に XTYP_ADVDATAが来たら、データを取得してやりたい処理をします。 アドバイズループを終了したいときはXTYP_ADVSTOPを送ってやります。

メニューの「DDE」「DDE初期化」を選択した後、「アドバイズループ開始要求」 を選択します。すると、サーバーから次々と時刻データが送られてくるので これを表示します。サーバーを終了させてしまうと、当然時刻は止まってしまいます。



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

// hotcl01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "DDE(&D)" BEGIN MENUITEM "DDE初期化(&I)", IDM_INIT MENUITEM SEPARATOR MENUITEM "アドバイズループ開始要求(&S)", IDM_HOTSTART MENUITEM SEPARATOR MENUITEM "DDE終了処理(&X)", IDM_DDEEND END END

これは、どうということもないメニューのリソースです。

// hotcl01.cpp #ifndef STRICT #define STRICT #endif #define SERVICE "hotsv01" #define TOPIC "NEKO_DEMO_WAKARU" #define ITEM "HOT_ITEM" #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HDDEDATA CALLBACK DdeMyProc(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int MyDdeInit(HWND); int MyDdeEnd(HWND); char szClassName[] = "hotcl01"; //ウィンドウクラス char szBuffer[256]; HWND hParent; //親ウィンドウのハンドル HSZ hszService; HSZ hszTopic; HSZ hszItem; DWORD ddeInst; HCONV hConv;

接続するサービスはhotsv01(前章で作ったサーバー)、トピックはNEKO_DEMO_WAKARU、 項目名はHOT_ITEMとします。

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; } //ウィンドウ・クラスの登録 ATOM 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 hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるDDE-hotクライアント",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 240,//幅 75, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; hParent = hWnd; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

このへんはいつもと同じです。メインウィンドウの大きさは 時刻表示ができる程度に小さくしてあります。また、メインウィンドウの ハンドルは後で必要になるのでグローバル変数にコピーしておきました。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 10, 10, szBuffer, strlen(szBuffer)); EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_INIT: MyDdeInit(hWnd); break; case IDM_HOTSTART: DdeClientTransaction(NULL, 0, hConv, hszItem, CF_TEXT, XTYP_ADVSTART | XTYPF_ACKREQ, 3000, NULL); break; case IDM_DDEEND: DdeClientTransaction(NULL, 0, hConv, hszItem, CF_TEXT, XTYP_ADVSTOP, 3000, NULL); MyDdeEnd(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if(DdeUninitialize(ddeInst) != 0) MessageBox(hWnd, "DdeUninitialize成功", szClassName, MB_OK); else MessageBox(hWnd, "DdeUninitialize失敗", szClassName, MB_OK); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

親ウィンドウのプロシージャです。

XTYP_ADVSTARTとXTYPF_ACKREQを組み合わせるとデータをクライアント が確認するまでサーバーは次のデータを送らないようにします。 XTYPF_NODATAと組み合わせると実際のデータは送られてきません。 (データの変更のみを知ることができる)

int MyDdeInit(HWND hWnd) { UINT uResult; uResult = DdeInitialize(&ddeInst, DdeMyProc, APPCMD_CLIENTONLY, 0); if (uResult == DMLERR_NO_ERROR) MessageBox(hWnd, "DdeInitialize成功", "OK", MB_OK); else { MessageBox(hWnd, "DdeInitialize失敗", "Error", MB_OK); return -1; } hszService = DdeCreateStringHandle(ddeInst, SERVICE, CP_WINANSI); hszTopic = DdeCreateStringHandle(ddeInst, TOPIC, CP_WINANSI); hszItem = DdeCreateStringHandle(ddeInst, ITEM, CP_WINANSI); hConv = DdeConnect(ddeInst, hszService, hszTopic, NULL); if (hConv == 0) { MessageBox(hWnd, "DdeConnect失敗です", szClassName, MB_OK); if(DdeUninitialize(ddeInst) != 0) MessageBox(hWnd, "DdeUninitialize実行しました", szClassName, MB_OK); return -3; } return 0; }

DDEを初期化してサーバーに接続する関数です。これも,特に説明は不要ですね。

int MyDdeEnd(HWND hWnd) { BOOL bResult, bService, bTopic, bItem; bResult = DdeDisconnect(hConv); if (bResult) MessageBox(hWnd, "DdeDisconnect成功", szClassName, MB_OK); bService = DdeFreeStringHandle(ddeInst, hszService); bTopic = DdeFreeStringHandle(ddeInst, hszTopic); bItem = DdeFreeStringHandle(ddeInst, hszItem); if (bService * bTopic * bItem) { MessageBox(hWnd, "DdeFreeStringHandle成功", szClassName, MB_OK); return 0; } else { MessageBox(hWnd, "DdeFreeStringHandle失敗", szClassName, MB_OK); return -1; } }

通信を終了する関数です。この中ではDdeUninitialize関数は呼んでいません。 アプリケーション終了時に呼んでいます(親のプロシージャを見て下さい)。 なぜかというとDdeUninitialize関数を実行するとこのアプリケーションでは 再度サーバーに接続できなくなってしまうからです。

HDDEDATA CALLBACK DdeMyProc(UINT uType, UINT uFmt, HCONV hconv, HSZ hszTpc1, HSZ hszTpc2, HDDEDATA hData, DWORD dwData1, DWORD dwData2) { switch (uType) { case XTYP_ADVDATA: // hszTpc2:項目名 hszTpc1:トピック名 DdeQueryString(ddeInst, hszTpc2, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, ITEM) != 0) return (HDDEDATA)FALSE; DdeQueryString(ddeInst, hszTpc1, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, TOPIC) != 0) return (HDDEDATA)FALSE; DdeGetData(hData, (LPBYTE)szBuffer, sizeof(szBuffer), 0); InvalidateRect(hParent, NULL, TRUE); return (HDDEDATA)TRUE; } return (HDDEDATA)FALSE;

DDEコールバック関数です。サーバーがデーターを送ってきたら XTYP_ADVDATAが来るので、ここでやりたい処理をします。 ここでは、hDataをszBufferにコピーして、クライアント領域を再描画させています。

サーバーとクライアントを起動して、いろいろ実験してみて下さい。 特にサーバーを終了してからクライアントを終了させたり,その逆をやってみて 失敗する関数を確認してみて下さい。


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

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