COMポートの一覧を表示・プロパティの変更及びポートの選択をする(ダイアログボックス版)

icon 項目のみ表示/展開表示の切り替え

概要

ダイアログボックスにCOMポートの一覧を表示します。選択されているCOMポートのプロパティの編集ができます。
ダイアログボックスを終了すると選択されたCOMポート名をメッセージボックスで表示します。 コンパイルにはWDK7が必要です。

実行例


コンパイル方法

Visual C++ 2008 Express Edition又はStandard Editon/Visual C++ 2013 Expressで動作確認をしている。動作OSはWindows 32bit/64bit
コンパイルするには、WDK 7(http://msdn.microsoft.com/library/windows/hardware/gg487428)のインストールが必要である。
WDK 7のインクルートディレクトリ・ライブラリディレクトリをVisual C++のプロジェクトに指定しなければならない。なお、インクルードファイルは、単純にWDK 7のインクルードパスのみ指定するとWDK 7のヘッダファイルがVisual C++標準のヘッダファイルより古いため同一ファイル名のヘッダファイルがある場合WDK 7の方が参照され意味不明のエラーが大量に発生する。例えば下記の様に指定するとVisual C++のヘッダファイルが優先されコンパイル可能となる。
$(VCInstallDir)include;C:\WinDDK\7600.16385.1\inc\api

プログラムソースの概要

gcommlist.cpp

_tWinMain

DialogBoxParam APIでダイアログボックスを開きます。COMM_DCB構造体のアドレスを引数とします。
COMM_DCB構造体は、ダイアログボックスが閉じるときに選択されたCOMポート名とCOMポートのプロパティ(ボーレート・パリティ等)が返される。 ダイアログボックスのプロシージャからの戻り値が0の時はCOMポートが存在又はオープンできないときです。
0以外が返されたとき、COMM_DCB構造体のCOMポート名をメッセージボックスで表示します。

CommSelDlgProc

ダイアログボックスのプロシージャです。
WM_INITDIALOG
ダイアログボックスの初期化時に呼び出されます。
DialogBoxParam APIから渡さたCOMM_DCB構造体のアドレスがlParamに渡されますので、スタティック変数comm_dcbpにアドレスを保存します。
COMM_DCB構造体のメンバを初期化します。
GetDlgItem APIでCOMポートの一覧を表示するコンボボックスのウィンドウハンドルを取得する。
EnumCommPort関数でCOMポート一覧を取得しコンボボックスにCOMポートを登録する。
EnumCommPort関数の戻り値が0の場合はCOMポートが存在しないのでEndDialog APIでダイアログボックスを終了させます。
WM_COMMAND
各ボタンがクリックされた場合およびコンボボックスでアイテムが選択されたときやに呼び出されます。
wParamの下位ワード(16bit)にコントロールのID、wParamの上位ワードにメッセージが格納されています。
コントロールのID別に処理をswitchステートメントで記述しています。
IDC_COMM_PORT_COMBOBOX
コンボボックスの選択されているアイテムに変化があった場合CBN_SELCHANGEメッセージが送られるので、 コンボボックスの選択されているアイテムの文字列を取得しcomm_dcb_set関数でCOMポートを開きCOMポートのプロパティを取得します。
IDC_COMM_PROPERTY
プロパティボタンをクリックした時、呼び出されます。
COMポートが開かれていないときは、コンポボックスの選択アイテムの文字列を取得しcomm_dcb_set関数でCOMポートを開きCOMポートのプロパティを取得します。
CommConfigDialog APIでCOMポートの設定ダイアログボックスを表示します。
設定ダイアログが正常に終了したら、SetCommState APIで先ほどで設定した値をCOMポートに設定します。
IDOK
COMポートが開かれているときはCOMポートを閉じてEndDialog APIでダイアログボックスを終了させます。
IDCANCEL
COMポートが開かれているときはCOMポートを閉じてEndDialog APIでダイアログボックスを終了させます。

EnumCommPort

COMポートをSETUP系のAPIで順番に取得し見つかるたびに引数で指定されたコールバック関数を呼び出します。
SetupDiGetClassDevs APIでデバイス情報セットへのハンドルを取得します。
以下の手順を全てのCOMポートの列挙が終了するまでCOMポート毎に実行します。
  • SetupDiEnumDeviceInfo APIでデバイスインターフェースを列挙して行きます。
  • SetupDiOpenDevRegKey APIでCOMポート番号を取得します。
  • SetupDiGetDeviceRegistryProperty APIでデバイスの情報を取得します。
  • 取得したCOMポート番号とデバイス情報を引数としてコールバック関数を呼び出します。
  • 全てのCOMポートの列挙が終了したらSetupDiDestroyDeviceInfoList APIでデバイス情報セットを解放します。

    EnumCommPortFunc

    EnumCommPort関数からCOMポート毎に呼び出されます。引数にはCOMポート名と説明文字列とEnumCommPort関数に渡した第2引数がそのまま渡されます。
    ここでは引数の文字列をコンボボックスに登録します。

    comm_dcb_set

    指定COMポート(例 "COM1")を開いてDCBをセット COMポートのハンドルを返します。
    DCBはGetCommState APIで取得します。

ソースコード

gcommlist.cpp

// COMポートの一覧を表示・プロパティの変更及びポートの選択をする(ダイアログボックス版)
// Visual C++ 2008 UNICODE/マルチバイト  32bit/64bit
//
//        コンパイルするにはWDK7がインストールされインクルードファイル及び
//  ライブラリファイルのパスが設定されている必要性があり
//        インクルードファイルのパス設定は、WDK7のみ設定するとインクルード
//  ファイルの呼び込み優先順位がWDK7→Visual C++標準パスとなりWDK7の
//  ヘッダーファイルから古いヘッダファイルがインクルードされエラーが
//  多数発生するため、優先順位をVisual C++標準パス→WDK7となるようにする
//  インクルードパスの設定例
//    追加のインクルードディレクトリ $(VCInstallDir)include;C:¥WinDDK¥7600.16385.1¥inc¥api
//        ライブラリディレクトリの設定も必要
//              Win32 C:¥WinDDK¥7600.16385.1¥lib¥wxp¥i386
//              Win64 C:¥WinDDK¥7600.16385.1¥lib¥wlh¥amd64
//

#include <windows.h>
#include <setupapi.h>
#include <commctrl.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>

extern "C" {
        #include "hidsdi.h"
}

#pragma comment(lib,"hid.lib")
#pragma comment(lib,"setupapi.lib")

#include "resource.h"

LRESULT CALLBACK CommSelDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

//    COMポート毎に呼び出されるコールバック関数の型定義
typedef void EnumCommPortFuncDef(TCHAR* port,TCHAR* other,void* temp);

//      COMポートを列挙する
//              func:COMポート毎に呼び出されるコールバック関数,temp:コールバック関数に渡される引数 
int EnumCommPort(EnumCommPortFuncDef* func,void* temp);

//      COMポート毎に呼び出されるコールバック関数
//              port:ポート名COMx,opt:ポートの説明,temp:EnumCommPort関数の第1引数
void EnumCommPortFunc(TCHAR* port,TCHAR* opt,void* temp);

struct COMM_DCB{
        TCHAR   devName[8];
        COMMCONFIG config;
};

//      指定COMポート(例 "COM1")を開いてDCBをセット
HANDLE comm_dcb_set(TCHAR* shortname,COMM_DCB* comm_dcbp,HWND hWnd);

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,
                   TCHAR* lpszCmdLine, int nCmdShow){
        COMM_DCB comm_dcb;
        if(DialogBoxParam(hInstance, _TEXT("COMM_DLG"), 0, (DLGPROC)CommSelDlgProc,(LPARAM)&comm_dcb)){
                MessageBox(0 , comm_dcb.devName , _TEXT("GCOMLIST:選択されたCOMポート") , MB_OK);
        }
        return 0;
}

LRESULT CALLBACK CommSelDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
        HWND hWnd;
        static COMM_DCB* comm_dcbp;
        static HANDLE hcom=0;

        switch (msg) {
                case WM_INITDIALOG:{
                        comm_dcbp=(COMM_DCB*)lParam;
                        comm_dcbp->config.dwSize=sizeof(COMMCONFIG);
                        comm_dcbp->config.dcb.DCBlength=sizeof(DCB);

                        hWnd=GetDlgItem(hDlg,IDC_COMM_PORT_COMBOBOX);
                        if(EnumCommPort(EnumCommPortFunc,(void*)&hWnd)==0){
                                MessageBox(hDlg,_TEXT("エラー"),_TEXT("COMポートが見つかりませんでした。"),MB_OK);
                                EndDialog(hDlg, FALSE);
                                return TRUE;
                        }
                        SendMessage(hWnd,CB_SETCURSEL,0,(LPARAM)0);     //      初期時に選択されるアイテムを選択
                        return TRUE;
                }
                case WM_COMMAND:
                        switch (LOWORD(wParam)) {
                                case IDC_COMM_PORT_COMBOBOX:
                                        if(HIWORD(wParam) == CBN_SELCHANGE){    //      コンボボックスのアイテムが選択されたときに呼び出される
                                                hWnd=GetDlgItem(hDlg,IDC_COMM_PORT_COMBOBOX);
                                                TCHAR shortname[64];
                                                GetWindowText(hWnd,shortname,sizeof(shortname)/sizeof(shortname[0]));
                                                TCHAR* p=shortname;
                                                while(*p){
                                                        if(*p==_T('(')){
                                                                *p=_T('¥0');
                                                        }
                                                        ++p;
                                                }
                                                if(hcom)
                                                        CloseHandle(hcom);
                                                hcom=comm_dcb_set(shortname,comm_dcbp,hDlg);
                                        }
                                        break;
                                case IDC_COMM_PROPERTY:{        //      プロパティボタンをクリックした時
                                        if(hcom==0){
                                                hWnd=GetDlgItem(hDlg,IDC_COMM_PORT_COMBOBOX);
                                                TCHAR shortname[64];
                                                GetWindowText(hWnd,shortname,sizeof(shortname)/sizeof(shortname[0]));
                                                TCHAR* p=shortname;
                                                while(*p){
                                                        if(*p==_T('(')){
                                                                *p=_T('¥0');
                                                        }
                                                        ++p;
                                                }
                                                hcom=comm_dcb_set(shortname,comm_dcbp,hDlg);
                                        }
                                        if(CommConfigDialog(comm_dcbp->devName,hDlg,&(comm_dcbp->config) )){
                                                SetCommState(hcom,&(comm_dcbp->config.dcb));
                                        }
                                        return TRUE;
                                }
                                case IDOK:
                                        if(hcom){
                                                CloseHandle(hcom);
                                                hcom=0;
                                        }
                                        EndDialog(hDlg, TRUE);
                                        return TRUE;
                                case IDCANCEL:
                                        if(hcom){
                                                CloseHandle(hcom);
                                                hcom=0;
                                        }
                                        EndDialog(hDlg,FALSE);
                                        return FALSE;
                                default:
                                        return FALSE;
                        }
                        default:
                                return FALSE;
        }
        return TRUE;
}

//      COMポートを列挙する
//    func:COMポート毎に呼び出されるコールバック関数,temp:コールバック関数に渡される引数
//        COMポート数を返す
int EnumCommPort(EnumCommPortFuncDef* func,void* temp){
        HDEVINFO hDevInfo;
        DWORD MemberIndex=0;
        SP_DEVINFO_DATA Data={ sizeof(SP_DEVINFO_DATA) };
        int max=0;
        hDevInfo=SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT,0,0,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // デバイス情報セットを取得
        if(hDevInfo==0) //      デバイス情報セットが取得できなかった場合
                return 0;
        Data.cbSize=sizeof(Data);

        while( SetupDiEnumDeviceInfo(hDevInfo,max,&Data)){  // デバイスインターフェイスの取得
                TCHAR* port=0;
                TCHAR* other=0;

                DWORD dataT;
                DWORD size;
                LPTSTR buf;
                TCHAR name[256];
//      COMポート名の取得
                HKEY key=SetupDiOpenDevRegKey(hDevInfo,&Data,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_QUERY_VALUE);
                if(key){
                        DWORD type=0;
                        size=sizeof(name);
                        RegQueryValueEx(key,_T("PortName"),NULL,&type,(LPBYTE)name,&size);
                        port=name;
                }
//      デバイスの説明を取得
                size=0;
                buf=NULL;
                while(!SetupDiGetDeviceRegistryProperty(hDevInfo,&Data,SPDRP_DEVICEDESC,&dataT,(PBYTE)buf,size,&size)){
                        if(GetLastError()==ERROR_INSUFFICIENT_BUFFER){
                                if(buf)
                                        LocalFree(buf);
                                buf=(LPTSTR)LocalAlloc(LPTR,size*2);
                        }else
                                break;
                }
                func(port,buf,temp);    //      コールバック関数の呼び出し
                if(buf)
                        LocalFree(buf);
                ++max;
        }
        SetupDiDestroyDeviceInfoList(hDevInfo); //      デバイス情報セットを解放
        return max;
}

//      COMポート毎に呼び出されるコールバック関数
//              port:ポート名COMx,opt:ポートの説明,temp:EnumCommPort関数の第2引数
void EnumCommPortFunc(TCHAR* port,TCHAR* opt,void* temp){
        HWND hWnd=*(HWND*)temp;
        TCHAR buf[256];
        _stprintf_s(buf,sizeof(buf)/sizeof(buf[0]),_TEXT("%s(%s)"),port,opt);
        SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)buf);
}

//      指定COMポート(例 "COM1")を開いてDCBをセット COMポートのハンドルを返す
HANDLE comm_dcb_set(TCHAR* shortname,COMM_DCB* comm_dcbp,HWND hWnd){
        _tcscpy_s(comm_dcbp->devName,16,shortname);
        TCHAR comname[16];
        _stprintf_s(comname,sizeof(comname)/sizeof(comname[0]),_TEXT("¥¥¥¥.¥¥%s"),shortname);

        HANDLE hcom=CreateFile(comname , GENERIC_READ | GENERIC_WRITE ,0, NULL , OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
        if(hcom==INVALID_HANDLE_VALUE){
                MessageBox(hWnd,comname,_TEXT("Open Error"),MB_OK);
                return 0;
        }
        GetCommState(hcom,&(comm_dcbp->config.dcb) );
        return hcom;
}

resource.h

#define  IDC_COMM_PORT_COMBOBOX  1000
#define IDC_COMM_PROPERTY 1010

resource.rc

#include <windows.h>
#include "resource.h"


COMM_DLG DIALOG DISCARDABLE 0, 0, 247, 63
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT
CAPTION "RS232C通信設定"
FONT 9, "MS Shell Dlg"
{
 CONTROL "ポート", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 14, 64, 14
 CONTROL "COMBOBOX1", IDC_COMM_PORT_COMBOBOX, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_STANDARD, 48, 12, 192, 140

 CONTROL "OK(&O)", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 21, 42, 64, 14
 CONTROL "キャンセル(&C)", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 92, 42, 64, 14
 CONTROL "プロパティ(&P)", IDC_COMM_PROPERTY, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 163, 42, 64, 14
}

ソースファイルと実行ファイルのダウンロード

ダウンロード gcomlist.zip(32.3kByte)

ZIPファイルに含まれるファイル
gcomlist.cpp
resource.h
resource.rc
gcomlist.exe