自分用の偏った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 型のパブリック変数で、コマンドラインの引数がそのまま入っている。
メンバ宣言
// アイコン情報 NOTIFYICONDATA m_icon;
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);
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);
ヘッダのDECLARE_MESSAGE_MAPの前に..
afx_msg LRESULT OnNotifyTrayicon(WPARAM, LPARAM);
本体のMESSAGE_MAPの前に..
#define NOTIFY_TRAYICON (WM_USER + 1)
本体のMESSAGE_MAP内で..
ON_MESSAGE(NOTIFY_TRAYICON, OnNotifyTrayicon)
アイコンをShell_NotifyIconする前に..
// アイコンに関するイベントの識別子 m_icon.uCallbackMessage = NOTIFY_TRAYICON;
本体にポップアップを表示する関数を追加
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 )
| 警告の指定子 | 意味 |
|---|---|
| once | 指定したメッセージは 1 回だけしか表示しません。 |
| default | 指定したメッセージの動作はコンパイラのデフォルトと同じです。 |
| 1,2,3,4 | 指定した警告メッセージに指定の警告レベルを割り当てます。 |
| disable | 指定した警告メッセージの出力を行いません。 |
| error | 指定した警告メッセージをエラーとして出力します。 |
任意の警告番号
if (CAtisViewerApp::GetComCtl32Version() >= COMCTL32_471) {
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);
システムメニューの「閉じる」を無効にすれば、連動してウィンドウ右上の閉じるボタン(×)も無効に。
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)についても同様。
リソースエディタで適当なダイアログバーを作成する。
CMainFrameにメンバ変数を追加する
protected: CDialogBar m_wndDialogBar;
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);
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()
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()
ハンドラをCMainFrame.cpp内に記述する
void CMainFrame::OnEnChange() {
CEdit *edit1 = (CEdit *)m_wndDialogBar.GetDlgItem(IDC_EDIT1);
CString edit1Value;
edit1->GetWindowText(edit1Value);
SetWindowMessage(edit1Value);
// ...
}
void CMainFrame::OnBnClicked() {
// ...
}
グローバルメモリのハンドラを用意。
// グローバルメモリ IMalloc* g_pMalloc;
コンストラクタなどで初期化。
CoGetMalloc(1,>m_pMalloc);
デストラクタなどで開放。
g_pMalloc->Release();
こういうコードを用意する。
// !危険(リエントラント未対応部分)
_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;
}
こういう関数を呼ぶ。
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);
}
簡単。でもたぶん昔の環境だと動かない。
::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';
}