TOP


K's Know-how

自分用の偏ったKnow-how集。VC++編(一部例外あり)。

タスクバーにアイコンを出さない

スタイルにWS_EX_TOOLWINDOWを指定したウィンドウを、アプリケーションのMainFrameにする。

非矩形ウィンドウを生成する

ウィンドウの形のリージョンを作成し、CWin::SetWindowRgn()で設定する。

簡単に非矩形ウィンドウ用リージョンを生成する

ビットマップを作成し、この関数を呼べばいい。与えるビットマップの左上角の色が透明色の扱いになる。

HRGN ::CreateRgn(HBITMAP bitmap)
{
	BITMAP bm;
	BITMAPINFOHEADER bi;
	HDC hDC;
	HRGN hRgn = NULL;
	LPRECT lpRect;
	LPRGNDATA lpRgnData;
	LPCOLORREF lpScanData;
	int x, y, z, len, nRect = 0;
	COLORREF transcolor;

	hDC = CreateCompatibleDC( NULL );
	if ( hDC )
	{
		GetObject( bitmap, sizeof(BITMAP), &bm );
		lpRgnData = (LPRGNDATA) new BYTE[sizeof(RGNDATAHEADER) + sizeof(RECT) * bm.bmWidth * bm.bmHeight];
		if ( lpRgnData )
		{
			lpRect = (LPRECT) lpRgnData->Buffer;
			lpScanData = new COLORREF[bm.bmWidth];
			if ( lpScanData )
			{
				ZeroMemory( &bi, sizeof(BITMAPINFOHEADER) );
				bi.biSize = sizeof(BITMAPINFOHEADER);
				bi.biWidth = bm.bmWidth;
				bi.biHeight = bm.bmHeight;
				bi.biPlanes = 1;
				bi.biBitCount = 32;
				bi.biCompression = BI_RGB;
				bool first = true;
				for( y = 1; y <= bm.bmHeight; y++ )
				{
					GetDIBits( hDC, bitmap, bm.bmHeight - y, 1, lpScanData, (LPBITMAPINFO) &bi, DIB_RGB_COLORS );
					for( x = 0; x < bm.bmWidth; x++ )
					{
						if (first) {
							transcolor = lpScanData[x];
							first = false;
						}
						if ( lpScanData[x] != transcolor )
						{
							for( len = 1, z = x++; x < bm.bmWidth; x++, len++ )
							{
								if ( lpScanData[x] == transcolor )
								{
									break;
								}
							}
							lpRect->left = z;
							lpRect->top = y - 1;
							lpRect->right = z + len;
							lpRect->bottom = y;
							lpRect++;
							nRect++;
						}
					}
				}
				lpRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
				lpRgnData->rdh.iType = RDH_RECTANGLES;
				lpRgnData->rdh.nRgnSize = sizeof(RGNDATAHEADER) + sizeof(RECT) * nRect;
				lpRgnData->rdh.nCount = nRect;
				lpRgnData->rdh.rcBound.left =
				lpRgnData->rdh.rcBound.top = 0;
				lpRgnData->rdh.rcBound.right = bm.bmWidth;
				lpRgnData->rdh.rcBound.bottom = bm.bmHeight;
				hRgn = ExtCreateRegion( NULL, lpRgnData->rdh.nRgnSize, lpRgnData );
				delete lpScanData;
			}
			delete lpRgnData;
		}
		DeleteDC( hDC );
	}
	return hRgn;
}


old: for( y = 1; y < bm.bmHeight; y++ )
new: for( y = 1; y <= bm.bmHeight; y++ )
こんなページ存在自体を忘れていたのですが、ここをご覧になったFさんとおっしゃる方から上記の間違いの指摘をいただきました。訂正させていただきます。ありがとうございます。(2003/11/29追記)

ダイアログベースのアプリケーションでコマンドラインを参照する

VC++6でAppWizardを使ってMFC Applicationを作ると、CYourApplicationApp::InitInstance()内に以下のようなコードが挿入される。


	// DDE、file open など標準のシェル コマンドのコマンドラインを解析します。
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// コマンドラインでディスパッチ コマンドを指定します。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

このコードがコマンドラインを解析して、パラメータで指定されたファイルを開いたり、印刷したりする。 ところが、アプリケーションの種類をダイアログベースや"ドキュメント/ビューアーキテクチャのサポート"をオフにして作成すると、このコードが生成されない。で、どうするかというと、CMainApp::m_lpCmdLineというメンバ変数を直に参照する。これは LPTSTR 型のパブリック変数で、コマンドラインの引数がそのまま入っている。

タスクトレイにアイコンを出す
  1. メンバ宣言

    	// アイコン情報
    	NOTIFYICONDATA m_icon;
    
  2. OnInitDialog()などで..

    	// アイコン情報初期化
    	ZeroMemory(&m_icon, sizeof(NOTIFYICONDATA));
    	// アイコンのファイルサイズ
    	m_icon.cbSize = sizeof(NOTIFYICONDATA);
    	// アイコンの識別ID
    	m_icon.uID = 1;
    	// イベントと関連づけるウインドウ
    	m_icon.hWnd = GetSafeHwnd();
    	// アイコンの設定
    	m_icon.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    	// アプリケーションのアイコン
    	m_icon.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    	// バルーンの文字列
    	lstrcpy(nIcon.szTip, (::AfxGetAppName());
    	// タスクトレイに表示
    	::Shell_NotifyIcon( NIM_ADD, &m_icon);
    
  3. OnInitDialog()などで..

    	// タスクトレイのアイコンを削除
    	Shell_NotifyIcon( NIM_DELETE, &m_icon);
    	// タスクトレイを更新する
    	PostMessage(WM_NULL, 0, 0); 
    
タスクトレイにきれいなアイコンを出す

::LoadImage()を使って標準(32x32)ではないサイズのアイコンをロードする。

	m_icon.hIcon = (HICON)::LoadImage(
		::AfxGetInstanceHandle(),
		MAKEINTRESOURCE(IDR_MAINFRAME),
		IMAGE_ICON,
		16, // desired width
		16, // desired height
		LR_DEFAULTCOLOR);
タスクトレイでポップアップメニューを出す
  1. ヘッダのDECLARE_MESSAGE_MAPの前に..

    	afx_msg LRESULT OnNotifyTrayicon(WPARAM, LPARAM);
    
  2. 本体のMESSAGE_MAPの前に..

    #define NOTIFY_TRAYICON (WM_USER + 1)
    
  3. 本体のMESSAGE_MAP内で..

    	ON_MESSAGE(NOTIFY_TRAYICON, OnNotifyTrayicon)
    
  4. アイコンをShell_NotifyIconする前に..

    	// アイコンに関するイベントの識別子
    	m_icon.uCallbackMessage = NOTIFY_TRAYICON;
    
  5. 本体にポップアップを表示する関数を追加

    LRESULT CYourApp::OnNotifyTrayicon(WPARAM wparam, LPARAM lparam)
    {
    	if(lparam == WM_RBUTTONDOWN) {
    		CPoint point;
    		::GetCursorPos(&point);
    		SetForegroundWindow();
    		m_popup.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x - 2, point.y - 2, this);
    	}
    	return 0;
    }
    
ブラウザのキオスクモード
Internet Explorer-kをつける。
Netscape Navigator-skをつける。すでにNavigatorが起動していると効果がない。
ショートカットを作る
HRESULT CreateLink(LPCSTR lpszPathObj, LPSTR lpszPathLink, LPSTR lpszDesc)
{
    HRESULT hres;
    IShellLink* psl;
 
    // Get a pointer to the IShellLink interface.
    hres = CoCreateInstance(&CLSID_ShellLink, NULL,
        CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl);
    if (SUCCEEDED(hres)) {
        IPersistFile* ppf;
 
        // Set the path to the shortcut target, and add the
        // description.
        psl->lpVtbl->SetPath(psl, lpszPathObj);
        psl->lpVtbl->SetDescription(psl, lpszDesc);
 
       // Query IShellLink for the IPersistFile interface for saving the
       // shortcut in persistent storage.
        hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile,
            &ppf);
 
        if (SUCCEEDED(hres)) {
            WORD wsz[MAX_PATH];
 
            // Ensure that the string is ANSI.
            MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1,
                wsz, MAX_PATH);
 
            // Save the link by calling IPersistFile::Save.
            hres = ppf->lpVtbl->Save(ppf, wsz, TRUE);
            ppf->lpVtbl->Release(ppf);
        }
        psl->lpVtbl->Release(psl);
    }
    return hres;
}
重複起動の禁止

InitInstance()で..
	m_hEvent = ::CreateEvent(NULL, FALSE, FALSE,_T("UNIQUE ID"));
	if (m_hEvent == NULL) {
		return FALSE;
	}
	if (::GetLastError() == ERROR_ALREADY_EXISTS) {
		::CloseHandle(m_hEvent);
		return FALSE;
	}

ExitInstance()で..
	::CloseHandle(m_hEvent);

特定のワーニング出力を禁止

そういえば「ワーニング」ってバカっぽいって話がどこかであったな。でもまあいいや。バカだし。

#pragma warning( disable : 4507 )

書式
#pragma warning( warning-specifier : warning-number-list )
機能
コンパイラが出力する警告メッセージの動作を設定します。
パラメータ
  • warning-specifier
    警告の指定子意味
    once指定したメッセージは 1 回だけしか表示しません。
    default指定したメッセージの動作はコンパイラのデフォルトと同じです。
    1,2,3,4指定した警告メッセージに指定の警告レベルを割り当てます。
    disable指定した警告メッセージの出力を行いません。
    error指定した警告メッセージをエラーとして出力します。
  • warning-number-list

    任意の警告番号

クールバー(CToolBar::CreateEx())を使う条件
	if (CAtisViewerApp::GetComCtl32Version() >= COMCTL32_471) {
OSのバージョンをプログラムでチェックする

	OSVERSIONINFO Info;

	Info.dwOSVersionInfoSize = sizeof Info;
	GetVersionEx( &Info );

リソースの文字列を取得する

CString::LoadStringを使う

無効領域を塗りつぶす
BOOL CCLTVView::OnEraseBkgnd(CDC* pDC) 
{
	CBrush br(GetSysColor(COLOR_3DSHADOW));
	FillOutsideRect(pDC, &br);
	return TRUE;
}
システムカラーを使う

COLORREF ::GetSysColor(id);

xボタンの無効化

システムメニューの「閉じる」を無効にすれば、連動してウィンドウ右上の閉じるボタン(×)も無効に。

    CMenu   *menu=CWnd::GetSystemMenu(FALSE);
    menu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND|MF_GRAYED);

以下のようにすれば、有効に戻すことも可能。

    CMenu   *menu=CWnd::GetSystemMenu(FALSE);
    menu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND|MF_ENABLED);

ほかの[_](最小化=SC_MINIMIZE)や[□](最大化=SC_MAXIMIZE)についても同様。

ダイアログバーの作り方
  1. リソースエディタで適当なダイアログバーを作成する。

  2. CMainFrameにメンバ変数を追加する

    protected:
    	CDialogBar m_wndDialogBar;
    
  3. CMainFrame::OnCreate()内でダイアログバーを生成する

    	EnableDocking(CBRS_ALIGN_ANY);	// すでに指定されていれば不要
    
    	if (!m_wndDialogBar.Create(this,IDD_DIALOGBAR,CBRS_TOP,IDD_DIALOGBAR)) {
    		TRACE0("Failed to create status bar\n");
    		return -1;      // 作成に失敗
    	}
    	m_wndDialogBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
    	DockControlBar(&m_wndDialogBar);
    
  4. CMainFrame.cppの先頭にあるメッセージマップにエントリーする

    ON_BN_CLICKEDとかは各コントロールクラスの説明に記載されている。 IDC_EDIT1とかはダイアログバーに貼り付けたコントロールのID。 ハンドラ名(OnBnClickedとか)は任意。

    BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
    	//{{AFX_MSG_MAP(CMainFrame)
    	ON_WM_CREATE()
    	ON_WM_KEYDOWN()
    	ON_WM_LBUTTONDOWN()
    	//}}AFX_MSG_MAP
    	ON_EN_CHANGE(IDC_EDIT1,OnEnChange) // これはエディットの変更
    	ON_BN_CLICKED(IDC_BUTTON1,OnBnClicked)	//これはボタンクリック
    END_MESSAGE_MAP()
    
  5. CMainFrame.hの末尾にあるテーブルにメッセージハンドラの宣言を追加する

    // 生成されたメッセージ マップ関数
    protected:
    	//{{AFX_MSG(CMainFrame)
    	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    	afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    	//}}AFX_MSG
    	afx_msg void OnEnChange();	// ←これ
    	afx_msg void OnBnClicked();	// ←これも
    	DECLARE_MESSAGE_MAP()
    
  6. ハンドラをCMainFrame.cpp内に記述する

    void CMainFrame::OnEnChange() {
    	CEdit *edit1 = (CEdit *)m_wndDialogBar.GetDlgItem(IDC_EDIT1);
    	CString edit1Value;
    	edit1->GetWindowText(edit1Value);
    	SetWindowMessage(edit1Value);
    	// ...
    }
    
    void CMainFrame::OnBnClicked() {
    	// ...
    }
    
ディレクトリ選択ダイアログの使い方
  1. グローバルメモリのハンドラを用意。

    	// グローバルメモリ
    	IMalloc* g_pMalloc;
    
  2. コンストラクタなどで初期化。

        CoGetMalloc(1,>m_pMalloc);
    
  3. デストラクタなどで開放。

    	g_pMalloc->Release();
    
  4. こういうコードを用意する。

    // !危険(リエントラント未対応部分)
    _TCHAR shared_path_block[_MAX_PATH];
    
    // コールバック関数
    int WINAPI BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
    {
        switch (uMsg) {
        case BFFM_INITIALIZED:
    		// shared_path_blockを反映する
            SendMessage(hwnd, BFFM_SETSELECTION,TRUE,(LPARAM)shared_path_block);
            break;
        }
        return 0;
    }
    
  5. こういう関数を呼ぶ。

    void CYourApp::ShowPathDialog(CString &strPath, UINT nCaptionID)
    {
        BROWSEINFO bi;
        LPITEMIDLIST pidlMyComp;
        LPITEMIDLIST pidlBrowse;
    	// PIDL確保
    	if (!SUCCEEDED(SHGetSpecialFolderLocation(
    			AfxGetMainWnd()->GetSafeHwnd(), CSIDL_DESKTOP, &pidlMyComp))) {
    		// 失敗
            return;
    	}
    	if (strPath.IsEmpty()) {
    		// カレントディレクトリ取得
    		_TCHAR path[_MAX_PATH];
    		_tgetcwd(path, _MAX_PATH);
    		strncpy(shared_path_block, path, _MAX_PATH);
    	} else {
    		strncpy(shared_path_block, strPath, _MAX_PATH);
    	}
    	// BROWSEINFO構造体作成
        bi.hwndOwner = AfxGetMainWnd()->GetSafeHwnd();
        bi.pidlRoot = pidlMyComp;
        bi.pszDisplayName = shared_path_block;
    	CString title((LPCSTR)nCaptionID);
        bi.lpszTitle = title;
        bi.ulFlags = 0;
    	bi.lpfn = BrowseCallbackProc; // コールバックしなければNULL(bi.lpfn = NULL;)
        bi.lParam = 0;
    	// ダイアログ表示
        pidlBrowse = SHBrowseForFolder(&bi); 
        if (pidlBrowse != NULL) {
            // パスの取得
    		LPTSTR p = strPath.GetBuffer(_MAX_PATH);
            SHGetPathFromIDList(pidlBrowse, p);
    		strPath.ReleaseBuffer();
            // PIDL解放
            g_pMalloc->Free(pidlBrowse); 
        }
        // メモリ解放
        g_pMalloc->Free(pidlMyComp);
    }
    
ブラウザでHTMLファイルを開く

簡単。でもたぶん昔の環境だと動かない。

	::ShellExecute(m_hWnd, "open", "septigram\\index.html", NULL, "C:\\pub", SW_SHOW);
実行ファイルのディレクトリを取得
    char    fullpath[_MAX_PATH];
	::GetModuleFileName(NULL, fullpath, sizeof(fullpath));
	char *p = ::strrchr(fullpath, '\\');
	if (p != NULL) {
		*(p + 1) = '\0';
	}

TOP