EternalWindows
スマートカード / スマートカードダイアログ

カードリーダにカードがセットされているかどうかは、カードを利用するアプリケーションにとって重要な事柄です。 前々節では、SCardGetStatusChangeを呼び出してカードがセットされているかどうかを確認し、 セットされていない場合はユーザーにその旨を通知しましたが、 標準化されたダイアログでカードのセットを促したい場合もあると思われます。 このような場合は、SCardUIDlgSelectCardを呼び出します。

LONG SCardUIDlgSelectCard(
  LPOPENCARDNAME_EX pDlgStruc
);

pDlgStrucは、ダイアログの情報を格納するOPENCARDNAME_EX構造体のアドレスを指定します。 OPENCARDNAME_EX構造体は、次のように定義されています。

typedef struct {
  DWORD dwStructSize;
  SCARDCONTEXT hSCardContext;
  HWND hwndOwner;
  DWORD dwFlags;
  LPCTSTR lpstrTitle;
  LPCTSTR lpstrSearchDesc;
  HICON hIcon;
  POPENCARD_SEARCH_CRITERIA pOpenCardSearchCriteria;
  LPOCNCONNPROC lpfnConnect;
  LPVOID pvUserData;
  DWORD dwShareMode;
  DWORD dwPreferredProtocols;
  LPTSTR lpstrRdr;
  DWORD nMaxRdr;
  LPTSTR lpstrCard;
  DWORD nMaxCard;
  DWORD dwActiveProtocol;
  SCARDHANDLE hCardHandle;
} OPENCARDNAME_EX,  *POPENCARDNAME_EX,  *LPOPENCARDNAME_EX;

dwStructSizeは、構造体のサイズをバイト単位で指定します。 hSCardContextは、リソースマネージャのハンドルを指定します。 hwndOwnerは、ダイアログの親ウインドウとするウインドウハンドルを指定します。 不要な場合は、NULLを指定することができます。 dwFlagsは、ダイアログの表示に関する定数を指定します。 lpstrTitleは、ダイアログのタイトルにしたい文字列を指定します。 NULLを指定した場合は、デフォルトの文字列が表示されます。 lpstrSearchDescは、カードがセットされていない場合に表示する文字列を指定します。 NULLを指定した場合は、デフォルトの文字列が表示されます。 hIconは、ダイアログに表示するアイコンのハンドルを指定します。 NULLを指定した場合は、リーダに埋め込まれたアイコンが表示されます。 pOpenCardSearchCriteriaは、基本的にNULLを指定します。 lpfnConnectは、カードに接続する際に呼ばれる関数のアドレスを指定します。 アプリケーション自らSCardConnectを呼び出すつもりがない場合は、NULLを指定します。 pvUserDataは、lpfnConnectに指定した関数の引数とするユーザーデータを指定します。 lpfnConnectがNULLの場合やユーザーデータが不要な場合は、NULLを指定します。 dwShareModeは、共有モードを指定します。 dwPreferredProtocolsは、カードのプロトコルを指定します。 lpstrRdrは、リーダ名を受け取るバッファを指定します。 nMaxRdrは、lpstrRdrに指定したバッファのサイズを指定します。 lpstrCardは、カード名を受け取るバッファを指定します。 nMaxCardは、lpstrCardに指定したバッファのサイズを指定します。 dwActiveProtocolは、0で問題ないと思われます。 hCardHandleは、カードと接続されたハンドルを受け取る変数のアドレスを指定します。

dwFlagsに指定できる定数を次に示します。

定数 説明
SC_DLG_FORCE_UI リーダにカードがセットされているかどうかに関わらず、ダイアログを表示する。
SC_DLG_MINIMAL_UI リーダにカードがセットされていない場合は、ダイアログを表示する。
SC_DLG_NO_UI ダイアログは表示しない。リーダにカードがセットされていない場合は、必ず失敗する。

OPENCARDNAME_EX構造体のlpfnConnect及び、dwShareModeとdwPreferredProtocolsについて補足します。 lpfnConnectにNULLを指定した場合は、SCardUIDlgSelectCardが内部でカードへの接続を行うことになりますが、 dwShareModeに0を指定した場合はこれが行われません。 接続に関するパラメータが足りないため、hCardHandleにはNULLが格納されることになります。 一方、dwShareModeが0以外の場合は、その値とdwPreferredProtocolsの値を考慮して、 内部でSCardConnectが呼ばれることになります。 lpfnConnectにアプリケーションが用意した関数のアドレスを指定する場合は、 アプリケーション自らSCardConnectを呼び出すということですから、 dwShareModeとdwPreferredProtocolsは共に0を指定します。

今回のプログラムは、SCardUIDlgSelectCardを呼び出してカードの接続を行います。 SCardUIDlgSelectCardを呼び出すには、scarddlg.libへのリンクが必要になります。

#include <windows.h>
#include <winscard.h>

#pragma comment (lib, "winscard.lib")
#pragma comment (lib, "scarddlg.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	SCARDCONTEXT    hContext;
	LONG            lResult;
	OPENCARDNAME_EX openCard;
	TCHAR           szReaderName[256];
	TCHAR           szCardName[256]; 

	lResult = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
	if (lResult != SCARD_S_SUCCESS) {
		if (lResult == SCARD_E_NO_SERVICE)
			MessageBox(NULL, TEXT("Smart Cardサービスが起動されていません。"), NULL, MB_ICONWARNING);
		return 0;
	}

	openCard.dwStructSize            = sizeof(OPENCARDNAME_EX);
	openCard.hSCardContext           = hContext;
	openCard.hwndOwner               = NULL;
	openCard.dwFlags                 = SC_DLG_FORCE_UI;
	openCard.lpstrTitle              = NULL;
	openCard.lpstrSearchDesc         = NULL;
	openCard.hIcon                   = NULL;
	openCard.pOpenCardSearchCriteria = NULL;
	openCard.lpfnConnect             = NULL;
	openCard.pvUserData              = NULL;
	openCard.dwShareMode             = SCARD_SHARE_SHARED;
	openCard.dwPreferredProtocols    = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
	openCard.lpstrRdr                = szReaderName;
	openCard.nMaxRdr                 = sizeof(szReaderName);
	openCard.lpstrCard               = szCardName;
	openCard.nMaxCard                = sizeof(szCardName);
	openCard.dwActiveProtocol        = 0;
	openCard.hCardHandle             = NULL;

	lResult = SCardUIDlgSelectCard(&openCard);
	if (lResult != SCARD_S_SUCCESS) {
		if (lResult == SCARD_E_NO_READERS_AVAILABLE)
			MessageBox(NULL, TEXT("カードリーダが接続されていません。"), NULL, MB_ICONWARNING);
		SCardReleaseContext(hContext);
		return 0;
	}

	if (openCard.hCardHandle != NULL)
		MessageBox(NULL, TEXT("カードに接続しました。"), TEXT("OK"), MB_OK);
	
	MessageBox(NULL, szCardName, TEXT("OK"), MB_OK);
	
	SCardReleaseContext(hContext);

	return 0;
}

OPENCARDNAME_EX構造体のdwFlagsにSC_DLG_FORCE_UIを指定しているため、 リーダにカードがセットされているかを問わずダイアログが表示されることになります。 また、lpfnConnectにNULLを指定して、dwShareModeに0以外の値を指定しているため、 カードへの接続も行われます。 逆に0を指定した場合、SCardUIDlgSelectCardは、 リーダ名とカード名を返すだけの関数となります。

接続ルーチンの実装

SCardUIDlgSelectCardは、ダイアログの表示と共にカードの接続もサポートしていますが、 このカードの接続に関してはアプリケーションが自ら行うこともできます。 OPENCARDNAME_EX構造体のlpfnConnectに次のようなプロトタイプを持つ関数を実装した場合、 カードの接続段階で呼び出されることになります。

SCARDHANDLE WINAPI ConnectRoutine(SCARDCONTEXT hSCardContext, LPTSTR szReader, LPTSTR mszCards, LPVOID pvUserData)
{
	SCARDHANDLE       hCard;
	DWORD             dwActiveProtocol;
	SCARD_READERSTATE readerState;
	
	readerState.szReader = szReader;

	SCardLocateCards(hSCardContext, mszCards, &readerState, 1);
	if (!(readerState.dwEventState & SCARD_STATE_ATRMATCH))
		MessageBox(NULL, TEXT("認識できないカードです。"), NULL, MB_ICONWARNING);

	SCardConnect(hSCardContext, szReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
	
	return hCard;
}

この関数で最低限行わなければならないのはカードへの接続ですが、 それだけでは独自の接続ルーチンを実装する意味がないため、 何らかの特別な処理を行いたいものです。 上記の例では、SCardLocateCardsを呼び出して、 スマートカードデータベースに書き込まれていないカードを認識できないことにしています。 OPENCARDNAME_EX構造体のlpfnConnectを初期化する場合は、 同構造体のdwShareModeとdwPreferredProtocolsに0を指定して問題ありません。



戻る