開発 Tips (C/C++/Visual C++/MFC/Windows プログラミング)


Windows プログラミング

開発 Tips


凡例:
本頁内 : 頁内へのリンク
別頁 : サイト内の別頁へのリンク
外頁 : 外部サイトへのリンク

【Q&A】 API が失敗した時に原因をしる方法

【Q】 多くの API は失敗すると FALSE を返したりしますが,具体的な原因を知る方法はないでしょうか.

【A】 GetLastError を使って次のようにデバッグ用のメッセージ ボックスを出してみるのはどうでしょう.

void ErrorMessageBox(HWND hWnd)
{
#ifdef _DEBUG

   LPVOID lpMsgBuf;
   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) /* デフォルト言語 */,
                 (LPTSTR)&lpMsgBuf, 0, NULL);
   MessageBox(hWnd, LPCTSTR(lpMsgBuf), _T("Error"), MB_OK | MB_ICONINFORMATION);
   LocalFree(lpMsgBuf);

#else // _DEBUG

   UNUSED(hWnd);

#endif // _DEBUG
}

【Q&A】 エスケープ シーケンス

【Q】 NEC PC-9801 + DOS のプログラムを PC/AT 互換機 + Windows 98/NT の DOS 窓に移植しようとしたらエスケープ シーケンスが動作しないようなのですが.

【A】 PC/AT 互換機では config.sys に ansi.sys を device として登録すればエスケープシーケンスが使えます.但し其れは DOS 窓 (NT では DOS 窓では有りませんが) + 16Bit アプリの場合で,Win32 では代わりに ConsoleAPI を使います.

例.

// ConsoleUtility.h

class ConsoleUtility
{
public:
   static const unsigned short COLOR_FOREGROUND_BLUE     ;
   static const unsigned short COLOR_FOREGROUND_GREEN    ;
   static const unsigned short COLOR_FOREGROUND_RED      ;
   static const unsigned short COLOR_FOREGROUND_INTENSITY;
   static const unsigned short COLOR_BACKGROUND_BLUE     ;
   static const unsigned short COLOR_BACKGROUND_GREEN    ;
   static const unsigned short COLOR_BACKGROUND_RED      ;
   static const unsigned short COLOR_BACKGROUND_INTENSITY;

   static unsigned int getConsoleTitle(char* szTitleBuf, unsigned int size);
   static bool         setConsoleTitle(const char* szTitle);
   static bool         putS(const char* s);
   static char         getChar();
   static bool         setCursorPosition (short x, short y);
   static bool         setInputCursorPosition (short x, short y);
   static bool         setOutputCursorPosition(short x, short y);
   static bool         setAttribute(unsigned short attribute);
   static bool         fillAttribute(short x, short y, unsigned int length, unsigned short attribute);
   static short        getConsoleWidth ();
   static short        getConsoleHeight();
};

// ConsoleUtility.cpp

#include <windows.h>
#include <cassert>

const unsigned short ConsoleUtility::COLOR_FOREGROUND_BLUE      = FOREGROUND_BLUE;
const unsigned short ConsoleUtility::COLOR_FOREGROUND_GREEN     = FOREGROUND_GREEN;
const unsigned short ConsoleUtility::COLOR_FOREGROUND_RED       = FOREGROUND_RED;
const unsigned short ConsoleUtility::COLOR_FOREGROUND_INTENSITY = FOREGROUND_INTENSITY;
const unsigned short ConsoleUtility::COLOR_BACKGROUND_BLUE      = BACKGROUND_BLUE;
const unsigned short ConsoleUtility::COLOR_BACKGROUND_GREEN     = COLOR_BACKGROUND_GREEN;
const unsigned short ConsoleUtility::COLOR_BACKGROUND_RED       = BACKGROUND_RED;
const unsigned short ConsoleUtility::COLOR_BACKGROUND_INTENSITY = COLOR_BACKGROUND_INTENSITY;

unsigned int ConsoleUtility::getConsoleTitle(char* szTitleBuf, unsigned int size)
{
   return ::GetConsoleTitle(szTitleBuf, size);
}

bool ConsoleUtility::setConsoleTitle(const char* szTitle)
{
   return !!::SetConsoleTitle(szTitle);
}

bool ConsoleUtility::putS(const char* s)
{
   const HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
   assert(hStdOut != INVALID_HANDLE_VALUE);

   DWORD cCharsWritten;
   bool retflag = !!::WriteConsole(hStdOut, s, strlen(s), &cCharsWritten, NULL);

   const CHAR crlf[] = "\n";
   if (!::WriteConsole(hStdOut, crlf, strlen(crlf), &cCharsWritten, NULL))
       retflag = false;

   return retflag;
}

char ConsoleUtility::getChar()
{
   const HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
   assert(hStdIn != INVALID_HANDLE_VALUE);

   DWORD dwInputMode;
   BOOL bSuccess = ::GetConsoleMode(hStdIn, &dwInputMode);
   assert(bSuccess);

   bSuccess = ::SetConsoleMode(hStdIn,
   dwInputMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT);
   assert(bSuccess);

   CHAR chBuf;
   DWORD dwRead;
   bSuccess = ::ReadFile(hStdIn, &chBuf, sizeof(chBuf), &dwRead, NULL);
   assert(bSuccess);

   bSuccess = ::SetConsoleMode(hStdIn, dwInputMode);
   assert(bSuccess);
   return chBuf;
}

bool ConsoleUtility::setCursorPosition(short x, short y)
{
   const bool b1(setInputCursorPosition (x, y));
   const bool b2(setOutputCursorPosition(x, y));
   return (b1 && b2);
}

bool ConsoleUtility::setInputCursorPosition(short x, short y)
{
   const HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
   if (hStdIn == INVALID_HANDLE_VALUE)
       return false;

   COORD coordScreen;
   coordScreen.X = x;
   coordScreen.Y = y;
   return !!::SetConsoleCursorPosition(hStdIn, coordScreen);
}

bool ConsoleUtility::setOutputCursorPosition(short x, short y)
{
   const HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
   if (hStdOut == INVALID_HANDLE_VALUE)
       return false;

   COORD coordScreen;
   coordScreen.X = x;
   coordScreen.Y = y;
   return !!::SetConsoleCursorPosition(hStdOut, coordScreen);
}

bool ConsoleUtility::setAttribute(unsigned short attribute)
{
   const HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
   if (hStdOut == INVALID_HANDLE_VALUE)
       return false;

   return !!::SetConsoleTextAttribute(hStdOut, attribute);
}

bool ConsoleUtility::fillAttribute(short x, short y, unsigned int length, unsigned short attribute)
{
   const HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
   if (hStdOut == INVALID_HANDLE_VALUE)
       return false;

   COORD coordScreen;
   coordScreen.X = x;
   coordScreen.Y = y;

   DWORD cCharsWritten;
   return !!::FillConsoleOutputAttribute(hStdOut, attribute, length, coordScreen, &cCharsWritten);
}

short ConsoleUtility::getConsoleWidth()
{
   const HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
   assert(hConsole != INVALID_HANDLE_VALUE);

   CONSOLE_SCREEN_BUFFER_INFO csbi;
   BOOL bSuccess = ::GetConsoleScreenBufferInfo(hConsole, &csbi);
   assert(bSuccess);
   return csbi.dwSize.X;
}

short ConsoleUtility::getConsoleHeight()
{
   const HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
   assert(hConsole != INVALID_HANDLE_VALUE);

   CONSOLE_SCREEN_BUFFER_INFO csbi;
   BOOL bSuccess = ::GetConsoleScreenBufferInfo(hConsole, &csbi);
   assert(bSuccess);
   return csbi.dwSize.Y;
}

【Q&A】 多重起動の防止

【Q】アプリケーションを一つだけ起動して二つ目以降は起動を禁止したいのですがどうしたら良いでしょうか.

【A】

■ Windows 3.X の場合

・WinMain の引数で判定

例.

int APIENTRY WinMain(HINSTANCE hinstIn, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
   /* ... */
   if (hinstPrev != NULL)
       return FALSE;
   /* ... */
}

■ Win32 の場合

・自ソフトの登録クラス名を探して判定.

例.MFC で云うと CWinApp::InitInstance にあたる箇所で

if (FindWindow(_T("MyWindowClass"), NULL) != NULL)
   return FALSE;

・Mutex を作成して判定する

例.MFC で云うと CWinApp::InitInstance にあたる箇所で

HANDLE m_hMutex; /* アプリケーション クラスのメンバー等,NULL で初期化しておく */

m_hMutex = CreateMutex(NULL, TRUE, _T("MyApplication"));
if (m_hMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS)
   return FALSE;

MFC で云うと CWinApp::ExitInstance にあたる箇所で

if (m_hMutex != NULL)
   ReleaseMutex(m_hMutex);

【Q&A】 動作している Windows の種類

【Q】 現在どのウィンドウズで動作しているか知りたいのですが.

【A】 95/NT3.5 以降であれば GetVersionEx で判ります.

細かくは,

・95 で マイナー バージョンが 0,ビルド No. が 950
・95 + SP1 で マイナー バージョンが 0,ビルド No. が 950
・OSR2 と OSR2.1 でマイナー バージョンが 0, ビルドNo. が 1111
・OSR2.5 でマイナー バージョンが 0,ビルド No. が 1212
・98 でマイナー バージョンが 10,ビルド No. が 1998

・OSVERSIONINFO 構造体 の szCDBVersion で OSR2 か OSR2.1 かの判別

【Q&A】 実行中の別アプリケーションの実行ファイル パス名

【Q】 実行中の別アプリケーションのパス名を取りたいのですが.

【A】 ウィンドウ タイトルから探すのであれば

・Windows 9X の場合

ToolHelp32 を利用.

LoadLibrary(_T("KERNEL32.DLL")) 後 GetProcAddress() にて以下のエントリー ポイントを取得し,

FindWindow                  // ウィンドウ タイトルからウィンドウ ハンドルの取得
GetWindowThreadProcessId()  // プロセスの ID を取得
CreateToolhelp32Snapshot()  // Snapshot ハンドルの取得
Process32First()            // プロセス検索
Process32Next()             // 次のプロセス検索
CloseHandle()               // Snapshot ハンドル close

して,検索でプロセスの ID と一致する PROCESSENTRY32 構造体のメンバ th32ProcessID があれば其の時のメンバ szExeFile からモジュールのパス名が判る.

・Windows NT4 の場合

LoadLibrary(_T("PSAPI.DLL")) 後 GetProcAddress() にて以下のエントリー ポイントを取得し,

FFindWindow                 // ウィンドウ タイトルからウィンドウ ハンドルの取得
GetWindowThreadProcessId()  // プロセスの ID を取得
EnumProcesses()             // プロセスの ID を列挙
OpenProcess()               // プロセス ハンドルの取得
EnumProcessModules()        // モジュール ハンドルの取得
GetModuleFileNameEx()       // モジュールのパス名の取得

・Windows 2000 の場合

どちらの方法でも O.K.

【Q&A】 IME のオン・オフ

【Q】 IME をプログラムからオン・オフしたいのですが.

【A】

HIMC himc;
himc = ImmGetContext(hWnd);
ImmSetOpenStatus(himc, オンかオフか);
ImmReleaseContext(hWnd, himc);

# imm.h と imm32.lib を忘れずに

【Q&A】 ダイアログの閉じるボタンを無効に

【Q】 ダイアログの閉じるボタンを無効にしたいのですがどうもうまく行きません.

【A】

WM_INITDIALOG ハンドラで (デフォルトの処理後),

EnableMenuItem(GetSystemMenu(hDialog, FALSE), SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
/* 但し hDialog はダイアログ ボックスのウィンドウ ハンドル */

【Q&A】 キーが押されたら処理 (システム フック)

【Q】自分のアプリケーションにフォーカスが無いときでも或るキーが押されたら処理をしたいのですが.

【A】システム フックを使うのはどうでしょうか.

以下は C のサンプルです.この部分を DLL 化してアプリケーションから呼ぶようにしないと駄目です.一部 Microsoft Visual C++ 依存です.

例.

/* hook.c */

#include <windows.h>

/* DLL のインスタンス ハンドル */
extern HINSTANCE _hInstance;

/* フックプロシージャで使用する変数は共有メモリにおく */
/*                                                    */
/* これは Visual C++ の場合で                         */
/* リンカのオプションに /SECTION:.share,RWS を追加    */
#pragma data_seg(".share")

HHOOK _hHook = NULL;
HWND  _hWnd  = NULL;

#pragma data_seg()

/* ユーザー定義のメッセージの例                                 */
/* (WM_USER + 50) が厭なら RegisterWindowMessage でも為て下さい */
#define WM_USERMESSAGE (WM_USER + 50)

/* フック プロシージャ */
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
   /* 此処では 'A' が押されたら予め設定済みの _hWnd にメッセージ WM_USERMESSAGE を送る */
   if (nCode >= 0 && nCode != HC_NOREMOVE && wParam == 'A') {
       PostMessage(_hWnd, WM_USERMESSAGE, 0, 0);
       /* 此れはフックされたアプリケーションの方で 'A' の入力が要らない場合         */
       /* (フックされたアプリケーションの方でも 'A' の入力が必要なら以下二行は削除) */
       CallNextHookEx(_hHook, nCode, wParam, lParam);
       return 1;
   }
   return CallNextHookEx(_hHook, nCode, wParam, lParam);
}

/* フックのセット                                 */
/* キー 'A' を押すと,ここで渡したウィンドウに    */
/* メッセージ WM_USERMESSAGE が送られる           */
/* この関数を export してアプリケーションから呼ぶ */
BOOL Set(HWND hWnd)
{
   /* キーのフック */
   _hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)HookProc, _hInstance, 0);
   /* ここで hInstance は DLL のインスタンス ハンドル */
   _hWnd  = hWnd;
   return (_hHook != NULL);
}

/* アンフック                                     */
/* フックが不必要になったら呼ぶ                   */
/* この関数を export してアプリケーションから呼ぶ */
void Reset()
{
   if (_hHook != NULL) {
       UnhookWindowsHookEx(_hHook);
       _hHook = NULL;
   }
}

【Q&A】 アプリケーションをタスク トレイに

【Q】 アプリケーションをタスク トレイに常駐させたいのですが.

【A】 Shell_NotifyIcon を使います.

例.

/* (WM_USER + 100) が厭なら RegisterWindowMessage でも為て下さい */
const UINT WM_NOTIFYICON = WM_USER + 100;

const bool AddTaskBarIcon(HINSTANCE hInstance, HWND hWnd, UINT nID, LPCTSTR lpszTip = NULL)
{
   NOTIFYICONDATA tnid ;
   memset(&tnid, 0, sizeof(NOTIFYICONDATA));
   tnid.cbSize = sizeof(NOTIFYICONDATA);
   tnid.hWnd   = hWnd;
   tnid.uID    = nID ;
   tnid.uFlags = (lpszTip == NULL || _tcslen(lpszTip) == 0)
                 ? (NIF_MESSAGE | NIF_ICON          )
                 : (NIF_MESSAGE | NIF_ICON | NIF_TIP);
   tnid.uCallbackMessage
               = WM_NOTIFYICON;
   tnid.hIcon  = HICON(LoadImage(hInstance, MAKEINTRESOURCE(nID), IMAGE_ICON, 16, 16, 0));
   _tcscpy(tnid.szTip, (lpszTip == NULL) ? _T("") : lpszTip);

   return !!Shell_NotifyIcon(NIM_ADD, &tnid);
}

const bool DeleteTaskBarIcon(HWND hWnd, UINT uID)
{
   NOTIFYICONDATA tnid ;
   memset(&tnid, 0, sizeof(NOTIFYICONDATA));
   tnid.cbSize = sizeof(NOTIFYICONDATA);
   tnid.hWnd   = hWnd;
   tnid.uID    = uID;
   return !!Shell_NotifyIcon(NIM_DELETE, &tnid);
}

【Q&A】 IE (等) を起動する方法

【Q】 アプリケーションで或る箇所をクリックされたら或る URL の HTML ファイルを表示させたいのですが.

【A】 ShellExecute で URL を渡します.標準で設定してあるブラウザ (IE やNetscape 等) が立ち上がります.
ShellExecute では同様にファイルやショートカットも起動出来ます.

例.

ShellExecute(NULL, _T("open"), _T("http://www.yahoo.co.jp/"), NULL, NULL, SW_SHOW);

【Q&A】 フォルダ選択ダイアログ

【Q】 フォルダ選択ダイアログを簡単に出す方法はないでしょうか.

【A】SHBrowseForFolder を使うのはどうでしょうか.
プログラム構造の詳細はシェル ネームスペースに関して調べてみて下さい.

例.

LPMALLOC        lpMalloc    = NULL;
HRESULT         hr          = SHGetMalloc(&lpMalloc);
if (FAILED(hr))
   return;

// ブラウズ情報受け取りバッファ領域の確保
LPSTR           lpBuffer    = LPSTR(lpMalloc->Alloc(_MAX_PATH));
if (lpBuffer == NULL)
   return;

// ダイアログ表示時のルートフォルダのPIDLを取得
// ※以下はデスクトップをルートとしている.デスクトップをルートとする
// 場合は,単に bi.pidlRoot に0を設定するだけでもよい.その他の特
// 殊フォルダをルートとする事もできる.詳細は
// SHGetSpecialFolderLocationのヘルプを参照の事.
LPITEMIDLIST    pidlRoot; // ブラウズのルート PIDL
if (!SUCCEEDED(SHGetSpecialFolderLocation(hWnd, CSIDL_DESKTOP, &pidlRoot)) {
   lpMalloc->Free(lpBuffer);
   return;
}

// BROWSEINFO 構造体の初期値設定
// BROWSEINFO 構造体の各メンバの詳細説明もヘルプを参照
BROWSEINFO      bi;
bi.hwndOwner                = hWnd;
bi.pidlRoot                 = pidlRoot;
bi.pszDisplayName           = lpBuffer;
bi.lpszTitle                = _T("フォルダを選択して下さい");
bi.ulFlags                  = 0;
bi.lpfn                     = 0;
bi.lParam                   = 0;
// フォルダ選択ダイアログの表示
LPITEMIDLIST    pidlBrowse; // ユーザーが選択した PIDL
pidlBrowse                  = SHBrowseForFolder(&bi);
if (pidlBrowse != NULL) {
   // PIDL形式の戻り値のファイルシステムのパスに変換
   if (SHGetPathFromIDList(pidlBrowse, lpBuffer)) {
       // 取得成功
   }
   // SHBrowseForFolder の戻り値 PIDL を解放
   lpMalloc->Free(pidlBrowse);
}
// クリーンアップ処理
lpMalloc->Free(pidlRoot);
lpMalloc->Free(lpBuffer);
lpMalloc->Release();

【Q&A】 ファイルやフォルダの存在を確認したい

【Q】ファイルやディレクトリが存在するかどうか調べたいのですけれど,どうするのが良いでしょうか.CreateFile でエラーを見る方法を試しましたが,これだとファイルなのかディレクトリなのか区別が付きませんし,向かないようです.

【A】例えば GetFileAttributes で 0xFFFFFFFF が返ってきたら存在しない,と云う判定ではどうでしょう.ディレクトリかどうかも返り値の FILE_ATTRIBUTE_DIRECTORY ビットで判ります.

【Q&A】 XOR で文字列を描画したい

【Q】 CAD を作成中です.ラバー バンドに文字を出そうとして,SetROP2(R2_XORPEN) して文字列を描画しようとしたのですが,うまく XOR で描画されないようです.どうしたら良いでしょうか.

【A】ビットマップに描画して,そのビットマップを SRCINVERT で BitBlt (や StretchBlt) するのはどうでしょう.

【Q&A】 GDI リソースが減っていく

【Q】 プログラムを実行すると,GDI リソースが減っていくようなのですが.

【A】 何処かでリソースを開放し忘れている可能性が高いです.先ずその箇所を特定して修正するのが常道かと思います."リソース メーター" や "BoundsChecker" のようなツールを使うのが良いと思います.

多いのが,

・GDI オブジェクトを Create して DeleteObject していない.
・GDI オブジェクトを SelectObject したままで DeleteObject した.

です.

処理の前後で,SaveDC と RestoreDC を呼ぶ,という卑怯なやり方もあります.

【Q&A】 シャット ダウン時やログオフ時の処理

【Q】 シャット ダウン時やログオフ時にアプリケーションの後処理をやりたいのですが.

【A】 WM_QUERYENDESESSION をハンドルして後処理が済む迄の間,FALSE を返し続け,後処理が終了したら,TRUE を返します.シャット ダウン中かログオフ中かは,WM_QUERYENDESESSION のパラメータで判ります.

【情報】 64 ビット ウィンドウズ on インテル Itanium

マイクロソフトがインテル Itanium プロセッサ向け 64 ビット版ウィンドウズのプレビュー・リリースを発表

64 ビット版ウィンドウズ 開発者向け情報

http://www.microsoft.com/windows2000/guide/platform/strategic/64bit.asp

Itanium 開発者向け情報

http://developer.intel.com/design/ia-64/

【Q&A】 太線で破線や点線を描きたい

【Q】 太線で破線や点線を描きたいのですが,CreatePen(PS_DASH, 10, RGB(0x00, 0x00, 0x00)) 等としても,破線が描画されません.

【A】 CreatePen の代わりに ExtCreatePen を使用してジオメトリックペンを使ってみてください.

例.

   LOGBRUSH logBrush;
   logBrush.lbColor = RGB(0x00, 0x00, 0x00);
   logBrush.lbHatch = 0;
   logBrush.lbStyle = BS_SOLID;

   HPEN hpen = ExtCreatePen(PS_DASH | PS_GEOMETRIC | PS_ENDCAP_FLAT,
                            10, &logBrush, 0, NULL);

但し,Windows 9X 系ではこの方法でもうまく行きません.ユーザースタイル (PS_USERSTYLE) を使用してみて下さい.

ちなみに MFC の場合は次のようになります.

例.

   LOGBRUSH logBrush;
   logBrush.lbColor = color;
   logBrush.lbHatch = 0;
   logBrush.lbStyle = BS_SOLID;
   CPen pen(PS_DASH | PS_GEOMETRIC | PS_ENDCAP_FLAT, 10, &logBrush);

【Q&A】 クリップボードにテキストを設定したりクリップボードからテキストを取得したい

【Q】 クリップボードにテキストをコピーしたり,クリップボードのテキストをペーストしたりしたいのですが.

【A】 クリップボード用の API を使うことで下記のように出来ます.

例.

#include <windows.h>
#include <tchar.h>
#include <assert.h>

/* クリップボードへのテキストのコピー */
/* (C/C++ 用)                         */
static BOOL SetText(LPCTSTR text /* テキスト */)
{
   BOOL            isSuccess   = FALSE;
   const size_t    length      = sizeof(TCHAR) * (_tcslen(text) + 1);
   const HGLOBAL   data        = GlobalAlloc(GMEM_MOVEABLE, length);

   assert(text != NULL);
   if (data != NULL) {
       const LPVOID buffer = GlobalLock(data);
       if (buffer != NULL) {
           memcpy(buffer, text, length);
           GlobalUnlock(data);
           if (OpenClipboard(GetDesktopWindow())) {
               if (EmptyClipboard()) {
                   if (SetClipboardData(CF_TEXT, data) != (HANDLE)0)
                       isSuccess = TRUE;
               }
               CloseClipboard();
           }
       }
       GlobalFree(data);
   }
   return isSuccess;
}


#include <windows.h>
#include <tchar.h>

/* クリップボードからテキストの取得 */
/* (C/C++ 用)                       */
static BOOL GetText(LPTSTR buffer /* テキスト用バッファ */, size_t bufferSize /* テキスト用バッファのサイズ */)
{
   BOOL isSuccess = FALSE;
   if (OpenClipboard((HWND)0)) {
       if (IsClipboardFormatAvailable(CF_TEXT)) {
           const HGLOBAL data = GetClipboardData(CF_TEXT);
           if (data != (HGLOBAL)0) {
               const LPCTSTR text = (LPCTSTR)GlobalLock(data);
               if (text != NULL) {
                   const size_t length = _tcslen(text);
                   if (length < bufferSize) {
                       memcpy(buffer, text, sizeof(TCHAR) * (length + 1));
                       isSuccess = TRUE;
                   }
               }
               GlobalUnlock(data);
           }
       }
       CloseClipboard();
   }
   return isSuccess;
}

【Q&A】 現プロセスの実行ファイルのパス名を取得したい

【Q】 上記「実行中の別アプリケーションの実行フル パス名」は判ったのですけれど,自分自身のプロセスのパス名を取るときはもっと簡単に出来るのでしょうか.

【A】 はい.モジュール ハンドルが簡単に取得出来,それを GetModuleFileName に渡すだけです.

例.

#include <windows.h>

/* 現プロセスの実行ファイルのパス名の取得 */
/* (C/C++ 用)                             */
BOOL GetCurrentProcessFileName(LPTSTR pathName /* パス名受け取り用バッファ */)
{
   const HMODULE moduleHandle = GetModuleHandle(NULL);
   return (moduleHandle == (HMODULE)0)
          ? FALSE
          : (GetModuleFileName(moduleHandle, pathName, _MAX_PATH) > 0);
}

【Q&A】 一気にディレクトリ構造を作りたい

【Q】 CreateDirectory という API では,目的のディレクトリまでの途中のディレクトリが無いと作れません.一つ宛作って行っても良いのですが,もっと一気にディレクトリ構造を作る方法はないのでしょうか.

【A】 MakeSureDirectoryPathExists という API でまさにそれが可能です.

例.

#include <windows.h>
#include <imagehlp.h>                /* for MakeSureDirectoryPathExists */
#pragma comment(lib, "imagehlp.lib") /* for MakeSureDirectoryPathExists */

/* ディレクトリの作成                                                                     */
/*   フルパスで指定したディレクトリを作成します.                                         */
/*   指定された各ディレクトリがまだ存在しない場合,それらのディレクトリを順に作成します. */
/* (C/C++ 用,Windows 95,NT3.1 以降)                                                     */
BOOL CreateSureDirectory(LPTSTR directoryName /* フルパスのディレクトリ */)
{
   return MakeSureDirectoryPathExists(directoryName);
}

【Q&A】 システムの特殊なフォルダのパス名を取得したい

【Q】 "マイ ドキュメント" フォルダや"お気に入り" フォルダ等, システムのフォルダのパス名を取得したいのですが.

【A】 SHGetSpecialFolderPath 等の API で可能です.

例.

#include <windows.h>
#include <shlobj.h>                 // for SHGetSpecialFolderPath
#pragma comment(lib, "shell32.lib") // for SHGetSpecialFolderPath

// システムの特殊なフォルダのパス名の取得
// (C++ 用,Windows NT 4.0, Windows 95 以降)
//   Windows 2000, Windows NT 4.0 + IE 4.0, Windows 98, Windows 95 + IE 4.0 以降であれば SHGetSpecialFolderPath が使える
//   また,システム ディレクトリは GetSystemDirectory,
//   Windows ディレクトリは GetWindowsDirectory,
//   一時ファイル用のディレクトリは GetTempPath がそれぞれ使用出来る.
bool GetSpecialFolderPath(LPTSTR pathName /* パス名用バッファ */, int nFolder /* フォルダを指定する CSIDL の値 */)
{
   bool        isSuccess(FALSE);
   IMalloc*    pMalloc;

   if (::SHGetMalloc(&pMalloc) == NOERROR) {
       ITEMIDLIST* pidl;
       if (::SHGetSpecialFolderLocation(::GetDesktopWindow(), nFolder, &pidl) == NOERROR) {
           if (::SHGetPathFromIDList(pidl, pathName))
               isSuccess = true;
           pMalloc->Free(pidl);
       }
       pMalloc->Release();
   }
   return isSuccess;
}

// 「送る」フォルダのパス名の取得
// (C++ 用)
bool GetSendToPath(LPTSTR pathName /* パス名用バッファ */)
{
   return GetSpecialFolderPath(pathName, CSIDL_SENDTO);
}

【Q&A】 ショートカットを作成したい

【Q】 ショートカットの作成の方法を教えてください.

【A】 IShellLink を使うことで作成出来ます.
※ Windows NT 4.0,Windows 95 以降.

例.

#include <windows.h>
#include <tchar.h>
#include <shlobj.h> // for IShellLink

// ショートカットの作成
// (C++ 用)
bool CreateShortcut(LPCTSTR targetFileName /* ショートカットのリンク先 */, LPCTSTR targetArgument /* コマンドライン引数 */, LPCTSTR linkFileName /* ショートカットのパス名 */)
{
   bool            isSuccess(false);
   HRESULT         result = ::CoInitialize(NULL);
   if (SUCCEEDED(result)) {
       IShellLink* pShellLink;
       result = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink);
       if (SUCCEEDED(result)) {
           IPersistFile*   pPersistFile;
           result = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
           if (SUCCEEDED(result)) {
               result = pShellLink->SetPath(targetFileName);
               if (SUCCEEDED(result)) {
                   result = (targetArgument == NULL) ? S_OK : pShellLink->SetArguments(targetArgument);
                   if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                       result = pPersistFile->Save(linkFileName, true);
#else   //  _UNICODE
                       //  Unicodeに変換
                       wchar_t wlinkFileName[MAX_PATH];
                       ::MultiByteToWideChar(CP_ACP, 0, linkFileName, -1, wlinkFileName, MAX_PATH);
                       result = pPersistFile->Save(wlinkFileName, true);
#endif  //  _UNICODE
                       if (SUCCEEDED(result))
                           isSuccess = true;
                   }
               }
               pPersistFile->Release();
           }
           pShellLink->Release();
       }
       CoUninitialize();
   }
   return isSuccess;
}

// ディレクトリ名の正規化 (サブルーチン)
// (C++ 用)
static void RegulateDirectoryName(LPTSTR directoryName)
{
   const size_t directoryNameTextLength(::_tcslen(directoryName));
   if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
       directoryName[directoryNameTextLength    ] = _T('\\');
       directoryName[directoryNameTextLength + 1] = _T('\0');
   }
}

// ディレクトリ名とファイル名を連結してパス名へ (サブルーチン)
// (C++ 用)
static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
{
   ::_tcscpy(pathName, directoryName);
   RegulateDirectoryName(pathName);
   ::_tcscat(pathName, fileName);
}

// ショートカットの作成
// (C++ 用)
bool CreateShortcut(LPCTSTR targetPathName /* ショートカットのリンク先 */, LPCTSTR targetArgument /* コマンドライン引数 */, LPCTSTR linkDirectoryName /* ショートカットを置くディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR linkFileName[_MAX_PATH];
   ::_tcscpy(linkFileName, linkName  );
   ::_tcscat(linkFileName, _T(".lnk"));
   TCHAR linkPathName[_MAX_PATH];
   MakePathName(linkPathName, linkDirectoryName, linkFileName);
   return CreateShortcut(targetPathName, targetArgument, linkPathName);
}

【Q&A】 ショートカットからパスを取得したい

【Q】 上記のショートカットを作成するのとは逆に,ショートカットからパスを取り出したいのですが.

【A】 同じく IShellLink を使うことで取得出来ます.
※ Windows NT 4.0,Windows 95 以降.

例.

#include <windows.h>
#include <tchar.h>
#include <shlobj.h> // for IShellLink

// ショートカットからのパス名の取得
bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
{
   bool            temp(false);
   HRESULT         result = ::CoInitialize(NULL);
   if (SUCCEEDED(result)) {
       IShellLink* pShellLink;
       result = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink);
       if (SUCCEEDED(result)) {
           IPersistFile*   pPersistFile;
           result = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
           if (SUCCEEDED(result)) {
#ifdef  _UNICODE
               result = pPersistFile->Load(linkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#else   //  _UNICODE
               //  Unicodeに変換
               wchar_t wlinkPathName[_MAX_PATH];
               ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
               result = pPersistFile->Load(wlinkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#endif  //  _UNICODE
               if (SUCCEEDED(result)) {
                   WIN32_FIND_DATA findData;
                   result = pShellLink->GetPath(targePathName, _MAX_PATH, &findData, 0);
                   if (SUCCEEDED(result)) {
                       result = (targetArgument == NULL) ? S_OK : pShellLink->GetArguments(targetArgument, int(targetArgumentMaxTextLength));
                       if (SUCCEEDED(result))
                           temp = true;
                   }
               }
               pPersistFile->Release();
           }
           pShellLink->Release();
       }
       CoUninitialize();
   }
   return temp;
}

// ディレクトリ名の正規化 (サブルーチン)
// (C++ 用)
static void RegulateDirectoryName(LPTSTR directoryName)
{
   const size_t directoryNameTextLength(::_tcslen(directoryName));
   if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
       directoryName[directoryNameTextLength    ] = _T('\\');
       directoryName[directoryNameTextLength + 1] = _T('\0');
   }
}

// ディレクトリ名とファイル名を連結してパス名へ (サブルーチン)
// (C++ 用)
static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
{
   ::_tcscpy(pathName, directoryName);
   RegulateDirectoryName(pathName);
   ::_tcscat(pathName, fileName);
}

// ショートカットからのパス名の取得
bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkDirectoryName /* ショートカットが在るディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR linkFileName[_MAX_PATH];
   ::_tcscpy(linkFileName, linkName  );
   ::_tcscat(linkFileName, _T(".lnk"));
   TCHAR linkPathName[_MAX_PATH];
   MakePathName(linkPathName, linkDirectoryName, linkFileName);
   return ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, linkPathName);
}

// 「送る」からのパス名の取得
bool ReadSendTo(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR directoryName[_MAX_DIR];
   return ::SHGetSpecialFolderPath(::GetDesktopWindow(), directoryName, CSIDL_SENDTO, false)
          ? ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, directoryName, linkName)
          : false;
}

【Q&A】 インターネット ショートカットを作成して「お気に入り」に追加したい

【Q】 インターネット ショートカットを作ったり,それを「お気に入り」に追加したりしたいのですが.

【A】 IUniformResourceLocator を使うことで「お気に入り」に追加が出来ます.
※ Windows 95,Windows NT 4.0,Internet Explorer 4.0 以降

例.

#include <tchar.h>
#include <shlobj.h>   // for SHGetSpecialFolderPath
#include <intshcut.h> // for IUniformResourceLocator

// インターネット ショートカットの作成
// (C++ 用)
bool CreateInternetShortcut(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
{
   bool            temp(false);
   HRESULT         result = ::CoInitialize(NULL);
   if (SUCCEEDED(result)) {
       IUniformResourceLocator *pUniformResourceLocator    = NULL;
       result                                              = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (LPVOID*)&pUniformResourceLocator);
       if (SUCCEEDED(result)) {
           IPersistFile *pPersistFile  = NULL;
           result                      = pUniformResourceLocator->QueryInterface(IID_IPersistFile, (void **)&pPersistFile);
           if (SUCCEEDED(result)) {
               pUniformResourceLocator->SetURL(urlText, 0);
#ifdef  _UNICODE
               pPersistFile->Save(linkPathName, TRUE);
#else   //  _UNICODE
               //  Unicodeに変換
               wchar_t wlinkPathName[MAX_PATH];
               ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, MAX_PATH);
               pPersistFile->Save(wlinkPathName, TRUE);
#endif  //  _UNICODE
               temp = true;
               pPersistFile->Release();
           }
           pUniformResourceLocator->Release();
       }
   }
   return temp;
}

// ディレクトリ名の正規化 (サブルーチン)
// (C++ 用)
static void RegulateDirectoryName(LPTSTR directoryName)
{
   const size_t directoryNameTextLength(::_tcslen(directoryName));
   if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
       directoryName[directoryNameTextLength    ] = _T('\\');
       directoryName[directoryNameTextLength + 1] = _T('\0');
   }
}

// ディレクトリ名とファイル名を連結してパス名へ (サブルーチン)
// (C++ 用)
static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
{
   ::_tcscpy(pathName, directoryName);
   RegulateDirectoryName(pathName);
   ::_tcscat(pathName, fileName);
}

// インターネット ショートカットの作成
// (C++ 用)
bool CreateInternetShortcut(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkDirectoryName /* ショートカットを置くディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR linkFileName[_MAX_DIR ];
   ::_tcscpy(linkFileName, linkName  );
   ::_tcscat(linkFileName, _T(".url"));
   TCHAR linkPathName[_MAX_PATH];
   MakePathName(linkPathName, linkDirectoryName, linkFileName);
   return CreateInternetShortcut(urlText, linkPathName);
}

// 「お気に入り」への追加
// (C++ 用)
bool CreateFavorites(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR directoryName[_MAX_DIR];
   return ::SHGetSpecialFolderPath(::GetDesktopWindow(), directoryName, CSIDL_FAVORITES, false)
          ? CreateInternetShortcut(urlText, directoryName, linkName)
          : false;
}

【Q&A】 「お気に入り」から URL を取得したい

【Q】 上記のインターネット ショートカットを作成して「お気に入り」に追加するのとは逆に,「お気に入り」から URL を取り出したいのですが.

【A】 同じく IUniformResourceLocator を使うことで「お気に入り」から URL が取得出来ます.
※ Windows 95,Windows NT 4.0,Internet Explorer 4.0 以降

例.

#include <tchar.h>
#include <shlobj.h>   // for SHGetSpecialFolderPath
#include <intshcut.h> // for IUniformResourceLocator

// インターネット ショートカットからの URL の取得
bool ReadInternetShortcut(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
{
   bool            temp(false);
   HRESULT         result = ::CoInitialize(NULL);
   if (SUCCEEDED(result)) {
       IUniformResourceLocator *pUniformResourceLocator    = NULL;
       result                                              = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (LPVOID*)&pUniformResourceLocator);
       if (SUCCEEDED(result)) {
           IPersistFile *pPersistFile  = NULL;
           result                      = pUniformResourceLocator->QueryInterface(IID_IPersistFile, (void **)&pPersistFile);
           if (SUCCEEDED(result)) {
#ifdef  _UNICODE
               result = pPersistFile->Load(linkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#else   //  _UNICODE
               //  Unicodeに変換
               wchar_t wlinkPathName[MAX_PATH];
               ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
               result = pPersistFile->Load(wlinkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#endif  //  _UNICODE
               if (SUCCEEDED(result)) {
                   LPSTR buffer;
                   result = pUniformResourceLocator->GetURL(&buffer);
                   if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                       ::MultiByteToWideChar(CP_ACP, 0, buffer, -1, urlText, _MAX_PATH);
#else   //  _UNICODE
                       _tcscpy(urlText, buffer);
#endif  //  _UNICODE
                       temp = true;
                   }
               }
               pPersistFile->Release();
           }
           pUniformResourceLocator->Release();
       }
   }
   return temp;
}

// ディレクトリ名の正規化 (サブルーチン)
// (C++ 用)
static void RegulateDirectoryName(LPTSTR directoryName)
{
   const size_t directoryNameTextLength(::_tcslen(directoryName));
   if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
       directoryName[directoryNameTextLength    ] = _T('\\');
       directoryName[directoryNameTextLength + 1] = _T('\0');
   }
}

// ディレクトリ名とファイル名を連結してパス名へ (サブルーチン)
// (C++ 用)
static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
{
   ::_tcscpy(pathName, directoryName);
   RegulateDirectoryName(pathName);
   ::_tcscat(pathName, fileName);
}

// インターネット ショートカットからの URL の取得
bool ReadInternetShortcut(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkDirectoryName /* ショートカットが在るディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR linkFileName[_MAX_PATH];
   ::_tcscpy(linkFileName, linkName  );
   ::_tcscat(linkFileName, _T(".url"));
   TCHAR linkPathName[_MAX_PATH];
   MakePathName(linkPathName, linkDirectoryName, linkFileName);
   return ReadInternetShortcut(urlText, linkPathName);
}

// 「お気に入り」からの URL の取得
bool ReadFavorites(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkName /* ショートカットの名称 */)
{
   TCHAR directoryName[_MAX_DIR];
   return ::SHGetSpecialFolderPath(::GetDesktopWindow(), directoryName, CSIDL_FAVORITES, false)
          ? ReadInternetShortcut(urlText, directoryName, linkName)
          : false;
}

【Q&A】 クリップボードを監視したい

【Q】 クリップボードに変更があったときに何か処理をしたいのですが.

【A】 クリップボードを監視するには,SetClipboardViewer,ChangeClipboardChain を使います.また,WM_DRAWCLIPBOARD と WM_CHANGECBCHAIN の二つのメッセージを処理します.具体的には以下のようにします.

例.

/* クリップボードを監視する */
/* ウィンドウ プロシジャ    */
/* (C/C++ 用)               */
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   /* クリップボード ビューア チェーンにおける次のウィンドウのハンドル */
   static HWND nextClipboardViewerHandle_ = (HWND)0;

   switch (message) {
       case WM_CREATE:
           /* クリップボード ビューア チェーンにこのウィンドウを追加し,             */
           /* クリップボード ビューア チェーンにおける次のウィンドウのハンドルを記憶 */
           nextClipboardViewerHandle_ = SetClipboardViewer(hWnd);

           ; /* その他の初期化処理 */
           break;

       case WM_DESTROY:
           /* クリップボード ビューア チェーンからこのウィンドウを削除し, */
           /* 次のウィンドウのハンドルと置き換える                         */
           ChangeClipboardChain(hWnd, nextClipboardViewerHandle_);

           ; /* その他の終了処理 */
           break;

       case WM_DRAWCLIPBOARD: /* クリップボードが変更されたときの通知 */
           ; /* 何かクリップボード変更時の処理をここに書く */

           /* 次のクリップボード ビューアへ送る */
           SendMessage(nextClipboardViewerHandle_, WM_DRAWCLIPBOARD, 0, 0L);
           break;

       case WM_CHANGECBCHAIN: /* クリップボード ビューア チェーンに変更 (追加・削除) が有ったときの通知 */
           {
               const HWND hWndRemove  = (HWND)wParam; /* チェーンから削除するウインドウハンドル         */
               const HWND hWndNewNext = (HWND)lParam; /* 削除したウインドウハンドルと置き換えるハンドル */

               if (hWndRemove == nextClipboardViewerHandle_)
                   nextClipboardViewerHandle_ = hWndNewNext;
               if (nextClipboardViewerHandle_ != (HWND)0)
                   /* 次のクリップボード ビューアへ送る */
                   SendMessage(nextClipboardViewerHandle_, WM_CHANGECBCHAIN, wParam, lParam);
           }
           break;

       default:
           return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

【Q&A】 文字列を半角から全角,全角から半角に変換したい

【Q】 文字列内の半角文字を全角文字に,全角文字を半角文字に変換したりしたいのですが.

【A】 LCMapString を使うことで,全角文字への変換,半角文字の変換,ひらがなへの変換,カタカナへの変換,大文字への変換,小文字への変換等が可能です.

例.

// 全角への変換
const int size(::_tcslen(fromText /* 変換元の文字列へのアドレス */));
::
LCMapString(::GetUserDefaultLCID(), // ロケール識別子
             LCMAP_FULLWIDTH       , // マップ変換の種類
             fromText              , // 変換元の文字列へのアドレス
             size                  , // 変換元の文字列の文字数
             toText                , // 変換後の文字列を格納する為のバッファへのアドレス
             size * 2 + 1            // 変換後の文字列を格納する為のバッファの文字数 (終端の '\0' を含む大きさ)
);

上記コードの,LCMAP_FULLWIDTH を LCMAP_HALFWIDTH に換えると 半角文字 への変換となります.同様に,ひらがなへの変換には LCMAP_HIRAGANA,カタカナへの変換には LCMAP_KATAKANA,大文字への変換には LCMAP_UPPERCASE,小文字への変換には LCMAP_LOWERCASE を使います.

【Q&A】 IME のオン/オフを監視したい

【Q】 全アプリケーションを通じて IME のオン/オフを監視したいのですが.

【A】 システム フックを使って WM_IME_NOTIFY メッセージを監視することで可能です.

例.

#include <windows.h>
#include <tchar.h>
#include <imm.h>

#pragma comment(lib, "imm32.lib") // for ImmGetContext, ImmReleaseContext, ImmGetOpenStatus

/* DLL のインスタンス ハンドル */
extern HINSTANCE instanceHandle_;

/* RegisterWindowMessage 用のユニークな文字列 */
#define WM_USER_MESSAGE_TEXT    _T("WM_USER_MESSAGE_TEXT")

/* フックプロシージャで使用する変数は共有メモリにおく */
/*                                                    */
/* Visual C++ の場合                                 */
#pragma data_seg(".share")
UINT        WM_USERMESSAGE_IME          = 0;
HWND        keyboarHookdWndowHandle_    = (HWND )0;
HHOOK       hookWindowProcedureHandle_  = (HHOOK)0;
BOOL        isIMEOpen_                  = FALSE;
#pragma data_seg()

/* リンカのオプションに /SECTION:.share,RWS を追加 */
#pragma comment(linker, "/SECTION:.share,RWS")

/* フック プロシージャ */
LRESULT CALLBACK WindowProcedure(int code, WPARAM wParam, LPARAM lParam)
{
   CWPRETSTRUCT* callWindowProcedureStruct = (CWPRETSTRUCT*)lParam;
   if (code >= 0 && code == HC_ACTION && callWindowProcedureStruct->message == WM_IME_NOTIFY &&
       (callWindowProcedureStruct->wParam == IMN_SETOPENSTATUS || callWindowProcedureStruct->wParam == IMN_OPENSTATUSWINDOW)) {
       HIMC himc = ImmGetContext(callWindowProcedureStruct->hwnd);
       if (himc != (HIMC)0) {
           BOOL isIMEOpen = ImmGetOpenStatus(himc);
           ImmReleaseContext(callWindowProcedureStruct->hwnd, himc);
           if (isIMEOpen_ != isIMEOpen) {
               if (keyboarHookdWndowHandle_ != (HWND)0)
                   PostMessage(keyboarHookdWndowHandle_, WM_USERMESSAGE_IME, (WPARAM)isIMEOpen, 0L);
               isIMEOpen_ = isIMEOpen;
           }
       }
   }
   return CallNextHookEx(hookWindowProcedureHandle_, code, wParam, lParam);
}

/* フックのセット                          */
/* ここで渡したウィンドウに                 */
/* メッセージ WM_USERMESSAGE_IME が送られる   */
/* wParam : 仮想キーコード, lParam : 0     */
BOOL WINAPI Set(HWND keyboarHookdWndowHandle)
{
   HWND focusedWindowHandle;
   HIMC himc;

   /* キーのフック */
   /* ここで instanceHandle_ は DLL のインスタンス ハンドル */
   WM_USERMESSAGE_IME          = RegisterWindowMessage(WM_USER_MESSAGE_TEXT);
   keyboarHookdWndowHandle_    = keyboarHookdWndowHandle;
   hookWindowProcedureHandle_  = SetWindowsHookEx(WH_CALLWNDPROCRET, (HOOKPROC)WindowProcedure  , instanceHandle_, 0);

   focusedWindowHandle         = GetFocus();
   if (focusedWindowHandle == (HWND)0)
       focusedWindowHandle = GetDesktopWindow();
   himc                        = ImmGetContext(focusedWindowHandle);
   if (himc != (HIMC)0) {
       isIMEOpen_ = ImmGetOpenStatus(himc);
       ImmReleaseContext(focusedWindowHandle, himc);
   }

   return (hookWindowProcedureHandle_ != (HHOOK)0);
}

/* アンフック                    */
/* フックが不必要になったら呼ぶ */
void WINAPI Reset()
{
   if (hookWindowProcedureHandle_ != (HHOOK)0) {
       UnhookWindowsHookEx(hookWindowProcedureHandle_);
       hookWindowProcedureHandle_ = (HHOOK)0;
   }
}

【Q&A】 マウス ポインタがウィンドウから出たときに何か処理を実行したい

【Q】 描画ツール等で,マウス ポインタがウィンドウから出たときに,何か処理を実行したいのですが.

【A】 TrackMouseEvent という API を使用することで可能です (Windows 98, Windows NT 4.0 以降).

例.(MFC を使用しない場合)

#define _WIN32_WINNT 0x0400
#include <windows.h>
// …

static void TrackMouseLeaveEvent(HWND windowHandle)
{
   // マウスポインタがウィンドウから離れた時にメッセージをポストする
   // (Windows 98, Windows NT 4.0 以降)
   TRACKMOUSEEVENT trackmouseevent;
   trackmouseevent.cbSize      = sizeof(trackmouseevent);
   trackmouseevent.dwFlags     = TME_LEAVE;
   trackmouseevent.hwndTrack   = windowHandle;
   trackmouseevent.dwHoverTime = HOVER_DEFAULT;
   TrackMouseEvent(&trackmouseevent);
}

static LRESULT CALLBACK WindowProcedure(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
   {
       case WM_MOUSEMOVE:
           TrackMouseLeaveEvent(windowHandle);
           // …
           break;

       case WM_MOUSELEAVE:
           // カーソルがウィンドウの外に出た時の処理
           // … 省略 …
           break;

       // …

       default:
           return DefWindowProc(windowHandle, message, wParam, lParam);
   }
   return 0;
}

例.(MFC を使用する場合)

// ウィンドウ クラスのヘッダー ファイル View.h
#pragma once

#include <afxwin.h>

class View : public CWnd
{
protected:
   afx_msg void OnMouseMove(UINT nFlags, CPoint point);
   afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
   DECLARE_MESSAGE_MAP()

private:
   void TrackMouseLeaveEvent() const;
   // …
};

// ウィンドウ クラスの cpp ファイル View.cpp
#include "View.h"

BEGIN_MESSAGE_MAP(View, CWnd)
   ON_WM_MOUSEMOVE()
   ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()

void View::OnMouseMove(UINT nFlags, CPoint point)
{
   TrackMouseLeaveEvent();
   // …
}

LRESULT View::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
   // カーソルがウィンドウの外に出た時の処理
   // … 省略 …

   return 0L;
}

void View::TrackMouseLeaveEvent() const
{
   // マウスポインタがウィンドウから離れた時にメッセージをポストする
   // (Windows 98, Windows NT 4.0 以降)
   TRACKMOUSEEVENT trackmouseevent;
   trackmouseevent.cbSize      = sizeof(trackmouseevent);
   trackmouseevent.dwFlags     = TME_LEAVE;
   trackmouseevent.hwndTrack   = GetSafeHwnd();
   trackmouseevent.dwHoverTime = HOVER_DEFAULT;
   ::TrackMouseEvent(&trackmouseevent);
}
// …

【Q&A】 ウィンドウズ インストーラによって作成されたショートカットからパスを取得したい

【Q】 「【Q&A】 ショートカットからパスを取得したい」の方法では,ウィンドウズ インストーラによって作成されたショートカットの場合パスがうまく取得できないようなんですが.

【A】 MsiGetShortcutTarget と MsiGetComponentPath という API を使用することで可能です (Windows 2000,Windows Me 以降).

#define _WIN32_WINNT 0x0500

#include <windows.h>
#include <tchar.h>
// Windows NT 4.0,Windows 95,Internet Explorer 4.0 以降
#include <shlobj.h> // for IShellLink
// Windows 2000,Windows Me 以降
#include <msi.h>    // for MsiGetShortcutTarget, MsiGetComponentPath, MAX_FEATURE_CHARS

#if     _MSC_VER >= 1300
   // WindowsInstaller で作成されたショートカットからのパス名の取得
   static bool ReadMsiShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       if (targePathName != NULL) {
           const size_t    bufferLength = 39;
           TCHAR           productCode  [bufferLength         ];
           TCHAR           featureId    [MAX_FEATURE_CHARS + 1];
           TCHAR           componentCode[bufferLength         ];
           UINT            result = ::MsiGetShortcutTarget(linkPathName, productCode, featureId, componentCode);
           if (result == ERROR_SUCCESS) {
               DWORD targePathNameLength = DWORD(_MAX_PATH);
               result = ::MsiGetComponentPath(productCode, componentCode, targePathName, &targePathNameLength);
               if (result == INSTALLSTATE_LOCAL)
                   return true;
           }
       }
       return false;
   }
#endif  //  _MSC_VER

// ショートカットからのパス名の取得
bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
{
#if     _MSC_VER >= 1300
   if (ReadMsiShortcut(targePathName, linkPathName))
       return true;
#endif  //  _MSC_VER

  bool            temp(false);
  HRESULT         result = ::CoInitialize(NULL);
  if (SUCCEEDED(result)) {
      IShellLink* pShellLink;
      result = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink);
      if (SUCCEEDED(result)) {
          IPersistFile*   pPersistFile;
          result = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
          if (SUCCEEDED(result)) {
#ifdef  _UNICODE
              result = pPersistFile->Load(linkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#else   //  _UNICODE
              //  Unicodeに変換
              wchar_t wlinkPathName[_MAX_PATH];
              ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
              result = pPersistFile->Load(wlinkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#endif  //  _UNICODE
              if (SUCCEEDED(result)) {
                  WIN32_FIND_DATA findData;
                  result = pShellLink->GetPath(targePathName, _MAX_PATH, &findData, 0);
                  if (SUCCEEDED(result)) {
                      result = (targetArgument == NULL) ? S_OK : pShellLink->GetArguments(targetArgument, int(targetArgumentMaxTextLength));
                      if (SUCCEEDED(result))
                          temp = true;
                  }
              }
              pPersistFile->Release();
          }
          pShellLink->Release();
      }
      CoUninitialize();
  }
  return temp;
}

// ディレクトリ名の正規化 (サブルーチン)
// (C++ 用)
static void RegulateDirectoryName(LPTSTR directoryName)
{
  const size_t directoryNameTextLength(::_tcslen(directoryName));
  if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
      directoryName[directoryNameTextLength    ] = _T('\\');
      directoryName[directoryNameTextLength + 1] = _T('\0');
  }
}

// ディレクトリ名とファイル名を連結してパス名へ (サブルーチン)
// (C++ 用)
static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
{
  ::_tcscpy(pathName, directoryName);
  RegulateDirectoryName(pathName);
  ::_tcscat(pathName, fileName);
}

// ショートカットからのパス名の取得
bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkDirectoryName /* ショートカットが在るディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
{
  TCHAR linkFileName[_MAX_PATH];
  ::_tcscpy(linkFileName, linkName  );
  ::_tcscat(linkFileName, _T(".lnk"));
  TCHAR linkPathName[_MAX_PATH];
  MakePathName(linkPathName, linkDirectoryName, linkFileName);
  return ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, linkPathName);
}

// 「送る」からのパス名の取得
bool ReadSendTo(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkName /* ショートカットの名称 */)
{
  TCHAR directoryName[_MAX_DIR];
  return ::SHGetSpecialFolderPath(::GetDesktopWindow(), directoryName, CSIDL_SENDTO, false)
         ? ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, directoryName, linkName)
         : false;
}


【Q&A】 プラグインを実現したい

【Q】 プラグインの形で,後から動的に機能を追加できるようなアプリケーションを作りたいんですが.

【A】 決められた形式の DLL が存在したらそれを LoadLibrary してメニューに追加するようにしてはどうでしょう.

例.

// Plug-in をサポートした実行ファイル (LLTestSample.exe) のサンプル ソース

#include <windows.h>
#include <tchar.h>
#include <map>
#include <cassert>

class PlugIn
{
private:
   typedef void (*TITLE_PROCEDURE  )(TCHAR* buffer, size_t bufferSize);
   typedef void (*COMMAND_PROCEDURE)()      ;

   static const TCHAR  titleProcedureName  [];
   static const TCHAR  commandProcedureName[];

   enum { nameLength = 1024 };

   TCHAR               name[nameLength];
   COMMAND_PROCEDURE   command;
   bool                isValid;

   static FARPROC Procedure(HMODULE libraryHandle, const TCHAR* procedureName)
   {
       assert(libraryHandle != NULL);
       return ::GetProcAddress(libraryHandle, procedureName);
   }

   bool LoadName(HMODULE libraryHandle)
   {
       const TITLE_PROCEDURE titleProcedure = TITLE_PROCEDURE(Procedure(libraryHandle, titleProcedureName));
       if (titleProcedure == NULL) {
           name[0] = _T('\0');
           return false;
       }
       titleProcedure(name, nameLength);
       return true;
   }

   bool LoadCommand(HMODULE libraryHandle)
   {
       command = COMMAND_PROCEDURE(Procedure(libraryHandle, commandProcedureName));
       return (command != NULL);
   }

public:
   PlugIn() : isValid(false)
   { name[0] = _T('\0'); }

   PlugIn(HMODULE libraryHandle )
   { Initialize(libraryHandle); }

   PlugIn(const PlugIn& o) : command(o.command), isValid(o.IsValid())
   { ::lstrcpy(name, o.name); }

   const PlugIn& operator =(const PlugIn& o)
   {
       command = o.command;
       isValid = o.IsValid();
       ::lstrcpy(name, o.name);
       return *this;
   }

   bool Initialize(HMODULE libraryHandle)
   {
       isValid = (LoadName(libraryHandle) && LoadCommand(libraryHandle));
       return IsValid();
   }

   const TCHAR* Name() const
   { return name; }

   bool Command() const
   {
       if (IsValid()) {
           assert(command != NULL);
           command();
           return true;
       }
       return false;
   }

   bool IsValid() const
   { return isValid; }
};

const TCHAR PlugIn::titleProcedureName  [] = _T("Title"  );
const TCHAR PlugIn::commandProcedureName[] = _T("Command");

class PlugInManager
{
private:
   std::map<UINT, PlugIn>  plugInTable;

   static bool AppendMenu(HMENU menuHandle, UINT commandID, const TCHAR* menuText)
   {
       return !!::AppendMenu(menuHandle, 0, commandID, menuText);
   }

   static FARPROC Procedure(HMODULE libraryHandle, const TCHAR* procedureName)
   {
       return (libraryHandle == NULL) ? NULL : ::GetProcAddress(libraryHandle, procedureName);
   }

   void LoadLibrary(const TCHAR* libraryName, HMENU menuHandle, UINT commandID)
   {
       const HMODULE libraryHandle = ::LoadLibrary(libraryName);
       if (libraryHandle == NULL)
           return;

       PlugIn plugIn;
       if (plugIn.Initialize(libraryHandle)) {
           AppendMenu(menuHandle, commandID, plugIn.Name());
           plugInTable[commandID] = plugIn;
       }
   }

public:
   static const TCHAR  fileName[];

   void Load(HMENU menuHandle, UINT commandIDFirst)
   {
       WIN32_FIND_DATA findFileData;
       const HANDLE    find = ::FindFirstFile(PlugInManager::fileName, &findFileData);
       if (find != INVALID_HANDLE_VALUE) {
           for (UINT number = 0; ; number++) {
               LoadLibrary(findFileData.cFileName, menuHandle, commandIDFirst + number);
               if (!::FindNextFile(find, &findFileData))
                   break;
           }
           ::FindClose(find);
       }
   }

   bool Command(UINT commandID)
   {
       const std::map<UINT, PlugIn>::const_iterator iterator = plugInTable.find(commandID);
       if (iterator == plugInTable.end())
           return false;
       return iterator->second.Command();
   }
};

const TCHAR PlugInManager::fileName[] = _T("*.dls");

class Application
{
private:
   static const TCHAR      className_[];
   static PlugInManager    plugInManager_;

   class Menu
   {
   private:
       static HMENU            mainMenuHandle_  ;
       static HMENU            fileMenuHandle_  ;
       static HMENU            sampleMenuHandle_;

   public:
       static bool Create(HWND windowHandle)
       {
           mainMenuHandle_     = ::CreateMenu();
           fileMenuHandle_     = ::CreateMenu();
           sampleMenuHandle_   = ::CreateMenu();
           if (mainMenuHandle_ != NULL && fileMenuHandle_ != NULL && sampleMenuHandle_ != NULL) {
               if (::AppendMenu(mainMenuHandle_, MF_POPUP, UINT(fileMenuHandle_  ), _T("ファイル(&F)"              )) &&
                   ::AppendMenu(fileMenuHandle_, 0       , IDM_EXIT               , _T("アプリケーションの終了(&X)")) &&
                   ::AppendMenu(mainMenuHandle_, MF_POPUP, UINT(sampleMenuHandle_), _T("サンプル(&S)"              ))) {
                   if (::SetMenu(windowHandle, mainMenuHandle_))
                       return true;
               }
           }
           Destroy();
           return false;
       }

       static void Destroy()
       {
           if (sampleMenuHandle_ != NULL) { ::DestroyMenu(sampleMenuHandle_); sampleMenuHandle_    = NULL; }
           if (fileMenuHandle_   != NULL) { ::DestroyMenu(fileMenuHandle_  ); fileMenuHandle_      = NULL; }
           if (mainMenuHandle_   != NULL) { ::DestroyMenu(mainMenuHandle_  ); mainMenuHandle_      = NULL; }
       }

       static HMENU PlugIn(HWND windowHandle, int position)
       {
           const HMENU menuHandle = ::GetMenu(windowHandle);
           return (menuHandle == NULL) ? NULL : ::GetSubMenu(menuHandle, position);
       }
   };

   static void InitializePlugIn(HWND windowHandle)
   {
       const int   plugInMenuPosition(1);
       const UINT  plugInCommandIDFirst(1000);
       plugInManager_.Load(Menu::PlugIn(windowHandle, plugInMenuPosition), plugInCommandIDFirst);
       ::DrawMenuBar(windowHandle);
   }

   static bool RegisterClass(HINSTANCE instanceHandle)
   {
       WNDCLASSEX wcex;
       wcex.cbSize         = sizeof(WNDCLASSEX);
       wcex.style          = CS_HREDRAW | CS_VREDRAW;
       wcex.lpfnWndProc    = WindowProcedure;
       wcex.cbClsExtra     = 0;
       wcex.cbWndExtra     = 0;
       wcex.hInstance      = instanceHandle;
       wcex.hIcon          = NULL;
       wcex.hCursor        = ::LoadCursor(NULL, IDC_ARROW);
       wcex.hbrBackground  = HBRUSH(COLOR_WINDOW + 1);
       wcex.lpszMenuName   = NULL;
       wcex.lpszClassName  = className_;
       wcex.hIconSm        = NULL;
       return (::RegisterClassEx(&wcex) != NULL);
   }

   static bool Initialize(HINSTANCE instanceHandle, int commandShow)
   {
       if (RegisterClass(instanceHandle)) {
           const TCHAR name[]       = _T("LLTestSample");
           const HWND  windowHandle = ::CreateWindow(className_, name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, instanceHandle, NULL);
           if (windowHandle != NULL && Menu::Create(windowHandle)) {
               InitializePlugIn(windowHandle);
               ::ShowWindow(windowHandle, commandShow);
               ::UpdateWindow(windowHandle);
               return true;
           }
       }
       return false;
   }

   static void End()
   { Menu::Destroy(); }

   static LRESULT CALLBACK WindowProcedure(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
   {
       switch (message) {
           case WM_COMMAND:
               {
                   const commandID(LOWORD(wParam));
                   if (plugInManager_.Command(commandID))
                       return 0;
                   switch(commandID) {
                       case IDM_EXIT:
                           ::DestroyWindow(windowHandle);
                          return 0;
                   }
               }
               break;
           case WM_DESTROY:
               ::PostQuitMessage(0);
               return 0;
       }
       return ::DefWindowProc(windowHandle, message, wParam, lParam);
   }

   static int MainLoop()
   {
       MSG message;
       while (::GetMessage(&message, NULL, 0, 0)) {
           ::TranslateMessage(&message);
           ::DispatchMessage (&message);
       }
       return message.wParam;
   }

public:
   enum { IDM_EXIT = 100 };

   static int Run(HINSTANCE instanceHandle, int commandShow)
   {
       if (Initialize(instanceHandle, commandShow)) {
           const int result(MainLoop());
           End();
           return result;
       }
       return 0;
   }
};

const TCHAR     Application::className_[]   = _T("LLTestSampleClass");
PlugInManager   Application::plugInManager_;

HMENU           Application::Menu::mainMenuHandle_      = NULL;
HMENU           Application::Menu::fileMenuHandle_      = NULL;
HMENU           Application::Menu::sampleMenuHandle_    = NULL;

int APIENTRY WinMain(HINSTANCE instanceHandle, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int commandShow)
{
return Application::Run(instanceHandle, commandShow); }


// Plug-in をサポートした実行ファイル (LLTestSample.exe) 用の Plug-in (DLLSample1.dls: DLL形式) のサンプル ソース

#include <windows.h>
#include <tchar.h>

BOOL APIENTRY DllMain(HANDLE /* hModule */, DWORD /* ul_reason_for_call */, LPVOID /* lpReserved */)
{
return TRUE; }

extern "C" {
   __declspec(dllexport) void Title(TCHAR* buffer, size_t bufferLength)
   {
       ::lstrcpyn(buffer, _T("DLLサンプル1(&1)"), bufferLength);
   }

   __declspec(dllexport) void Command()
   {
       ::MessageBox(NULL, _T("1!"), _T("DLLSample1"), MB_OK);
   }
}


// Plug-in をサポートした実行ファイル (LLTestSample.exe) 用の Plug-in (DLLSample2.dls: DLL形式) のサンプル ソース

#include <windows.h>
#include <tchar.h>

BOOL APIENTRY DllMain(HANDLE /* hModule */, DWORD /* ul_reason_for_call */, LPVOID /* lpReserved */)
{
return TRUE; }

extern "C" {
   __declspec(dllexport) void Title(TCHAR* buffer, size_t bufferLength)
   {
       ::lstrcpyn(buffer, _T("DLLサンプル2(&2)"), bufferLength);
   }

   __declspec(dllexport) void Command()
   {
       ::MessageBox(NULL, _T("2!"), _T("DLLSample2"), MB_OK);
   }
}