#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
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);
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;
}
低機能な連想配列クラスです。お仕事プログラムで、キーと値を対にして格納するようなファイルを読み込む際に、値をパースする関数のポインタを格納するためにさらさらっと書いたものなので、動作確認は十分にしていません。キーや値の列挙等はサポートしていません。(そのうち気が向いたらつけるかもしれませんが)キーは文字列、しかも 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"];
# イマイチ汎用性に欠けているとはいえ、こういう流用可能なクラスを一所懸命作っているあたり、仕事がいまひとつ調子が出てないのがバレバレですな・・・。
# 調子がよければこんなの作ってませんとも 笑。
こんな警告が出た。
コンパイルしています...
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 ? メンバアクセス演算子? どちらも使ってないよ。はっきり言ってこのエラーメッセージは意味不明。
#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;
}
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.lib と Libcmtd.lib を追加し(区切りはスペースでもカンマでもいいみたい)、「オブジェクト/ライブラリ モジュール」の方で、あらためて Nafxcwd.lib と Libcmtd.lib の順で指定したら、その順にリンクされるからうまくいく、と書いてある。
実はこれには補足があって、Nafxcwd.lib とLibcmtd.lib はともにデバッグ用のライブラリである。なので、Release でエラーが出る場合には、それぞれ Nafxcw.lib, Libcmt.lib に読み替えて設定する必要がある。
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 。フォームモジュールにコマンドボタンを一個。
メンバへのポインタ演算子ってやつがある。MSDNライブラリでの説明はこう。
メンバへのポインタ演算子:.*および->*
pm-expression:cast-expressionpm-expression.*cast-expressionpm-expression->*cast-expression二項演算子
.*は第 1 オペランドを第 2 オペランドに結合します。第 1 オペランドはクラス型のオブジェクトとし、第 2 オペランドはメンバへのポインタ型とする必要があります。二項演算子
->*も第 1 オペランドを第 2 オペランドに結合します。この第 1 オペランドはクラス型のオブジェクトを指すポインタ、第 2 オペランドはメンバへのポインタ型にする必要があります。演算子
.*を使った式の第 1 オペランドの型は、第 2 オペランドで指定した、メンバへのポインタと同じクラス型にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。演算子
->*を使った式の第 1 オペランドの型は、第 2 オペランドで指定した型の "クラス型へのポインタ" にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。
この説明で、は、はーん、ってわかった人いますかね。私はわかりませんでした。
この演算子が何なのか、何でこんなものいるか、というと、まず、クラスのメンバ関数を、関数ポインタからコールしたい、という場合があるとします。クラスのメンバ関数も関数なので、もちろん関数ポインタをとることができます。たとえばこんな感じ。
MFC の CWinThread をカプセル化するクラスを作ろうと思ったのが始まりだったのですが、AfxBeginThread に、派生クラスのメンバ関数は、たとえstatic であっても渡せないようです。ポインタのサイズが違うらしいんですね。(参考: ロベールのC++教室 - 第58章 メンバ関数ポインタ天国 -)
カプセル化したいのはワーカスレッドだったのですが、結局、こんな感じで落ち着きました。以下、基本クラスのヘッダファイルと実装ファイルです。
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 クライアント アプリケーションの作成手順
に必要な手順がまとめてくれてあります。さらに簡潔に書くとこんな感じでしょう。
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; // 初期化
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]のところに載ってるけど、エントリの内容についてはあまり載ってないです。