第152章 DDEサーバーを作る


今回は,DDEサーバーを作ります。サーバーといっても クライアントがデータを求めてきたときだけデータを 返します。また、クライアントがデータを変更することも できます。



まず、DdeInitialize関数で初期化するのは同じです。 しかし、3番目の引数はAPPCMD_CLIENTONLYでは困りますね。 一般的なAPPCLASS_STANDARDにします。

次にDdeNameService関数でサーバーのサポートするサービス名を 登録します。一般にDDEではサービス名はアプリケーション名です。

さて、サーバーではDDEコールバック関数にプログラムの中心があります。

もう一度DDEコールバック関数の引数を見てみます。

HDDEDATA CALLBACK DdeCallback( UINT uType, UINT uFmt, HCONV hconv, HSZ hsz1, HSZ hsz2, HDDEDATA hdata, DWORD dwData1, DWORD dwData2);

クライアントがDdeConnect関数でサービス名とトピック名を指定してくると サーバーはXTYP_CONNECTを受け取ります。このとき、コールバック関数のhsz1 はトピック名、hsz2はサービス名をあらわします。トピック名と サーバー名を確認してOKならTRUEを返します。だめならFALSEを返します。

クライアントがDdeClientTransaction関数でデータを要求してきたら サーバーはXTYP_REQUESTを受け取ります。これに対して DdeCreateDataHandle関数の戻り値を返します。

クライアントがDdeClientTransaction関数でデータを送ってきたら サーバーはXTYP_POKEを受け取ります。このトランザクションを 処理したらDDE_FACKを返します。ビジーで処理できないときは DDE_FBUSYを、無視する場合はDDE_FNOTPROCESSEDを返します。 また、この場合hsz1はトピック名、hsz2は項目名となります。

これだけの予備知識があれば、何とか簡単なサーバーを作れます。

今回はサービス名が「dde03」トピック名が「NEKO_DEMO_WAKARU」 項目が「DATA_ITEM」であるデータの受け渡しをするプログラムを 考えます。

では、プログラムを見てみることにします。

// dde03.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HDDEDATA CALLBACK MyDdeProc(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int MyDdeInitialize(HWND); int EndMyDde(HWND); char szClassName[] = "dde03"; //ウィンドウクラス DWORD ddeInst; HSZ hszService; HSZ hszTopic; 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 = NULL; //メニュー名 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サーバー", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_CREATE: MyDdeInitialize(hWnd); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { EndMyDde(hWnd); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

親ウィンドウが作られると自作のMyDdeInitialize関数を呼んでDDEを初期化します。 終了時にはEndMyDde関数で後始末をします。

// DDEコールバック関数 HDDEDATA CALLBACK MyDdeProc(UINT uType, UINT uFmt, HCONV hConv, HSZ hszTpc1, HSZ hszTpc2, HDDEDATA hdata, DWORD dwData1, DWORD dwData2) { char szBuffer[256]; static char szData[256];//データ用 クライアントから書きこまれないと何もなし HDDEDATA hData; switch (uType) { case XTYP_CONNECT: //hszTpc2:サービス名 hszTpc1:トピック名 DdeQueryString(ddeInst, hszTpc2, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "dde03") != 0) return (HDDEDATA)FALSE; DdeQueryString(ddeInst, hszTpc1, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "NEKO_DEMO_WAKARU") != 0) return (HDDEDATA)FALSE; MessageBox(NULL, "dde03サーバに接続しました", "dde03", MB_OK); return (HDDEDATA)TRUE; case XTYP_REQUEST: //hszTpc2:項目名 hsz1:トピック名 DdeQueryString(ddeInst, hszTpc2, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "DATA_ITEM") != 0) return (HDDEDATA)FALSE; DdeQueryString(ddeInst, hszTpc1, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "NEKO_DEMO_WAKARU") != 0) return (HDDEDATA)FALSE; hData = DdeCreateDataHandle(ddeInst, (LPBYTE)szData, strlen(szData)+1, 0, hszTpc2, CF_TEXT, 0); return hData; case XTYP_POKE: //hszTpc2:項目名 hszTpc1:トピック名 DdeQueryString(ddeInst, hszTpc2, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "DATA_ITEM") != 0) return (HDDEDATA)FALSE; DdeQueryString(ddeInst, hszTpc1, szBuffer, sizeof(szBuffer), CP_WINANSI); if (strcmp(szBuffer, "NEKO_DEMO_WAKARU") != 0) return (HDDEDATA)FALSE; DdeGetData(hdata, (LPBYTE)szData, sizeof(szData), 0); MessageBox(NULL, szData, "dde03サーバ", MB_OK); return (HDDEDATA)DDE_FACK; default: return (HDDEDATA)FALSE; } return (HDDEDATA)FALSE; }

ここが中核部分です。

DWORD DdeQueryString( DWORD idInst, // インスタンスID HSZ hsz, // ストリングハンドル LPTSTR psz, // バッファ DWORD cchMax, // バッファの大きさ int iCodePage // コードページ );

ストリングハンドルのあらわすテキストをバッファにコピーします。 コードページはCP_WINANSIまたはCP_WINUNICODEです。

HDDEDATA DdeCreateDataHandle( DWORD idInst, //インスタンスID LPBYTE pSrc, // ソースバッファ DWORD cb, // DDEオブジェクトの大きさ DWORD cbOff, // ソースバッファのオフセット HSZ hszItem, // 項目名 UINT wFmt, // クリップボードフォーマット UINT afCmd // 作成フラグ );

DDEオブジェクトを作成して,pScrをコピーします。 成功すればデータハンドルを返します。失敗すれば0を返します。

afCmdをHDATA_APPOWNEDを指定するとサーバーがデータハンドルを 持つことを示します。この場合終了したらDdeFreeDataHandleで開放しなくては いけません。これを指定しない場合はコールバック関数から制御が 離れると自動的に無効になります。

int MyDdeInitialize(HWND hWnd) { if (DdeInitialize(&ddeInst, MyDdeProc, APPCLASS_STANDARD, 0) == DMLERR_NO_ERROR) { MessageBox(hWnd, "DdeInitialize成功", "OK", MB_OK); } else { MessageBox(hWnd, "DdeInitialize失敗", "OK", MB_OK); return -1; } hszService = DdeCreateStringHandle(ddeInst, "dde03", CP_WINANSI); hszTopic = DdeCreateStringHandle(ddeInst, "NEKO_DEMO_WAKARU", CP_WINANSI); DdeNameService(ddeInst, hszService, 0, DNS_REGISTER); return 0; }

サービス名を「dde03」トピック名を「NEKO_DEMO_WAKARU」にします。

HDDEDATA DdeNameService( DWORD idInst, // インスタンスID HSZ hsz1, // サービス名のハンドル HSZ hsz2, // 予約済み UINT afCmd // サービス名フラグ );

サーバーのサポートするサービス名を登録または解除します。

afCmdにはDNS_REGISTERを指定するとサービス名の登録、 DNS_UNREGISTERを指定すると解除となります。

成功したら0以外失敗したら0を返します。

int EndMyDde(HWND hWnd) { if (DdeNameService(ddeInst, hszService, 0, DNS_UNREGISTER) !=0) MessageBox(hWnd, "ネームサービス解除成功", "OK", MB_OK); DdeFreeStringHandle(ddeInst, hszService); DdeFreeStringHandle(ddeInst, hszTopic); if (DdeUninitialize(ddeInst) != 0) MessageBox(hWnd, "DdeUninitialize成功", "OK", MB_OK); return 0; }

この関数でDDEを終了させます。

今回作ったプログラムはこれだけではほとんど何もしません。 このプログラムに対するクライアントを作ってみてください。


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

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