第4回 クリップボード その1

 

BTMemoに限らず、たいていのアプリケーションはクリップボードを介して相互にデータを交換することができます。

というか、そのために用意されている仕組みがクリップボードです。

ここでは、クリップボードの使い方をお伝えします。

 

(1)プレーンテキストをクリップボードに登録する

 

まず、クリップボードへデータを登録する手順を兼ねまして、プレーンテキストをクリップボードへ登録する方法です。

        //クリップボードに格納するデータです。ここでは例としてリテラル文字列を使用します。

        char szSrcData[] = "この文字列をクリップボードに登録します"

 

        //(A)クリップデータへ格納するデータは共有メモリに格納する必要があります。

        //   必要なだけの共有メモリを獲得してそこにデータをコピーします

        int len = strlen(szSrcData)+1;

        HGLOBAL hData = (HGLOBAL)::GlobalAlloc(GHND, (DWORD)len);

        char *pData = (char *)::GlobalLock(hData);

        strcpy(pData, szSrcData);

        ::GlobalUnlock(hData);

 

        //(B)クリップボードをオープンします。

        ::OpenClipboard(NULL);

 

        //(C)今クリップボードに入っているデータを廃棄します。

        ::EmptyClipboard();

 

        //(D)クリップボードへグローバルメモリを登録する

        if (::SetClipboardData(CF_TEXT, hData) == NULL) {

                // 登録失敗の場合は、自分で共有メモリを開放する必要があります。

                ::GlobalFree(hData);

        }

        //(E)クリップボードをクローズする

        ::CloseClipboard();

 

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

(B)(C)(E)は、おまじないみたいなものですので説明は省きます。

私が思うに、クリップボードを扱う際の主題は(A)と(D)です。

 

まず(D)ですが、クリップボードに登録するデータを格納した共有メモリのハンドルと、

その共有メモリ内に入っているデータの種類を示すクリップボードフォーマットをを指定します。

上記例では、クリップボードフォーマットに CF_TEXT を使用していますがこれは、

・共有メモリにはプレーンテキスト(シフトJIS)が格納されている

・共有メモリの先頭から文字列が始まりヌル文字で終わる

と言うことを意味しています。

なお、登録が成功した場合、登録したハンドルの所有権はクリップボードに移ります。

プログラムでハンドルを廃棄したり、データを変更するような事は厳禁です。

 

次に(A)ですが、(D)で CF_TEXT を指定するために、それに合致したデータを作りこんでいるわけです。

 

 

 

(2)クリップボードフォーマット

 

クリップボードフォーマットには、CF_TEXTの様にあらかじめ用途の決められているものがあります。

BTMemoでは他にCF_HDROPやCF_BITMAPというクリップボードフォーマットを使用しています。

CF_HDROPは、エクスプローラ等でファイルをコピーした際に使用されているフォーマットです。

CF_BITMAPは、ペイントブラシなどで、画像をコピーした際に使用されているフォーマットです。

 

さらに、プログラムが自分専用のクリップボードフォーマット(番号)をOSから割り当ててもらうことができます。

        // BTMemo用のクリップボードフォーマットの割り当てを得る

        int CF_TAD = ::RegisterClipboardFormat("CF_BURUTURI_DATA_BUS");

 

   −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

プログラムに固有の文字列をキーワードとします。キーワードが最初に使われたとき、本当に割り当てが行われ、

キーワードにが既に使われていれば、最初に割り当てられたのと同じものが返却されます。

したがって、誰が最初かなんて事は気にしなくても良いのです。

 

BTMemo以外のプログラムでも上記と同じキーワードを指定してクリップボードフォーマットの割り当てを得れば、

BTMemoとクリップボードを介してデータを交換することができます。

共有メモリ内のデータフォーマットは、BTMemoのファイルフォーマットとほとんど同じです。

(カレンダーのソースにサンプルがあります。興味があればそちらをご覧下さい)

 

上記と同じ事がBTMemo以外のプログラムにも当てはまります。

        // インターネットエクスプローラがURLをクリップボードへ格納する際のクリップボードフォーマットを得るには

        int CF_URL = ::RegisterClipboardFormat(CFSTR_SHELLURL);   // CFSTR_SHELLURLはマクロで、文字列定数が宣言されてます

 

   −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

このクリップボードフォーマットを使用すれば、インターネットエクスプローラが登録したURLをクリップボードから取得できます。

共有メモリ内のデータフォーマットはCF_TEXTと同じで単なる文字列です。

 

正確にいうと、このクリップボードフォーマットでクリップボードからデータを得れるかどうか未確認です。

しかしドラッグ&ドロップ(クリップボードと同じようにクリップボードフォーマットと共有メモリを使用してデータ交換します)では

データ交換できます。

 

 

(3)クリップボードフォーマットが異なるデータを同時にクリップボードに登録する

        //(A)さまざまなクリップボードフォーマット用のデータを用意します。

        HGLOBAL hText = GetTextData();      // プレーンテキストのデータを共有メモリに格納し、そのハンドルを返却する関数を仮定

        HGLOBAL hMemo = GetBTMemoData();    // BTMemo独自形式のデータを共有メモリに格納し、そのハンドルを返却する関数を仮定

 

        //(B)クリップボードをオープンします。

        ::OpenClipboard(NULL);

 

        //(C)今クリップボードに入っているデータを廃棄します。

        ::EmptyClipboard();

 

        //(D)クリップボードへグローバルメモリを登録する

        ::SetClipboardData(CF_TEXT, hText) ;     // プレーンテキストのデータを登録します

        ::SetClipboardData(CF_TAD , hMemo) ;     // BTMemo独自形式のデータを登録します。

 

        //(E)クリップボードをクローズする

        ::CloseClipboard();

 

   −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

 

クリップボードからデータを取り出すプログラムが、登録されているクリップボードフォーマットを見て、自分が扱えるデータだけを持っていくことになります。

BTMemo以外のプログラムはBTMemo独自形式のデータは使用せず、プレーンテキストだけを持っていくでしょう。

BTMemoは、まずBTMemo独自形式の有無を調べ、有ればそれを最優先して取り出します。それが無ければプレーンテキストを処理します。

 

 

 

(4)CF_HDROP形式でクリップボードに登録するデータを作成する

 

カレンダーからドラッグ&ドロップでBTMemoにリンクを作成するときに使用してます。

// パス名からCF_HDROP形式のデータを得る

HGLOBAL GetClipData_HDROP(char *pszPathName)

{

        // メモリを獲得しクリップボードデータを作成

        HGLOBAL hData = (HGLOBAL)::GlobalAlloc(GHND, (DWORD)sizeof(DRAGDATA));

        if (hData == NULL) {

                return NULL;

        }

        DRAGDATA *pData = (DRAGDATA *)::GlobalLock(hData);

        pData->df.pFiles = sizeof(DROPFILES);           // 変数名はポインタですが、メモリの先頭からのバイト位置です

        strcpy(pData->szPathName, pszPathName);         // 複数のファイルを登録したい場合は、続けてパス名を登録すればよかったと思うのですが…

                                                        // 正確なところは失念してしまいました

        ::GlobalUnlock(hData);

        return hData;

}

   −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

カレンダーのソースから抜粋してきました。

クリップボードの処理でもドラッグ&ドロップの処理でもデータの作り方は同じです。