< February 2004 | March 2004 | April 2004 >

March 31, 2004

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

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

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

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

派生クラスのメンバ関数のポインタ

MFC の CWinThread をカプセル化するクラスを作ろうと思ったのが始まりだったのですが、AfxBeginThread に、派生クラスのメンバ関数は、たとえstatic であっても渡せないようです。ポインタのサイズが違うらしいんですね。(参考: ロベールのC++教室 - 第58章 メンバ関数ポインタ天国 -)

カプセル化したいのはワーカスレッドだったのですが、結局、こんな感じで落ち着きました。以下、基本クラスのヘッダファイルと実装ファイルです。

続きを読む...

March 30, 2004

SHBrowseForFolder クラス

名づけて、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
続きを読む...

March 26, 2004

ATL COM AppWizard プロジェクトで MFC を使う

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 クラスで HTTP を実装してみる

MFCのWinInetクラスを使ってみました。印象としてはVBのInetコントロールと同じくらいお手軽です。VC++6.0のヘルプなら

MSDN ライブラリ Visual Studio 6.0
┗Visual C++ ドキュメント
 ┗Visual C++ ユーザーズ ガイド
  ┗Visual C++ プログラマーズ ガイド
   ┗プログラム機能の追加
    ┗詳細
     ┗インターネットのトピック
      ┗インターネットのプログラミング
       ┗代表的な HTTP クライアント アプリケーションの作成手順

に必要な手順がまとめてくれてあります。さらに簡潔に書くとこんな感じでしょう。

  1. CInternetSession オブジェクトを作成。サーバーに接続されます。
  2. CInternetSession::GetHttpConnection をでサーバに接続します。CHttpConnection オブジェクトが返されます。
  3. CHttpConnection::OpenRequest を呼び出して、サーバへの要求をオープンします。CHttpFile オブジェクトが返されます。
  4. CHttpFile::SendRequestを呼び出して、要求を送信します。
  5. CHttpFile より、受信したデータを読み取ります。
  6. CInternetSession オブジェクトを破棄すると、接続は終了し、開いているファイルのハンドルと接続が自動的にクリアされます。
続きを読む...

March 25, 2004

プリンタ追加バッチ

大量のプリンタのインストールをバッチファイルで行うことはできないかと調べていたら、189105 - ユーザーによる操作なしで Windows にプリンタを追加する方法というのを発見した。これを使えば、プリンタのインストールをこんな感じでバッチファイルに記述することができる。

rundll32 printui.dll,PrintUIEntry /if /b "プリンタに付ける名前"  /f "INFファイル名" /q /r "ポート(LPT1:とか)" /m "プリンタのモデル名"

詳しい使い方は、 rundll32 printui.dll,PrintUIEntry /? で、ヘルプを見ることができる。以下はその内容。

続きを読む...

March 24, 2004

error C2143

switch文の閉じカッコの位置で"error C2143: 構文エラー : ';' が '}' の前に必要です。"とコンパイルエラーになることがある。それはたとえばこんな場合。

switch ( foo )
{
case 'A':
	// 処理...
	break;
case 'B':
}

実はこれは、case 'B':ラベルと}の間に有効なステートメントが存在しないと起こるエラーである。回避策としては、break;でも書いとくか、なんなら;(セミコロン)だけの行でもよい。

March 23, 2004

クラスのメンバ変数をstatic, const, あるいはその両方で宣言する

staticメンバ変数

クラスのメンバ変数で、値をオブジェクトごとに持つのではなく、すべてのオブジェクトで同じ変数を参照したい場合は、staticをつけて宣言する。

// sample.h
class Sample
{
public:
    Sample ();
    ~Sample ();
    
private:
    static int _staticVar;
};

staticメンバ変数の初期化は、下のように実装ファイルで定義をかく。(処理系によって若干解釈が違うといううわさも)

// sample.cpp
#include "sample.h"
int Sample::_staticVar = 0;    // 初期化

constメンバ変数

クラスのメンバ変数の値を、オブジェクト作成後に変更できなくするには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)
{
    // 初期化処理...
}

static constメンバ変数

その両方の場合は?...初期化の方法はひとつ。

// sample.h
class Sample
{
public:
    Sample ();
    ~Sample ();
    
private:
    static const int _staticConstVar;
};
// sample.cpp
#include "sample.h"
const int Sample::_staticConstVar = 0;    // 初期化

March 15, 2004

DLLよりコールした GetModuleFileName で取得されるパス

実行モジュールのパスを取得する方法として、GetModuleFileName関数がある。この関数の1つ目の引数 hModule にNULLを指定すると、VBで言うところの App.Path の代わりに使えますよ、とした情報は多い。しかし、MSDNをひいてみると

hModule
[入力]モジュールのハンドルを指定します。このモジュールを含む実行可能ファイルのパスが取得されます。NULL を指定すると、現在のプロセスを作成するために使われたファイルのパスを取得します。

つまり、NULLを指定したときに取得できるのは、プロセスを起動したモジュールのパスであることがわかる。注意すべきはDLLの場合で、プロセスにアタッチするため、DLLをロードした大元のEXEファイル、ということになる。

続きを読む...

March 12, 2004

メモリ リークの検出と特定

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__を記憶してくれているらしい。

続きを読む...

March 11, 2004

DLL作成のためのDEFファイル

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]
library
DLL の内部名を指定します
address
OSが DLL ファイルをロードするベース アドレス。指定なしは、0x10000000。
entryname
エクスポートする関数の定義名
internalname
エクスポートする関数内部名。省略時は同名の関数。
ordinal
エクスポートするエントリにつける番号。
NONAME
多分システムの隠し関数みたいに、名前はないけど@ordinalで呼び出せる関数になるんだと思う。未確認
DATA
??
PRIVATE
??

EXPORTS 文[MSDN ライブラリ Visual Studio 6.0]のところに載ってるけど、エントリの内容についてはあまり載ってないです。

March 10, 2004

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

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

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

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

March 9, 2004

ハンガリー記法

Windows APIの関数の引数などで、DWORDならdwValue、LPSTRならlpStringなどとなっているのをよく見かけるが、頭に型をあらわす1~2文字を付加したあの命名法を、ハンガリー記法、というらしい。
1-2-3.変数名の命名(Windows API Topics)

VBからコール可能なDLLの作成

以前に、VC++でDLLを作る方法を調べたが、どうやらその方法で作成したDLLは、VBでDeclareを使用してコールすることができないらしい。
週刊Bravo!! > Programing Tips > Visual C++ > DLLの情報をもとに、私が理解した範囲では、

  • 関数はWINAPIで修飾する必要がある(__stdcall呼出規約である必要がある)
  • __stdcall関数は、__declspec(dllexport)でエクスポートすると、名前が_funcname@nのように修飾されてしまう(呼び出しは可能)
  • エクスポートはDEFファイルで行うと、関数名が修飾されない

というかんじ。

ワークロード・ガバナ

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のドキュメントがあります。

March 8, 2004

EBCDIKとEBCDICに関する追加解説

    EBCDIKとEBCDICに関する追加解説

    IBM汎用機の文字コード体系はEBCDIC(Extended Binary Coded Decimal Interchange Code)という。これにカナを拡張したものが、EBCDIK(Extended Binary-Coded-Decimal Interchange Kana Code)と呼ばれるものである。しかし、これとは別に、EBCDICを各国語拡張した文字コード体系があり、これもEBCDICと呼ばれる。もちろん、漢字を含む日本語対応のEBCDICもある。そのため、EBCDIKと、日本語のEBCDICのどちらも実際に存在するものである。EBDICはバリエーションが多く、日本語の使用できるEBCDICは1種類ではない。

    March 7, 2004

    修正案

      ちょっと強引だけど、とりあえずIE6とMozilla Firefoxで表示可能なので、大丈夫でしょう。

      続きを読む...

      March 5, 2004

      新しいウインドウで開く

      Firefoxもそうだけど、IEはリンクをShift + クリックすると、リンク先を新しいウインドウで開くことができる。
      実はこれはHTMLヘルプでも可能なことを発見。ただしIEで開く。

      Cの宣言の読み方

      Cの宣言はよみにくい。MSDNのヘルプを見ても???だったのでちょっと整理してみました。基本的な読み方は以下の順です。

      1. 識別子を探す
      2. 識別子の含まれる一番内側の丸カッコから、順に外側のかっこに向かって、以下を繰り返す。
        1. 識別子の後ろの()または[]を探す
        2. 識別子の前の*を探す

      char *( *(*var)() )[10];を例にとると

      1. 識別子→var
      2. 識別子はポインタ
      3. 識別子は関数ポインタ
      4. 識別子のさす関数の戻り値はポインタ
      5. 識別子のさす関数の戻り値の要素10個の配列へのポインタ
      6. 識別子のさす関数の戻り値のさす配列の要素はポインタ
      7. 識別子のさす関数の戻り値のさす配列の要素はchar型のポインタ

      March 3, 2004

      <<演算子のオーバーロード

      rhs、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;
      };
      
      続きを読む...

      March 2, 2004

      rhs, lhs

      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、すなわち「右辺値」を意味する慣例的な識別子であるらしい。

      続きを読む...

      March 1, 2004

      CTime と struct tm の挿入子

      struct tmとMFCのCTimeに対して挿入子を定義したのであげておく。コンソールにログを出力するときにでもまた使うかと思って。

      続きを読む...

      error C2360: 'i' の初期化が 'case' ラベルによって行われませんでした。

      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 foo(1);

      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++特有の新構文なのでしょうか。そのような情報は見つけられなかったのですが。

      続きを読む...