CDXAReader.cpp
上传用户:xjjlds
上传日期:2015-12-05
资源大小:22823k
文件大小:15k
源码类别:

多媒体编程

开发平台:

Visual C++

  1. /* 
  2.  * Copyright (C) 2003-2005 Gabest
  3.  * http://www.gabest.org
  4.  *
  5.  *  This Program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2, or (at your option)
  8.  *  any later version.
  9.  *   
  10.  *  This Program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13.  *  GNU General Public License for more details.
  14.  *   
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with GNU Make; see the file COPYING.  If not, write to
  17.  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
  18.  *  http://www.gnu.org/copyleft/gpl.html
  19.  *
  20.  */
  21. #include "stdafx.h"
  22. #include <initguid.h>
  23. #include <uuids.h>
  24. #include "cdxareader.h"
  25. #include "........includematroskamatroska.h"
  26. #include "........includeoggOggDS.h"
  27. #include "........includemoreuuids.h"
  28. #include "......DSUtilDSUtil.h"
  29. /////////
  30. static DWORD EDC_crctable[256] = 
  31. {
  32. 0x00000000l, 0x90910101l, 0x91210201l, 0x01b00300l,
  33. 0x92410401l, 0x02d00500l, 0x03600600l, 0x93f10701l,
  34. 0x94810801l, 0x04100900l, 0x05a00a00l, 0x95310b01l,
  35. 0x06c00c00l, 0x96510d01l, 0x97e10e01l, 0x07700f00l,
  36. 0x99011001l, 0x09901100l, 0x08201200l, 0x98b11301l,
  37. 0x0b401400l, 0x9bd11501l, 0x9a611601l, 0x0af01700l,
  38. 0x0d801800l, 0x9d111901l, 0x9ca11a01l, 0x0c301b00l,
  39. 0x9fc11c01l, 0x0f501d00l, 0x0ee01e00l, 0x9e711f01l,
  40. 0x82012001l, 0x12902100l, 0x13202200l, 0x83b12301l,
  41. 0x10402400l, 0x80d12501l, 0x81612601l, 0x11f02700l,
  42. 0x16802800l, 0x86112901l, 0x87a12a01l, 0x17302b00l,
  43. 0x84c12c01l, 0x14502d00l, 0x15e02e00l, 0x85712f01l,
  44. 0x1b003000l, 0x8b913101l, 0x8a213201l, 0x1ab03300l,
  45. 0x89413401l, 0x19d03500l, 0x18603600l, 0x88f13701l,
  46. 0x8f813801l, 0x1f103900l, 0x1ea03a00l, 0x8e313b01l,
  47. 0x1dc03c00l, 0x8d513d01l, 0x8ce13e01l, 0x1c703f00l,
  48. 0xb4014001l, 0x24904100l, 0x25204200l, 0xb5b14301l,
  49. 0x26404400l, 0xb6d14501l, 0xb7614601l, 0x27f04700l,
  50. 0x20804800l, 0xb0114901l, 0xb1a14a01l, 0x21304b00l,
  51. 0xb2c14c01l, 0x22504d00l, 0x23e04e00l, 0xb3714f01l,
  52. 0x2d005000l, 0xbd915101l, 0xbc215201l, 0x2cb05300l,
  53. 0xbf415401l, 0x2fd05500l, 0x2e605600l, 0xbef15701l,
  54. 0xb9815801l, 0x29105900l, 0x28a05a00l, 0xb8315b01l,
  55. 0x2bc05c00l, 0xbb515d01l, 0xbae15e01l, 0x2a705f00l,
  56. 0x36006000l, 0xa6916101l, 0xa7216201l, 0x37b06300l,
  57. 0xa4416401l, 0x34d06500l, 0x35606600l, 0xa5f16701l,
  58. 0xa2816801l, 0x32106900l, 0x33a06a00l, 0xa3316b01l,
  59. 0x30c06c00l, 0xa0516d01l, 0xa1e16e01l, 0x31706f00l,
  60. 0xaf017001l, 0x3f907100l, 0x3e207200l, 0xaeb17301l,
  61. 0x3d407400l, 0xadd17501l, 0xac617601l, 0x3cf07700l,
  62. 0x3b807800l, 0xab117901l, 0xaaa17a01l, 0x3a307b00l,
  63. 0xa9c17c01l, 0x39507d00l, 0x38e07e00l, 0xa8717f01l,
  64. 0xd8018001l, 0x48908100l, 0x49208200l, 0xd9b18301l,
  65. 0x4a408400l, 0xdad18501l, 0xdb618601l, 0x4bf08700l,
  66. 0x4c808800l, 0xdc118901l, 0xdda18a01l, 0x4d308b00l,
  67. 0xdec18c01l, 0x4e508d00l, 0x4fe08e00l, 0xdf718f01l,
  68. 0x41009000l, 0xd1919101l, 0xd0219201l, 0x40b09300l,
  69. 0xd3419401l, 0x43d09500l, 0x42609600l, 0xd2f19701l,
  70. 0xd5819801l, 0x45109900l, 0x44a09a00l, 0xd4319b01l,
  71. 0x47c09c00l, 0xd7519d01l, 0xd6e19e01l, 0x46709f00l,
  72. 0x5a00a000l, 0xca91a101l, 0xcb21a201l, 0x5bb0a300l,
  73. 0xc841a401l, 0x58d0a500l, 0x5960a600l, 0xc9f1a701l,
  74. 0xce81a801l, 0x5e10a900l, 0x5fa0aa00l, 0xcf31ab01l,
  75. 0x5cc0ac00l, 0xcc51ad01l, 0xcde1ae01l, 0x5d70af00l,
  76. 0xc301b001l, 0x5390b100l, 0x5220b200l, 0xc2b1b301l,
  77. 0x5140b400l, 0xc1d1b501l, 0xc061b601l, 0x50f0b700l,
  78. 0x5780b800l, 0xc711b901l, 0xc6a1ba01l, 0x5630bb00l,
  79. 0xc5c1bc01l, 0x5550bd00l, 0x54e0be00l, 0xc471bf01l,
  80. 0x6c00c000l, 0xfc91c101l, 0xfd21c201l, 0x6db0c300l,
  81. 0xfe41c401l, 0x6ed0c500l, 0x6f60c600l, 0xfff1c701l,
  82. 0xf881c801l, 0x6810c900l, 0x69a0ca00l, 0xf931cb01l,
  83. 0x6ac0cc00l, 0xfa51cd01l, 0xfbe1ce01l, 0x6b70cf00l,
  84. 0xf501d001l, 0x6590d100l, 0x6420d200l, 0xf4b1d301l,
  85. 0x6740d400l, 0xf7d1d501l, 0xf661d601l, 0x66f0d700l,
  86. 0x6180d800l, 0xf111d901l, 0xf0a1da01l, 0x6030db00l,
  87. 0xf3c1dc01l, 0x6350dd00l, 0x62e0de00l, 0xf271df01l,
  88. 0xee01e001l, 0x7e90e100l, 0x7f20e200l, 0xefb1e301l,
  89. 0x7c40e400l, 0xecd1e501l, 0xed61e601l, 0x7df0e700l,
  90. 0x7a80e800l, 0xea11e901l, 0xeba1ea01l, 0x7b30eb00l,
  91. 0xe8c1ec01l, 0x7850ed00l, 0x79e0ee00l, 0xe971ef01l,
  92. 0x7700f000l, 0xe791f101l, 0xe621f201l, 0x76b0f300l,
  93. 0xe541f401l, 0x75d0f500l, 0x7460f600l, 0xe4f1f701l,
  94. 0xe381f801l, 0x7310f900l, 0x72a0fa00l, 0xe231fb01l,
  95. 0x71c0fc00l, 0xe151fd01l, 0xe0e1fe01l, 0x7070ff00l
  96. };
  97. static DWORD build_edc(const void* in, unsigned from, unsigned upto)
  98. {
  99. const BYTE* p = (BYTE*)in + from;
  100. DWORD result = 0;
  101. for(; from < upto; from++)
  102. result = EDC_crctable[(result ^ *p++) & 0xffL] ^ (result >> 8);
  103. return result;
  104. }
  105. /////////
  106. #ifdef REGISTER_FILTER
  107. const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
  108. {
  109. {&MEDIATYPE_Stream, &MEDIASUBTYPE_NULL}
  110. };
  111. const AMOVIESETUP_PIN sudOpPin[] =
  112. {
  113. {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut}
  114. };
  115. const AMOVIESETUP_FILTER sudFilter[] =
  116. {
  117. {&__uuidof(CCDXAReader), L"CDXA Reader", MERIT_UNLIKELY, countof(sudOpPin), sudOpPin}
  118. };
  119. CFactoryTemplate g_Templates[] =
  120. {
  121. {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CCDXAReader>, NULL, &sudFilter[0]}
  122. };
  123. int g_cTemplates = countof(g_Templates);
  124. STDAPI DllRegisterServer()
  125. {
  126. SetRegKeyValue(
  127. _T("Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770}"), _T("{D367878E-F3B8-4235-A968-F378EF1B9A44}"), 
  128. _T("0"), _T("0,4,,52494646,8,4,,43445841")); // "RIFFxxxxCDXA"
  129. SetRegKeyValue(
  130. _T("Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770}"), _T("{D367878E-F3B8-4235-A968-F378EF1B9A44}"), 
  131. _T("Source Filter"), _T("{D367878E-F3B8-4235-A968-F378EF1B9A44}"));
  132. return AMovieDllRegisterServer2(TRUE);
  133. }
  134. STDAPI DllUnregisterServer()
  135. {
  136. DeleteRegKey(_T("Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770}"), _T("{D367878E-F3B8-4235-A968-F378EF1B9A44}"));
  137. return AMovieDllRegisterServer2(FALSE);
  138. }
  139. extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
  140. BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
  141. {
  142.     return DllEntryPoint((HINSTANCE)hModule, dwReason, 0); // "DllMain" of the dshow baseclasses;
  143. }
  144. #endif
  145. //
  146. // CCDXAReader
  147. //
  148. CCDXAReader::CCDXAReader(IUnknown* pUnk, HRESULT* phr)
  149. : CAsyncReader(NAME("CCDXAReader"), pUnk, &m_stream, phr, __uuidof(this))
  150. {
  151. if(phr) *phr = S_OK;
  152. }
  153. CCDXAReader::~CCDXAReader()
  154. {
  155. }
  156. STDMETHODIMP CCDXAReader::NonDelegatingQueryInterface(REFIID riid, void** ppv)
  157. {
  158. return 
  159. QI(IFileSourceFilter)
  160. __super::NonDelegatingQueryInterface(riid, ppv);
  161. }
  162. STDMETHODIMP CCDXAReader::Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt) 
  163. {
  164. CMediaType mt;
  165. m_mt = mt;
  166. if(!m_stream.Load(pszFileName))
  167. return E_FAIL;
  168. m_fn = pszFileName;
  169. mt.majortype = MEDIATYPE_Stream;
  170. mt.subtype = m_stream.m_subtype;
  171. m_mt = mt;
  172. return S_OK;
  173. }
  174. STDMETHODIMP CCDXAReader::GetCurFile(LPOLESTR* ppszFileName, AM_MEDIA_TYPE* pmt)
  175. {
  176. if(!ppszFileName) return E_POINTER;
  177. if(!(*ppszFileName = (LPOLESTR)CoTaskMemAlloc((m_fn.GetLength()+1)*sizeof(WCHAR))))
  178. return E_OUTOFMEMORY;
  179. wcscpy(*ppszFileName, m_fn);
  180. return S_OK;
  181. }
  182. // CCDXAStream
  183. CCDXAStream::CCDXAStream()
  184. {
  185. m_subtype = MEDIASUBTYPE_NULL;
  186. m_hFile = INVALID_HANDLE_VALUE;
  187. m_llPosition = m_llLength = 0;
  188. m_nFirstSector = 0;
  189. m_nBufferedSector = -1;
  190. }
  191. CCDXAStream::~CCDXAStream()
  192. {
  193. if(m_hFile != INVALID_HANDLE_VALUE)
  194. {
  195. CloseHandle(m_hFile);
  196. m_hFile = INVALID_HANDLE_VALUE;
  197. }
  198. }
  199. bool CCDXAStream::Load(const WCHAR* fnw)
  200. {
  201. if(m_hFile != INVALID_HANDLE_VALUE)
  202. {
  203. CloseHandle(m_hFile);
  204. m_hFile = INVALID_HANDLE_VALUE;
  205. }
  206. m_hFile = CreateFile(CString(fnw), GENERIC_READ, FILE_SHARE_READ, NULL, 
  207. OPEN_EXISTING, FILE_ATTRIBUTE_READONLY|FILE_FLAG_SEQUENTIAL_SCAN, (HANDLE)NULL);
  208. if(m_hFile == INVALID_HANDLE_VALUE)
  209. {
  210. return(false);
  211. }
  212. BYTE hdr[RIFFCDXA_HEADER_SIZE];
  213. DWORD NumberOfBytesRead;
  214. if(!ReadFile(m_hFile, (LPVOID)hdr, RIFFCDXA_HEADER_SIZE, &NumberOfBytesRead, NULL)
  215. || *((DWORD*)&hdr[0]) != 'FFIR' || *((DWORD*)&hdr[8]) != 'AXDC'
  216. || *((DWORD*)&hdr[4]) != (*((DWORD*)&hdr[0x28])+0x24))
  217. {
  218. CloseHandle(m_hFile);
  219. m_hFile = INVALID_HANDLE_VALUE;
  220. return(false);
  221. }
  222. LARGE_INTEGER size = {0, 0};
  223. size.LowPart = (LONG)GetFileSize(m_hFile, (LPDWORD)&size.HighPart);
  224. m_llLength = int((size.QuadPart - RIFFCDXA_HEADER_SIZE) / RAW_SECTOR_SIZE) * RAW_DATA_SIZE;
  225. if(!LookForMediaSubType()) 
  226. {
  227. m_llPosition = m_llLength = 0;
  228. CloseHandle(m_hFile);
  229. m_hFile = INVALID_HANDLE_VALUE;
  230. return(false);
  231. }
  232. m_llPosition = 0;
  233. m_nBufferedSector = -1;
  234. return(true);
  235. }
  236. HRESULT CCDXAStream::SetPointer(LONGLONG llPos)
  237. {
  238. return (llPos >= 0 && llPos < m_llLength) ? m_llPosition = llPos, S_OK : S_FALSE;
  239. }
  240. HRESULT CCDXAStream::Read(PBYTE pbBuffer, DWORD dwBytesToRead, BOOL bAlign, LPDWORD pdwBytesRead)
  241. {
  242. CAutoLock lck(&m_csLock);
  243. PBYTE pbBufferOrg = pbBuffer;
  244. LONGLONG pos = m_llPosition;
  245. while(pos >= 0 && pos < m_llLength && dwBytesToRead > 0)
  246. {
  247. UINT sector = m_nFirstSector + int(pos/RAW_DATA_SIZE);
  248. __int64 offset = pos%RAW_DATA_SIZE;
  249. if(m_nBufferedSector != sector)
  250. {
  251. LARGE_INTEGER FilePointer;
  252. FilePointer.QuadPart = RIFFCDXA_HEADER_SIZE + sector*RAW_SECTOR_SIZE;
  253. SetFilePointer(m_hFile, (LONG)FilePointer.LowPart, (PLONG)&FilePointer.HighPart, FILE_BEGIN);
  254. memset(m_sector, 0, sizeof(m_sector));
  255. DWORD NumberOfBytesRead = 0;
  256. int nRetries = 3;
  257. while(nRetries--)
  258. {
  259. NumberOfBytesRead = 0;
  260. if(!ReadFile(m_hFile, m_sector, RAW_SECTOR_SIZE, &NumberOfBytesRead, NULL)
  261. || NumberOfBytesRead != RAW_SECTOR_SIZE) 
  262. break;
  263. if(*(DWORD*)&m_sector[RAW_SECTOR_SIZE-4] == 0) // no CRC? it happens...
  264. break;
  265. if(build_edc(m_sector, RAW_SYNC_SIZE + RAW_HEADER_SIZE, RAW_SECTOR_SIZE) == 0) 
  266. break;
  267. TRACE(_T("CCDXAStream: CRC error at sector %d (fp=0x%I64x, retriesleft=%d)n"), sector, FilePointer.QuadPart, nRetries);
  268. }
  269. m_nBufferedSector = sector;
  270. }
  271. DWORD l = min(min(dwBytesToRead, RAW_DATA_SIZE - offset), m_llLength - pos);
  272. memcpy(pbBuffer, &m_sector[RAW_SYNC_SIZE + RAW_HEADER_SIZE + RAW_SUBHEADER_SIZE + offset], l);
  273. pbBuffer += l;
  274. pos += l;
  275. dwBytesToRead -= l;
  276. }
  277. if(pdwBytesRead) *pdwBytesRead = pbBuffer - pbBufferOrg;
  278. m_llPosition += pbBuffer - pbBufferOrg;
  279. if(dwBytesToRead != 0) return S_FALSE;
  280. return S_OK;
  281. }
  282. LONGLONG CCDXAStream::Size(LONGLONG* pSizeAvailable)
  283. {
  284. if(pSizeAvailable) *pSizeAvailable = m_llLength;
  285.     return m_llLength;
  286. }
  287. DWORD CCDXAStream::Alignment()
  288. {
  289.     return 1;
  290. }
  291. void CCDXAStream::Lock()
  292. {
  293.     m_csLock.Lock();
  294. }
  295. void CCDXAStream::Unlock()
  296. {
  297.     m_csLock.Unlock();
  298. }
  299. //
  300. bool CCDXAStream::LookForMediaSubType()
  301. {
  302. BYTE buff[RAW_DATA_SIZE];
  303. m_subtype = MEDIASUBTYPE_NULL;
  304. m_llPosition = 0;
  305. for(int iSectorsRead = 0;
  306. Read(buff, RAW_DATA_SIZE, 1, NULL) == S_OK && iSectorsRead < 1000;
  307. iSectorsRead++)
  308. {
  309. if(*((DWORD*)&buff[0]) == 0xba010000) 
  310. {
  311. m_llPosition = 0;
  312. m_llLength -= iSectorsRead*RAW_DATA_SIZE;
  313. m_nFirstSector = iSectorsRead;
  314. if((buff[4]&0xc4) == 0x44) m_subtype = MEDIASUBTYPE_MPEG2_PROGRAM;
  315. else if((buff[4]&0xf1) == 0x21) m_subtype = MEDIASUBTYPE_MPEG1System;
  316. return m_subtype != MEDIASUBTYPE_NULL;
  317. }
  318. else if(*((DWORD*)&buff[0]) == 'SggO') 
  319. {
  320. m_llPosition = 0;
  321. m_llLength -= iSectorsRead*RAW_DATA_SIZE;
  322. m_nFirstSector = iSectorsRead;
  323. m_subtype = MEDIASUBTYPE_Ogg;
  324. return(true);
  325. }
  326. else if(*((DWORD*)&buff[0]) == 0xA3DF451A) 
  327. {
  328. m_llPosition = 0;
  329. m_llLength -= iSectorsRead*RAW_DATA_SIZE;
  330. m_nFirstSector = iSectorsRead;
  331. m_subtype = MEDIASUBTYPE_Matroska;
  332. return(true);
  333. }
  334. else if(*((DWORD*)&buff[0]) == 'FMR.') 
  335. {
  336. m_llPosition = 0;
  337. m_llLength -= iSectorsRead*RAW_DATA_SIZE;
  338. m_nFirstSector = iSectorsRead;
  339. m_subtype = MEDIASUBTYPE_RealMedia;
  340. return(true);
  341. }
  342. else if(*((DWORD*)&buff[0]) == 'FFIR' && *((DWORD*)&buff[8]) == ' IVA')
  343. {
  344. m_llPosition = 0;
  345. m_llLength = min(m_llLength, *((DWORD*)&buff[4])+8);
  346. m_nFirstSector = iSectorsRead;
  347. m_subtype = MEDIASUBTYPE_Avi;
  348. return(true);
  349. }
  350. else if(*((DWORD*)&buff[4]) == 'voom' || *((DWORD*)&buff[4]) == 'tadm'
  351. || *((DWORD*)&buff[4]) == 'pytf' && *((DWORD*)&buff[8]) == 'mosi' && *((DWORD*)&buff[16]) == '14pm')
  352. {
  353. m_llPosition = 0;
  354. m_llLength -= iSectorsRead*RAW_DATA_SIZE;
  355. m_nFirstSector = iSectorsRead;
  356. m_subtype = MEDIASUBTYPE_QTMovie;
  357. return(true);
  358. }
  359. }
  360. m_llPosition = 0;
  361. CRegKey majorkey;
  362. CString majortype = _T("\Media Type\{e436eb83-524f-11ce-9f53-0020af0ba770}");
  363. if(ERROR_SUCCESS == majorkey.Open(HKEY_CLASSES_ROOT, majortype, KEY_READ))
  364. {
  365. TCHAR subtype[256+1];
  366. DWORD len = 256;
  367. for(int i = 0; ERROR_SUCCESS == majorkey.EnumKey(i, subtype, &len); i++, len = 256)
  368. {
  369. CRegKey subkey;
  370. if(ERROR_SUCCESS != subkey.Open(HKEY_CLASSES_ROOT, majortype + _T("\") + subtype, KEY_READ))
  371. continue;
  372. for(int j = 0; true; j++)
  373. {
  374. TCHAR number[10];
  375. _stprintf(number, _T("%d"), j);
  376. TCHAR pattern[256+1];
  377. ULONG len = 256;
  378. if(ERROR_SUCCESS != subkey.QueryStringValue(number, pattern, &len))
  379. break;
  380. CString p = pattern;
  381. p += ',';
  382. __int64 offset = 0;
  383. DWORD cb = 0;
  384. CByteArray mask, val;
  385. int nMatches = 0, nTries = 0;
  386. for(int k = 0, l; nTries >= 0 && (l = p.Find(',', k)) >= 0; k = l+1, nTries++)
  387. {
  388. CString s = p.Mid(k, l-k);
  389. TRACE(s + 'n');
  390. TCHAR* end = NULL;
  391. switch(nTries&3)
  392. {
  393. case 0: offset = _tcstol(s, &end, 10); break;
  394. case 1: cb = _tcstol(s, &end, 10); break;
  395. case 2: StringToBin(s, mask); break;
  396. case 3: StringToBin(s, val); break;
  397. default: nTries = -1; break;
  398. }
  399. if(nTries >= 0 && (nTries&3) == 3)
  400. {
  401. if(cb > 0 && val.GetSize() > 0 && cb == val.GetSize())
  402. {
  403. if(offset >= 0 && S_OK == SetPointer(offset)
  404. || S_OK == SetPointer(m_llLength + offset))
  405. {
  406. CAutoVectorPtr<BYTE> pData;
  407. if(pData.Allocate(cb))
  408. {
  409. DWORD BytesRead = 0;
  410. if(S_OK == Read(pData, cb, 1, &BytesRead) && cb == BytesRead)
  411. {
  412. if(mask.GetSize() < cb)
  413. {
  414. int i = mask.GetSize();
  415. mask.SetSize(cb);
  416. for(; i < cb; i++) mask[i] = 0xff;
  417. }
  418. for(int i = 0; i < cb; i++)
  419. pData[i] &= (BYTE)mask[i];
  420. if(memcmp(pData, val.GetData(), cb) == 0)
  421. nMatches++;
  422. }
  423. }
  424. }
  425. offset = 0; cb = 0;
  426. mask.RemoveAll(); val.RemoveAll();
  427. }
  428. }
  429. }
  430. if(nMatches > 0 && nMatches*4 == nTries)
  431. {
  432. m_subtype = GUIDFromCString(subtype);
  433. return S_OK;
  434. }
  435. }
  436. }
  437. }
  438. return(false);
  439. }