VC++でF12を押下して変数や関数の定義位置に移動することができるが、リビルド可能なプロジェクトなのに、「シンボル 'foo' は定義されていません」とメッセージボックスが出たり、ジャンプした先がぜんぜん定義位置でもなんでもなかったり、ということがある。どうやら、プロジェクトのブラウズ情報がおかしくなっているようだ。
プロジェクトのブラウズ情報は、ビルド時の出力ディレクトリの、拡張子が".bsc"のファイルであるらしい。これは拡張子が".sbr"のファイルを元に生成されるらしい。ということで、この二つを削除して、プロジェクトを開き、もう一度、何か識別子の上にカーソルを移動し、F12を押下すると
「C:\foo\Debug\foo.bsc
このプロジェクトのブラウズ情報はありません。
ビルドの設定を変更して、ブラウズ情報を生成するためにプロジェクトをリビルドしますか?
」
などと表示される。「はい」を選択すると、ブラウズ情報を再作成することができる。
MFC の CWinThread をカプセル化するクラスを作ろうと思ったのが始まりだったのですが、AfxBeginThread に、派生クラスのメンバ関数は、たとえstatic であっても渡せないようです。ポインタのサイズが違うらしいんですね。(参考: ロベールのC++教室 - 第58章 メンバ関数ポインタ天国 -)
カプセル化したいのはワーカスレッドだったのですが、結局、こんな感じで落ち着きました。以下、基本クラスのヘッダファイルと実装ファイルです。
名づけて、VB 汎用モジュール アーカイブプロジェクト、第一弾です。いつまでもVB6使ってますが、有用なモジュールが結構たまっているのでちょっとづつ整理して行こうと思っています。
このモジュールは以下のようにしてフォルダ選択ダイアログを表示します。ちゃんと初期フォルダも選択されます。
Private Sub Command1_Click()
Dim objBrowse As New CBrowseFolder
objBrowse.Flags = BIF_NEWDIALOGSTYLE
objBrowse.InitDir = CurDir()
Set objBrowse.Owner = Me
objBrowse.RootFolder = CSIDL_DESKTOP
objBrowse.Title = "あなたの一番大事なフォルダを選択してください"
Call MsgBox(objBrowse.Show())
End Sub
ATL COM AppWizard で作成したプロジェクトでMFCを使いたいことがある。しかし、適当な位置にMFCの宣言を入れたとしても、プリプロセス時にエラーになってしまう。
c:\program files\microsoft visual studio\vc98\mfc\include\afxv_w32.h(14) : fatal error C1189: #error : WINDOWS.H already included. MFC apps must not #include <windows.h>
これを実現する方法が、173974 - HOWTO: Add MFC Support to an ATL Project にある。具体的には afxwin.h、afxext.h、afxdisp.h を、atlbase.hより先にインクルードしてやる。
// stdafx.h : 標準のシステム インクルード ファイル、 // または参照回数が多く、かつあまり変更されない // プロジェクト専用のインクルード ファイルを記述します。 . . #define _ATL_APARTMENT_THREADED // ↓atlbase.h をインクルードしている部分を探して // ↓その直前で下の3つをインクルードする #include <afxwin.h> // MFC core and standard components #include <afxext.h> // MFC extensions #include <afxdisp.h> // MFC Automation extensions // ↓atlbase.h のインクルード位置 #include <atlbase.h> // CComModule クラスから派生したクラスを使用して、オーバーライドする場合 // _Module の名前は変更しないでください。 . .
その他の、たとえば afxmt.h なんかはどこでインクルードしてもかまわない。それから、CWinApp クラスのオブジェクトが必要になる。サービスの場合などは、Win32 Console Application で、「MFCをサポートするアプリケーション」を選択した時のコードを流用すればいい(と思う)。この辺のことは、AfxWinInit をVC++のヘルプで引くと書いてある。あとは、プロジェクトの設定で、MFCを使用しない、になっていないことを確認すればOKである。
MFCのWinInetクラスを使ってみました。印象としてはVBのInetコントロールと同じくらいお手軽です。VC++6.0のヘルプなら
MSDN ライブラリ Visual Studio 6.0 ┗Visual C++ ドキュメント ┗Visual C++ ユーザーズ ガイド ┗Visual C++ プログラマーズ ガイド ┗プログラム機能の追加 ┗詳細 ┗インターネットのトピック ┗インターネットのプログラミング ┗代表的な HTTP クライアント アプリケーションの作成手順
に必要な手順がまとめてくれてあります。さらに簡潔に書くとこんな感じでしょう。
大量のプリンタのインストールをバッチファイルで行うことはできないかと調べていたら、189105 - ユーザーによる操作なしで Windows にプリンタを追加する方法というのを発見した。これを使えば、プリンタのインストールをこんな感じでバッチファイルに記述することができる。
rundll32 printui.dll,PrintUIEntry /if /b "プリンタに付ける名前" /f "INFファイル名" /q /r "ポート(LPT1:とか)" /m "プリンタのモデル名"
詳しい使い方は、 rundll32 printui.dll,PrintUIEntry /? で、ヘルプを見ることができる。以下はその内容。
switch文の閉じカッコの位置で"error C2143: 構文エラー : ';' が '}' の前に必要です。"とコンパイルエラーになることがある。それはたとえばこんな場合。
switch ( foo )
{
case 'A':
// 処理...
break;
case 'B':
}
実はこれは、case 'B':ラベルと}の間に有効なステートメントが存在しないと起こるエラーである。回避策としては、break;でも書いとくか、なんなら;(セミコロン)だけの行でもよい。
クラスのメンバ変数で、値をオブジェクトごとに持つのではなく、すべてのオブジェクトで同じ変数を参照したい場合は、staticをつけて宣言する。
// sample.h class Sample { public: Sample (); ~Sample (); private: static int _staticVar; };
staticメンバ変数の初期化は、下のように実装ファイルで定義をかく。(処理系によって若干解釈が違うといううわさも)
// sample.cpp #include "sample.h" int Sample::_staticVar = 0; // 初期化
クラスのメンバ変数の値を、オブジェクト作成後に変更できなくするにはconstをつけて宣言する。
// sample.h class Sample { public: Sample (); Sample (const int foo); ~Sample (); private: const int _constVar; };
constメンバ変数の初期化は、コンストラクタに初期化リストをつけてやれば初期化できる。
// sample.cpp #include "sample.h" Sample::Sample () : _constVar (0) { // 初期化処理... } // 引数で初期化も可能。 Sample::Sample (const int foo) : _constVar (foo) { // 初期化処理... }
その両方の場合は?...初期化の方法はひとつ。
// sample.h class Sample { public: Sample (); ~Sample (); private: static const int _staticConstVar; };
// sample.cpp #include "sample.h" const int Sample::_staticConstVar = 0; // 初期化
実行モジュールのパスを取得する方法として、GetModuleFileName関数がある。この関数の1つ目の引数 hModule にNULLを指定すると、VBで言うところの App.Path の代わりに使えますよ、とした情報は多い。しかし、MSDNをひいてみると
- hModule
- [入力]モジュールのハンドルを指定します。このモジュールを含む実行可能ファイルのパスが取得されます。NULL を指定すると、現在のプロセスを作成するために使われたファイルのパスを取得します。
つまり、NULLを指定したときに取得できるのは、プロセスを起動したモジュールのパスであることがわかる。注意すべきはDLLの場合で、プロセスにアタッチするため、DLLをロードした大元のEXEファイル、ということになる。
MFC AppWizardなんかでアプリを作成すると、Debug動かしたときに、プログラム終了時に開放されていないメモリに対して警告を出してくれる。
Detected memory leaks!
Dumping objects ->
C:\work\Foo\Foo.cpp(240) : {53} normal block at 0x003F4548, 1 bytes long.
Data: < > 00
Object dump complete.
スレッド 0xD40 終了、終了コード 2 (0x2)。
プログラム 'C:\work\Foo\Debug\Foo.exe' はコード 2 (0x2) で終了しました。
この場合、Foo.cppの240行目に
LPTSTR szBuff = (LPTSTR) new TCHAR[nLen];
てな具合にnewで確保したメモリがあるわけだが、それが開放されていない、ってことを言ってくれている。newをコールするときに、__FILE__と__LINE__を記憶してくれているらしい。
VBからコール可能なDLLの作成 ででてきた、DLL作成のための標準的なDEFファイル
LIBRARY [library][BASE=address] EXPORTS entryname1[=internalname1] [@ordinal1[NONAME]] [DATA] [PRIVATE] entryname2[=internalname2] [@ordinal2[NONAME]] [DATA] [PRIVATE] entryname3[=internalname3] [@ordinal3[NONAME]] [DATA] [PRIVATE]
EXPORTS 文[MSDN ライブラリ Visual Studio 6.0]のところに載ってるけど、エントリの内容についてはあまり載ってないです。
以前から思っていたことだが、VC++で1つワークスペースを開いてて、別のDSWファイルををダブルクリックして開くと、先に開いてたのと同じVC++のウインドウで開いてしまい、最初に開いてたワークスペースは結果として閉じてしまう。複数VC++起動したのになーって。複数のワークスペースを開くには?に回答を発見した。
エクスプローラのメニューから [ツール]-[フォルダオプション] でプロジェクトワークスペースの設定をDDEを使用しないように変更すればOKです。
Windows Xpの場合はフォルダオプションの「ファイルの種類」で、拡張子DSWを選択し、「詳細設定」ボタンを押下、出てきたウインドウの「MSDEVで開く」をダブルクリックして、「DDEを使う」のチェックをはずせば期待の動作をするようになった。
Windows APIの関数の引数などで、DWORDならdwValue、LPSTRならlpStringなどとなっているのをよく見かけるが、頭に型をあらわす1~2文字を付加したあの命名法を、ハンガリー記法、というらしい。
1-2-3.変数名の命名(Windows API Topics)
MSDE 2000は、製品版のSQLServerとの差異化を図るために、9個以上同時に処理を行うと、パフォーマンスを低下させるような機構が組み込まれている。詳しい情報のありか、Microsoft SQL Server - SQL Server 2000 ワークロード ガバナについて
SQL Server 2000 Desktop Engine (MSDE 2000) および SQL Server 2000 Personal Edition に同梱されている Microsoft® SQL Server™ 2000 データベース エンジンには、ワークロード ガバナが付属しています。ワークロード ガバナは、ユーザーが少ない場合の一般的な負荷を超える負荷がデータベース エンジンにかかった場合にパフォーマンスを制限するようなデザインになっています。詳しい情報を記載したWordのドキュメントがあります。
IBM汎用機の文字コード体系はEBCDIC(Extended Binary Coded Decimal Interchange Code)という。これにカナを拡張したものが、EBCDIK(Extended Binary-Coded-Decimal Interchange Kana Code)と呼ばれるものである。しかし、これとは別に、EBCDICを各国語拡張した文字コード体系があり、これもEBCDICと呼ばれる。もちろん、漢字を含む日本語対応のEBCDICもある。そのため、EBCDIKと、日本語のEBCDICのどちらも実際に存在するものである。EBDICはバリエーションが多く、日本語の使用できるEBCDICは1種類ではない。
Firefoxもそうだけど、IEはリンクをShift + クリックすると、リンク先を新しいウインドウで開くことができる。
実はこれはHTMLヘルプでも可能なことを発見。ただしIEで開く。
Cの宣言はよみにくい。MSDNのヘルプを見ても???だったのでちょっと整理してみました。基本的な読み方は以下の順です。
char *( *(*var)() )[10];を例にとると
varrhs、lhsででてきた複素数(Complex)クラスに、挿入演算子を定義してみました。まず、クラス定義
// Complex.h class Complex { public: Complex (int real = 0, int img = 0); friend Complex operator+ (Complex lhs, Complex rhs); friend std::ostream &operator<< (std::ostream& o, Complex& c); private: int real, img; };
lhsとrhs、という識別子にぶつかることがある。たとえばVBならCFooというクラスを定義する。メンバはプロパティがひとつ。グローバル変数である。
'// CFoo.cls
Option Explicit
Public Foo As String
それから、CBarというクラスを定義する。CFooをインターフェースとしてインプリメントする。オブジェクトドロップダウンリストからCFooを選択すると、実装すべきインターフェースのスケルトンが作成される。
'// CBar.cls
Option Explicit
Implements CFoo
Private Property Let CFoo_Foo(ByVal RHS As String)
End Property
Private Property Get CFoo_Foo() As String
End Property
ほらでてきた。Property Let CFoo_Fooの引数に注目。これはright-hand side、すなわち「右辺値」を意味する慣例的な識別子であるらしい。
struct tmとMFCのCTimeに対して挿入子を定義したのであげておく。コンソールにログを出力するときにでもまた使うかと思って。
VC++ の 6.0 ではしばしばこんなエラーに悩むことが
コンパイル中...
baz.cpp
C:\foo\bar\baz.cpp(999) : error C2360: 'i' の初期化が 'case' ラベルによって行われませんでした。
C:\foo\bar\baz.cpp(999) : 'i' の宣言を確認してください。
cl.exe の実行エラー
これをよくみるのはこんな風に、switch のなかで for 文を書いたときだったりする。
int foo (int number) { int ret = 0; switch (number) { default: for (int i = 0; i < number; i ++) { ret += i; } // FALLTHROUGH case 0: return ret; break; } }
これは、本来この i は for 文の中だけのスコープのはずが、ブロック終了後もスコープを持っているためである。このコンパイルエラーも意味は、この例で言うと、case 0: にジャンプした際、i が初期化されていないままになってしまう、という意味であるようだ。VC++ 6.0が準拠しているC++の規格が若干古いためらしい。g++ でコンパイルする場合は、上の例は正である。
int nList(1);
仕事で、他人のソースを見ていたら、関数内にこんな記述を見つけました。一瞬、配列かなと思ったんだけど、丸括弧です。クラスで引数をとるコンストラクタならわかるのですが、int型です。でも、前後の流れから、どうやら値が1のint型の変数を宣言しているようです。ためしにこんなのを作ってみました。
#include "stdafx.h"
#include "cstdio"
int main(int argc, char* argv[])
{
int foo(5); // じゃ、5を入れてみよう
printf( "foo は %d\n", foo );
return 0;
}
実行結果。予想通りです。
foo は 5
ちなみに、拡張子を"cpp"から"c"に変えてコンパイルしようとすると、
error C2143: 構文エラー : ')' が 'constant' の前に必要です。 error C2143: 構文エラー : ';' が 'constant' の前に必要です。 error C2059: 構文エラー : ')'
とかいっぱい警告が出ました。C++特有の新構文なのでしょうか。そのような情報は見つけられなかったのですが。