デバッグ出力
簡易的にデバッグする際は、非常に便利。
ただし、スレッド等を使用している中で使うとエラー?で落ちることもある。
str.Format(_T("kind = 0x%02X\n"),m_uc_Buf[0]);
OutputDebugString(str);
MFCでクラス間をまたぐグローバル変数
MFCで画面(クラス)を複数作る場合、クラス間のグローバル関数を作ると便利(オブジェクト指向とは・・・)である。以下にやり方をメモする。
- hoge.h(hogeDlg.hではない)に変数を定義する
- 使いたい別のクラス内で以下を宣言 ChogeApp* pApp = (ChogeApp*)AfxGetApp();
- 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を生成したいときがある。その場合は、下記のように変更することができる。
- プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。詳細については、「方法 : プロジェクト プロパティ ページを開く」を参照してください。
- [リンカ] フォルダをクリックします。
- [全般] プロパティ ページをクリックします。
- [出力ファイル] プロパティを変更します。
エラーメッセージ
プリコンパイル ヘッダー ファイルが旧バージョンのコンパイラで作成されています。また、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でリターンキーを押すと終了してしまう問題の処置として
- 「メッセージ」より、「WM_ONKEYDOWN」を選択し追加する。(クラスビュー)
- 次に、「オーバーライド」を押し、メンバ関数一覧を表示する。「BOOL PreTranslateMessage(MSG* pMSG);」を追加する。
- PreTranslateMessageの中に以下のコードを書く
if(pMsg->message == WM_KEYDOWN)
{
switch(pMsg->wParam)
{
case VK_RETURN: return TRUE;
case VK_ESCAPE: return TRUE;
}
}
追加と削除操作ができない場合の処置
コード要素'xxxx'が読み取り専用であるため、追加と削除操作は実行できません。
このような場合は、
- 「xxxxに関係するファイルを閉じる」
- インテリセンスのファイル(プロジェクト名.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