ASIOデバイスの列挙
ASIO SDKでは、コンピュータに実装されているデバイスの管理を"AsioDriverList"クラスが担当しています。
ドライバーに関する情報は、レジストリに保存されているため、RegOpenKeyやRegEnumKeyを用いて読み取りを行います。
AsioDriverListのコンストラクタでドライバーの列挙を行っています。レジストリのHKEY_LOCAL_MACHINEの下にあるASIO_PATHキーから、順次ドライバー情報を読み出します。ドライバーはCOMで書かれているので、CoInitialize()関数で初期化を行う必要があります。
AsioDriverList::AsioDriverList () { HKEY hkEnum = 0; char keyname[MAXDRVNAMELEN]; LPASIODRVSTRUCT pdl; LONG cr; DWORD index = 0; BOOL fin = FALSE; numdrv = 0; lpdrvlist = 0; cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum); while (cr == ERROR_SUCCESS) { if ((cr = RegEnumKey(hkEnum,index++,(LPTSTR)keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) { lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist); } else fin = TRUE; } if (hkEnum) RegCloseKey(hkEnum); pdl = lpdrvlist; while (pdl) { numdrv++; pdl = pdl->next; } if (numdrv) CoInitialize(0); // initialize COM }
以下の実装は、同様の機能をC#を用いて書き直したものです。C#は、内部でCOMの初期化を行っているため、改めてCoInitialize()を呼び出す必要はありません。
public void InitList() { RegistryKey regkey = Registry.LocalMachine.OpenSubKey(@"software\asio", false); if (regkey == null) return; string[] keyNames = regkey.GetSubKeyNames(); foreach (string keyName in keyNames) { AsioDrvStruct asioDrv = NewDrvStruct(regkey, keyName, NumDrv); AsioDrvList.Add(asioDrv); } regkey.Close(); }
ドライバーに関する情報を構造体へ格納する
以下の関数は、ドライバーを列挙するためのサービス関数です。各ドライバーに関する情報を構造体に保存します。
以下にドライバーの情報を保存する構造体を示します。
struct asiodrvstruct { int drvID; CLSID clsid; char dllpath[MAXPATHLEN]; char drvname[MAXDRVNAMELEN]; LPVOID asiodrv; struct asiodrvstruct *next; };
ドライバーのパスを取得する関数を用いて、ドライバーの名前などを取得します。
実装されているドライバーの数が、RegEnumKeyを実行してみるまで分からないため、ポインターでチェーンする手法を用いて可変長の構造体アレーを実現しています。
static LPASIODRVSTRUCT newDrvStruct (HKEY hkey,char *keyname,int drvID,LPASIODRVSTRUCT lpdrv) { HKEY hksub; char databuf[256]; char dllpath[MAXPATHLEN]; WORD wData[100]; CLSID clsid; DWORD datatype,datasize; LONG cr,rc; if (!lpdrv) { if ((cr = RegOpenKeyEx(hkey,(LPCTSTR)keyname,0,KEY_READ,&hksub)) == ERROR_SUCCESS) { datatype = REG_SZ; datasize = 256; cr = RegQueryValueEx(hksub,COM_CLSID,0,&datatype,(LPBYTE)databuf,&datasize); if (cr == ERROR_SUCCESS) { rc = findDrvPath (databuf,dllpath,MAXPATHLEN); if (rc == 0) { lpdrv = new ASIODRVSTRUCT[1]; if (lpdrv) { memset(lpdrv,0,sizeof(ASIODRVSTRUCT)); lpdrv->drvID = drvID; MultiByteToWideChar(CP_ACP,0,(LPCSTR)databuf,-1,(LPWSTR)wData,100); if ((cr = CLSIDFromString((LPOLESTR)wData,(LPCLSID)&clsid)) == S_OK) { memcpy(&lpdrv->clsid,&clsid,sizeof(CLSID)); } datatype = REG_SZ; datasize = 256; cr = RegQueryValueEx(hksub,ASIODRV_DESC,0,&datatype,(LPBYTE)databuf,&datasize); if (cr == ERROR_SUCCESS) { strcpy(lpdrv->drvname,databuf); } else strcpy(lpdrv->drvname,keyname); } } } RegCloseKey(hksub); } } else lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next); return lpdrv; }
以下の実装は、同様の機能をC#を用いて書き直したものです。
List<AsioDrvStruct>を用いて情報を管理するため、C#の実装ではポインターのチェーンは用いません。
public class AsioDrvStruct { public int drvID; public Guid clsID; public string dllPath; public string drvName; } private AsioDrvStruct NewDrvStruct( RegistryKey regkey, string keyName, int drvID) { RegistryKey regSubkey = regkey.OpenSubKey(keyName, false); string strClsid = (string)regSubkey.GetValue("CLSID"); string strDesc = (string)regSubkey.GetValue("Description"); regSubkey.Close(); AsioDrvStruct asioDrv = new AsioDrvStruct(); asioDrv.drvID = drvID; asioDrv.clsID = Guid.Parse(strClsid); asioDrv.dllPath = FindDrvPath(strClsid); asioDrv.drvName = strDesc; return asioDrv; }
ドライバーのパスを得る
CLSIDからドライバーのパスを取得します。
取得手順は、最初に"clsid"のルートクラスを開き、そして検索対象のCLSID、そして"InprocServer32"と階層を降りて行き、バリューを取得します。最後にドライバーファイルが存在するかどうかを確認しています。
#define ASIODRV_DESC "description" #define INPROC_SERVER "InprocServer32" #define COM_CLSID "clsid" #define ASIO_PATH "software\\asio"
static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize) { HKEY hkEnum,hksub,hkpath; char databuf[512]; LONG cr,rc = -1; DWORD datatype,datasize; DWORD index; OFSTRUCT ofs; HFILE hfile; BOOL found = FALSE; CharLowerBuff(clsidstr,strlen(clsidstr)); if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) { index = 0; while (cr == ERROR_SUCCESS && !found) { cr = RegEnumKey(hkEnum,index++,(LPTSTR)databuf,512); if (cr == ERROR_SUCCESS) { CharLowerBuff(databuf,strlen(databuf)); if (!(strcmp(databuf,clsidstr))) { if ((cr = RegOpenKeyEx(hkEnum,(LPCTSTR)databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) { if ((cr = RegOpenKeyEx(hksub,(LPCTSTR)INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) { datatype = REG_SZ; datasize = (DWORD)dllpathsize; cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize); if (cr == ERROR_SUCCESS) { memset(&ofs,0,sizeof(OFSTRUCT)); ofs.cBytes = sizeof(OFSTRUCT); hfile = OpenFile(dllpath,&ofs,OF_EXIST); if (hfile) rc = 0; } RegCloseKey(hkpath); } RegCloseKey(hksub); } found = TRUE; // break out } } } RegCloseKey(hkEnum); } return rc; }
以下の実装は、同様の機能をC#を用いて書き直したものです。
private string FindDrvPath(string clsid) { string dllPath = ""; RegistryKey regRootKey = Registry.ClassesRoot.OpenSubKey("CLSID", false); if (regRootKey != null) { RegistryKey regClsidKey = regRootKey.OpenSubKey(clsid, false); if (regClsidKey != null) { RegistryKey regServerKey = regClsidKey.OpenSubKey("InprocServer32", false); if (regServerKey != null) { dllPath = (string)regServerKey.GetValue(""); regServerKey.Close(); } regClsidKey.Close(); } regRootKey.Close(); } return File.Exists(dllPath) ? dllPath : ""; }