frog.raindrop.jp.knowledge > C++ プログラミング
C++に関してです。C++もあまり得意じゃないです。

March 25, 2009

(編集中)

#ifndef __INPUTBOX__H
#define __INPUTBOX__H

int InputBox (HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, LPTSTR lpszBuffer, DWORD dwBufferSize);

#endif    //__INPUTBOX__H

#include <windows.h>
#include <tchar.h>
#include "resource.h"
#include "inputbox.h"

typedef struct InputBoxParam
{
    LPCTSTR lpszTitle;
    LPCTSTR lpszPrompt;
    LPTSTR lpszBuffer;
    DWORD cBuffer;
} INPUTBOXPARAM, *LPINPUTBOXPARAM;


INT_PTR CALLBACK DialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    UINT len = 0;
    LPINPUTBOXPARAM pParam = NULL;

    switch (uMsg)
    {
    case WM_INITDIALOG:
        if (lParam)
        {
            pParam = reinterpret_cast <LPINPUTBOXPARAM> (lParam);
            SetWindowText (hwndDlg, pParam->lpszTitle);
            SetDlgItemText (hwndDlg, IDC_STATIC_PROMPT, pParam->lpszPrompt);
            SetDlgItemText (hwndDlg, IDC_EDIT_INPUTBOX, pParam->lpszBuffer);
            SetProp (hwndDlg, _T ("InputBoxParam"), reinterpret_cast <HANDLE> (lParam));
        }
        break;

    case WM_COMMAND:
        switch (LOWORD (wParam))
        {
        case IDOK:
            pParam = reinterpret_cast <LPINPUTBOXPARAM> (GetProp (hwndDlg, _T ("InputBoxParam")));
            if (pParam)
            {
                len = GetDlgItemText (hwndDlg, IDC_EDIT_INPUTBOX, pParam->lpszBuffer, pParam->cBuffer);
            }
            EndDialog (hwndDlg, len);
            break;

        case IDCANCEL:
            EndDialog (hwndDlg, 0);
            break;

        case IDC_EDIT_INPUTBOX:
            {
                WORD notify = HIWORD (wParam);
                if (EN_SETFOCUS == notify)
                {
                    HWND hControl = GetDlgItem (hwndDlg, IDC_EDIT_INPUTBOX);
                    SendMessage (hControl, EM_SETSEL, (WPARAM) 0, (LPARAM) -1);
                }
            }
            break;
        default:
            break;
        }
        break;
    }
    return 0;
}

int InputBox (HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, LPTSTR lpszBuffer, DWORD dwBufferSize)
{
    INPUTBOXPARAM Param;
    ZeroMemory (&Param, sizeof (INPUTBOXPARAM));
    Param.lpszTitle = lpCaption;
    Param.lpszPrompt = lpText;
    Param.lpszBuffer = lpszBuffer;
    Param.cBuffer = dwBufferSize;

    INT_PTR result = DialogBoxParam ((HINSTANCE) GetModuleHandle (NULL), (LPCTSTR) IDD_INPUTBOX, NULL, DialogProc, reinterpret_cast<LPARAM> (&Param));

    return result;
}
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_INPUTBOX DIALOGEX 0, 0, 274, 62
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,203,7,64,14
    PUSHBUTTON      "キャンセル",IDCANCEL,203,23,64,14
    LTEXT           "スタティック",IDC_STATIC_PROMPT,7,7,191,30
    EDITTEXT        IDC_EDIT_INPUTBOX,7,43,260,12,ES_AUTOHSCROLL
END
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by getpath.rc
//
#define IDD_INPUTBOX                    101
#define IDC_STATIC_PROMPT               1001
#define IDC_EDIT_INPUTBOX               1002

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1003
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

March 4, 2009

IPv4 でマルチキャストに送出するパケットの TTL を設定する。

IPv4 でマルチキャストのパケットを送出する際、デフォルトのままでは TTL が 1 になってしまう。ルータ越えの必要がある場合は以下のようにして、送信に使用するソケットのオプションを変更する。(下記は TTL を 32 に変更した例)

// #include <Winsock2.h>
int ttl = 32;
int result = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof ttl);
if (SOCKET_ERROR == result)
{
    // エラー処理は省略...
}

C# の場合はこんなの。

// using System.Net.Sockets;
int ttl = 32;
socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, ttl);

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;
}

February 7, 2006

しょぼ Hash クラス

低機能な連想配列クラスです。お仕事プログラムで、キーと値を対にして格納するようなファイルを読み込む際に、値をパースする関数のポインタを格納するためにさらさらっと書いたものなので、動作確認は十分にしていません。キーや値の列挙等はサポートしていません。(そのうち気が向いたらつけるかもしれませんが)キーは文字列、しかも TCHAR [] のみの対応です。よく言えばシンプルで、メモリの消費量も小さいので、固定のディクショナリとしてなら使い道があるかもしれません。

CLightHash <int, -1> IntHash;
IntHash.Add ("abcde", 5);
IntHash.Add ("abc", 7);
IntHash.Add ("ac", 6);
int nRet;
nRet = IntHash.Explore ("abcde");
nRet = IntHash.Explore ("abcdef");
nRet = IntHash.Explore ("abc");
nRet = IntHash.Explore ("ac");
nRet = IntHash.Explore ("");

nRet = IntHash ["abcde"];
nRet = IntHash ["abcdef"];
nRet = IntHash ["abc"];
nRet = IntHash ["ac"];
nRet = IntHash [""];
IntHash ["123"] = 123;
nRet = IntHash ["123"];

# イマイチ汎用性に欠けているとはいえ、こういう流用可能なクラスを一所懸命作っているあたり、仕事がいまひとつ調子が出てないのがバレバレですな・・・。
# 調子がよければこんなの作ってませんとも 笑。

続きを読む...

January 26, 2006

対数あれこれ

苦手な対数のお勉強だ。

x = n p

のとき、pn を底とする x の対数と呼び、以下のように表記する。

p = log n x

プログラミング言語では、定数 e を底とする自然対数を返す log (number) 関数が定義されていることが多い。任意の数 n を底とした対数が得たければ、以下のようにする。

log n x = log (x) / log (n)
続きを読む...

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

ログクラス

めもめも。
続きを読む...

April 4, 2005

std::list の operator ==

std::list には、 operator == が実装されているので、以下のようにリストが等しいかを比較することができる。
#include <list>

int main(
    int     argc,
    char**  argv )
{
    std::list<Foo>  FooList1;
    std::list<Foo>  FooList2;
    
    if ( FooList1 == FooList2 )
        std::cout << "FooList1 == FooList2" << endl;
    else
        std::cout << "FooList1 != FooList2" << endl;
    
    return 0;
}
上のソースで Foo が、組み込み型の typedef だったりすると、上のソースはコンパイルできるが、ユーザ定義型の場合はコンパイルエラーとなる。 VC++ .NET だとこんな感じ。
続きを読む...

June 7, 2004

error LNK2005

MFC を使用したプロジェクトで、こんなリンクエラーがでた。

コードを生成中...
リンク中...
nafxcw.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) はすでに LIBCMT.lib(new.obj) で定義されています
nafxcw.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) はすでに LIBCMT.lib(delete.obj) で定義されています
Release/Ocrreruset.exe : fatal error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました
link.exe の実行エラー

ググってみたら、148652 - [PRB] C ランタイム ライブラリを MFC ライブラリより先にリンクしたときの LNK2005 エラーというのが見つかった。これによると、

CRT ライブラリでは、new、delete、および DllMain の各関数で弱い外部リンケージを使用します。MFC ライブラリにも new、delete、および DllMain の各関数が含まれており、このために MFC ライブラリを CRT ライブラリより先にリンクする必要があります。

ということであるらしい。解決策として、プロジェクトの設定でリンクの「無視するライブラリ」に Nafxcwd.libLibcmtd.lib を追加し(区切りはスペースでもカンマでもいいみたい)、「オブジェクト/ライブラリ モジュール」の方で、あらためて Nafxcwd.libLibcmtd.lib の順で指定したら、その順にリンクされるからうまくいく、と書いてある。

実はこれには補足があって、Nafxcwd.lib とLibcmtd.lib はともにデバッグ用のライブラリである。なので、Release でエラーが出る場合には、それぞれ Nafxcw.lib, Libcmt.lib に読み替えて設定する必要がある。

続きを読む...

April 30, 2004

static

VB 使いが C++ で違和感を感じるもののひとつが static かも知れないな。たとえば関数スコープの変数を、static/Staticで加算していくこんなクラス

' VB
' CIncriment
Option Explicit

Public Function Incriment() As Long
Static slngNumber       As Long
    
    slngNumber = slngNumber + 1
    Incriment = slngNumber
End Function
// C++
// CIncriment
class CIncriment {
public:
	int Incriment ()
	{
		static int siNumber = 0;
		return ++siNumber;
	}
};

これで、だいたい一緒かな?これを、インスタンスを作成しては、Incriment メソッドをコールしてみる。
まず VB 。フォームモジュールにコマンドボタンを一個。

続きを読む...

April 28, 2004

atoin 関数

atoi 関数のレングス指定あり版です。なんてことはないですが、しょっちゅう作るのも面倒かと思って。

続きを読む...

April 27, 2004

メンバへのポインタ演算子 ってやつ

メンバへのポインタ演算子ってやつがある。MSDNライブラリでの説明はこう。

メンバへのポインタ演算子: .* および ->*
pm-expression :
cast-expression
pm-expression .* cast-expression
pm-expression ->* cast-expression
二項演算子 .* は第 1 オペランドを第 2 オペランドに結合します。第 1 オペランドはクラス型のオブジェクトとし、第 2 オペランドはメンバへのポインタ型とする必要があります。
二項演算子 ->* も第 1 オペランドを第 2 オペランドに結合します。この第 1 オペランドはクラス型のオブジェクトを指すポインタ、第 2 オペランドはメンバへのポインタ型にする必要があります。
演算子 .* を使った式の第 1 オペランドの型は、第 2 オペランドで指定した、メンバへのポインタと同じクラス型にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。
演算子 ->* を使った式の第 1 オペランドの型は、第 2 オペランドで指定した型の "クラス型へのポインタ" にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。

この説明で、は、はーん、ってわかった人いますかね。私はわかりませんでした。

この演算子が何なのか、何でこんなものいるか、というと、まず、クラスのメンバ関数を、関数ポインタからコールしたい、という場合があるとします。クラスのメンバ関数も関数なので、もちろん関数ポインタをとることができます。たとえばこんな感じ。

続きを読む...

March 31, 2004

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

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

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

続きを読む...

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 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 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 9, 2004

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

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

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

というかんじ。