トップ回答者
コールバック関数と timeSetEvent関数について

質問
-
Visual Studio 2008を用いてMFCの作成を行っています。
void CADinputView::OnInputsingle() での処理を timeSetEvent関数を用いて1秒毎にコールバックしたいと考えています。
ただ、コールバック関数と timeSetEvent関数の扱い方がライブラリを参照しても理解できませんでした。解決方法を教えて頂けないでしょうか?
以下プログラム
// ADinputView.cpp : CADinputView クラスの実装
//#include "stdafx.h"
#include "ADinput.h"
#include "ADinputDoc.h"
#include "ADinputView.h"
#include "fbiad.h"
#include "fbida.h"
#include <math.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CADinputViewIMPLEMENT_DYNCREATE(CADinputView, CView)
BEGIN_MESSAGE_MAP(CADinputView, CView)
// 標準印刷コマンド
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CADinputView::OnFilePrintPreview)
ON_COMMAND(ID_INPUTSINGLE, &CADinputView::OnInputsingle)
ON_COMMAND(ID_STOP, &CADinputView::OnStop)
END_MESSAGE_MAP()// CADinputView コンストラクション/デストラクション
CADinputView::CADinputView()
{
// TODO: 構築コードをここに追加します。}
CADinputView::~CADinputView()
{
}BOOL CADinputView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: この位置で CREATESTRUCT cs を修正して Window クラスまたはスタイルを
// 修正してください。return CView::PreCreateWindow(cs);
}// CADinputView 描画
void CADinputView::OnDraw(CDC* /*pDC*/)
{
CADinputDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;// TODO: この場所にネイティブ データ用の描画コードを追加します。
}
// CADinputView 印刷
void CADinputView::OnFilePrintPreview()
{
AFXPrintPreview(this);
}BOOL CADinputView::OnPreparePrinting(CPrintInfo* pInfo)
{
// 既定の印刷準備
return DoPreparePrinting(pInfo);
}void CADinputView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 印刷前の特別な初期化処理を追加してください。
}void CADinputView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 印刷後の後処理を追加してください。
}void CADinputView::OnRButtonUp(UINT nFlags, CPoint point)
{
ClientToScreen(&point);
OnContextMenu(this, point);
}void CADinputView::OnContextMenu(CWnd* pWnd, CPoint point)
{
theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
}
// CADinputView 診断#ifdef _DEBUG
void CADinputView::AssertValid() const
{
CView::AssertValid();
}void CADinputView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}CADinputDoc* CADinputView::GetDocument() const // デバッグ以外のバージョンはインラインです。
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CADinputDoc)));
return (CADinputDoc*)m_pDocument;
}
#endif //_DEBUG
// CADinputView メッセージ ハンドラvoid CADinputView::OnInputsingle()
{
// TODO: ここにコマンド ハンドラ コードを追加します。
int nRet; // 関数の実行結果
char cDisp1[200]; // メッセージ配列
char cDisp2[200];
char cDisp3[200];
WORD wData1[1]; // データ配列
WORD wData2[1];
double dVolt1; // 電圧値
double dVolt2;
ADBOARDSPEC BoardSpec[3]; // ADBOARDSPEC 構造体
ADSMPLCHREQ SmplChReq[3]; // ADSMPLCHREQ 構造体
SmplChReq[0].ulChNo = 1;
SmplChReq[0].ulRange = AD_10V; // インタフェースモジュールの初期化を行います
SmplChReq[1].ulChNo = 2;
SmplChReq[1].ulRange = AD_10V; // インタフェースモジュールの初期化を行います/////AD_part//////////////////////////////////////////////////////////////////////////////////
hDeviceHandle = AdOpen ("FBIAD1");
if (hDeviceHandle == INVALID_HANDLE_VALUE)
{
AfxMessageBox("デバイスのオープンに失敗しました", MB_OK, 0);
return;
}
else{
//デバイスの情報取得
nRet = AdGetDeviceInfo( hDeviceHandle, &BoardSpec[0] );
if(nRet != AD_ERROR_SUCCESS)
{
AfxMessageBox("デバイスの情報取得に失敗しました", MB_OK, 0);
}
//サンプリング1
nRet = AdInputAD( hDeviceHandle, 1, AD_INPUT_SINGLE, &SmplChReq[0], wData1);
if(nRet != AD_ERROR_SUCCESS)
{
AfxMessageBox("アナログ入力に失敗しました", MB_OK, 0);
}
else{
//電圧算出
dVolt1 =( 10/pow(2.0, (int)BoardSpec[0].ulResolution)*wData1[0] - 5)*2;
sprintf_s(cDisp1, "Ch1入力電圧は%1.3fVです", dVolt1);
AfxMessageBox(cDisp1, MB_OK, 0);
}
//サンプリング2
nRet = AdInputAD( hDeviceHandle, 1, AD_INPUT_SINGLE, &SmplChReq[1], wData2);
if(nRet != AD_ERROR_SUCCESS)
{
AfxMessageBox("アナログ入力に失敗しました", MB_OK, 0);
}
else{
//電圧算出
dVolt2 =( 10/pow(2.0, (int)BoardSpec[0].ulResolution)*wData2[0] - 5)*2;
sprintf_s(cDisp2, "Ch2入力電圧は%1.3fVです", dVolt2);
AfxMessageBox(cDisp2, MB_OK, 0);
}
}int j;
double result2;
for (j = 0 ; j < 1 ; j++) {
result2 = dVolt1+ dVolt2; /* 結果を result に格納している */}
sprintf_s(cDisp3, "Ch1出力電圧は%1.3fVです", result2);
AfxMessageBox(cDisp3, MB_OK, 0);/////DA_part////////////////////////////////////////////////////////////////////////////////////////
//ボードの初期処理
hDeviceHandleDA = DaOpen("FBIDA1");
if(hDeviceHandleDA == INVALID_HANDLE_VALUE)
{
AfxMessageBox("デバイスのオープンに失敗しました",MB_OK,0);
return;
}
//アナログ出力設定情報読み出し
nRet = DaGetSamplingConfig(hDeviceHandleDA,&gConfig);
if(nRet != DA_ERROR_SUCCESS){
DaClose(hDeviceHandle);
AfxMessageBox("アナログ入力更新に失敗しました");
return;
}//ボードの設定
nRet = DaSetBoardConfig(hDeviceHandleDA,200,NULL,NULL,0);
if(nRet != DA_ERROR_SUCCESS){
AfxMessageBox("ボードの設定に失敗しました");
return;
}
//出力設定
gConfig.ulSmplRepeat = 1; //繰り返し回数=なし
gConfig.fSmplFreq = 20000; //出力更新レート= 20kHz
gConfig.SmplChReq[0].ulChNo = 1; //出力チャンネル= チャンネル1
gConfig.SmplChReq[0].ulRange = DA_10V; //出力レンジ= ±10V
//アナログ出力更新設定
nRet = DaSetSamplingConfig(hDeviceHandleDA,&gConfig);
if(nRet != DA_ERROR_SUCCESS){
AfxMessageBox("アナログ出力更新に失敗しました");
return;
}
//バッファ内の出力データをクリアします
nRet = DaClearSamplingData(hDeviceHandleDA);
if(nRet != DA_ERROR_SUCCESS){
AfxMessageBox("バッファのクリアに失敗しました");
return;
}
//出力するデータをバッファにセットします
nRet = DaSetSamplingData(hDeviceHandleDA, &result2,1);
if(nRet != DA_ERROR_SUCCESS){
AfxMessageBox("出力データの登録に失敗しました");
return;
}
//アナログ出力を開始します
nRet = DaStartSampling(hDeviceHandleDA,FLAG_ASYNC);
if(nRet != DA_ERROR_SUCCESS){
AfxMessageBox("アナログ出力に失敗しました");
return;
}////////////////////////////////////////////////////////////////////////////////////////////////////
}
回答
-
以下にSample Codeがあります。
[Using Multimedia Timers]
http://msdn.microsoft.com/en-us/library/dd757664(v=VS.85).aspx
※上のLinkから読んでいくと良いです。timeSetEventは古いAPIであり、制約があります。
[TimeProc Callback Function]
http://msdn.microsoft.com/en-us/library/dd757631(v=VS.85).aspx
Applications should not call any system-defined functions from inside a callback function, except for PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and OutputDebugString.制約が結構厳しいので、Worker Threadで処理したほうが良いでしょう。
[AfxBeginThread]
http://msdn.microsoft.com/en-us/library/s3w9x78e(v=VS.90).aspxtimeSetEventによる処理でもそうですが、別Threadで処理が走るため状況によっては同期をとる必要が出てきます。
- 回答としてマーク 山本春海 2011年4月4日 5:12
-
どの部分がわからないのでしょう。ちなみにコールバックを使用する場合は
下の様な感じ(コンパイルしてませんあしからず)。class MM_TIMER
{
friend void CALLBACK f_TimeOut_MM(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser,
DWORD_PTR dw1,
DWORD_PTR dw2);
public: void Start(){
m_Timer_ID = ::timeSetEvent( 1000, 1, ( LPTIMECALLBACK)f_TimeOut_MM, ( DWORD_PTR)this,
TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
}
protected:
void TimeOut(){
}
};
void CALLBACK f_TimeOut_MM(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser,
DWORD_PTR dw1,
DWORD_PTR dw2)
{
MM_TIMER * Obj = ( MM_TIMER *)dwUser;
Obj->TimeOut();
}- 回答としてマーク 山本春海 2011年4月4日 5:12
すべての返信
-
どの部分がわからないのでしょう。ちなみにコールバックを使用する場合は
下の様な感じ(コンパイルしてませんあしからず)。class MM_TIMER
{
friend void CALLBACK f_TimeOut_MM(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser,
DWORD_PTR dw1,
DWORD_PTR dw2);
public: void Start(){
m_Timer_ID = ::timeSetEvent( 1000, 1, ( LPTIMECALLBACK)f_TimeOut_MM, ( DWORD_PTR)this,
TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
}
protected:
void TimeOut(){
}
};
void CALLBACK f_TimeOut_MM(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser,
DWORD_PTR dw1,
DWORD_PTR dw2)
{
MM_TIMER * Obj = ( MM_TIMER *)dwUser;
Obj->TimeOut();
}- 回答としてマーク 山本春海 2011年4月4日 5:12
-
不勉強で申し訳ありませんが提供していただいた情報を具体的にどのように組み込んでいけばいいのか教えていただけないでしょうか?
どの辺がわからないのでしょうか?
読んだ上で、どの部分をどう解釈できた、どの部分がどのようにわからないといったことを出していきましょう。全くわからないのであれば、実現するレベルに到達していないのかもしれません。
どこにどのように当てはめれば良いかとその場限りの解決策を求めるのではなく、調べる・試行錯誤することで自分の力を伸ばすようにしましょう。調査観点一例:
static キーワードのついたメンバー関数がどういうものなのか、コールバック関数はなぜ static キーワードのついた関数でなければならないのか、コールバックする際には渡すポインタに this を渡す狙いはなぜなのか、こういった点で調査してみてください。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。 -
以下にSample Codeがあります。
[Using Multimedia Timers]
http://msdn.microsoft.com/en-us/library/dd757664(v=VS.85).aspx
※上のLinkから読んでいくと良いです。timeSetEventは古いAPIであり、制約があります。
[TimeProc Callback Function]
http://msdn.microsoft.com/en-us/library/dd757631(v=VS.85).aspx
Applications should not call any system-defined functions from inside a callback function, except for PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and OutputDebugString.制約が結構厳しいので、Worker Threadで処理したほうが良いでしょう。
[AfxBeginThread]
http://msdn.microsoft.com/en-us/library/s3w9x78e(v=VS.90).aspxtimeSetEventによる処理でもそうですが、別Threadで処理が走るため状況によっては同期をとる必要が出てきます。
- 回答としてマーク 山本春海 2011年4月4日 5:12
-
こんにちは、junglemeji さん。
MSDN フォーラムのご利用ありがとうございます。オペレーターの山本です。
みなさんから参考になるアドバイスをいただいているようでしたので、勝手ながら私のほうで回答としてマークさせていただきました。
アドバイスくださったみなさん、情報ありがとうございます。
解決に役立った投稿や、参考になる情報など有効な情報をいただいた場合には、その情報に回答としてマークすることをお願いしています。
今後、同じ問題でこのスレッドを参照される方にも、有効な情報がわかりやすくなるかと思いますので、ご協力のほど、よろしくお願いいたします。
今後とも、MSDN フォーラムをよろしくお願いいたします。それでは。
日本マイクロソフト株式会社 フォーラム オペレーター 山本 春海