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
要するに、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;
}
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;
}
こんな警告が出た。
コンパイルしています...
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 ? メンバアクセス演算子? どちらも使ってないよ。はっきり言ってこのエラーメッセージは意味不明。
.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
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);
}
ダイアログを表示して、コンボボックスをマウスでポイントすると、ドロップダウンリストの方はツールチップを表示しますが、ドロップダウンの方は表示しません。しかしドロップダウンの上辺の端ぎりぎりくらいにポインタを持っていくと、上のように表示されます。これは、ドロップダウンの方は入力するためのエディットボックスが前面に作成されているためです。というわけで、今回のお題はこちら。
表題の通りです。コメントもありません。DEBUGCONSOLEが定義されている場合のみコンソールが作成されます。
CDebugConsole クラスのメンバを以下に示します。
CDebugConsole クラスのインスタンスは1個のみの存在とし、CDebugConsole::GetInstance () で参照を取得します。
int n = 10;
CDebugConsole::GetInstance ().Printf ("n は %d です。", n);
また、C ソースにインクルードした場合に同等の機能を提供する関数も定義しています。
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); }
実行してみると、なかなか期待したとおりの動き。でもよく見ると、なぜか最大化した時にタスクバーと重なってしまいます。前回ステータスバーを付けてなかったら気付かないかもですが、とにかく、タスクバーのサイズを無視して、デスクトップいっぱいいっぱいまで最大化してしまっているようです。
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 となります }
実行してみましたが・・あれ?効いてませんね・・

よく忘れるのでリンク。
Visual SourceSafe の起動時に使用できるコマンド ライン オプション
タイトルの通り。以下の情報を元に、派生クラスを作成しました。コントロールが重なっている場合に考慮できていませんが。
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 クラスも使えちゃう♥ やったあ。
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 として取り上げられている。
VC++ 6.0 のプロジェクトワークスペースを開くとき、"ClassView 情報ファイルにアクセスできません。ClassView 情報が使用できません。"というメッセージが表示されることがある。同じプロジェクトを2つ開こうとしたときや、Visual Source Safe でソース管理しているときなんかに出くわすんだけど、これはプロジェクトの *.ncb ファイルがロックされているため起こる。ソース管理時は *.ncb ファイルは管理の対象としない方がいい。
VC++でF12を押下して変数や関数の定義位置に移動することができるが、リビルド可能なプロジェクトなのに、「シンボル 'foo' は定義されていません」とメッセージボックスが出たり、ジャンプした先がぜんぜん定義位置でもなんでもなかったり、ということがある。どうやら、プロジェクトのブラウズ情報がおかしくなっているようだ。
プロジェクトのブラウズ情報は、ビルド時の出力ディレクトリの、拡張子が".bsc"のファイルであるらしい。これは拡張子が".sbr"のファイルを元に生成されるらしい。ということで、この二つを削除して、プロジェクトを開き、もう一度、何か識別子の上にカーソルを移動し、F12を押下すると
「C:\foo\Debug\foo.bsc
このプロジェクトのブラウズ情報はありません。
ビルドの設定を変更して、ブラウズ情報を生成するためにプロジェクトをリビルドしますか?
」
などと表示される。「はい」を選択すると、ブラウズ情報を再作成することができる。
以前から思っていたことだが、VC++で1つワークスペースを開いてて、別のDSWファイルををダブルクリックして開くと、先に開いてたのと同じVC++のウインドウで開いてしまい、最初に開いてたワークスペースは結果として閉じてしまう。複数VC++起動したのになーって。複数のワークスペースを開くには?に回答を発見した。
エクスプローラのメニューから [ツール]-[フォルダオプション] でプロジェクトワークスペースの設定をDDEを使用しないように変更すればOKです。
Windows Xpの場合はフォルダオプションの「ファイルの種類」で、拡張子DSWを選択し、「詳細設定」ボタンを押下、出てきたウインドウの「MSDEVで開く」をダブルクリックして、「DDEを使う」のチェックをはずせば期待の動作をするようになった。
Old&New: Computer: Visual Studio 6.0: 秀丸とVC の連携に、VC++でカーソルのある行をにカーソルがある状態で秀丸を開く方法が紹介されてます。使えるー。