// // Microsoft Windows Media Technologies // Copyright (C) Microsoft Corporation, 1999 - 2001. All rights reserved. // // MSHDSP.DLL is a sample WMDM Service Provider(SP) that enumerates fixed drives. // This sample shows you how to implement an SP according to the WMDM documentation. // This sample uses fixed drives on your PC to emulate portable media, and // shows the relationship between different interfaces and objects. Each hard disk // volume is enumerated as a device and directories and files are enumerated as // Storage objects under respective devices. You can copy non-SDMI compliant content // to any device that this SP enumerates. To copy an SDMI compliant content to a // device, the device must be able to report a hardware embedded serial number. // Hard disks do not have such serial numbers. // // To build this SP, you are recommended to use the MSHDSP.DSP file under Microsoft // Visual C++ 6.0 and run REGSVR32.EXE to register the resulting MSHDSP.DLL. You can // then build the sample application from the WMDMAPP directory to see how it gets // loaded by the application. However, you need to obtain a certificate from // Microsoft to actually run this SP. This certificate would be in the KEY.C file // under the INCLUDE directory for one level up. // MDSPDevice.cpp : Implementation of CMDSPDevice #include "hdspPCH.h" #include "mshdsp.h" #include "wmsstd.h" #define STRSAFE_NO_DEPRECATE #include "strsafe.h" ///////////////////////////////////////////////////////////////////////////// // CMDSPDevice HRESULT CMDSPDevice::InitGlobalDeviceInfo() { return SetGlobalDeviceStatus(m_wcsName, 0, FALSE); } CMDSPDevice::CMDSPDevice() { m_wcsName[0] = 0; } CMDSPDevice::~CMDSPDevice() { } STDMETHODIMP CMDSPDevice::GetName(LPWSTR pwszName, UINT nMaxChars) { HRESULT hr = E_FAIL; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pwszName); CPRg(nMaxChars>wcslen(m_wcsName)); if( m_wcsName[0] ) { wcscpy(pwszName, m_wcsName); hr = S_OK; } else { hr = WMDM_E_NOTSUPPORTED; } Error: hrLogDWORD("IMDSPDevice::GetName returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetManufacturer(LPWSTR pwszName, UINT nMaxChars) { HRESULT hr = S_OK; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pwszName); if(FAILED(UtilGetManufacturer(m_wcsName, &pwszName, nMaxChars))) { if (hr != STRSAFE_E_INSUFFICIENT_BUFFER) { hr = E_NOTIMPL; } } Error: hrLogDWORD("IMDSPDevice::GetManufacturer returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetVersion(DWORD * pdwVersion) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDevice::GetVersion returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetType(DWORD * pdwType) { HRESULT hr = S_OK; WMDMID snTmp; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pdwType); *pdwType = WMDM_DEVICE_TYPE_STORAGE | WMDM_DEVICE_TYPE_NONSDMI; snTmp.cbSize = sizeof(WMDMID); hr = UtilGetSerialNumber(m_wcsName, &snTmp, FALSE); if( hr == S_OK ) { *pdwType |= WMDM_DEVICE_TYPE_SDMI; } hr=S_OK; Error: hrLogDWORD("IMDSPDevice::GetType returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetSerialNumber( PWMDMID pSerialNumber, BYTE abMac[WMDM_MAC_LENGTH]) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pSerialNumber); hr = UtilGetSerialNumber(m_wcsName, pSerialNumber, TRUE); if( hr == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) ) { hr = WMDM_E_NOTSUPPORTED; } if( hr == S_OK ) { // MAC the parameters HMAC hMAC; CORg(g_pAppSCServer->MACInit(&hMAC)); CORg(g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pSerialNumber), sizeof(WMDMID))); CORg(g_pAppSCServer->MACFinal(hMAC, abMac)); } Error: hrLogDWORD("IMDSPDevice::GetSerialNumber returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetPowerSource(DWORD * pdwPowerSource, DWORD * pdwPercentRemaining) { HRESULT hr=S_OK; CFRg(g_pAppSCServer); if( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pdwPowerSource); CARg(pdwPercentRemaining); *pdwPowerSource = WMDM_POWER_CAP_EXTERNAL | WMDM_POWER_IS_EXTERNAL | WMDM_POWER_PERCENT_AVAILABLE; *pdwPercentRemaining = 100; Error: hrLogDWORD("IMDSPDevice::GetPowerSource returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetStatus(DWORD * pdwStatus) { HRESULT hr = S_OK; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pdwStatus); CHRg(GetGlobalDeviceStatus(m_wcsName, pdwStatus)); if( !( *pdwStatus & WMDM_STATUS_BUSY) ) { *pdwStatus = WMDM_STATUS_READY; } hr = S_OK; Error: hrLogDWORD("IMDSPDevice::GetStatus returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetDeviceIcon(ULONG *hIcon) { HRESULT hr = S_OK; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CFRg(g_hinstance); CARg(hIcon); CWRg( (*hIcon)=HandleToULong(LoadIconA(g_hinstance, MAKEINTRESOURCEA(IDI_ICON_PM)) )); Error: hrLogDWORD("IMDSPDevice::GetDeviceIcon returned 0x%08lx", hr, hr); return hr; } // Opaque Command to get extended certification information // // GUID = {C39BF696-B776-459c-A13A-4B7116AB9F09} // static const GUID guidCertInfoEx = { 0xc39bf696, 0xb776, 0x459c, { 0xa1, 0x3a, 0x4b, 0x71, 0x16, 0xab, 0x9f, 0x9 } }; typedef struct { HRESULT hr; DWORD cbCert; BYTE pbCert[1]; } CERTINFOEX; static const BYTE bCertInfoEx_App[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; static const BYTE bCertInfoEx_SP[] = { 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 }; STDMETHODIMP CMDSPDevice::SendOpaqueCommand(OPAQUECOMMAND *pCommand) { HRESULT hr; HMAC hMAC; BYTE abMACVerify[WMDM_MAC_LENGTH]; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } // Compute and verify MAC // CORg( g_pAppSCServer->MACInit(&hMAC) ); CORg( g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(&(pCommand->guidCommand)), sizeof(GUID)) ); CORg( g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(&(pCommand->dwDataLen)), sizeof(pCommand->dwDataLen)) ); if( pCommand->pData ) { CORg( g_pAppSCServer->MACUpdate(hMAC, (BYTE*)(pCommand->pData), pCommand->dwDataLen) ); } CORg( g_pAppSCServer->MACFinal(hMAC, abMACVerify) ); if (memcmp(abMACVerify, pCommand->abMAC, WMDM_MAC_LENGTH) != 0) { CORg(WMDM_E_MAC_CHECK_FAILED); } // Take action based on the command GUID // if( memcmp(&(pCommand->guidCommand), &guidCertInfoEx, sizeof(GUID)) == 0 ) { // // Command to exchange extended authentication information // CERTINFOEX *pCertInfoEx; DWORD cbData_App = sizeof( bCertInfoEx_App )/sizeof( BYTE ); DWORD cbData_SP = sizeof( bCertInfoEx_SP )/sizeof( BYTE ); DWORD cbData_Return = sizeof(CERTINFOEX) + cbData_SP; // The caller must include their extended cert info // if( !pCommand->pData ) { CORg( E_INVALIDARG ); } // Map the data in the opaque command to a CERTINFOEX structure // pCertInfoEx = (CERTINFOEX *)pCommand->pData; // In this simple extended authentication scheme, the caller must // provide the exact cert info // if( (pCertInfoEx->cbCert != cbData_App) || (memcmp(pCertInfoEx->pbCert, bCertInfoEx_App, cbData_App) != 0) ) { CORg( WMDM_E_NOTCERTIFIED ); } // Free the caller data and allocate enough data for our return data // CoTaskMemFree( pCommand->pData ); CFRg( (pCommand->pData = (BYTE *)CoTaskMemAlloc(cbData_Return)) ); pCommand->dwDataLen = cbData_Return; // Copy the extended cert info into return data structure // pCertInfoEx = (CERTINFOEX *)pCommand->pData; pCertInfoEx->hr = S_OK; pCertInfoEx->cbCert = cbData_SP; memcpy( pCertInfoEx->pbCert, bCertInfoEx_SP, cbData_SP ); // Compute MAC on return data // CORg( g_pAppSCServer->MACInit( &hMAC ) ); CORg( g_pAppSCServer->MACUpdate( hMAC, (BYTE*)(&(pCommand->guidCommand)), sizeof(GUID) ) ); CORg( g_pAppSCServer->MACUpdate( hMAC, (BYTE*)(&(pCommand->dwDataLen)), sizeof(pCommand->dwDataLen) ) ); if( pCommand->pData ) { CORg( g_pAppSCServer->MACUpdate( hMAC, (BYTE*)(pCommand->pData), pCommand->dwDataLen ) ); } CORg( g_pAppSCServer->MACFinal( hMAC, pCommand->abMAC ) ); hr = S_OK; } else { CORg(WMDM_E_NOTSUPPORTED); } Error: hrLogDWORD("IMDSPDevice::SendOpaqueCommand returned 0x%08lx", hr, hr); return hr; } // IMDSPDevice2 STDMETHODIMP CMDSPDevice::GetStorage( LPCWSTR pszStorageName, IMDSPStorage** ppStorage ) { HRESULT hr; HRESULT hrTemp; WCHAR pwszFileName[MAX_PATH+1]; char pszTemp[MAX_PATH]; CComObject *pStg = NULL; // Get name of new file DWORD dwLen = wcslen(m_wcsName); // We reserve one char for the \ that might be added below if (dwLen >= ARRAYSIZE(pwszFileName)-1) { hr = STRSAFE_E_INSUFFICIENT_BUFFER; //defined in strsafe.h goto Error; } wcscpy( pwszFileName, m_wcsName ); if( pwszFileName[dwLen-1] != '\\' ) wcscat( pwszFileName, L"\\" ); hrTemp = StringCchCatW( pwszFileName, ARRAYSIZE(pwszFileName) - 1, // - 1 ensures the result fits into a MAX_PATH buffer. // This makes the wcscpy into pStg->m_wcsName (below) safe. pszStorageName ); if (FAILED(hrTemp)) { // The file does not exist hr = E_FAIL; // @@@@ Something else? S_FALSE? goto Error; } WideCharToMultiByte(CP_ACP, NULL, pwszFileName, -1, pszTemp, MAX_PATH, NULL, NULL); if( GetFileAttributesA( pszTemp ) == -1 ) { // The file does not exist hr = S_FALSE; goto Error; } // Create new storage object hr = CComObject::CreateInstance(&pStg); hr = pStg->QueryInterface( IID_IMDSPStorage, reinterpret_cast(ppStorage)); wcscpy(pStg->m_wcsName, pwszFileName); Error: if( hr != S_OK ) { *ppStorage = NULL; } hrLogDWORD("IMDSPDevice::GetStorage returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetFormatSupport2( DWORD dwFlags, _WAVEFORMATEX** ppAudioFormatEx, UINT* pnAudioFormatCount, _VIDEOINFOHEADER** ppVideoFormatEx, UINT* pnVideoFormatCount, WMFILECAPABILITIES** ppFileType, UINT* pnFileTypeCount ) { return E_NOTIMPL; } STDMETHODIMP CMDSPDevice::GetSpecifyPropertyPages( ISpecifyPropertyPages** ppSpecifyPropPages, IUnknown*** pppUnknowns, ULONG *pcUnks ) { HRESULT hr; IUnknown** ppUnknownArray = NULL; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(ppSpecifyPropPages); CARg(pppUnknowns); CARg(pcUnks); // This object also supports the ISpecifyPropertyPages interface CORg( QueryInterface( __uuidof(ISpecifyPropertyPages), reinterpret_cast(ppSpecifyPropPages) ) ); // Return one IUnknown interface, property page will QI for IDevice ppUnknownArray = (IUnknown**)CoTaskMemAlloc( sizeof(IUnknown*[1]) ); CORg( QueryInterface( __uuidof(IUnknown), reinterpret_cast(&ppUnknownArray[0]) ) ); *pppUnknowns = ppUnknownArray; *pcUnks = 1; Error: hrLogDWORD("IMDSPDevice::GetSpecifyPropertyPages returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetPnPName( LPWSTR pwszPnPName, UINT nMaxChars ) { return E_NOTIMPL; } // ISpecifyPropertyPages STDMETHODIMP CMDSPDevice::GetPages(CAUUID *pPages) { HRESULT hr = S_OK; if( pPages == NULL ) { return E_POINTER; } // Return the GUID for our property page pPages->cElems = 1; pPages->pElems = (GUID *)CoTaskMemAlloc( sizeof(GUID) * pPages->cElems ); if( pPages->pElems == NULL ) { hr = E_OUTOFMEMORY; } else { memcpy( &pPages->pElems[0], &__uuidof(HDSPPropPage), sizeof(GUID) ); } return( hr ); } // IMDSPDeviceControl STDMETHODIMP CMDSPDevice::GetDCStatus(/*[out]*/ DWORD *pdwStatus) { HRESULT hr = E_FAIL; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = GetStatus(pdwStatus); Error: hrLogDWORD("IMDSPDeviceControl::GetDCStatus returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetCapabilities(/*[out]*/ DWORD *pdwCapabilitiesMask) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } if( !pdwCapabilitiesMask ) { return E_INVALIDARG; } *pdwCapabilitiesMask = WMDM_DEVICECAP_CANSTREAMPLAY; hr = S_OK; Error: hrLogDWORD("IMDSPDeviceControl::GetCapabilities returned 0x%08lx", hr, hr); return S_OK; } STDMETHODIMP CMDSPDevice::Play() { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Play returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::Record(/*[in]*/ _WAVEFORMATEX *pFormat) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Record returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::Pause() { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Pause returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::Resume() { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Resume returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::Stop() { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Stop returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::Seek(/*[in]*/ UINT fuMode, /*[in]*/ int nOffset) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } hr = WMDM_E_NOTSUPPORTED; Error: hrLogDWORD("IMDSPDeviceControl::Seek returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::GetFormatSupport( _WAVEFORMATEX **pFormatEx, UINT *pnFormatCount, LPWSTR **pppwszMimeType, UINT *pnMimeTypeCount) { HRESULT hr = S_OK; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(pFormatEx); CARg(pppwszMimeType); CARg(pnFormatCount); CARg(pnMimeTypeCount); *pnFormatCount = 1; *pFormatEx = (_WAVEFORMATEX *)CoTaskMemAlloc(sizeof(_WAVEFORMATEX)); CPRg( *pFormatEx ); (*pFormatEx)->wFormatTag = WMDM_WAVE_FORMAT_ALL; (*pFormatEx)->nChannels = 2; (*pFormatEx)->cbSize = 0; (*pFormatEx)->nSamplesPerSec = 0; (*pFormatEx)->nAvgBytesPerSec = 0; (*pFormatEx)->nBlockAlign = 0; (*pFormatEx)->wBitsPerSample = 0; *pnMimeTypeCount= 1; *pppwszMimeType = (LPWSTR *)CoTaskMemAlloc(sizeof(LPWSTR)*1); CPRg(*pppwszMimeType); **pppwszMimeType = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR)*(wcslen(WCS_MIME_TYPE_ALL)+1)); CPRg(**pppwszMimeType); wcscpy(**pppwszMimeType, WCS_MIME_TYPE_ALL); Error: hrLogDWORD("IMDSPDevice::GetFormatSupport returned 0x%08lx", hr, hr); return hr; } STDMETHODIMP CMDSPDevice::EnumStorage(IMDSPEnumStorage** ppEnumStorage) { HRESULT hr; CFRg(g_pAppSCServer); if ( !(g_pAppSCServer->fIsAuthenticated()) ) { CORg(WMDM_E_NOTCERTIFIED); } CARg(ppEnumStorage); CComObject *pEnumObj; CORg(CComObject::CreateInstance(&pEnumObj)); hr = pEnumObj->QueryInterface( IID_IMDSPEnumStorage, reinterpret_cast(ppEnumStorage) ); if( FAILED(hr) ) { delete pEnumObj; } else { // wcscpy(pEnumObj->m_wcsPath, m_wcsName); hr = StringCbCopyW(pEnumObj->m_wcsPath, ARRAYSIZE(pEnumObj->m_wcsPath), m_wcsName); if (FAILED(hr)) { (*ppEnumStorage)->Release(); *ppEnumStorage = NULL; goto Error; } } Error: hrLogDWORD("IMDSPDevice::EnumStorage returned 0x%08lx", hr, hr); return hr; }