プログラムを書こう!

実務や自作アプリ開発で習得した役に立つソフトウェア技術情報を発信するブログ

MFCでシリアル通信を行う。

この記事は2018年12月11日に投稿しました。

目次

  1. はじめに
  2. MFCでシリアル通信を行う
  3. おわりに

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているMFCでシリアル通信を行う方法についてです。

目次へ

2. MFCでシリアル通信を行う

MFCでシリアル通信を行う方法ですが、今回は実装例で示します。

実装例

f:id:paveway:20181210213055p:plain
MFCでシリアル通信を行うサンプルダイアログ

CSampleSerialDlg.h

#pragma once

// CSampleSerialDlg ダイアログ
class CSampleSerialDlg : public CDialogEx
{
// コンストラクション
public:
    CSampleSerialDlg(CWnd* pParent = NULL);    // 標準コンストラクター

// ダイアログ データ
    enum { IDD = IDD_SAMPLESERIAL_DIALOG };

// 実装
protected:
    virtual BOOL OnInitDialog();
    virtual void DoDataExchange(CDataExchange* pDX);   // DDX/DDV サポート

    // メッセージハンドラ
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedBnSendData();
    afx_msg void OnBnClickedBnClear();
    afx_msg void OnBnClickedBnClose();
    afx_msg LRESULT OnCopyData(WPARAM wParam, LPARAM lParam);
    afx_msg LRESULT OnRecvThreadTerminated(WPARAM wParam, LPARAM lParam);

// 値変数
private:
    CString m_sendData;
    CString m_recvData;

// 内部関数
private:
    BOOL InitSerial();
    BOOL UninitSerial();
    BOOL SendData(CString sendData);
    BOOL StartRecvThread();
    static UINT CallRecvThread(LPVOID pParam);
    void RecvThread();

// 内部変数
private:
    HANDLE m_hSerial;
    CWinThread* m_pRecvThread;
    BOOL m_bIsRecvThreadTerminated;
};

CSampleSerialDlg.cpp

#include "stdafx.h"
#include "SampleSerial.h"
#include "SampleSerialDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CSampleSerialDlg ダイアログ

// ユーザ定義メッセージ
#define WM_USER_READ_THREAD_TERMINATE  (WM_USER + 100)    // 受信スレッド停止メッセージ

/**
 * @brief コンストラクタ
 */
CSampleSerialDlg::CSampleSerialDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CSampleSerialDlg::IDD, pParent)
    , m_sendData(_T(""))
    , m_recvData(_T(""))
    , m_bIsRecvThreadTerminated(FALSE)
{

}

/**
 * @brief ダイアログデータを交換する時に呼び出されます。
 *
 * @param [in] pDX CDataExchangeオブジェクトへのポインタ―
 */
void CSampleSerialDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EN_SEND_DATA, m_sendData);
    DDX_Text(pDX, IDC_EN_RECV_DATA, m_recvData);
}

// メッセージマップ
BEGIN_MESSAGE_MAP(CSampleSerialDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BN_SEND_DATA, &CSampleSerialDlg::OnBnClickedBnSendData)
    ON_BN_CLICKED(IDC_BN_CLEAR, &CSampleSerialDlg::OnBnClickedBnClear)
    ON_BN_CLICKED(IDC_BN_CLOSE, &CSampleSerialDlg::OnBnClickedBnClose)
    ON_MESSAGE(WM_COPYDATA, CSampleSerialDlg::OnCopyData)
    ON_MESSAGE(WM_USER_READ_THREAD_TERMINATE, CSampleSerialDlg::OnRecvThreadTerminated)
END_MESSAGE_MAP()

// CSampleSerialDlg メッセージ ハンドラー

/**
 * @brief ダイアログの初期化の時に呼び出されます。
 *
 * @return 処理結果 TRUE:成功/FALSE:失敗
 */
BOOL CSampleSerialDlg::OnInitDialog()
{
    // 親クラスの関数を呼び出します。
    CDialogEx::OnInitDialog();

    // シリアル通信の初期化処理を行ないます。
    if (!InitSerial())
    {
        // シリアル通信の初期化処理に失敗した場合、終了します。
        return FALSE;
    }

    // 受信スレッドを開始します。
    StartRecvThread();

    return TRUE;
}

/**
 * @brief 送信ボタンが押下された時に呼び出されます。
 */
void CSampleSerialDlg::OnBnClickedBnSendData()
{
    // 入力された送信データを取得します。
    UpdateData(TRUE);
    CString sendData = m_sendData.GetString();

    // データを送信します。
    SendData(sendData);
}

/**
 * @brief クリアボタンが押下された時に呼び出されます。
 */
void CSampleSerialDlg::OnBnClickedBnClear()
{
    m_recvData = _T("");
    UpdateData(FALSE);
}

/**
 * @brief 閉じるボタンが押下された時に呼び出されます。
 */
void CSampleSerialDlg::OnBnClickedBnClose()
{
    // シリアル受信スレッドを停止します。
    m_bIsRecvThreadTerminated = TRUE;
}

/**
 * @brief OnCopyDataメッセージが送信された時に呼び出されます。
 *
 * @param [in] wParam パラメータ
 * @param [in] lParam パラメータ
 * @return 処理結果 0:成功/0以外:失敗
 */
LRESULT CSampleSerialDlg::OnCopyData(WPARAM wParam, LPARAM lParam)
{
    // 取得した受信データを受信データ表示用テキストボックスに追加します。
    COPYDATASTRUCT* pData = (COPYDATASTRUCT*)lParam;
    char* pRecvData = (char*)pData->lpData;
    CString recvData;
    recvData.Format(_T("%s"), pRecvData);
    CString data = m_recvData.GetString();
    m_recvData.SetString(data + recvData);
    UpdateData(FALSE);

    return 0;
}

/**
 * @brief 受信スレッドが停止した時に呼び出されます。
 *
 * @param [in] wParam パラメータ(未使用)
 * @param [in] lParam パラメータ(未使用)
 * @return 処理結果 0:成功/0以外:失敗
 *         ※ここでは常に成功にしています。
 */
LRESULT CSampleSerialDlg::OnRecvThreadTerminated(WPARAM wParam, LPARAM lParam)
{
    // シリアル通信の終了処理を行います。
    UninitSerial();

    // ダイアログを閉じます。
    EndDialog(0);

    return 0;
}

// 内部関数

/**
 * @brief シリアル通信の初期化処理を行います。
 *
 * @return 処理結果 TRUE:成功/FALSE:失敗
 */
BOOL CSampleSerialDlg::InitSerial()
{
    // シリアルポートをオープンする。
    m_hSerial = CreateFile(
        _T("COM1"),
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);

    // シリアルポートがオープンできない場合
    if (m_hSerial == INVALID_HANDLE_VALUE)
    {
        // 終了します。
        return FALSE;
    }

    // シリアルポートの通信条件を取得します。
    DCB dcb;
    ZeroMemory(&dcb, sizeof(dcb));
    if (!GetCommState(m_hSerial, &dcb))
    {
        // シリアルポートの通信条件が取得できない場合、終了します。
        CloseHandle(m_hSerial);
        m_hSerial = NULL;
        return FALSE;
    }

    // シリアル通信の入出力バッファをクリアします。
    PurgeComm(m_hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

    // シリアルポートの通信条件を設定します。
    dcb.fBinary = TRUE;
    dcb.BaudRate = 9600;
    dcb.fParity = FALSE;
    dcb.Parity = NOPARITY;
    dcb.ByteSize = 8;
    dcb.StopBits = ONESTOPBIT;
    dcb.fOutxCtsFlow = TRUE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDsrSensitivity = FALSE;
    dcb.fTXContinueOnXoff = FALSE;
    dcb.fOutX = FALSE;
    dcb.fInX = FALSE;
    dcb.fDtrControl = DTR_CONTROL_ENABLE;
    dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
    dcb.fNull = TRUE;

    // シリアル通信の設定を行います。
    if (!SetCommState(m_hSerial, &dcb))
    {
        // シリアル通信の設定でエラーの場合、終了します。
        CloseHandle(m_hSerial);
        m_hSerial = NULL;
        return FALSE;
    }

    // シリアル通信のタイムアウトパラメータを取得します。
    COMMTIMEOUTS timeouts;
    ZeroMemory(&timeouts, sizeof(timeouts));
    if (!GetCommTimeouts(m_hSerial, &timeouts))
    {
        // シリアル通信のタイムアウトパラメータの取得でエラーの場合、終了します。
        CloseHandle(m_hSerial);
        m_hSerial = NULL;
        return FALSE;
    }

    // シリアル通信のタイムアウトパラメータを再設定します。
    timeouts.ReadIntervalTimeout = MAXDWORD;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 100;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 10;
    if (!SetCommTimeouts(m_hSerial, &timeouts))
    {
        // シリアル通信のタイムアウトパラメータの再設定でエラーの場合、終了します。
        CloseHandle(m_hSerial);
        m_hSerial = NULL;
        return FALSE;
    }

    // シリアル通信の拡張機能を設定します。
    EscapeCommFunction(m_hSerial, SETRTS);

    return TRUE;
}

/**
 * @brief シリアル通信の終了処理を行います。
 *
 * @return 処理結果 TRUE:成功/FALSE:失敗
 *         ※ここでは常に成功としています。
 */
BOOL CSampleSerialDlg::UninitSerial()
{
    // シリアルのハンドルをクローズします。
    CloseHandle(m_hSerial);
    m_hSerial = NULL;

    return TRUE;
}

/**
 * @brief データを送信します。
 *
 * @param [in] sendData 送信するデータ
 * @return 処理結果 TRUE:成功/FALSE:失敗
 */
BOOL CSampleSerialDlg::SendData(CString sendData)
{
    // 送信するデータをCString型からchar型に変換します。
    int writeLen = sendData.GetLength();
    char* buf = new char[writeLen + 1];
    strcpy_s(buf, writeLen + 1, (char*)sendData.GetBuffer());

    // データを全て送信するまで繰り返します。
    int totalWriteBytes = 0;
    do
    {
        int writeBytes = 0;
        BOOL bRet = WriteFile(m_hSerial, buf, writeLen - totalWriteBytes, (LPDWORD)&writeBytes, NULL); 
        
        // エラーまたはデータを送信できなかった場合
        if (!bRet || (writeBytes == 0))
        {
            // 終了します。
            break;
        }
        totalWriteBytes += writeBytes;
    }
    while (totalWriteBytes != writeLen);
    return TRUE;
}

/**
 * @brief 受信スレッドを開始します。
 *
 * @return 処理結果 TRUE:成功/FALSE:失敗
 */
BOOL CSampleSerialDlg::StartRecvThread()
{
    // 受信スレッドを生成します。
    m_pRecvThread = AfxBeginThread(CallRecvThread, (LPVOID)this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
    
    // 受信スレッドが生成できない場合
    if (!m_pRecvThread)
    {
        // エラーで終了します。
        return FALSE;
    }
    
    // 受信スレッドを開始します。
    m_pRecvThread->m_pMainWnd = this;
    m_pRecvThread->m_bAutoDelete = TRUE;
    m_pRecvThread->ResumeThread();
    return TRUE;
}

/**
 * @brief 受信スレッド本体を呼び出します。
 *
 * @param [in] pParam パラメータ
 * @return 処理結果 0:成功/0以外:失敗
 */
UINT CSampleSerialDlg::CallRecvThread(LPVOID pParam)
{
    // 受信処理はCSampleSerialDlgのメンバ関数で行います。
    CSampleSerialDlg* pDlg = dynamic_cast<CSampleSerialDlg*>(reinterpret_cast<CWnd*>(pParam));
    if (pDlg)
    {
        pDlg->RecvThread();
    }
    return 0;
}

/**
 * @brief 受信スレッド本体
 *        10ms間隔でポーリングします。
 */
void CSampleSerialDlg::RecvThread()
{
    // 受信スレッド停止フラグが未設定の間、繰り返します。
    do
    {
        // 10ms待ちます。
        Sleep(10);

        // 受信データを1バイト読み出します。
        char achar = 0;
        DWORD dwRead = 0;
        BOOL bRet = ReadFile(m_hSerial, (LPVOID)&achar, 1, &dwRead, NULL);

        // 受信データの読み出しでエラーの場合
        if (!bRet)
        {
            // エラーをクリアして、終了します。
            ULONG portError = 0;
            COMSTAT cs;
            ZeroMemory(&cs, sizeof(cs));
            ClearCommError(m_hSerial, &portError, &cs);
            break;
        }

        // 受信データがある場合
        if (dwRead > 0)
        {
            // 受信データをUIに反映します。
            char recvData[1];
            recvData[0] = achar;
            COPYDATASTRUCT data;
            memset(&data, 0, sizeof(data));
            data.dwData = 0;
            data.lpData = recvData;
            data.cbData = sizeof(recvData);
            this->SendMessage(WM_COPYDATA, 0, (LPARAM)&data);
        }
    }
    while (!m_bIsRecvThreadTerminated);

    // スレッド停止処理を呼び出します。
    this->PostMessage(WM_USER_READ_THREAD_TERMINATE);
}

目次へ

3. おわりに

別の人が作ったコードを元に、シリアル通信部分のみ抜き出しました。
個人的には受信処理はポーリングではなく、イベント待ちなどがいいのですが、イベント待ちのサンプルは今後試したいと思います。

知識・スキルの販売サイト【ココナラ】

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

目次へ

SwiftのUIImageがサポートする画像フォーマット

この記事は2018年12月10日に投稿しました。

目次

  1. はじめに
  2. SwiftのUIImageがサポートする画像フォーマット
  3. おわりに

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回はPWEditorで対応した画像表示機能で表示可能な画像フォーマットについてです。

目次へ

2. SwiftのUIImageがサポートする画像フォーマット

SwiftのUIImageがサポートする画像フォーマットは以下の表の通りです。

フォーマット 拡張子
Tagged Image File Format (TIFF) .tiff , .tif
Joint Photographic Experts Group (JPEG) .jpg , jpeg
Graphic Interchange Format (GIF) .gif
Portable Network Graphic (PNG) .png
Windows Bitmap Format (DIB) .bmp , BMPf
Windows Icon Format .ico
Windows Cursor .cur
XWindow bitmap .xbm

目次へ

3. おわりに

PWEditorはエディタですが、ユーザの要望もあり個人的にもどう実装できるのか興味があったので、画像表示機能に対応しました。
その際、どの画像フォーマットが対応しているのか知らなかったので、調査しました。
主要な画像フォーマットは対応していることがわかりました。

未経験から最短でエンジニア転職を目指す

目次へ

C/C++で文字列を分割する。

この記事は2018年12月09日に投稿しました。

目次

  1. はじめに
  2. C/C++で文字列を分割する
  3. おわりに

プログラミング言語C++ 第4版

プログラミング言語C++ 第4版

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているC/C++で文字列を分割する方法についてです。

目次へ

2. C/C++で文字列を分割する

C/C++で文字列を分割するには、strtok_s関数を使用します。

書式

char *strtok_s(
    char *strToken,
    const char *strDelimit,
    char **context
);

引数

strToken
分割対象の文字列
分割された部分はNULLに置き換えられます。
元の変数を変更したくない場合、別の変数にコピーしてコピーした変数を分割してください。

strDelimit
区切り文字列

context
strtok_sの呼び出しと呼び出しの間の位置情報の格納に使用

戻り値

strTokenで見つかった次のトークンへのポインターを返します。
これ以上トークンがない場合、NULLを返します。

実装例

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // 分割対象の文字列
    char* strToken = "abcde,fghij:12345";
    
    // 区切り文字列(複数指定可)
    // 今回","、":"で分割します。
    char* strDelimit = ",:";
    
    // strtok_sの内部で使用します。
    char* context;
    
    // 対象の文字列(strToken)を区切り文字列(strDelimit)で分割します。
    // 1つ目に分割された文字列(例ではabcde)が戻り値tokenに返されます。
    char* token = strtok_s(strToken, strDelimit, &context);
    
    // 分割する文字列がなくなるまで繰り返します。
    while (token)
    {
        print("%s\n", token);
        
        // 次に分割する場合、strtok_sの第1引数にNULLを指定します。
        token = strtok_s(NULL, strDelimit, &context);
    }
    
    return 0;
}

実行結果

abcde
fghij
12345

目次へ

3. おわりに

C標準で文字列を分割する関数はstrtokになります。
Visual Studioの場合、strtok関数はコンパイルエラーになるため、strtok_s関数を使用します。

ITエンジニアの無料カウンセリング【ポテパンフリーランス】

目次へ

MFCで多言語化対応

この記事は2018年12月08日に投稿しました。

目次

  1. はじめに
  2. MFCで多言語化
  3. おわりに

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているMFCでダイアログを中央に表示する方法についてです。

目次へ

2. MFCで多言語化

MFCで多言語化にするには、以下の手順で行います。

  1. Visual Studioで、リソースビューを開きます。
  2. リソースの追加で、String Tableを追加します。
  3. 追加したString Tableを選択し、プロパティのLanguageで言語を選択します。
  4. String Tableを開き、IDキャプションを入力します。
    キャプションが多言語化する文言で、IDでプログラムから指定することになります。
  5. プログラムからLoadString関数で文字列を取得します。

書式

int LoadString(
    HINSTANCE hInst,
    UINT uID,
    LPSTR lpBuffer,
    int cchBufferMax
);

引数

hInst
モジュールのインスタンスハンドルを指定します。
アプリケーションの場合、アプリケーションのインスタンスハンドルを指定します。
DLLの場合、DLLのモジュールハンドルを指定します。

uID
String Tableで設定したID

lpBuffer
取得した文字列を格納するバッファ

cchBufferMax
取得した文字列を格納するバッファのサイズ

戻り値

成功した場合、バッファにコピーした文字数(終端のNULL文字は含みません)
指定したIDが存在しない場合、0が返却されます。

実装例

例)ボタンに"はい"を表示します。

String Table
※値は自動で付番されたものを使用します。

ID キャプション
IDS_BN_OK 6000 はい
  • アプリケーションの場合
// 文字列を設定するボタン
CButton m_bnOK;

// インスタンスハンドルを取得します。
HINSTANCE hInst = AfxGetInstanceHandle();

// String Tableに設定された文字列を取得します。
TCHAR res[UCHAR_MAX];
memset(&res[0], 0, sizeof(res));
LoadString(hInst, IDS_BN_OK, (LPTSTR)res, UHCAR_MAX);

// ボタンに文字列を設定します。
m_bnOK.SetWindow(res);
  • DLLの場合
// DLLファイル名
#define DLL_NAME   "Sample.dll"

// 文字列を設定するボタン
CButton m_bnOK;

// DLLのモジュールハンドルを取得します。
HMODULE hModule = GetModuleHandle(DLL_NAME);

// String Tableに設定された文字列を取得します。
TCHAR res[UCHAR_MAX];
memset(&res[0], 0, sizeof(res));
LoadString(hModule, IDS_BN_OK, (LPTSTR)res, UHCAR_MAX);

// ボタンに文字列を設定します。
m_bnOK.SetWindow(res);

目次へ

3. おわりに

IDは一意に指定する必要があります。
またString Tableは画面ごとに作れないため、IDに画面名を付けて一意にしました。
そのためどうしてもIDが長くなりました。
それを嫌ってIDを連番にすることも考えましたが、IDを見ただけではどこで使用する文字列かわからなくなるため、この方法をやめました。

実装してみましたが、やっぱりめんどくさいです。

企業向けプログラミング研修【CodeCamp】

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

目次へ

VB6でイベントを使用する。

この記事は2018年12月07日に投稿しました。

目次

  1. はじめに
  2. VB6でイベントを使用する
  3. おわりに

かんたんプログラミングVisual Basic6 基礎編

かんたんプログラミングVisual Basic6 基礎編

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているVB6でイベントを使用する方法についてです。

目次へ

2. VB6でイベントを使用する

VB6でイベントを使用する方法ですが、VB6でイベントは用意されていません。
そのためWin32APIのイベント関連の関数を使用します。

実装例

' APIの宣言
' イベントを生成します。
Private Declare Function CreateEvent _
           Lib "kernel32" _
           Alias "CreateEventA" ( _
           ByVal lpEventAttributes As Long, _
           ByVal bManualReset As Long, _
           ByVal bInitialState As Long, _
           ByVal lpName As Long _
           ) As Long

' イベントをシグナル状態にします。
Private Declare Function SetEvent _
           Lib "kernel32" _
           (ByVal hEvent As Long) As Long

' イベントをリセットします。
Private Declare Function ResetEvent_
           Lib "kernel32" _
           (ByVal hEvent As Long) As Long

' イベントハンドラをクローズします。
Private Declare Function CloseHandle _
           Lib "kernel32" _
           (ByVal hObject As Long) As Long

' メッセージ待ちまたはイベント待ちを行います。
Private Declare Function MsgWaitForMultipleObjects _
           Lib "user32" ( _
           ByVal nCount As Long, _          ' オブジェクトハンドルの配列に入っているハンドルの数
           pHandles As Long, _              ' オブジェクトハンドルの配列へのポインタ
           ByVal fWaitAll As Long, _        ' すべてを待機するか、いずれか1つを待機するか
           ByVal dwMilliseconds As Long, _  ' タイムアウト時間(ms)
           ByVal dwWakeMask As Long _       ' 待機するイベントの種類
           ) As Long

' メッセージ待ちするメッセージ
Private Const QS_KEY            As Long = &H1
Private Const QS_MOUSEMOVE      As Long = &H2
Private Const QS_MOUSEBUTTON    As Long = &H4
Private Const QS_POSTMESSAGE    As Long = &H8
Private Const QS_TIMER          As Long = &H10
Private Const QS_PAINT          As Long = &H20
Private Const QS_SENDMESSAGE    As Long = &H40
Private Const QS_HOTKEY         As Long = &H80
Private Const QS_MOUSE          As Long = QS_MOUSEMOVE Or QS_MOUSEBUTTON
Private Const QS_INPUT          As Long = QS_MOUSE Or QS_KEY
Private Const QS_ALLINPUT       As Long = QS_SENDMESSAGE Or QS_PAINT _
                                       Or QS_TIMER Or QS_POSTMESSAGE _
                                       Or QS_MOUSEBUTTON Or QS_MOUSEMOVE _
                                       Or QS_HOTKEY Or QS_KEY

' MsgWaitForMultipleObjectsの戻り値
Private Const WAIT_OBJECT_0     As Long = 0           ' イベントがシグナル状態になった。
Private Const WAIT_FAILED       As Long = &HFFFFFFFF  ' 関数がエラーになった。

' 無限待ち
Private Const INFINITE          As Long = &HFFFFFFFF

' イベントハンドル
Dim mhEvent As Long

' フォームがロードされた時に呼び出されます。
Private Sub Form_Load()
   ' イベントを生成します。
   mhEvent = CreateEvent(0, 0, 0, 0)
End Sub

' フォームがアンロードされた時に呼び出されます。
Private Sub Form_Unload(Cancel As Integer)
   ' イベントハンドルをクローズします。
   CloseHandle(mhEvent)
End Sub

' イベント待ちにします。
Private Sub Command1_Click()
   Dim nRet As Long

   ' イベントをリセットします。
   ResetEvent mhEvent

   ' メッセージかイベントがシグナル状態になるまでループします。
   Do
       ' メッセージとイベントを無限待ちします。
       ' メッセージは上記で定義したすべてのメッセージ(QS_XXX)を待ちます。
       nRet = MsgWaitForMultipleObjects( _
                   1, _
                   mhEvent, _
                   0, _
                   INFINITE, _
                   QS_ALLINPUT)

       ' メッセージまたはイベントが発生した場合の処理を行います。
       Select Case nRet
           ' 関数が失敗した場合(フォームが閉じられた場合など)
           Case WAIT_FAILED
               MsgBox "WAIT_FAILED"
               Exit Sub

           ' 対象のイベントがシグナル状態になった場合
           Case WAIT_OBJECT_0
               ' ループを終了して、イベント待ちを解除します。
               Exit Do

           ' 上記以外のメッセージの場合
           Case Else
               ' 一旦OSに制御を戻します。
               DoEvents
       End Select
   Loop

   ' イベント待ち後の処理を行います。

End Sub

' イベントをシグナル状態にします。
Private Sub Command2_Click()
   SetEvent mhEvent
End Sub

目次へ

3. おわりに

業務で起動画面をモードレスで起動し、かつ画面が閉じた後に後処理を行う必要があり、イベント待ちすることにしました。
WaitForSingleObject関数は知っていましたが、MsgWaitForMultipleObjects関数は初めて知りました。
まさに今回の要件にぴったりの機能でした。

プログラミングスクール【Tech Boost】

かんたんプログラミング Visual Basic 6 応用編

かんたんプログラミング Visual Basic 6 応用編

目次へ

MFCでダイアログを中央に表示する。

この記事は2018年12月06日に投稿しました。

目次

  1. はじめに
  2. MFCでダイアログを中央に表示する
  3. おわりに

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

かんたん Visual C++ [改訂2版] (プログラミングの教科書)

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているMFCでダイアログを中央に表示する方法についてです。

目次へ

2. MFCでダイアログを中央に表示する

MFCでダイアログを中央に表示するには、次の関数を使用します。

  • 親ウィンドウに対して中央に表示する。 CenterWindow()

  • デスクトップに対して中央に表示する。 CenterWindow(GetDesktopWindow())

目次へ

3. おわりに

起動のダイアログが画面の中央に表示されないため調べてみました。
注意点として起動のダイアログの場合、DoModal関数を呼び出す前にCenterWindow関数を呼び出すとクラッシュします。
そのため起動のダイアログのOnShowWindowイベントハンドラあたりで呼び出すといいようです。

とりあえず稼ぎたいなら、ITエンジニア【IT派遣テクノウェイブ】

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

標準講座MFC6.0―Visual C++による効率的なWindowsプログラミング (Programmer’s SELECTION)

目次へ

Swiftで値の中の最小値・最大値を取得する。

この記事は2018年12月05日に投稿しました。

目次

  1. はじめに
  2. Swiftで値の中の最小値・最大値を取得する
  3. おわりに

1. はじめに

こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回はPWEditorで使用しているSwiftで、値の中の最小値・最大値を取得する方法についてです。

目次へ

2. Swiftで値の中の最小値・最大値を取得する

Swiftで値の中の最小値・最大値を取得するには、minmaxを使用します。

書式

値の中の最小値を取得します。

// 引数2つ
min<T>(x: T, y: T) -> T

// 引数3つ以上
min<T>(x: T, y: T, z: T, rest: T...) -> T

値の中の最大値を取得します。

// 引数2つ
max<T>(x: T, y: T) -> T

// 引数3つ以上
max<T>(x: T, y: T, z: T, rest: T...) -> T

実装例

  1. x, yの小さいほうを取得します。
let x = 5
let y = 10
let value = min(x, y) // value = 5になります。
  1. x, yの小さいほう、またはデフォルト値10を取得します。
let x = 15
let y = 20
let value = min(x, y, 10) // value = 10(デフォルト値)になります。
  1. x, yの大きいほうを取得します。
let x = 5
let y = 10
let value = min(x, y) // value = 10になります。
  1. x, yの大きいほう、またはデフォルト値20を取得します。
let x = 5
let y = 10
let value = min(x, y, 20) // value = 20(デフォルト値)になります。

目次へ

3. おわりに

何か別のことを調べているときにminmaxを見つけました。
じみに便利です。
特に3つ以上比較できるのが便利です。

エンジニアのためのQ&Aサイト【teratail】

目次へ