#define STRICT
#include <windows.h>

#include <setupapi.h>
#pragma comment(lib, "setupapi.lib")

HANDLE
OpenDeviceInterface(const GUID* InterfaceClassGuid, int MemberIndex)
{
	HANDLE DeviceInterfaceHandle = INVALID_HANDLE_VALUE;
	HDEVINFO DeviceInfoSet = INVALID_HANDLE_VALUE;
	SP_DEVICE_INTERFACE_DETAIL_DATA* DeviceInterfaceDetailData = NULL;
	//
	SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
	DWORD RequiredSize;

	/* SetupDiGetClassDevsへのWin32呼び出しによって、指定したGUIDを持つデバイスに関する「デバイス情報セット」が開きます。
	 * DIGCF_PRESENTフラグとDIGCF_INTERFACEDEVICEフラグによって、存在するデバイスだけが見つかるようにしています。
	 */
	DeviceInfoSet = SetupDiGetClassDevs(
		(GUID*)InterfaceClassGuid,
		NULL,
		NULL,
		DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
	if(DeviceInfoSet == INVALID_HANDLE_VALUE) {
		goto L_EXIT;
	}

	/* 次に、目的のデバイスインスタンスに関する情報のSP_DEVICE_INTERFACE_DATAコンテキスト構造体を取得するために、
	 * SetupDiEnumDeviceInterfacesを呼び出しています。
	 * OpenDeviceInterfaceは1つのインスタンスに関する情報だけを取得しようとしますが、
	 * MemberIndexパラメータを0からOpenDeviceInterfaceが失敗するまでとGetLastErrorがERROR_NO_MORE_ITEMSを返すまでに増やすことによって、
	 * すべてのインスタンスを見つけることもできます。
	 */
	DeviceInterfaceData.cbSize = sizeof DeviceInterfaceData;
	if(!SetupDiEnumDeviceInterfaces(
		DeviceInfoSet,
		NULL,
		(GUID*)InterfaceClassGuid,
		MemberIndex,
		&DeviceInterfaceData)) {
		goto L_EXIT;
	}

	/* 次の処理は、見つかったインスタンスのシンボリックリンク名を取得することです。
	 * この文字列は、SetupDiGetDeviceInterfaceDetailへの呼び出しによって返されるSP_DEVICE_INTERFACE_DETAIL_DATA構造体の中にあります。
	 * SetupDiGetDeviceInterfaceDetailは2回呼び出す必要があります。
	 * 最初の呼び出しでSP_DEVICE_INTERFACE_DETAIL_DATA構造体に必要なサイズを取得し、2回目の呼び出しで実際に構造体を取得します。
	 */
	SetupDiGetDeviceInterfaceDetail(
		DeviceInfoSet,
		&DeviceInterfaceData,
		NULL,
		0,
		&RequiredSize,
		NULL);
	if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
		goto L_EXIT;
	}
	DeviceInterfaceDetailData = malloc(RequiredSize);
	if(!DeviceInterfaceDetailData) {
		goto L_EXIT;
	}
	DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
	if(!SetupDiGetDeviceInterfaceDetail(
		DeviceInfoSet,
		&DeviceInterfaceData,
		DeviceInterfaceDetailData,
		RequiredSize,
		NULL,
		NULL)) {
		goto L_EXIT;
	}

	/* 最終的に、適切なデバイスへのハンドルを開くためのファイル名がSP_DEVICE_INTERFACE_DETAIL_DATA.DevicePathに入ります。
	 * このファイル名がCreateFileに渡されて、SP_DEVICE_INTERFACE_DETAIL_DATA構造体は削除されます。
	 * すべてのパスに対するSetupDiDestroyDeviceInfoListを呼び出して、デバイス情報セットを忘れずに閉じてください。
	 */
	DeviceInterfaceHandle = CreateFile(
		DeviceInterfaceDetailData->DevicePath,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

L_EXIT:
	if(DeviceInterfaceDetailData) {
		free(DeviceInterfaceDetailData);
	}
	if(DeviceInfoSet != INVALID_HANDLE_VALUE) {
		SetupDiDestroyDeviceInfoList(DeviceInfoSet);
	}
	return DeviceInterfaceHandle;
}
