VC++(MFC)

デバッグ出力

簡易的にデバッグする際は、非常に便利。
ただし、スレッド等を使用している中で使うとエラー?で落ちることもある。
str.Format(_T("kind = 0x%02X\n"),m_uc_Buf[0]);
OutputDebugString(str);

MFCでクラス間をまたぐグローバル変数

MFCで画面(クラス)を複数作る場合、クラス間のグローバル関数を作ると便利(オブジェクト指向とは・・・)である。以下にやり方をメモする。
  1. hoge.h(hogeDlg.hではない)に変数を定義する
  2. 使いたい別のクラス内で以下を宣言 ChogeApp* pApp = (ChogeApp*)AfxGetApp();
  3. pApp->hogehoge;

winsock2で切断検知の方法

ノンブロッキングでパケットを受け、selectのタイムアウト機能を用いることで、切断検知&処理ができる。タイムアウトすると、recvの返り値が0となるので判定処理を記述する。下記はとなるワーカスレッド処理内のサンプル。参考サイトはGeek
UINT CACSQLDlg::ThreadProc3(void)
{
	int n;
	CString str;
	char buf[BUFFER_SIZE];
	fd_set	fds,readfds;
	timeval	tv;

	// winsock2の初期化
	WSAStartup(MAKEWORD(2,0), &m_wd_wsaSend);

	// ソケットの作成
	// UDP:SOCK_DGRAM TCP:SOCK_STREAM
	m_sk_sub = socket(AF_INET, SOCK_STREAM, 0);

	// 接続先指定用構造体の準備
	m_si_sub.sin_family = AF_INET;

	// ポート指定
	m_si_sub.sin_port = htons((u_short)m_dw_port_sub);
	m_si_sub.sin_addr.S_un.S_addr = inet_addr((CT2A)m_cs_ip_server);

	// Connect
	connect(m_sk_sub,(struct sockaddr *)&m_si_sub,sizeof(m_si_sub));

	// 関連付け
	bind(m_sk_sub, (struct sockaddr *)&m_si_acs, sizeof(m_sk_sub));

	// fd_setの初期化
	FD_ZERO(&readfds);

	// selectで待つ読み込みソケットとして
	FD_SET(m_sk_sub, &readfds);
	
	// タイムアウト設定
	tv.tv_sec = m_dw_wtime_sub;
	tv.tv_usec = 0;

    while ( 1 )
    {
		// 後はここで目的のイベントとm_hEventStopを
		// WaitForMultipleObjectsで待つなり
		// Sleep() を挟んで(いいかげんな)周期スレッドとするなり
		// 好きに処理をしましょう。
		memcpy (&fds, &readfds, sizeof(fd_set));
	
		// fdsに設定されたソケットが読み込み可能になるまで待機
		n = select(0, &fds, NULL, NULL, &tv);

		if(n == 0)
		{
			AfxMessageBox(_T("サーバから接続が切断されました\n"));
			break;
		}

		// パケット受信
		if(FD_ISSET(m_sk_sub,&fds))
		{
			memset (buf, 0, sizeof(buf));
			recv(m_sk_sub, buf, sizeof(buf), 0); 
			str.Format(_T("recv@SUB = %d\n"),n);
			OutputDebugString(str);

			// 変数へ代入
			AnalyPacket(buf);
			Tlm2Variable();
		
			// 画面更新
			//DrawGraph();
			//ShowText();
		}
    }
	//QuitProc(m_c_flg_sub);
	shutdown(m_sk_sub,1);					// 回線切断
	closesocket(m_sk_sub);					// Winsock2停止

	((CButton*)GetDlgItem(IDC_BUTTON3))->SetWindowText(_T("未接続"));
	m_c_flg_sub = 0;

	return 0;
}

同じ比率でサイズ変更を行う

フォームのリサイズと同じ比率でコントロールをリサイズします
bool flg = false;//起動時にイベントが発生してサイズを変えると落ちるため、回避のためのフラグ
void CtestDlg::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	
	// TODO: この位置にメッセージ ハンドラ用のコードを追加してください

	if(flg) GetDlgItem(IDC_STATIC)->MoveWindow(cx/10,cy/10, cx/2,cy/2);//動的なサイズの変更
	flg = true;
} 

生成するexe名の変更

ときおりプロジェクト名ではなく任意の名前でexeを生成したいときがある。その場合は、下記のように変更することができる。
  1. プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。詳細については、「方法 : プロジェクト プロパティ ページを開く」を参照してください。
  2. [リンカ] フォルダをクリックします。
  3. [全般] プロパティ ページをクリックします。
  4. [出力ファイル] プロパティを変更します。

エラーメッセージ

プリコンパイル ヘッダー ファイルが旧バージョンのコンパイラで作成されています。また、C++ のプリコンパイル済みヘッダー ファイルを C で使用しています (その逆も考えられます)。
cppに、cのソースコードをいれていると上記メッセージが表示される。
ファイル拡張子をcppに変更する

float(IEEE754)のデコード/コード

float	flo_in=-0.0225532F;
float	flo_out;
char	chr[4];
char	*p_flo;

p_flo = (char *)&flo_in;
chr[0] = p_flo[0];
chr[1] = p_flo[1];
chr[2] = p_flo[2];
chr[3] = p_flo[3];

flo_out = *(float *)(chr);

EditBoxのフォントを変更する

HFONT		    hFont;					// フォント設定
// フォントサイズ変更
hFont = CreateFont(12, 0, 0, 0,
           FALSE, FALSE, FALSE, 0,
           SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,
           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
           FIXED_PITCH | FF_MODERN, _T("MS ゴシック"));
        // FIXED_PITCH | FF_ROMAN, "MS 明朝");
        // FIXED_PITCH | FF_ROMAN, "明朝");
SendDlgItemMessage(IDC_EDIT39, WM_SETFONT, (WPARAM)hFont, 0L ); // フォントセット
SendDlgItemMessage(IDC_EDIT40, WM_SETFONT, (WPARAM)hFont, 0L ); // フォントセット

デュアルコアCPUでの実行に関して

タイマースレッドやワーカースレッドを使用したシリアル通信プログラム等をデュアルコアで実行する場合は、動作CPUを1つに固定した方がよい。何もしないと2つのCPUを用いた動作になるが、切替のオーバーヘッドが無視できず、実行周期が設定値に届かない(ことがある)。

動作CPUを固定するには、アプリ起動後、タスクマネージャ->プロセスから対象のアプリを選択し、右クリックの「関係の設定」を選択。CPUを選択する(どちらか、一方のCPUを選択すればよい@デュアル)。

ココに詳しい説明がありました。

プログラム的に、上記の手順を踏むには、SetProcessAffinityMaskというAPIを使えばいいらしい。
ココ

HANDLE hCP;
DWORD pamask, samask, patmp = 0;
int numcpu = 0;
hCP = GetCurrentProcess();
GetProcessAffinityMask(hCP, &pamask, &samask);
m_msg.Format(_T("lpProcessAffinityMask=%d\tlpSystemAffinityMask=%d\n"), pamask, samask);
patmp = pamask>>numcpu;
m_msg2.Format(_T("%d, numcpu:%d"),patmp,numcpu);
if(patmp > 1) SetProcessAffinityMask(hCP, 1<<numcpu);
m_msg3.Format(_T("1<<numcpu:%d, numcpu:%d"),1<<numcpu,numcpu);

// マルチコア対応 コピペ用
HANDLE hCP;
DWORD pamask, samask, patmp = 0;
int numcpu = 0;
hCP = GetCurrentProcess();
GetProcessAffinityMask(hCP, &pamask, &samask);
patmp = pamask>>numcpu;
if(patmp >= 1) SetProcessAffinityMask(hCP, 2<<numcpu);

INIファイルの生成

以下のプログラムをOnInitDialog()に書き込む
CWinApp* pApp;				// アプリケーション情報
CString sIniFileName;			// INI ファイル名称(フルパス)
CString str;

pApp = AfxGetApp();
TCHAR   drive[_MAX_DRIVE];
TCHAR   dir[_MAX_DIR];
TCHAR   fname[_MAX_FNAME];
TCHAR   buff[_MAX_PATH];

// INIファイル読み込み
////////////////////////////////////////////////////////////////////////////////////////////
//自己アプリのパス所得(大小文字識別付き)
GetModuleFileName( pApp->m_hInstance, buff, _MAX_PATH );
WIN32_FIND_DATA wfd;
HANDLE  h = ::FindFirstFile( buff, &wfd );
if( h != NULL ) {
       _tsplitpath( buff, drive, dir, NULL, NULL );
       _tmakepath( buff, drive, dir, wfd.cFileName, NULL );
       ::FindClose( h );
}
_tsplitpath( buff, drive, dir, fname, NULL );
_tmakepath( buff, drive, dir, fname, _T("ini") );
free( (void*) pApp->m_pszProfileName );
sIniFileName = pApp->m_pszProfileName = _tcsdup( buff );

// アプリケーション情報に INIファイル名称を設定する
free((void*) pApp->m_pszProfileName);
pApp->m_pszProfileName=_tcsdup(sIniFileName);

また、InitInstance()に書かれている以下をコメントアウトする。コメントアウトしないとレジストリ上で設定が読み書きされることになる。
//SetRegistryKey(_T("アプリケーション ウィザードで生成されたローカル アプリケーション"));

設定を読み込むには、
m_ComPort = pApp->GetProfileString(_T("Com"),_T("Port"),_T("COM1"));	// def:COM1
m_ComBR   = pApp->GetProfileInt(_T("Com"),_T("BR"),9600);		// def:19200
設定を書き込むには、
pApp->WriteProfileInt(_T("Test"),_T("test"),1);
pApp->WriteProfileString(_T("Main"),_T("Log1"),m_Log1_header);

COMポートの扱い方

以前は、STOPBITとFPARIの位置がズレていた。STOPBIT1は0を代入する。
hoge.hに以下を宣言する
HANDLE			m_hComm;			// シリアルポートのハンドル 
DCB			m_dcb;				// 通信パラメータ
CString		m_ComPort;			// 使用する仮想ポート def:COM1
DWORD			m_ComBR;			// 通信レート         def:9600
BYTE			m_ComPARITY;			// パリティ           def:0
DWORD			m_ComSTOPBIT;			// ストップビット     def:1
BYTE			m_ComFPAR;			// Fパリティ          def:0
BYTE			m_ComBYTE;			// BYTESIZE           def:8
hoge.cppに以下を書く。
str.Format(_T("\\\\.\\%s"),m_ComPort);
m_hComm = CreateFile(
       str,				// シリアルポートの文字列 
       GENERIC_READ | GENERIC_WRITE,	// アクセスモード:読み書き GENERIC_READ | GENERIC_WRITE
       0,				// 共有モード:他からはアクセス不可 
       NULL,				// セキュリティ属性:ハンドル継承せず 
       OPEN_EXISTING,		        // 作成フラグ:
       FILE_ATTRIBUTE_NORMAL,	        // 属性:FILE_ATTRIBUTE_NORMAL
       NULL				// テンプレートのハンドル:
);

if (m_hComm == INVALID_HANDLE_VALUE)
{
       err = GetLastError();
       str.Format(_T("シリアルポートを開くことが出来ませんでした\nError code: %d"),err);
       AfxMessageBox(str);
       exit(-1);
}

// 通信属性を設定する
GetCommState(m_hComm, &m_dcb);		    // DCB を取得 
m_dcb.BaudRate	= m_ComBR;
m_dcb.ByteSize	= m_ComBYTE;
m_dcb.Parity	= m_ComPARITY;
m_dcb.fParity	= m_ComFPAR;
m_dcb.StopBits	= m_ComSTOPBIT;
m_dcb.fBinary	= TRUE;			    // バイナリーモード
m_dcb.fNull	= FALSE;		    // NULLバイト破棄
SetCommState(m_hComm, &m_dcb);		    // DCB を設定 

//タイムアウト
COMMTIMEOUTS ctmo;
ctmo.ReadIntervalTimeout         = 10;		// 文字読み込みの間の時間
ctmo.ReadTotalTimeoutMultiplier  = 12;		// Read :文字数に対する乗数
ctmo.ReadTotalTimeoutConstant    = 144;	// Read :ミリ秒単位での定数
ctmo.WriteTotalTimeoutMultiplier = 12;	        // Write:文字数に対する乗数
ctmo.WriteTotalTimeoutConstant   = 144;	// Write:ミリ秒単位での定数
if (!SetCommTimeouts(m_hComm, &ctmo))
{
       CloseHandle(m_hComm);
       AfxMessageBox(_T("タイムアウト設定に失敗しました"));	
       exit(-1);
}

ワーカースレッド

ワーカスレッドが重い場合(単位時間あたりの実行回数が多くて)、適当なSleep()を入れてあげると軽くなる。
こんな処置でいいのか分からないけど。

hoge.hに以下を宣言する。
//// ワーカスレッド関連
static UINT ThreadEntry(LPVOID pParam);	// スレッド開始位置
void CPulseMonitorDlg::ThreadStop(void);
UINT ThreadProc(void);				// スレッド処理
CWinThread* m_pThread;				// スレッドオブジェクト
HANDLE m_hEventStop;				// 終了イベント
hoge.cppに以下を書く。
// ワーカースレッド定義 
///////////////////////////////////////////////////////////////////////////////////////////
// スレッド開始位置
UINT CPulseMonitorDlg::ThreadEntry(LPVOID pParam)
{
    CPulseMonitorDlg* pHogeHoge = (CPulseMonitorDlg*)pParam;	// 自オブジェクトの取得
    return pHogeHoge->ThreadProc();				// スレッド処理
}

// スレッド処理
UINT CPulseMonitorDlg::ThreadProc(void)
{
    unsigned long	nn;
    CString		str;
    DWORD		err = 0;
    COMSTAT		ComStat;
    DWORD		dwCount;
    DWORD		dwErrors;

    while ( 1 )
    {
       // 後はここで目的のイベントとm_hEventStopを
       // WaitForMultipleObjectsで待つなり
       // Sleep() を挟んで(いいかげんな)周期スレッドとするなり
       // 好きに処理をしましょう。
    }
   return 0;
}
// スレッドの終了
void CPulseMonitorDlg::ThreadStop(void)
{
   ASSERT(m_pThread != NULL);
  
   ::SetEvent(m_hEventStop);	// 終了イベントセット
  
   // スレッド終了待ち
   if ( ::WaitForSingleObject(m_pThread->m_hThread, 1000) == WAIT_TIMEOUT )
   {
     // スレッド強制停止
     // (絶対に停止するなら WaitForSingleObjectで INFINITE も可)
     ::TerminateThread(m_pThread->m_hThread, 0xffffffff);
     //::AfxMessageBox(_T("スレッド強制停止"));
  }
  
   // スレッドオブジェクト破棄
   delete m_pThread;
   m_pThread = NULL;
   
   ::ResetEvent(m_hEventStop);	// 終了イベントクリア
}
ワーカースレッドを開始するには、以下のようにする。
コンパイルは、「Debug」ではなく「Release」にすること。Debugコンパイルだとスレッド開始時にエラーが発生する。原因は不明。
//受信スレッド
ASSERT(m_pThread == NULL);
m_pThread = AfxBeginThread(ThreadEntry,
             			(LPVOID)this,
			THREAD_PRIORITY_NORMAL,
			0,
			CREATE_SUSPENDED,
			NULL);
m_pThread->m_bAutoDelete = FALSE;		// 自動破棄フラグクリア
m_pThread->ResumeThread();			// サスペンド解除

タイマースレッド

hoge.hに以下を宣言する。
// マルチメディアタイマ用ヘッダファイル
#include <windows.h>
#include <mmsystem.h>
#include "afxwin.h"
#pragma comment(lib,"winmm.lib")

// コールバック関数をクラスのメンバ関数に内包する
///////////////////////////////////////////////////////////////////////////////////////////
static	void CALLBACK TimerThreadProc (UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dummy1, DWORD dummy2);

// 実際に動作するランナップ時間測定用のスレッド
virtual void VirtualTimerThreadProc (void);
hoge.cppに以下を書く。
// マルチメディアタイマ関数群定義
///////////////////////////////////////////////////////////////////////////////////////////
void CALLBACK CPulseMonitorDlg::TimerThreadProc(UINT uID,UINT msg,DWORD dwUser, DWORD dwData1,DWORD dwData2)
{
       // コールバック関数定義
       // ユーザデータにクラスポインタを渡しメンバ関数へアクセスできるようにする
       CPulseMonitorDlg *pTObject = (CPulseMonitorDlg*)dwUser;
       pTObject->VirtualTimerThreadProc();
}

void CPulseMonitorDlg::VirtualTimerThreadProc()
{
        // ここに処理を書く
}
タイマスレッドを開始する場合
// タイマスレッドの取得
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
timeBeginPeriod(m_Step_ReLog);
//m_dwStart	= timeGetTime();
// ReLogスレッド
m_SCnt_ReLog =	m_ECnt_ReLog =	0;
m_ThrID_ReLog	= timeSetEvent(
				m_Step_ReLog,								// 間隔[ms]
				0,											// 分解能
				TimerThreadProc,									// 割り込み関数
				(DWORD)this,								// ユーザーパラメータ
				TIME_PERIODIC | TIME_CALLBACK_FUNCTION		// 動作フラグ
			);
タイマスレッドを停止する場合
		timeKillEvent(m_ThrID_ReLog		);
		timeEndPeriod(m_Step_ReLog);

マルチスレッド割り込み禁止

hogeDlg.cppの上の方に
CRITICAL_SECTION cs;

どこかで初期化すること
InitializeCriticalSection(&cs);

EnterCriticalSection(&cs);
// この間に処理を書く
LeaveCriticalSection(&cs);


ICONの変更

Picture ControlのICONを変更するためには、以下のようにする。
m_***.SetIcon(AfxGetApp()->LoadIcon(IDI_***));


リターンキーの処置

MFCの場合、EditBoxでリターンキーを押すと終了してしまう問題の処置として
  1. 「メッセージ」より、「WM_ONKEYDOWN」を選択し追加する。(クラスビュー)
  2. 次に、「オーバーライド」を押し、メンバ関数一覧を表示する。「BOOL PreTranslateMessage(MSG* pMSG);」を追加する。
  3. PreTranslateMessageの中に以下のコードを書く
if(pMsg->message == WM_KEYDOWN)
{
 switch(pMsg->wParam)
 {
  case VK_RETURN: return TRUE;
  case VK_ESCAPE: return TRUE;
 }
}


追加と削除操作ができない場合の処置

コード要素'xxxx'が読み取り専用であるため、追加と削除操作は実行できません。
このような場合は、
  1. 「xxxxに関係するファイルを閉じる」
  2. インテリセンスのファイル(プロジェクト名.ncb)を削除


特定のEditBoxのみを更新する

UpdateData(FALSE)以外に表示を更新する方法として、対象オブジェクトのControl変数(ここでは、m_btn)を作成し以下のように記述する
m_btn.SetWindowTextW(_T("hogehoge"));
または、
((CEdit*)GetDlgItem(IDC_EDIT1))->SetWindowText(_T("hogehoge"));

特定のEditBoxのみの色を変える

HBRUSH FDIR::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
   HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

   // エディットボックスの色変更
   if (nCtlColor == CTLCOLOR_EDIT)
       if (pWnd->m_hWnd == CWnd::GetDlgItem(IDC_EDIT1)->GetSafeHwnd())
       {
           // 文字色
           pDC->SetTextColor(RGB(255, 0, 0));

           // 背景色
           hbr = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
           pDC->SetBkColor(RGB(0, 0, 0));
       }
   
   return hbr;

}

WriteFile()でハングアップする事例の回避方法

WriteFileの最後の引数をNULLにするのではなく、OVERLAPPED変数を当てる

OVERLAPPED overlapped = {0,0,0,0,NULL};
overlapped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
WriteFile(hComm,
          byData,
          ulDataNum,
          &dwNumOfByteswritten,
          &overlapped);
GetOverlappedResult(hComm,
                    &overlapped,
                    &dwNumOfByteswritten,
                    TRUE);


ステータスバーの追加

// ステータスバー変数
CStatusBar		m_statusBar;			// ステータスバー
	TIMECAPS		Caps;					// 実行環境性能取得

DWORD err = 0;
const UINT  ind[4] = {0, IDS_STRING102};

       if (!err) if (!m_statusBar.Create(this)) err = 1;
       if (!err) if (!m_statusBar.SetIndicators(ind, 2)) err = 1;
       if (!err)
       {
           m_statusBar.SetPaneInfo(0, 0,			 SBPS_STRETCH,   0);
		m_statusBar.SetPaneText(0, _T("ファイル(F)>初期化(I)を行ってください"));
		RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
       }

   // ステータスバー初期化
   if (!err) if (!m_statusBar.Create(this)) err = 1;
if (!err) if (!m_statusBar.SetIndicators(ind, 2)) err = 1;
if (!err) if (!m_statusBar.SetIndicators(ind, 3)) err = 1;
if (!err) if (!m_statusBar.SetIndicators(ind, 4)) err = 1;
   if (!err)
       {
           m_statusBar.SetPaneInfo(0, 0,			 SBPS_STRETCH,   0);
           m_statusBar.SetPaneInfo(1, IDS_STRING102, SBPS_NORMAL, 100);
		m_statusBar.SetPaneInfo(2, IDS_STRING103, SBPS_NORMAL, 100);
		m_statusBar.SetPaneInfo(3, IDS_STRING103, SBPS_NORMAL,  40);
           if (!m_statusBar.SetPaneText(0, _T("ポートを設定し、接続を行ってください"))) err = 1;
		if (!m_statusBar.SetPaneText(1, App.m_ShowStsX)) err = 1;
		if (!m_statusBar.SetPaneText(2, App.m_ShowStsY)) err = 1;
		str.Format(_T("\t\t%s"),App.m_ComPort);
		if (!m_statusBar.SetPaneText(3, str )) err = 1;
       }
   if (!err) RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);


CStringからchar型への変換

// 値取り込み
char *s = new char[m_ShowCmd.GetLength()+1];
strcpy(s, (CT2A)m_ShowCmd);

文字型(16進)から、数字に変換する

char *s = new char[m_hex.GetLength()+1];
strcpy(s, (CT2A)m_hex);
TxBuffer[0] = (unsigned char)strtol(s, NULL, 16);

数字を文字型(n進数)に変換する(この場合は、2進数)

char out[256];
long b = 12356;
_ltoa(b,out,2);

CStringから切り出し(splitみたいなもの)

AfxExtractSubString(sHoge, rtmp, 0, ',');	
dat[m_lines].sday = sHoge;

CString型の付属機能として

S0 = "ABCDEF" 
S7 = S0.Left( 3 ); S0 から3バイトを抽出して、S7 に代入します。 ( ABCDEF )
S7 = S0.Right( 3 );  文字列の最後から3バイトを抽出します。 ( ABCDEF ) 
S7 = S0.Mid( 1 );  第1バイト以後を抽出します。( ABCDEF )
S7 = S0.Mid( 1, 3 );  第1バイト~第3バイトを抽出します。 ( ABCDEF )
S0 = "AbcAbcQAbcXAbc"; 
S7 = S0.SpanIncluding( "Abc" );  Abc が続いている範囲を抽出します。( AbcAbcQAbcXAbc )
S7 = S0.SpanExcluding( "XYZ" ); X, Y, Z, のどれかまでを抽出します。( AbcAbcQAbcXAbc )

特定のボタンをENA/DIS

GetDlgItem( IDC_CHECK)->EnableWindow( FALSE)

日本語のテキストファイルを読み込む

文字セットを「マルチバイト文字セット」にする。


あとは、通常のCStdioFileなどで読み込めばよい。
	// ログ解析
	CString			rtmp;
	CString			sHoge;

       CString         filter("CSV Files (*.csv) |*.csv|");
       CFileDialog     selDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, filter);
	int		        err		= 0;
	m_lines	= 0;

	// ログファイルの指定
       if (selDlg.DoModal() == IDOK)
         m_Log_Path = selDlg.GetPathName();
	else
		// ファイル指定されなかった場合は終了
		return;
	
	// (1)読み書き用にオープン
	if (!err)
	{
		if (!stdFile.Open(m_Log_Path, CFile::modeRead)) err = 1;
	}
	
	if (err)
	{
		AfxMessageBox(_T("ファイルの読み込みに失敗しました"));
		return;
	}

	// 切り出し
	while(stdFile.ReadString(rtmp) == TRUE)
	{
		//rtmp.Replace(sCut,_T(""));
		// データ格納
		AfxExtractSubString(sHoge, rtmp, 0, ',');	dat[m_lines].sday	= sHoge;
		AfxExtractSubString(sHoge, rtmp, 1, ',');	dat[m_lines].number	= atoi((CT2A)sHoge);
		if(dat[m_lines].number == 0) continue;		// ヘッダとみなす
		AfxExtractSubString(sHoge, rtmp, 2, ',');	dat[m_lines].name	= sHoge;
		AfxExtractSubString(sHoge, rtmp, 3, ',');	dat[m_lines].agreement	= sHoge;
		AfxExtractSubString(sHoge, rtmp, 4, ',');	dat[m_lines].odr_FGHIJ	= atoi((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp, 5, ',');	dat[m_lines].odr_KJ	= atoi((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp, 6, ',');	dat[m_lines].odr_M	= atoi((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp, 7, ',');	dat[m_lines].odr_Z	= atoi((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp, 8, ',');	dat[m_lines].tim_start	= sHoge;
		AfxExtractSubString(sHoge, rtmp, 9, ',');	dat[m_lines].tim_end	= sHoge;
		AfxExtractSubString(sHoge, rtmp,10, ',');	dat[m_lines].hours	= atof((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp,11, ',');	dat[m_lines].holiday	= atoi((CT2A)sHoge);
		AfxExtractSubString(sHoge, rtmp,12, ',');	dat[m_lines].kind	= sHoge;
		AfxExtractSubString(sHoge, rtmp,13, ',');	dat[m_lines].check	= sHoge;
		AfxExtractSubString(sHoge, rtmp,14, ',');	dat[m_lines].checker	= sHoge;
		AfxExtractSubString(sHoge, rtmp,15, ',');	dat[m_lines].memo	= sHoge;

		m_lines++;
	};

	if (!err) err = ListInsertItem();  // リストアイテム挿入
	stdFile.Close();

	UpdateData(FALSE);

	m_flgOpen = 1;
}

リストコントロールにおけるソート

結構面倒
void CmanhourDlg::OnLvnColumnclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{ 
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: ここにコントロール通知ハンドラ コードを追加します。
	*pResult = 0;

	RenumberItem();							// SortItems関数を呼び出す前には必ず実行

	m_iSubItem = pNMLV->iSubItem;
	static BOOL bSort = FALSE;				// staticで定義すること
	m_xcList.SortItems(CompareFunc,bSort);
	
	bSort = !bSort;							// bSortがFALSEならTRUE、TRUEならFALSEにする
}

void CmanhourDlg::RenumberItem()
{ 
	LV_ITEM lvItem;

	// m_list1はCListCtrl型のDDX変数で、ソートを行うリストビューコントロールのオブジェクトであるものとする
	for( int i = 0; i < m_xcList.GetItemCount(); i++ ) {
		lvItem.iItem = i;
		lvItem.iSubItem = 0;
		lvItem.mask = LVIF_PARAM;
		lvItem.lParam = i;		// ここで番号をアイテムに指定する
		m_xcList.SetItem(&lvItem);
	}
}

int CALLBACK CmanhourDlg::CompareFunc(LPARAM param1, LPARAM param2, LPARAM param3)
{ 
	// staticメンバ関数なので、GetParent()で親ウィンドウを取得することはできない
	CmanhourDlg* pDlg = (CmanhourDlg*)AfxGetMainWnd();
	int nSubItem  = pDlg->m_iSubItem;

	// 比較される2つのアイテムから「発売日」の文字列を取得する
	CString str1 = pDlg->m_xcList.GetItemText(param1, nSubItem  );
	CString str2 = pDlg->m_xcList.GetItemText(param2, nSubItem  );

	// strcmpを使うなら、降順の場合はstr1とstr2を逆にしなければならない
	int iReturn;
	if( !param3 )
		iReturn = strcmp( str1, str2 );		// 昇順
	else
		iReturn = strcmp( str2, str1 );		// 降順 
	return iReturn;
}

画面のリサイズ機能の実装

ウインドウのリサイズ自体は、リソースエディタの「Border」で「サイズ変更枠」を選択すればよい。これでウインドウ自体はリサイズが可能となるが、配置したコントロールはもとのままである。コントロールをリサイズするには、計算し設定する必要がある。まず、リサイズに発生するイベントハンドラ「OnSize」を追加する(追加は、メッセージからWM_SIZEを選択・追加する)。
例えば、リストコントロールがウインドウに配置されてるとし、そのコントロール変数がm_xcListだったとする。
メンバ変数に下記変数を宣言
CRect			m_rect;				// ウインドウサイズの取得

OnInitDialogの初期化欄に下記を追加する
if (IsWindow(m_xcList.GetSafeHwnd()))
{
   // ダイアログのサイズを取得
   GetClientRect(&m_rect);
   // ダイアログに合わせてリストビューのサイズ変更(初期)
   m_xcList.MoveWindow(0, 0, m_rect.Width(), m_rect.Height());
}
::SetWindowLong(m_hWnd, GWL_STYLE, ::GetWindowLong(m_hWnd, GWL_STYLE)|WS_CLIPCHILDREN);

OnSizeに
void CmanhourDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	// TODO: ここにメッセージ ハンドラ コードを追加します。
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
	// その時点では、リストビューのウィンドウハンドルはNULLである。
    if (IsWindow(m_xcList.GetSafeHwnd()))
	{
	// ダイアログに合わせてリストビューのサイズ変更
		m_xcList.MoveWindow(0, 0, cx, cy);
	}
} 

詳しい解説などはここを参考すると良い

サイズ変更の最大/最小サイズを固定する

ウィンドウが変更できるサイズを制限するには、WM_GETMINMAXINFO メッセージハンドラで MINMAXINFO 構造体メンバを設定します。

// フレームウィンドウクラスのWM_GETMINMAXINFOメッセージハンドラ
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
   // 幅400高さ300より小さくできないようにする
   lpMMI->ptMinTrackSize.x = 400;
   lpMMI->ptMinTrackSize.y = 300;

   // 幅600高さ500より大きくできないようにする
   lpMMI->ptMaxTrackSize.x = 600;
   lpMMI->ptMaxTrackSize.y = 500;
 
   CFrameWnd::OnGetMinMaxInfo(lpMMI);
}
     

VC++でGPIBを使用する方法

まず、ソースファイルのカレントディレクトリに
  • Decl-32.h
  • gpib-32.obj
  • ni488.h
をコピーする。上記ファイル群はNIのライブラリ&アプリをインストールするとPCのどこかにコピーされてる。

hoge.h内に
#include "stdio.h"
#include "Decl-32.h"
#include "afxwin.h"

#define BDINDEX                        0	// Board Index
#define PRIMARY_ADDR_OF_PPS            0	// Primary address of device
#define NO_SECONDARY_ADDR              0	// Secondary address of device
#define TIMEOUT                        T10s	// Timeout value = 10 seconds
#define EOTMODE                        1	// Enable the END  message
#define EOSMODE                        0	// Disable the EOS mode
#define SLEEPTIME                      100	// Sleep time for command sending [ms]
#define MINTVAL                        5000    // モニタリング周期[ms]
#define _REN                           1	// リモート制御開始
#define _GTL                           2	// リモート制御停止
#define _VSET                          3	// 電圧設定
#define _ISET                          4	// 電流設定
#define _SW0                           5	// 出力設定OFF
#define _SW1                           6	// 出力設定ON

プロジェクト設定のリンカのコマンドラインに、
./Gpib-32.obj
最終更新:2013年11月10日 09:25