|
| 1:ウィンドウキャプションのフォント情報を取得する |
キャプションのフォント情報を取得するには、SystemParametersInfoのSPI_GETNONCLIENTMETRICSを使用して、クライアント領域外のメトリック値を取得します。
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo (SPI_GETNONCLIENTMETRICS,sizeof(ncm),(void *)&ncm,0);
関数が成功すると様々なメトリック値が、ncmに通知され、キャプションフォントに関する情報は、LOGFONT構造体としてlfCaptionFontメンバに通知されます。例えばキャプション文字列の幅を求める場合には、次のようにします。
HDC dc;
HFONT hfcapt, hfold;
char cpstr[] = "...";
SIZE cpsize;
dc = GetDC 〜
hfcapt = CreateFontIndirect (&ncm.lfCaptionFont);
hfold = SelectObject (dc,hfcapt);
GetTextExtentPoint32 (dc,cpstr,sizeof(cpstr)-1,&cpsize);
DeleteObject (SelectObject(dc,hfold));
ReleaseDC 〜
|
| 2:ウィンドウキャプションの表示/非表示を切り替える |
|
ウィンドウのキャプションを非表示にしたり、あるいは、元に戻して表示するには、SetWindowLongでウィンドウスタイルを変更します。 以下は、クローズボタンのみを持つウィンドウについての例です。
DWORD orgWinId; // ウィンドウ作成時に取得したウィンドウID
void hideCaption (
HWND hw,
int noCaption // true : キャプションを非表示にする
) {
DWORD style;
style = GetWindowLong (hw,GWL_STYLE);
if (noCaption) {
SetWindowLong (hw,GWL_STYLE, style & ~(WS_CAPTION | WS_SYSMENU));
SetWindowLong (hw,GWL_ID,0);
} else {
SetWindowLong (hw,GWL_STYLE, style | WS_CAPTION | WS_SYSMENU);
SetWindowLong (hw,GWL_ID,orgWinId);
}
SetWindowPos (hw,NULL,0,0,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_FRAMECHANGED);
}
|
| 3:プログラムの多重起動を防止する |
|
プログラムの多重起動を防止するには、CreateMutexでミューテックス(Mutual Exclusive)オブジェクトを生成し排他処理を行います。つまり、プログラム起動時に、既に同一のミューテックスオブジェクトが存在する場合には、直ちに終了します。 但し、全く何もせず終了してしまうと、ユーザの操作を完全に無視してしまうことになるので、「既に起動済み」といったメッセージを表示するか、FindWindowで起動済みプログラムのウィンドウハンドルを取得し、フォアグラウンドに持ってくるのがよいでしょう。 ミューテックスオブジェクトに付ける名前は、プログラム名に何文字かを付加して一意性を高めたものがよいでしょう。プログラム名は、renameして実行されることを考慮して、実行時に動的に決定するのではなく、コンパイル時の定数として指定します。
HANDLE hme;
if ((hme = CreateMutex(NULL,TRUE,"MUTEX object name")) == NULL)
return 0;
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// 既に起動されている場合の処理
return 0;
// ミューテックスハンドルは、プロセス終了時にシステムが自動的にクローズ
}
// 通常の起動処理
|
| 4:プログラムアイコンをタスクトレイに設置し、ポップメニューを表示する | |||
|
タスクトレイにアイコンを置くには、ShellファンクションのShell_NotifyIconを使用します。この際、アイコンからのメッセージを処理するための親ウィンドウ(通常は、非可視)が必要になります。また、追加したアイコンは自動的には消滅しないので、プログラム終了時に、同じくShell_NotifyIconで削除します。 // アイコンの設置
setIcon (
HWND hw, // 親ウィンドウのハンドル
HICON hicon, // 設置するアイコンのハンドル
LPSTR tiptext // 表示するツールチップ・テキスト
) {
int rc;
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = hw;
nid.uID = 0;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.uCallbackMessage = WM_USER;
nid.hIcon = hicon;
strncpy (nid.szTip, tiptext, sizeof(nid.szTip));
rc = Shell_NotifyIcon (NIM_ADD, &nid);
DestroyIcon (hicon);
return rc;
}
// アイコンの削除
deleteIcon (HWND hw) {
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = hw;
nid.uID = 0;
return Shell_NotifyIcon (NIM_DELETE, &nid);
}
// 親ウィンドウのウィンドウプロシジャー
LRESULT WINAPI wndproc (HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
static HMENU hmpop;
switch (msg) {
case WM_CREATE:
hmpop = CreatePopupMenu (); /* メニュー作成 */ setIcon (wh, LoadIcon (〜), "ツールチップ・テキスト"); return 0;
case WM_USER:
if (lp == WM_RBUTTONDOWN) { // ポップアップメニューの表示
POINT pt;
GetCursorPos (&pt);
SetForegroundWindow (hw);
SetFocus (hw);
TrackPopupMenu (hmpop,TPM_LEFTALIGN|TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hw, NULL);
PostMessage (hw,WM_NULL,0,0);
return 0;
/*
これらの一連の手順は、ポップアップメニューが消えなくなったり、
あるいは、すぐ消えてしまうことを回避するための手順です。
マイクロソフト サポート技術情報 - 135788参照
*/
} // その他、ダブルクリックなどのメッセージ処理 return 0;
case WM_COMMAND:
// メニューコマンドの処理 return 0;
case WM_CLOSE:
deleteIcon (hw); DestroyMenu (hmpop); DestroyWindow (hw); return 0;
case WM_DESTROY:
PostQuitMessage (0); return 0;
}
}
|
| 5:特殊フォルダのパスを求める | ||
|
スタートメニューなどWindows上で特別な意味をもつフォルダのパスを求めるには、ShellファンクションのSHGetSpecialFolderPathを使用します。 但し、Windows95(i.e. 4.0未満)では、このファンクションは実装されていないので、SHGetSpecialFolderLocationとSHGetPathFromIDListを組み合わせて使用します。 例えば、Windows起動時にプログラムを実行するためのショートカットを格納する"スタートアップ"フォルダを求めるには、 [Win95以外]
#include <shlobj.h>
char path[MAX_PATH];
SHGetSpecialFolderPath (NULL, path, CSIDL_STARTUP, 0);
// スタートアップ・フォルダ(CSIDL_STARTUP)のパスがpathに格納されます。
// その他の特殊フォルダのID(CSIDL_*)は、shlobj.hに定義されています。
// 第4引数に真の値を指定すると、フォルダがないとき作成されます。
[Win95]
LPMALLOC ppm;
LPITEMIDLIST idl;
char path[MAX_PATH];
if (SHGetMalloc (&ppm) == NOERROR) {
if (SHGetSpecialFolderLocation (NULL, CSIDL_STARTUP, &idl) == NOERROR) {
SHGetPathFromIDList (idl,path);
ppm->lpVtbl->Free (ppm,idl);
}
ppm->lpVtbl->Release (ppm);
}
|
| 6:プログラムへのショートカットを生成する | ||||
|
プログラムへのショートカットを作成するには、COMライブラリのIShellLinkインターフェスを使用します。 ショートカットをIShellLinkで作成するには、必要最低限のパラメタとして、あらかじめ、プログラムと作成するショートカットのフルパスを求めておく必要があります。 プログラムのフルパスは、GetModuleFileNameで、また、ショートカットのフルパスは、Tips No.5などで保存先フォルダを求め、パスを生成します。
#include <shlobj.h>
char programPath[MAX_PATH],
shortcutPath[MAX_PATH];
IShellLink * sl;
IShellLinkVtbl * vp;
IPersistFile * pf;
WCHAR work[MAX_PATH];
GetModuleFileName (NULL,programPath,sizeof(programPath));
SHGetSpecialFolderPath (NULL,shortcutPath,CSIDL_STARTUP,1);
strcat (shortcutPath,"\\ShortcutName.lnk");
CoInitialize (NULL); // COMライブラリを初期化
if (CoCreateInstance (&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLink, (void **)&sl) == S_OK) { // *1
vp = sl->lpVtbl; // ベクターテーブルへのポインタを取り込む if (vp->QueryInterface (sl,&IID_IPersistFile,(void **)&pf)
== S_OK) { // *2
vp->SetPath (sl,programPath); // プログラムパス
vp->SetDescription (sl,NULL); // 説明(= ツールチップス)
vp->SetArguments (sl,NULL); // コマンドライン引数
vp->SetWorkingDirectory (sl,NULL); // 作業ディレクトリ
vp->SetIconLocation (sl,NULL,0); // Iconのパスまたはインデックス
vp->SetShowCmd (sl,SW_SHOWNORMAL); // 起動時のウィンドウ表示
MultiByteToWideChar (CP_ACP, 0, shortcutPath,
-1, work, MAX_PATH); // パスをユニコードに変換
pf->lpVtbl->Save (pf, work, TRUE); // ショートカットを保存
pf->lpVtbl->Release (pf); // IPersistFileへのポインタを破棄
} vp->Release (sl); // IShellLinkへのポインタを破棄 } CoUninitialize (); // COMライブラリをクローズ
|
| 7:アクティブデスクトップをチェックする |
デスクトップがアクティブデスクトップ状態であるか否かを検査するには、COMライブラリ(No.6の例参照)のIActiveDesktopインターフェスを使用します。#include <wininet.h> #include <shlobj.h> LPACTIVEDESKTOP sl; COMPONENTSOPT comopt;
ZeroMemory (&comopt, sizeof(comopt));
CoInitialize (NULL);
if (CoCreateInstance (&CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
&IID_IActiveDesktop, (void **)&sl) == S_OK) {
comopt.dwSize = sizeof (COMPONENTSOPT); sl->lpVtbl->GetDesktopItemOptions (sl, &comopt, 0); // Desktop情報を取得 sl->lpVtbl->Release (sl); } CoUninitialize (); // comopt.fActiveDesktopが真ならアクティブデスクトップ状態 |
| 8:壁紙を設定変更する |
|
壁紙の変更は、アクティブデスクトップであるか否か(No.7)によって手順が異なります。 アクティブデスクトップの場合には、COMライブラリ(No.6の例参照)のIActiveDesktopインターフェスを使用し、設定はSetWallpaper、配置方法の指定はSetWallpaperOptionsで行います 一方、非アクティブデスクトップの場合は、設定をSystemParametersInfoのSPI_SETDESKWALLPAPERで、配置方法の指定はレジストリを直接書き換えることにより行います。
#include <windows.h>
#include <wininet.h>
#include <shlobj.h>
setWallpaper (
char * bmPath, // ビットマップファイルのパス
int style /* 配置方法
WPSTYLE_CENTER : 中央に表示 WPSTYLE_TILE : 並べて表示 WPSTYLE_STRETCH : 拡大表示 */
) {
// 戻り値 = 0 : 設定に成功 // ≠ 0 : 設定に失敗 (エラー番号) LPACTIVEDESKTOP ap; COMPONENTSOPT comopt; HKEY key; int rc;
rc = 0;
ZeroMemory (&comopt, sizeof(comopt));
CoInitialize (NULL);
if (CoCreateInstance(&CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
&IID_IActiveDesktop, (void **)&ap) == S_OK) {
IActiveDesktopVtbl* vp;
WALLPAPEROPT wpopt;
WCHAR work[MAX_PATH];
vp = ap->lpVtbl;
comopt.dwSize = sizeof(comopt);
vp->GetDesktopItemOptions (ap,&comopt,0);
if (comopt.fActiveDesktop) {
// アクティブデスクトップの場合
MultiByteToWideChar (CP_ACP, 0, bmPath, -1,
work, MAX_PATH); // パスをユニコードに変換
if (vp->SetWallpaper (ap,work,0)) { // 壁紙を設定
rc = 1;
} else {
ZeroMemory (&wpopt, sizeof(wpopt));
wpopt.dwSize = sizeof (wpopt);
wpopt.dwStyle = style;
vp->SetWallpaperOptions (ap,&wpopt,0); // 配置方法を指定
if (vp->ApplyChanges (ap,AD_APPLY_ALL)) // 変更を適用
rc = 2;
}
}
vp->Release (ap);
}
CoUninitialize ();
if (comopt.fActiveDesktop)
return rc;
// 非アクティブデスクトップの場合
if (RegOpenKeyEx (HKEY_CURRENT_USER,"Control Panel\\Desktop",0,
KEY_ALL_ACCESS,&key) != ERROR_SUCCESS) {
rc = 3;
} else { // 配置方法をレジストリに書き込む
RegSetValueEx (key, "TileWallpaper", 0, REG_SZ,
style == WPSTYLE_TILE? "1":"0", 2);
RegSetValueEx (key, "WallpaperStyle", 0, REG_SZ,
style == WPSTYLE_STRETCH? "2":"0", 2);
RegCloseKey (key);
if (!SystemParametersInfo (SPI_SETDESKWALLPAPER,0,bmPath,
SPIF_UPDATEINIFILE))
rc = 4;
}
return rc;
}
[壁紙設定時の注意]
|
| 9:プログラムに関連付けされたファイルを開く |
プログラムからブラウザを起動してhtmlファイルを表示したいといったように、htmやtxtなど、ごく一般的な拡張子を持ったファイルを対応するアプリケーションに開かせるには、レジストリのHKEY_CLASSES_ROOTを検索し、アプリケーションのパスを求めて行うこともできますが、ShellファンクションのShellExecuteを使用すると、より簡単に行うことができます。
ShellExecute (hwnd, func, path, param, dir, show);
hwnd :親ウィンドウのハンドル
func :ファンクション ("open", "print", "explore")
path :パス(ファイル、ディレクトリ、アプリケーション)
param :アプリケーションへの引数
dir :デフォルト・ディレクトリ
show :起動時のウィンドウ表示方法
ex) ShellExecute (hwmain, "open", "test.htm", NULL, NULL, SW_SHOW);
|
| 10:ディレクトリをブラウジングする |
ファイルのパスは、コモンダイアログで取得できますが、ディレクトリパスをダイアログからブラウジングしながら取得するには、ShellファンクションのSHBrowseForFolderを使用します。
#include <shlobj.h>
BROWSEINFO bi;
LPITEMIDLIST idl;
char direc[MAX_PATH];
LPMALLOC ppm;
if (SHGetMalloc(&ppm) == NOERROR) { // NO.5参照
ZeroMemory (&bi,sizeof(bi));
bi.hwndOwner = hwnd; // Dialogの親ウィンドウ
bi.lpszTitle = title; // Dialogのウィンドウタイトル
if ((idl = SHBrowseForFolder(&bi)) != NULL) {
SHGetPathFromIDList (idl,direc); // NO.5参照
ppm->lpVtbl->Free (ppm,idl);
}
ppm->lpVtbl->Release (ppm);
}
もし、特定のディレクトリからブラウジングしたい場合は、以下のcallback関数を用意し、
static int CALLBACK bdcallback (HWND hwnd, UINT msg, LPARAM lp, LPARAM data) {
// data : initial directory
if (msg == BFFM_INITIALIZED && data)
SendMessage (hwnd, BFFM_SETSELECTION, (WPARAM)TRUE, (LPARAM)data);
return 0;
}SHBrowseForFolderを呼び出す前に、BROWSEINFO構造体に、関数とディレクトリを指定します。
bi.lpfn = bdcallback;
bi.lParam = (LPARAM)directory_path;
|
| 11:ファイルをゴミ箱経由で削除する |
DeleteFileによる完全削除ではなく、ファイルをゴミ箱経由で削除するには、ShellファンクションのSHFileOperationで、UNDO付き削除を実行します。
#include <windows.h> SHFILEOPSTRUCT fileop;
fileop.hwnd = hw; // 親ウィンドウのハンドル
fileop.wFunc = FO_DELETE; // 削除機能
fileop.pFrom = path; // 削除対象パス
fileop.pTo = NULL;
fileop.fFlags = FOF_ALLOWUNDO| // UNDO可(= ゴミ箱に移動)
FOF_NOCONFIRMATION| // 確認メッセージなし
FOF_SILENT; // プログレス非表示
SHFileOperation (&fileop);
|
| 12:ダイアログ・プロシジャーで矢印キーを検出する |
|
通常、ウィンドウ操作に関連するキー(矢印やTabなど)メッセージは、デフォルトのダイアログ・プロシジャーによって処理され、ユーザ定義のプロシジャーに通知される事はありません。 これらのキーを検知するには、2つの方法があります。一つは、SetWindowsHookExでダイアログメッセージをフックする方法。もう一つは、サブクラス化したダイアログ・プロシジャーで検知する方法です。ここでは、ダイアログのその他の処理にも応用できるサブクラス化方式の例を示します。
WNDPROC defwproc;
// ダイアログ・メインプロシジャー
PASCAL dlgproc (HWND hwnd, UINT msg, WPARAM wP, LONG lP) {
LRESULT CALLBACK subwproc (HWND hwnd, UINT msg, WPARAM wP, LONG lP);
if (msg == WM_INITDIALOG) {
// サブクラス化
defwproc = (WNDPROC)SetWindowLong (hwnd,GWL_WNDPROC,(UINT)subwproc);
return 1;
}
if (msg == WM_COMMAND) {
if (LOWORD(wP) == IDOK || LOWORD(wP) == IDCANCEL) {
SetWindowLong (hwnd,GWL_WNDPROC,(UINT)defwproc);
EndDialog (hwnd,0);
}
return 0;
}
return 0;
}
// サブクラス化したプロシジャー
LRESULT CALLBACK subwproc (HWND hwnd, UINT msg, WPARAM wP, LONG lP) {
if (msg == WM_GETDLGCODE)
return DLGC_WANTALLKEYS; // 全てのキーボードインプットを通知する
if (msg == WM_KEYDOWN) {
// 処理すべきキー(wP)かどうか判別する
}
return CallWindowProc (defwproc, hwnd, msg, wP, lP);
}
|
| 13:ワイルドカードのマッチング |
|
文字列とワイルドカードをマッチングするには、light-weight utility APIsのPathMatchSpecを使用します。単にワイルドカードにマッチするファイルを探すのなら、FindFirstFileを使えばよいわけですが、この場合は実際に入出力動作が伴うので、これを回避したい時に、PathMatchSpecを使用します。 但し、PathMatchSpecは、純粋に文字列とワイルド・パターンとのマッチングなので、DOSのショート・ファイル名を扱う場合は注意が必要です。例えば、*.htmは、FindFirstFileでは、foo1.htmとfoo2.html(foo2~1.htm)がマッチしますが、PathMatchSpecでは、foo1.htmしかマッチしません。 また、PathMatchSpecは、Internet Explorer 3.0以降のコンポーネント(shlwapi.dll)として提供される機能なので、それより古いバージョンでは利用できません。
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h>
printf ("%s\n",PathMatchSpec (name,"*.htm") ? "matched" : "unmatched");
|
| 14:他プロセスのステータスバーのテキストを取込む |
|
ステータスバーに書込まれたテキストは、SB_GETTEXTメッセージを当該ウィンドウに送ることで取り出すことができますが、送り先が自プロセスでない場合、双方のプロセスの仮想アドレス空間が異なるため、SB_GETTEXTを受けた側では、引数として渡された仮想アドレスを実アドレスに変換することができず異常終了します。 そこでこのような問題を解決するために、次のような手順を経てプロセス間のデータ交換を行います。
但し、この手順はWindows NT系の場合で、9x系ではメモリマップドファイルを使用します。
#include <windows.h>
#include <shlobj.h>
int sl;
HANDLE hproc;
DWORD pid;
char wk[64];
HWND hw = FindWindow (0,"他プロセスのウィンドウ"),
hsw = FindWindowEx (hw,0,STATUSCLASSNAME,0); // ステータスバーのハンドルを取得
if (!hw || !hsw) { // エラー処理
return;
}
sl = SendMessage (hsw,SB_GETTEXTLENGTH,0,0); // 取得テキストの長さを求める
if (!sl) { // テキストなし処理
return;
}
GetWindowThreadProcessId (hsw,&pid); // ウィンドウ作成元のプロセスIDを求める
if ((hproc = OpenProcess // プロセスをオープンしハンドルを求める
(PROCESS_VM_OPERATION|PROCESS_VM_READ, 0, pid)) != NULL) {
void *mem = VirtualAllocEx // そのプロセスのメモリ空間に領域を確保する
(hproc, 0, sl+4, MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
if (mem) {
SendMessage (hsw,SB_GETTEXT,0,(int)mem); // テキストを取り出す
ReadProcessMemory (hproc,mem,wk,sizeof(wk)-1,NULL); // 自メモリに読み込む
VirtualFreeEx (hproc,mem,0,MEM_RELEASE);
} else {
// VirtualAllocEx失敗
}
CloseHandle (hproc);
}
|
| 15:タスクバーの位置を求める |
|
タスクバーは位置、サイズが可変で、表示方法にもオプションがあるため、その状態を確認できないとデスクトップ全体を描画領域として使いたいとき、期待通りの結果が得られません。 タスクバーの状態を知るにはShellファンクションのSHAppBarMessageを使用します。
#include <windows.h>
#include <stdio.h>
APPBARDATA tb;
int bstat;
tb.cbSize = sizeof(tb);
bstat = SHAppBarMessage (ABM_GETSTATE,&tb); // 表示状態を求める
SHAppBarMessage (ABM_GETTASKBARPOS,&tb); // 位置サイズを求める
printf ("left=%d, top=%d, bottom=%d, right=%d\n",
tb.rc.left, tb.rc.top, tb.rc.bottom, tb.rc.right);
printf ("AutoHide %s\n", bstat & ABS_AUTOHIDE ? "ON" : "OFF");
printf ("AlwaysOnTop %s\n", bstat & ABS_ALWAYSONTOP ? "ON" : "OFF");
|
| ©2000 YMan |