C プログラミングの小技

C言語による WIN32API 利用 (Drag & Drop, Clipboard )    (2010/04/10)

はじめに

GUI環境では、CUIベースのコマンドを使用する場合 は、%SystemRoot%\system32\cmd.exe 等を起動し、コマンドを
実行させる必要が生じます。
必要な機能をGUIプログラムとして作成し、処理対象のファイル名を Drag&Drop で渡せると便利な場合も多いので、
簡単なGUIプログラムの雛形を用意します。
OLE を使用していないので、一部の情報は Drag&Drop で渡すことが出来ません。
対処方法として、クリップボードを利用する事でデータを渡します。
また、fprintf で情報を出力できると便利なケースも多いので、cmd.exe 以外の explorer.exe 等から起動された場合
には、コンソールウインドウも同時に生成する事にします。

雛形プログラムに関して

このプログラムは、プログラムを作成する時のスタートとな る最低限度の実装です。そのため、このプログラムが
出来る事は、ウインドウの生成、ドラッグアンドドロップの有効化、ドラックアンドドラックで受け取ったファイル名の
表示(コンソールに表示)と画面内でマウスの左ボタンをクリックする事で、クリップボード中のデータ読み出しと、
表示(コンソールに表示)する処理のみです。
cmd.exe からプログラムが起動された時は起動した cmd.exe の画面に表示されます。
explorer.exe からダブルクリックでコマンドが起動された時は、コンソールウインドウを同時に生成し、ドラックアンド
ドロップで送られてきたファイル名や、クリップボードから取得したデータは、生成したコンソール画面に表示します。
クリックボードは、Windows のプログラムで共有して使用しますので、処理に時間がかかる場合は、ローカルに
データをコピーし、使用を解除(CloseClipboard())するのが望ましい処理です。
C言語で記述された100行程度のプログラムで、WIN32APIのみ利用しているので容易に理解できると思います。

md5sum: 20b0579f91a1623544e2855c133d50cf DragAndDropEx.zip
Drag&Drop、Copy&Paste、の使用と同時に
プログラムアイコンに Drag&Drop して使いたい
DragAndDropExArg.c

ソフトの使用に関して

windows に cygwin が導入された環境で、 gcc (3.x.x) でコンパイルされることを想定しています。
実行時には、cygwin を必要としないように -mno-cygwin オプションを付けてコンパイルしています。
cygwin  のインストールに関しては、http://www.cygwin.com/setup.exe の setup.exe をダウンロードします。
実際のインストール方法は、多くのサイトで公開していますのでそちらを参照下さい。
現行の cygwin  のバージョンは 1.7.x で、ディフォルトの gcc 4.x は -mno-cygwin オプションが利用できない
ため、gcc 3.x. を使用しています。 今なら、MinGW-w64を使用する方法も有ります

ソフトのコンパイルと実行

以下の作業は、 "コマンド プロンプト" を起動して実行します。
ダウンロードしたファイルは、C:\DragAndDrop に保存されていると仮定しています。


C:\> cd C:\DragAndDrop>
C:\DragAndDrop>
C:\DragAndDrop>cat DragAndDropEx.c

#include <stdio.h>
#include <windows.h>
/* Drag & Drop でファイル名を受け取るプログラムの雛形
gcc -O2 -W -Wall -mno-cygwin -mwindows -o DragAndDropEx DragAndDropEx.c
gcc -O2 -W -Wall -mno-cygwin -mwindows -mconsole -o DragAndDropEx DragAndDropEx.c
or
gcc -O2 -W -Wall -mwindows -o DragAndDropEx DragAndDropEx.c
gcc -O2 -W -Wall -mwindows -mconsole -o DragAndDropEx DragAndDropEx.c
*/
TCHAR    fnameWithPath[MAX_PATH] ;

void processing( char *fnameWithPath )
{// fprintf の使用には、gcc のコンパイルオプションに -mconsole を指定することが必須
    fprintf( stdout, "D&D:%s\n", fnameWithPath ) ;
}

void messageFromClipboard( char *message )
{// fprintf の使用には、gcc のコンパイルオプションに -mconsole を指定することが必須
    fprintf( stdout, "Clipboard:%s\n", message ) ;
}

////////////////////////////////////////////////////////////////////////////////
//  ウィンドウプログラム部    // HWND は、対象ウィンドウを特定するハンドル (Window ID)
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch( message ) {
        case WM_CREATE:  // CreateWindowEx(...) で WS_EX_ACCEPTFILES, を指定済み
//          DragAcceptFiles(hWnd, TRUE); // この Drag & Drop操作を有効にする処理は不要
            break;
        case WM_DROPFILES:
// ドラッグアンドドロップで呼び出される
            SetForegroundWindow(hWnd); // 他のスレッドよりも若干高い優先順位を割り当て
            {register int i, max;
                for( i=0, max=DragQueryFile( (HDROP)wParam, -1, NULL, 0 ); i<max; i++) {
                    DragQueryFile( (HDROP)wParam, i, fnameWithPath, sizeof(fnameWithPath) ) ;
                    processing( fnameWithPath ) ;
                }
                DragFinish((HDROP)wParam);
            }// この{...}部は、スコープが限定されたブロックを生成 ブロック内定義は、private
            break;
       case WM_LBUTTONDOWN: // マウスの左ボタンクリックで呼び出される
            // OLE の drag & drop を使用していないので代替手段、クリップボードの読み出し
            {static char  *error_msg = "Clipboard Open Error!" ;
                if( OpenClipboard(NULL) == 0 ) {
                    messageFromClipboard( error_msg ) ;
                } else {
                    messageFromClipboard( GetClipboardData(CF_TEXT) ) ;
                    CloseClipboard();
                }
            }// この{...}部は、スコープが限定されたブロックを生成 ブロック内定義は、private
            break;
/*      major events
        case WM_RBUTTONDOWN:
            break;
        case WM_MOUSEMOVE:
            break;
        case WM_KEYDOWN:  // MessageBox(hWnd, "WM_KEYDOWN", "キーイベントの発生", MB_OK);
            break;
        case WM_SIZE:     // ウィンドウサイズ調整
            break;        // Send(If it's necessary) PostMessage(hWnd,WM_PAINT,0,0) or No break;
        case WM_PAINT:    // (再)描画処理
            {PAINTSTRUCT ps ;
                hdc = BeginPaint(hWnd, &ps);
                EndPaint(hWnd, &ps);
            }
            break;
*/
        case WM_CLOSE:    // 必要な確認処理後、DestroyWindow関数を呼び出す
            DestroyWindow( hWnd ) ;
            break;
        case WM_DESTROY:  // ウィンドウを破棄した後に送られるメッセージ
            PostQuitMessage( 0 ) ;
            break;
        default:
            return DefWindowProc( hWnd, message, wParam, lParam ) ;
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ (void)hInstance, (void)hPrevInstance, (void)lpCmdLine, (void)nCmdShow;
HWND        hWnd ;
WNDCLASSEX  wc ;
MSG         message ;
int         returnedValue ;          // BOOL が本来の正しい型
const char  *ClassName  = "Canvas",  *title  = "drag and drop" ;

    // ウィンドウクラスの情報を設定
    wc.cbSize        = sizeof(WNDCLASSEX) ;                   // 構造体のサイズ
    wc.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;  // スタイルの設定
    wc.lpfnWndProc   = WindowProcedure;                       // ウィンドウプロシージャ
    wc.cbClsExtra    = 0;                                     // 構造体が使用する extra メモリサイズ
    wc.cbWndExtra    = 0;                                     // ウィンドウが使用する extra メモリサイズ
    wc.hInstance     = hInstance;                             // インスタンスハンドル
    wc.hIcon         = LoadIcon(hInstance, "PRGICON") ;       // アイコン
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;          // マウスカーソル
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ;  // ウィンドウ背景
    wc.lpszMenuName  = NULL,                                  // メニュー名  MAKEINTRESOURCE(..) or
    wc.lpszClassName = TEXT(ClassName) ;                      // ウィンドウクラス名
    wc.hIconSm       = LoadIcon(hInstance, "PRGICON") ;       // 子アイコン
   
    if( RegisterClassEx( &wc ) == 0) return 1;               // ウィンドウクラスの登録

    if( (hWnd = CreateWindowEx(     // ウィンドウの生成
            WS_EX_ACCEPTFILES,      // ドラッグ&ドロップの許可 WS_EX_ACCEPTFILES
                                    // DragAcceptFiles(hWnd, TRUE); は不要
            wc.lpszClassName,       // ウィンドウクラス名
            TEXT(title),            // タイトルバーに表示する文字列
            WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
                                    // ウィンドウの種類  WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,          // ウィンドウを表示する位置(X座標) CW_USEDEFAULT,
            CW_USEDEFAULT,          // ウィンドウを表示する位置(Y座標) CW_USEDEFAULT,
            256,                    // ウィンドウの幅    DEFAULT ⇒  MAIN_LX_SIZE,
            128,                    // ウィンドウの高さ  DEFAULT ⇒  MAIN_LY_SIZE,
            NULL,                   // 親ウィンドウのウィンドウハンドル
            NULL,                   // メニューハンドル
            hInstance,              // インスタンスハンドル
            NULL                    // その他の作成データ
    ) ) == NULL ) return 1;

    ShowWindow( hWnd, SW_SHOW ) ;    // ウィンドウを表示
    UpdateWindow( hWnd ) ;           // ウィンドウの更新
    //  メニューの有効無効設定は、WindowProcedure の WM_CREATE で行うので、
    //  現時点において、hWnd でのアクセスは失敗する。

    while( (returnedValue = GetMessage(&message, NULL, 0, 0)) != 0 ) { // メインループ
        if( returnedValue == -1 ) return -1; // GetMessage() が失敗(-1)なら終了する
        TranslateMessage( &message ) ;
        DispatchMessage( &message ) ;
    }
    return 0;
}


C:\DragAndDrop>gcc -O2 -Wall -mno-cygwin -mwindows -mconsole -o DragAndDropEx DragAndDropEx.c

C:\DragAndDrop>cygcheck DragAndDropEx.exe
Found: DragAndDropEx.exe
DragAndDropEx.exe
  C:\WINDOWS\system32\msvcrt.dll
    C:\WINDOWS\system32\KERNEL32.dll
      C:\WINDOWS\system32\ntdll.dll
  C:\WINDOWS\system32\GDI32.dll
    C:\WINDOWS\system32\USER32.dll
  C:\WINDOWS\system32\SHELL32.DLL
    C:\WINDOWS\system32\ADVAPI32.dll
      C:\WINDOWS\system32\RPCRT4.dll
        C:\WINDOWS\system32\Secur32.dll
    C:\WINDOWS\system32\SHLWAPI.dll

C:\DragAndDrop>
C:\DragAndDrop
DragAndDropEx.exe

プログラムが起動した状態
ファイル(DragAndDropEx.c) をドラック&ドロップします。
プログラムは受け取ったファイル名をコンソールに出力します。
"D&D:C:\DragAndDrop\DragAndDropEx.c" が出力されました。
OLE を使用していないので、ドラック&ドロップ 出来ない場合は、クリップボードを介してデータを取り込みます。
(データ確認:%SystemRoot%\System32\clipbrd.exe) プログラムの画面中で、マウスの左ボタンをクリックします。
クリップボードから、データを読み込みコンソールに出力します。
"Clipboard:OpenClipboard(NULL) == 0" が出力されました。

終わりに

Drag & Drop で受け取ったファイル名を処理する場合は、
void processing( char *fnameWithPath )
{// fprintf の使用には、gcc のコンパイルオプションに -mconsole を指定することが必須
    fprintf( stdout, "D&D:%s\n", fnameWithPath ) ;
}

クリップボードから受け取ったデータを処理する場合は、
void messageFromClipboard( char *message )
{// fprintf の使用には、gcc のコンパイルオプションに -mconsole を指定することが必須
    fprintf( stdout, "Clipboard:%s\n", message ) ;
}

に必要な処理を記述すれば直ぐに利用できると思います。
簡単なサンプルですが、参考になれば幸いです。



go to TopPage go to CategoryTop