カテゴリ:C/C++/Win32( 131 )

e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

MFCをまねしたちょこっとした便利マクロ

MFCをまねしたちょこっとした便利マクロ


#include <tchar.h>
#define TRACE(str, ...) \
{ \
    TCHAR c[256]; \
    _stprintf( c, str, __VA_ARGS__ ); \
    OutputDebugString( c ); \
}

[PR]
by isoq | 2013-10-22 16:03 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

CTabView/CMFCTabCtrl のタブをマウスホイールでスクロールさせる

Firefox で Tab Mix Plus とかの設定調整アドオンを使用すると、タブ上にマウスポインタを置き、ホイールをスクロールすると、次々とタブを切り替える事が出来る機能を有効にすることが出来ます。これって結構便利です。

で、MFCのCTabViewを使ってみたところ、タブが収まりきらなくなると画面外にタブが並び、それを画面内に持ってくるにはボタンで、タブを1つずつ移動していかないといけない。これがとても面倒で、タブがたくさんあるとうっとうしい。ボタンが小さくて、右の方にあるから余計に使いにくい。

そこで、マウススクロールでタブを切り替えできないかと、それらしき機能を探したが見つからない。

CTabView で使用しているタブコントロールは、 CMFCTabCtrl です。

一番簡単なのは、CMFCBaseTabCtrl::GetActiveTab() で現在アクティブなタブのインデックスを取得して、CMFCBaseTab::SetActiveTab() を使って、+1 や -1 したインデックスのタブをアクティブにして、EnsureVisible() で画面に表示する方法。

でも、今回は、タブをアクティブにしないでスクロールだけしたかった。
というのも、たくさんタブがあって、その1つ1つがアクティブになった時に画像データを読み込むんだけど、画像データのサイズが半端なくでかい。
最小100MB~最大1GB強の画像データ・・・ってどんなんだ!

A4一枚程度を 1200dpi / 48bit カラーでスキャンした生データなのです。

だから、なるべく使ってない画像は開きたくない。

そこで、次のように、ちょっと危険な手を使って、スクロールを実現してみました。

// CMFCTabCtrl のハッククラス(ポインタをキャストして使用する)
class CMFCTabCtrlEx : public CMFCTabCtrl
{
public:
    int GetTabsHorzOffset(){ return m_nTabsHorzOffset; }    // オフセット取得
    void SetTabsHorzOffset(int nOffset)                     // オフセットをセット
    { 
        m_nTabsHorzOffset = nOffset;
        AdjustTabs();
        AdjustTabsScroll();
        RedrawWindow();
    }
    int GetTabsHorzOffsetMax(){ return m_nTabsHorzOffsetMax; } // オフセットの最大値を取得
private:
    CMFCTabCtrlEx();    // インスタンス化拒否
};
 
BOOL CXxxTabView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
    CMFCTabCtrlEx* pTabCtrlEx = (CMFCTabCtrlEx*)&GetTabControl();
    pTabCtrlEx->ScreenToClient(&pt);
    if(pTabCtrlEx->IsPtInTabArea(pt))
    {
        int nPos = pTabCtrlEx->GetTabsHorzOffset();
        int nMax = pTabCtrlEx->GetTabsHorzOffsetMax();
        int nScroll = 50;    // スクロール量(ピクセル?)
        if(zDelta < 0)
        {
            int nTarget = nPos + nScroll;
            if(nTarget > nMax) nTarget = nMax;
            if(nTarget < 0) nTarget = 0;
            pTabCtrlEx->SetTabsHorzOffset(nTarget);
        }
        else
        {
            int nTarget = nPos - nScroll;
            if(nTarget < 0) nTarget = 0;
            pTabCtrlEx->SetTabsHorzOffset(nTarget);
        }
    }
    else
    {
        return CTabView::OnMouseWheel(nFlags, zDelta, pt);
    }
}



CMFCTabCtrlのソースコードを眺めて、スクロール処理している部分を特定して、擬似的にそれを実行する。
変数が、protected だったから、CMFCTabCtrl のままだとアクセスできないから、ダミーの派生クラスCMFCTabCtrlExを作成して、そのポインタに無理矢理CMFCTabCtrlのアドレスを突っ込んで、独自処理を組み込んだ見せかけの派生クラス上で処理させました。

こんな事が出来るのも、C++だからこそ。
.NETじゃ絶対に出来ないなぁ。
[PR]
by isoq | 2012-01-31 01:06 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

MFC なぜかメッセージボックスが表示できなくなる原因が判明

ここのところ、作成したMFCプログラムでなぜか、どこに次のようなコードを書いてもメッセージボックスが表示できなくなってしまっていました。


MessageBox(_T("test"), _T("test"), MB_OK);


とか


AfxMessageBox(_T("test"));


とかです。

本当に基本的なメッセージボックスの表示方法ですが、どこに書いても音が鳴るだけで、メッセージボックスが表示されない!

そして、音の後に [Alt] キーを押すと、なぜかメッセージボックスが表示される・・・

メッセージボックスの表示が [Alt] キー入力を待っているような感じになってしまっていたのです。

原因がわからず、プログラム全体を 少しずつコメントしたり戻したりした結果、努力の甲斐無く、全く原因がつかめませんでした。

そして、スケルトンからやり直して、ちょっとづつコードを足していって・・・最初は問題なく、表示されていたため、ちょっとがんばってたくさんコードを移植すると発生! (^^;) もっとじっくりやればよかった・・・

それでもその間のコードを確認しながらやってみるてやっと鍵を見つけました!

ここです!


void CXxxxYyyyView::OnPaint()
{
    CPaintDC dc(this);    // これを消していました



CPaintDC は、画面の無効領域だけを更新するためのDCですが、使わなかったので、消してしまって、CDC* pDC = GetDC(); とやって、そちらのDCを使用していました。

が、どうやらここで、CPaintDC をインスタンス化しないことが原因で、メッセージボックスが出なくなっていたようです。

こんな事もあるんですね・・・・ MFC ・・・・・
[PR]
by isoq | 2012-01-29 17:52 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

MFCのCImageでPNGファイルのアルファブレンドがおかしいの解決方法

CImageで、PNGファイルを読み込むと、アルファブレンドがおかしい。

Photoshop、Office、Windows Paint(on Win7)では、正常に表示できる画像が、CImageで読み込むと、色が崩れたり、アルファ値がおかしかったりする。 Visual Studio 2010 IDE のリソースエディタ で読み込んでも、CImage と同じ画像が出てくるので、CImage自体に問題があり、Visual Studio 2010にもその問題が引き継がれているらしい。

これは、おそらくCImage のバグ。

なかなか原因がつかめなかったのですが、次のサイトで解決策を発見。

wrong alpha blending with CImage::AlphaBlend() & *.png

これを参考に、次のようにして問題が解決しました。


CImage m_image;
if(SUCCEEDED(m_image->Load(strFileName)))
{
    if(m_image->GetBPP() == 32)
    {
        // PNGのアルファ問題回避
        if(strFileName.Right(3).CompareNoCase(_T("png")) == 0)
        {
            unsigned char * pCol = 0;
            long w = m_image->GetWidth();
            long h = m_image->GetHeight();
            for(long y = 0; y < h; y ++)
            {
                for(long x = 0; x < w; x ++)
                {
                    pCol = (unsigned char *)m_image->GetPixelAddress(x,y);
                    unsigned char alpha = pCol[3];
                    if(alpha != 255)
                    {
                        pCol[0] = ((pCol[0] * alpha) + 128) >> 8;
                        pCol[1] = ((pCol[1] * alpha) + 128) >> 8;
                        pCol[2] = ((pCol[2] * alpha) + 128) >> 8;
                    }
                }
            }
        }

        // アルファチャンネルON
        m_image->SetHasAlphaChannel(true);
    }
}

[PR]
by isoq | 2012-01-29 12:54 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

MFCリボンのバグ。 サブウインドウのリボンでメニューを開くとメインウインドウが前面に表示される

Visual Studio 2010 / Visual C++ 2010 の MFC のリボンインターフェイスのバグ発見。
サブウインドウ上でRibbon ボタンによるメニュー表示の瞬間に、メインウインドウがアクティブになってしまう・・・

次のような場合に発生する。

① CMainFrame の代わりに、 CDialog などをメインウインドウにして、CMainFrame/View をサブウインドウにした場合。

② マルチトップレベルウインドウで、2つめ以降のCMainFrameを作成した場合

こんな時には、リボンがあるウインドウがメインウインドウではない状況が生まれるけど、このとき、サブウインドウでリボンのボタンのメニューを開こうとすると、メインウインドウがアクティブになって邪魔してくる。

原因を探ると、次の場所に行き着いた。


CMFCRibbonBaseElement* CMFCRibbonCategory::OnLButtonDown(CPoint point)
{
    CMFCRibbonBaseElement* pBtnScroll = HitTestScrollButtons(point);
    ...
    CMFCRibbonPanel* pPanel = GetPanelFromPoint(point);
    ...
    return pPanel->MouseButtonDown(point);



CMFCRibbonBaseElement* CMFCRibbonPanel::MouseButtonDown(CPoint point)
{
    if (m_pHighlighted != NULL)
    {
        ...
        m_pHighlighted->OnLButtonDown(point);



void CMFCRibbonButton::OnLButtonDown(CPoint point)
{
    ...
    OnShowPopupMenu();



void CMFCRibbonButton::OnShowPopupMenu()
{
    ASSERT_VALID(this);
    if (IsDroppedDown())
    {
        // if the button already has a menu, don't create another one!
        return;
    }
    CWnd* pWndParent = GetParentWnd();
    if (pWndParent->GetSafeHwnd() == NULL)
    {
        ASSERT(FALSE);
        return;
    }
    CMFCRibbonBar* pTopLevelRibbon = GetTopLevelRibbonBar();
    if (pTopLevelRibbon->GetSafeHwnd() == NULL)
    {
        ASSERT(FALSE);
        return;
    }
    CMFCRibbonBaseElement::OnShowPopupMenu();
    const BOOL bIsRTL = (pTopLevelRibbon->GetExStyle() & WS_EX_LAYOUTRTL);
    CWnd* pWndOwner = pTopLevelRibbon->GetSafeOwner();



CWnd* PASCAL CWnd::GetSafeOwner(CWnd* pParent, HWND* pWndTop)
{
    HWND hWnd = GetSafeOwner_(pParent->GetSafeHwnd(), pWndTop);
    return CWnd::FromHandle(hWnd);



HWND PASCAL CWnd::GetSafeOwner_(HWND hParent, HWND* pWndTop)
{
    // get window to start with
    HWND hWnd = hParent;
    if (hWnd == NULL)
    {
        CFrameWnd* pFrame = CCmdTarget::GetRoutingFrame_();        // ここがNULLになる!
        if (pFrame != NULL)
            hWnd = pFrame->GetSafeHwnd();
        else
            hWnd = AfxGetMainWnd()->GetSafeHwnd();    // だから、ここで メインウインドウが返っちゃう!
                    // → メインウインドウがポップアップメニューのオーナーになる
                    //     → メインウインドウがアクティブになっちゃう
    }



CFrameWnd* PASCAL CCmdTarget::GetRoutingFrame_()
{
    CFrameWnd* pFrame = AfxGetThreadState()->m_pRoutingFrame;    // これがNULLになっている!
    return pFrame;
}



CMFCRibbonButton::OnShowPopupMenu() をオーバーライドして、GetSafeOwner() を使わないように改造するのが一番簡単だけど、Visual C++ 2010 の リソースエディタでリボンを作成して、自動読み込みを行うと、どうもカスタムのCMFCRibbonButton派生クラスを差し込むことは難しいみたい。MFCのソースとにらめっこしたけど、どうやら不可能。こんな時は、ライブラリ内の特定クラスを無効化して、自分で再定義したクラスをはめ込みたいけど出来ないよね・・・。

だから、AfxGetThreadState()->m_pRoutingFrame に CMainFrame* を突っ込めば良いんじゃね??

って事で、次のようにしてみました。


BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
    AfxGetThreadState()->m_pRoutingFrame = this;
    return CFrameWndEx::PreTranslateMessage(pMsg);
}

CMainFrame::~CMainFrame()
{
    AfxGetThreadState()->m_pRoutingFrame = 0;
}



PreTranslateMessage で、CMainFrame にメッセージが飛んできたら常に AfxGetThreadState()->m_pRoutingFrame にポインタをセットします。

こうすることで、(正しい処置かどうかは不明ですが・・・)現在アクティブな CMainFrame 上にメニューを表示することが出来ました!

CMainFrame の消滅時に AfxGetThreadState()->m_pRoutingFrame をクリアしないと、終了時にエラーが出ました。
[PR]
by isoq | 2012-01-29 12:41 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

GUI プログラムでコンソールを使用する(標準出力) -簡易版-

GUI プログラムでコンソールを使用する(標準出力) を簡単にすると、こんな感じ。


AllocConsole();
freopen 括弧はじめ "CON", "w", stdout); // 標準出力の割り当て
_tsetlocale(LC_ALL, _T("Japanese"));


setlocale() を行わないと、Unicode 環境で Unicode をコンソールに出力したときに、
ASCII以外の文字が表示されない・・・
つまり、日本語、漢字が表示できない。

つまらないところで、つまずいてしまい、調べるのに時間が掛かってしまった。。。
[PR]
by isoq | 2011-12-09 15:20 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

Windows x64 環境向けアプリが 0xc000007b エラーで起動しない

原因は、いろいろあるかと思いますが、私の場合は、一部のDLLが32ビット版でした。

すべて64ビット版であることを再確認して、実行したところ、正しく動きました。

サードパーティ製のDLLなど、32ビットか64ビットか、わかりにくいことがありますね。

それではまりました。

aaa.dll と aaa.x64 というファイルがあって、 aaa.x64 を aaa.dll に改名して使ってみたところ、動きました。

どちらも、 x64 フォルダに入っていたんだけど、、、
ドキュメントに書いてあったか???


(2013-12-03 追記 ここから) ----------

旧VisualStudio(2003/7.1)からの更新版プロジェクトで、*.manifestファイルが含まれる場合、ファイル内にprocessorArchitecture="X86" 等の記述があり、ビルド時にそのまま埋め込まれるため、プラットフォームをX64でビルドすると、0xc000007b エラーのために起動不能になっていた。
単純に VisualStudio 2008/2010等のプロジェクトから*.manifestファイルを削除することで解決。

---------- (2013-12-03 追記 ここまで)
[PR]
by isoq | 2011-12-07 20:11 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

Visual Studio 2010 の Ctrl+クリック で定義表示を無効化

Microsoft Visual Studio 2010 では、エディタ上で Ctrl+クリック を行うと、「単語選択」ではなく、「その単語の定義へ移動」というコマンドになってしまうため、非常に不便でした。

この設定は、「ツール → オプション」 メニューの設定から、変更可能です。

追記(10/27):
これは、Productivity Power Tools という機能拡張の機能らしいです。
ツール→機能拡張マネージャ からインストールできます。(追記ここまで)

a0002261_11463070.jpg


あと、現在行のハイライトもうざいのでOFF。
(Highlight Current Line を OFF)

これで少しは使いやすく&見やすくなります。

あと、起動が遅い事と、動作が鈍い事が解決出来たら、すばらしい開発環境になりそうです。
[PR]
by isoq | 2011-09-16 11:50 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

サンプル:アクセッサー オペレータ

class Test
{
public:

Test()
{
for(int i=0; i<100; i++)
{
value[i] = i;
}
}

int operator[](int idx){ return value[idx]; }

int value[100];
};


int _tmain(int argc, _TCHAR* argv[])
{
Test t;

printf("%d\n", t[10]);

return 0;
}

[PR]
by isoq | 2011-01-18 11:35 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇

サンプル:テンプレートメンバ関数


class Test
{
public:
Test()
{
for(int i=0; i<100; i++)
{
iValue[i] = i;
dValue[i] = 1.0 / double(i);
}
}

void Evaluate()
{
//printf("%d, %f\n", GetAt(iValue, 10), GetAt(dValue, 10)); // <>は省略可
printf("%d, %f\n", GetAt(iValue, 10), GetAt(dValue, 10));
}

template
T GetAt(T* array_ptr, int index)
{
return array_ptr[index];
}


int iValue[100];
double dValue[100];
};

int _tmain(int argc, _TCHAR* argv[])
{
Test t;
t.Evaluate();
return 0;
}

[PR]
by isoq | 2011-01-18 11:33 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇