第94章 クリップボード その1


今回から、少し気分を変えてクリップボードについてやります。 エジットコントロールなどを使っていると、特に自分で クリップボードに対する操作をプログラムする必要ありません。 しかし、自前で操作できるといろいろ便利なこともあります。 今回は、テキストをクリップボードに貼り付けたり、 クリップボードのテキストを読み込んで、クライアント領域に 表示するプログラムを考えます。 テキストの場合はさほど難しくはありません。

クリップボードに文字列を転送するには次のようにします。

1.hGlobal = GlobalAlloc(GHND, iLength + 1); 2.pMem = GlobalLock(hGlobal); 3.for(i = 0; i < iLength; i++) pMem[i] = pStr[i]; 4.GlobalUnlock(hGlobal); 5.OpenClipboard(hWnd); 6.EmptyClipboard(); 7.SetClipboardData(CF_TEXT, hGlobal); 8.CloseClipboard();

ここで、注意することはSetClipboardDataを実行したあとは、 メモリブロックはWindowsのものとなるので、解放したりロックしたままで あってはいけません。

GlobalAllocについては、第82章を参照してください。

BOOL OpenClipboard( HWND hWndNewOwner // クリップボードをオープンするウィンドウのハンドル );

読んで字の如くの関数です。引数にはクリップボードを オープンするウィンドウのハンドルが来ます。 成功したらTRUE、失敗したらFALSEが返されます。

BOOL EmptyClipboard(VOID)

これも名前の通り、クリップボードをからにします。 成功したときはTRUE、失敗したときはFALSEが返されます。

HANDLE SetClipboardData( UINT uFormat, // フォーマット HANDLE hMem // データハンドル );

名前の通りデータをクリップボードにセットします。

uFormatには、セットするデータのフォーマットを指定します。 CF_BITMAP, CF_TEXTなどがあります。今回はCF_TEXTのみを使います。

BOOL CloseClipboard(VOID)

これも名前の通りの関数です。クリップボードをオープンしたら なるべく速やかにクローズする必要があります。 従って1つのメッセージを処理している間にオープンとクローズを 行う必要があります。

次にクリップボードからテキストを読み込むには 次のようにします。

1.クリップボードのオープン 2.hGlobal = GetCipboardData(CF_TEXT); 3.グローバルメモリブロックの大きさを調べる。   必要ならこの大きさのメモリを確保する 4.グローバルメモリブロックをロックする 5.データのコピー 6.ロックをはずす 7.クリップボードのクローズ

今回は、簡単のためにクリップボードから読み込んだり 転送したりする文字列のサイズを1キロバイト以内と します。従ってクリップボードに1キロを超えるデータが あっても表示できるデータは1キロまでです。 では、早速プログラムを見てみましょう。

// clip01.rcの一部 自分で作る人はwindows.hとシンボル定義のヘッダファイルを // インクルードしてください ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "クリップボード(&C)" BEGIN MENUITEM "書き込み(&W)", IDM_WRITE MENUITEM "読み込み(&R)", IDM_READ END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "クリップボード書き込み" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,72,50,14 PUSHBUTTON "キャンセル",IDCANCEL,130,72,50,14 EDITTEXT IDC_EDIT,7,7,173,53,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL END

今回はダイアログボックスも作ります。クリップボードに貼り付ける文字列 をダイアログボックスより入力します。ES_MULTILINE, ES_WANTRETURNなどに 注意してください。

// clip01.cpp #define STRICT #include <windows.h> #include "resource.h" #include <windowsx.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int WriteClip(HWND, char *); int ReadClip(HWND, char *); char szClassName[] = "clip01"; //ウィンドウクラス HINSTANCE hInst; 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, "猫でもわかるクリップボード",//タイトルバーにこの名前が表示されます 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); return TRUE; }

これも、いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static char string[1024]; HDC hdc; PAINTSTRUCT ps; RECT rc; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; case IDM_WRITE: DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); break; case IDM_READ: ReadClip(hWnd, string); break; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rc); DrawText(hdc, string, strlen(string), &rc, DT_WORDBREAK | DT_EXPANDTABS); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

メニューからIDM_WRITE(書き込み)が選択されたら、入力用の ダイアログボックスを出します。

メニューからIDM_READ(読み出し)が選択されたら、クリップボード から読み出すための自作関数ReadClipを呼び出します。

int WriteClip(HWND hwnd, char *str) { HGLOBAL hGlobal; int iLength; LPSTR lpStr; int i; iLength = lstrlen(str); if (iLength > 1024) iLength = 1024; hGlobal = GlobalAlloc(GHND, iLength + 1); if (hGlobal == NULL) return -1; lpStr = (LPSTR)GlobalLock(hGlobal); for (i = 0; i < iLength; i++) *lpStr++ = *str++; GlobalUnlock(hGlobal); if (OpenClipboard(hwnd) == 0) { GlobalFree(hGlobal); return -2; } EmptyClipboard(); SetClipboardData(CF_TEXT, hGlobal); CloseClipboard(); return 0; }

これは、後述のダイアログプロシージャから呼ばれる、クリップボードに 書き込むための関数です。

簡単のために書き込めるのは1キロバイト以内です。 最初にも書きましたがSetClipboardData関数を呼び出すと グローバルメモリブロックはWindowsの管理となるので 自分でGlobalFreeしてはいけません。筆者はこれを 実行するとどうなるのかと思い実験してみました。 わかったことは、筆者の環境ではGloblFree関数は成功します。 また、その後何事も起こりません。しかし、これはたまたま こうなっているだけの可能性が強いので実験はしないでください。

int ReadClip(HWND hwnd, char *str) { HGLOBAL hGlobal; LPSTR lpStr; int iLength, i; if(!IsClipboardFormatAvailable(CF_TEXT)) { MessageBox(hwnd, "クリップボードを読めません", "失敗", MB_OK); return -1; } OpenClipboard(hwnd); hGlobal = (HGLOBAL)GetClipboardData(CF_TEXT); if (hGlobal == NULL) { CloseClipboard(); return -2; } lpStr = (LPSTR)GlobalLock(hGlobal); iLength = lstrlen(lpStr) + 1; if (iLength > 1024) iLength = 1024; for (i = 0; i < iLength; i++) str[i] = lpStr[i]; GlobalUnlock(hGlobal); CloseClipboard(); InvalidateRect(hwnd, NULL, TRUE); return 0; }

クリップボードからの読み出し関数です。

BOOL IsClipboardFormatAvailable( UINT format // フォーマット );

クリップボードのデータが引数のフォーマットで利用できるか どうかを調べる関数です。クリップボードをオープンしなくても 使えます。

クリップボードからのデータの読み出しは最初に解説したとおりです。

BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { char str[1024]; switch(msg) { case WM_INITDIALOG: SendMessage(GetDlgItem(hDlg, IDC_EDIT), EM_SETLIMITTEXT, (WPARAM)1024, 0L); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT), str, sizeof(str)); WriteClip(hDlg, str); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } break; } return FALSE; }

ダイアログボックスのプロシージャです。

WM_INITDIALOGのところで、エジットコントロールの入力制限を しています。1キロ以上のテキストを入力できないようにしています。 EM_SETLIMITTEXTについては第74章で解説してあります。

今回は、大して難しくはありませんでした。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

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