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のみ利用しているので容易に理解できると思います。
ソフトの使用に関して
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:\DragAndDropDragAndDropEx.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 ) ;
}
に必要な処理を記述すれば直ぐに利用できると思います。
簡単なサンプルですが、参考になれば幸いです。