frog.raindrop.jp.knowledge > Visual C++

July 8, 2008

nmake 用の Makefile

汎用 Makefile の覚書。Visual C++ 2005 Express Edition では動作確認済み。
TARGET = hello
OBJS = hello.obj hello2.obj

CC = cl.exe
CFLAGS = /EHsc
LD = link.exe
LDFLAGS = /OUT:$(TARGET).exe

.c.obj :
	$(CC) $(CFLAGS) /c $<

.cpp.obj :
	$(CC) $(CFLAGS) /c $<

all: $(OBJS)
	$(LD) $(OBJS) $(LDFLAGS)

clean:
	del *.obj
	del *.exe
	

March 8, 2006

CTabCtrl のページいっぱいいっぱいに CListBox を貼り付ける

要するに、CTabCtrl::AdjustRect () の使い方です。引数の fLarger に FALSE を渡すと、タブページの領域の矩形を取得することができます。別に CListBox でなくてもよかったのですが、例では CListBox をタブに貼り付けます。

SDK の場合は SendMessage で、TCM_ADJUSTRECT をタブコントロールに送出する方法に読み替えます。TabCtrl_AdjustRect というマクロも用意されています。

class CMyTabDialog
    : public CDialog
{
private:
    CTabCtrl    m_Tab;          // タブです
    CListBox    m_ListBox;      // タブの上にはっつけるリストボックスです

BOOL CMyTabDialog::OnInitDialog (void)
{
    CRect Rect;
    
    m_Tab.GetWindowRect (Rect);         // タブのウインドウ矩形
    m_Tab.AdjustRect (FALSE, Rect);     // タブのページいっぱいいっぱいの領域の矩形を導出
    ScreenToClient (Rect);              // これをダイアログのクライアント座標に変換して
    m_ListBox.MoveWindow (Rect);        // リストボックスを移動します
    
    return TRUE;
}

February 24, 2006

ローカルネットワークを選択するコンボボックス

TCP サーバや UDP アプリなどの設定画面に登場する、バインドするネットワークアドレスを選択するコンボボックスです。はい、ものすごく限定的な Tips です。ネットワークインターフェースの列挙方法の一例、かな?

BOOL CXXXDialog::InitNetworkCombo (void)
{
    char szHostName [MAX_PATH];
    ZeroMemory (szHostName, sizeof szHostName);
    m_ComboNetwork.Clear ();
    
    // このコンピュータのホスト名を取得する
    int nRet = gethostname (szHostName, sizeof szHostName);
    if (SOCKET_ERROR == nRet)
    {
        // エラー処理
    }
    else
    {
        // ホスト名よりホストの情報を取得する
        struct hostent* pHostEnt = gethostbyname (szHostName);
        if (!pHostEnt)
        {
            // エラー処理
        }
        else
        {
            // INADDR_ANY を追加する
            int nIndex = m_ComboNetwork.AddString (_T ("すべてのネットワーク"));
            m_ComboNetwork.SetItemData (nIndex, INADDR_ANY);
            
            // 取得したアドレスのリストをコンボボックスに追加する
            for (int nCount = 0; pHostEnt->h_addr_list [nCount]; nCount ++)
            {
                struct in_addr Address;
                memcpy (&Address, pHostEnt->h_addr_list [nCount], sizeof (struct in_addr));
                nIndex = m_ComboNetwork.AddString (inet_ntoa (Address));
                m_ComboNetwork.SetItemData (nIndex, Address.S_un.S_addr);
            }
            
            // ループバックを追加する
            nIndex = m_ComboNetwork.AddString (_T ("127.0.0.1"));
            m_ComboNetwork.SetItemData (nIndex, INADDR_LOOPBACK);
            return TRUE;
        }
    }
    return FALSE;
}

January 23, 2006

error C2275 : typedef 識別子に、クラス メンバ アクセス演算子 (->) を使用しました。

こんな警告が出た。


コンパイルしています...
main.cpp
c:\foo\main.cpp(25) : error C2275: 'MyLt' : typedef 識別子に、クラス メンバ アクセス演算子 (->) を使用しました。
        c:\foo\main.cpp(7) : 'MyLt' の宣言を確認してください。

ビルドログは "file://c:\foo\Debug\BuildLog.htm" に保存されました。
foo - エラー 1、警告 0

コンパイルしたのはこれ。

#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>

struct MyLt
{
    bool operator () (const int lhs, const int rhs) const
    {
        return lhs < rhs;
    }
};

void main (int argc, char **argv)
{
    std::vector <int> IntVector;
    for (int i = 0; 10 > i; i++)
        IntVector.push_back (std::rand ());

    std::cout << "before sort" << std::endl;
    for (int i = 0; IntVector.size () > i; i++)
        std::cout << IntVector [i] << ", ";
    std::cout << std::endl;

    std::sort (IntVector.begin (), IntVector.end (), MyLt);     // error C2275

    std::cout << "after sort" << std::endl;
    for (int i = 0; IntVector.size () > i; i++)
        std::cout << IntVector [i] << ", ";
    std::cout << std::endl;
    return;
}

ハァ? typedef ? メンバアクセス演算子? どちらも使ってないよ。はっきり言ってこのエラーメッセージは意味不明。

続きを読む...

December 26, 2005

.NET の属性を使用して、Enum と Struct をタイプライブラリに含める方法

.NET で ATL を使用して COM DLL を作成しようとして・・・唖然としました。VC++ 6.0 で ATL COM ウィザード使ってたときと全く生成されるコードが違う・・・。一番の大きな違いは「属性」なるものが追加されたこと。宣言の前に大カッコでくくって指定するようです。

なんとか COM DLL を作り上げて、実装担当者に渡したところ、enum や struct をタイプライブラリにどうやって含めたのか聞かれました。これは私もドキュメントが見つけられず、試行錯誤ののち実装したもの。一応、以下のようにするとできますが、この方法で正しいのかわからない部分です。

#ifndef __EXPORTS__H
#define __EXPORTS__H

[
    export,         // タイプライブラリにエクスポートします
    v1_enum,        // 32bit 値の列挙型としてコンパイルします
    library_block,  // タイプライブラリの library ブロック内に配置します
    helpstring ("この enum は、タイプライブラリに含まれます。"),
]
enum ExportEnum
{
    nExportEnumValue1,
    nExportEnumValue2,
    nExportEnumValue3,
};

[
    export,         // タイプライブラリにエクスポートします
    library_block,  // タイプライブラリの library ブロック内に配置します
]
struct EXPORT_STRUCT
{
    long            nExportStructMember1;
    BYTE            ExportStructMember2 [10];
    VARIANT_BOOL    bExportStructMember3;
    BSTR            bstrExportStructMember4;
};


#endif  //__EXPORTS__H

September 15, 2005

入力可能なコンボボックスにツールチップを設定する

MFC で、入力可能なコンボボックス (スタイルに CBS_DROPDOWN が設定されているもの) に対して、ツールチップを設定します。サンプルは、上が入力不可 (ドロップダウンリスト)、下が入力可能 (ドロップダウン) のコンボボックスです。それぞれ、m_DropdownList と m_DropdownCombo にマッピングしてあります。

ダイアログ

以下が、OnInitDialog 内でのツールチップ作成部分

// OnInitDialog
    if (m_ToolTip.Create (this))
    {
        m_ToolTip.AddTool (&m_DropdownList, _T ("ドロップダウンリスト"));
        m_ToolTip.AddTool (&m_DropdownCombo, _T ("ドロップダウンコンボ"));
    }

で、PreTransrateMessage で以下のようにしてツールチップにマウスイベントを渡します。

// PreTranslateMessage
BOOL CDropdownTooltipDialog::PreTranslateMessage (MSG* pMsg)
{
    switch (pMsg->message)
    {
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_MOUSEMOVE:
        m_ToolTip.RelayEvent (pMsg);
        break;
    default:
        break;
    }
    return CDialog::PreTranslateMessage (pMsg);
}

ダイアログを表示して、コンボボックスをマウスでポイントすると、ドロップダウンリストの方はツールチップを表示しますが、ドロップダウンの方は表示しません。しかしドロップダウンの上辺の端ぎりぎりくらいにポインタを持っていくと、上のように表示されます。これは、ドロップダウンの方は入力するためのエディットボックスが前面に作成されているためです。というわけで、今回のお題はこちら。

続きを読む...

August 2, 2005

Windows AP にコンソール出力を提供するクラス

表題の通りです。コメントもありません。DEBUGCONSOLEが定義されている場合のみコンソールが作成されます。

CDebugConsole クラスのメンバを以下に示します。

GetInstance
唯一のインスタンスへの参照を返します
SetConsoleTitle
コンソールのタイトルバー文字列を更新します
Printf
標準出力に書式付きで出力します
Errorf
標準エラー出力に書式付きで出力します
VPrintf
引数リストへのポインタを使用して、標準出力に書式付きで出力します
VErrorf
引数リストへのポインタを使用して、標準エラー出力に書式付きで出力します
Dump
渡されたデータを16進でダンプした結果を標準出力に出力します

CDebugConsole クラスのインスタンスは1個のみの存在とし、CDebugConsole::GetInstance () で参照を取得します。

int n = 10;
CDebugConsole::GetInstance ().Printf ("n は %d です。", n);

また、C ソースにインクルードした場合に同等の機能を提供する関数も定義しています。

続きを読む...

July 3, 2005

システムメニューを持たない CDialog で、タイトルバーをダブルクリックされたら最大化 <-> 元のサイズに戻す

MFC CDialog ネタをもいっちょ。前回サンプル作ったので、その続きに実装しちゃいます。

今回のお題は、システムメニューを持たないダイアログに、「タイトルバーをダブルクリックされたら最大化 <-> 元のサイズに戻す」の機能を実装せよと言うもの。当然、最大化、最小化ボタンもありません。

ところで、VB6 でサイズ変更可能な Form を作成して、ControlBox プロパティに False を設定すると、システムメニューも最大化・最小化ボタンないウインドウができますが、ちゃあんとタイトルバーダブルクリックが効くんですよね。Spy++ でメッセージをキャプチャすると、WM_NCLBUTTONDBLCLK nHitTest = HTCAPTION が落ちてきているのが見えます。そこで、AP 内でもそれを判断させてみることにしてみました。

StatusDialog.cpp 内のメッセージマップ部分に1行追加します。

BEGIN_MESSAGE_MAP(CStatusDialog, CDialog)
    //{{AFX_MSG_MAP(CStatusDialog)
    ON_WM_SIZE()
    ON_WM_NCLBUTTONDBLCLK()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CStatusDialog クラスに OnNcLButtonDblClk を追加します。

// StatusDialog.h: CStatusDialog クラスの宣言の中。
// どこでもいいんだけど、他のメッセージハンドラと同じ位置に追加しておくと、後で見やすいかも。

protected:

    // 生成されたメッセージ マップ関数
    //{{AFX_MSG(CStatusDialog)
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnNcLButtonDblClk (UINT nHitTest, CPoint point);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

// StatusDialog.cpp

void CStatusDialog::OnNcLButtonDblClk (UINT nHitTest, CPoint point)
{
    if (HTCAPTION == nHitTest)
    {// ダブルクリックされたのはタイトルバー
        if (IsZoomed ())
        {// 最大化中
            SendMessage (WM_SYSCOMMAND, SC_RESTORE, 0);
        }
        else
        {// 最大化されてない
            SendMessage (WM_SYSCOMMAND, SC_MAXIMIZE, 0);
        }
        return;
    }

    CDialog::OnNcLButtonDblClk (nHitTest, point);
}

実行してみると、なかなか期待したとおりの動き。でもよく見ると、なぜか最大化した時にタスクバーと重なってしまいます。前回ステータスバーを付けてなかったら気付かないかもですが、とにかく、タスクバーのサイズを無視して、デスクトップいっぱいいっぱいまで最大化してしまっているようです。

続きを読む...

July 2, 2005

CDialog より派生したダイアログにステータスバーをつける

MFC ネタ。 CDialog より派生したダイアログウインドウに、ステータスバーをつけなきゃならなくなりました。とりあえずは AppWizard で SDI アプリケーションを作ってみて、それを参考にダイアログクラスに CStatusBar 型のメンバ変数を追加し、OnInitDialog に初期化コードを書いてみました。

#if !defined(AFX_STATUSDIALOG_H__815300F5_01CC_433D_9286_4C8CEEC28D27__INCLUDED_)
#define AFX_STATUSDIALOG_H__815300F5_01CC_433D_9286_4C8CEEC28D27__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// StatusDialog.h : ヘッダー ファイル
//

/////////////////////////////////////////////////////////////////////////////
// CStatusDialog ダイアログ

class CStatusDialog : public CDialog
{
// コンストラクション
public:
    CStatusDialog(CWnd* pParent = NULL);   // 標準のコンストラクタ

// ダイアログ データ
    //{{AFX_DATA(CStatusDialog)
    enum { IDD = IDD_STATUSDIALOG };
        // メモ: ClassWizard はこの位置にデータ メンバを追加します。
    //}}AFX_DATA


// オーバーライド
    // ClassWizard は仮想関数のオーバーライドを生成します。
    //{{AFX_VIRTUAL(CStatusDialog)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート
    //}}AFX_VIRTUAL

// インプリメンテーション
protected:

    // 生成されたメッセージ マップ関数
    //{{AFX_MSG(CStatusDialog)
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

    CStatusBar m_StatusBar;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ は前行の直前に追加の宣言を挿入します。

#endif // !defined(AFX_STATUSDIALOG_H__815300F5_01CC_433D_9286_4C8CEEC28D27__INCLUDED_)
// CStatusDialog.cpp の OnInitDialog
/////////////////////////////////////////////////////////////////////////////
// CStatusDialog メッセージ ハンドラ

BOOL CStatusDialog::OnInitDialog() 
{
    CDialog::OnInitDialog();
    
    // TODO: この位置に初期化の補足処理を追加してください

    if (!m_StatusBar.Create (this))
    {
        MessageBox (_T ("CStatusBar::Create failed"));
    }
    
    return TRUE;  // コントロールにフォーカスを設定しないとき、戻り値は TRUE となります
                  // 例外: OCX プロパティ ページの戻り値は FALSE となります
}

実行してみましたが・・あれ?効いてませんね・・

スクリーンショット (失敗編)

続きを読む...

June 27, 2005

VSS エクスプローラの起動時にデータベースを指定する

よく忘れるのでリンク。
Visual SourceSafe の起動時に使用できるコマンド ライン オプション

May 11, 2005

CToolTipCtrl で TTS_ALWAYSTIP を指定しても無効コントロールのツールチップが表示されない

タイトルの通り。以下の情報を元に、派生クラスを作成しました。コントロールが重なっている場合に考慮できていませんが。

続きを読む...

March 30, 2005

VC++ .NET と、メンバ関数テンプレート

Visual Studio .NET は大変使い勝手が気にいっていないが、ひとつ感動した点。

これがコンパイルできるようになった。

class CanCastAnytype
{
public:
	template <typename _anytype> operator _anytype() const
	{ return reinterpret_cast<_anytype>( *m_pValue ); }
protected:
	void	*m_pValue;
};

メンバ関数テンプレートである。これなら、Effective C++ に出てくる、万能 NULL クラスも使えちゃう♥ やったあ。

December 21, 2004

IDL ファイルのコンパイル時に MIDL2039 の警告が出る

VC++6.0 (5.0もらしいけど) で、ATL COM AppWizard プロジェクトを作成し、挿入 - ATLオブジェクトの新規作成を選択して、何かコントロールを追加する。コントロールの設定で、ストックプロパティを追加するときに、Font を含めてみよう。

warning MIDL2039 : interface does not conform to [oleautomation] attribute : [ Parameter 'pFont' of Procedure 'putref_Font' ( Interface 'IMyControl' ) ]
warning MIDL2039 : interface does not conform to [oleautomation] attribute : [ Parameter 'pFont' of Procedure 'put_Font' ( Interface 'IMyControl' ) ]
warning MIDL2039 : interface does not conform to [oleautomation] attribute : [ Parameter 'ppFont' of Procedure 'get_Font' ( Interface 'IMyControl' ) ]

とまあ、警告がずらずら。この件は、MS のサポート技術情報にも、FIX: MIDL2039 Warning with IFontDisp/IPictureDisp Parameter Type として取り上げられている。

続きを読む...

September 28, 2004

ウォッチにエラーをわかりやすく表示する

小ネタ。
GetLastError() の値は、擬似レジスタ ERR でウォッチペインに表示することができる。この値に、書式指定子 hr を付加すると、エラーメッセージとして表示することができる。

June 11, 2004

ClassView 情報ファイルにアクセスできません。ClassView 情報が使用できません。

VC++ 6.0 のプロジェクトワークスペースを開くとき、"ClassView 情報ファイルにアクセスできません。ClassView 情報が使用できません。"というメッセージが表示されることがある。同じプロジェクトを2つ開こうとしたときや、Visual Source Safe でソース管理しているときなんかに出くわすんだけど、これはプロジェクトの *.ncb ファイルがロックされているため起こる。ソース管理時は *.ncb ファイルは管理の対象としない方がいい。

March 31, 2004

プロジェクトのブラウズ情報を更新する

VC++でF12を押下して変数や関数の定義位置に移動することができるが、リビルド可能なプロジェクトなのに、「シンボル 'foo' は定義されていません」とメッセージボックスが出たり、ジャンプした先がぜんぜん定義位置でもなんでもなかったり、ということがある。どうやら、プロジェクトのブラウズ情報がおかしくなっているようだ。

プロジェクトのブラウズ情報は、ビルド時の出力ディレクトリの、拡張子が".bsc"のファイルであるらしい。これは拡張子が".sbr"のファイルを元に生成されるらしい。ということで、この二つを削除して、プロジェクトを開き、もう一度、何か識別子の上にカーソルを移動し、F12を押下すると
C:\foo\Debug\foo.bsc
このプロジェクトのブラウズ情報はありません。
ビルドの設定を変更して、ブラウズ情報を生成するためにプロジェクトをリビルドしますか?

などと表示される。「はい」を選択すると、ブラウズ情報を再作成することができる。

March 10, 2004

ワークスペースをダブルクリックしたときに新たにVC++を起動する

以前から思っていたことだが、VC++で1つワークスペースを開いてて、別のDSWファイルををダブルクリックして開くと、先に開いてたのと同じVC++のウインドウで開いてしまい、最初に開いてたワークスペースは結果として閉じてしまう。複数VC++起動したのになーって。複数のワークスペースを開くには?に回答を発見した。

エクスプローラのメニューから [ツール]-[フォルダオプション] でプロジェクトワークスペースの設定をDDEを使用しないように変更すればOKです。

Windows Xpの場合はフォルダオプションの「ファイルの種類」で、拡張子DSWを選択し、「詳細設定」ボタンを押下、出てきたウインドウの「MSDEVで開く」をダブルクリックして、「DDEを使う」のチェックをはずせば期待の動作をするようになった。

February 24, 2004

VC++ と秀丸を連携させる

Old&New: Computer: Visual Studio 6.0: 秀丸とVC の連携に、VC++でカーソルのある行をにカーソルがある状態で秀丸を開く方法が紹介されてます。使えるー。