846 lines
25 KiB
C++
846 lines
25 KiB
C++
|
// DvdCheck.cpp : Determine if DVD exists on a system. Differentiate between MCI and DirectShow Solutions
|
||
|
//
|
||
|
// Modified 3/31/99 by Steve Rowe (strowe)
|
||
|
// Modified 2000/10/11 by Glenn Evans (glenne) to hunt down version & company names & MCI solutions
|
||
|
//
|
||
|
|
||
|
#include <streams.h>
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <initguid.h>
|
||
|
#include <uuids.h>
|
||
|
#include "DvdDetect.h"
|
||
|
#include "crc32.h"
|
||
|
|
||
|
DVDResult::DVDResult()
|
||
|
: m_pName( NULL )
|
||
|
, m_pCompanyName( NULL )
|
||
|
, m_Version( 0 )
|
||
|
, m_fFound( false )
|
||
|
, m_dwCRC( 0 )
|
||
|
{
|
||
|
SetName( TEXT("") );
|
||
|
SetCompanyName( TEXT("") );
|
||
|
}
|
||
|
|
||
|
DVDResult::~DVDResult()
|
||
|
{
|
||
|
delete [] m_pName;
|
||
|
delete [] m_pCompanyName;
|
||
|
}
|
||
|
|
||
|
static TCHAR* CloneString( const TCHAR* pStr )
|
||
|
{
|
||
|
if( pStr ) {
|
||
|
TCHAR* pNew = new TCHAR[ lstrlen( pStr ) +1 ];
|
||
|
if( pNew ) {
|
||
|
lstrcpy( pNew, pStr );
|
||
|
return pNew;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void DVDResult::SetVersion( UINT64 version)
|
||
|
{
|
||
|
m_Version = version;
|
||
|
}
|
||
|
|
||
|
void DVDResult::SetCRC( DWORD CRC)
|
||
|
{
|
||
|
m_dwCRC = CRC;
|
||
|
}
|
||
|
|
||
|
void DVDResult::SetFound( bool fFound)
|
||
|
{
|
||
|
m_fFound = fFound;
|
||
|
}
|
||
|
|
||
|
void DVDResult::SetName( const TCHAR* pName )
|
||
|
{
|
||
|
delete [] m_pName;
|
||
|
m_pName = CloneString( pName );
|
||
|
}
|
||
|
|
||
|
void DVDResult::SetCompanyName( const TCHAR* pName )
|
||
|
{
|
||
|
delete [] m_pCompanyName;
|
||
|
m_pCompanyName = CloneString( pName );
|
||
|
}
|
||
|
|
||
|
// NOTE: strmiids.lib is necessary for this to compile.
|
||
|
|
||
|
// This is the KSProxy GUID.
|
||
|
DEFINE_GUID(DVD_KSPROXY, 0x17CCA71BL, 0xECD7, 0x11D0, 0xB9, 0x08, 0x00, 0xA0, 0xC9, 0x22,
|
||
|
0x31, 0x96);
|
||
|
|
||
|
|
||
|
//
|
||
|
// More or less lifted from the w2k SFP code
|
||
|
//
|
||
|
struct LANGANDCODEPAGE {
|
||
|
WORD wLanguage;
|
||
|
WORD wCodePage;
|
||
|
};
|
||
|
|
||
|
static HRESULT GetFileVersion (const TCHAR* pszFile, // file
|
||
|
DVDResult& result
|
||
|
)
|
||
|
{
|
||
|
DWORD dwSize, dwHandle;
|
||
|
VS_FIXEDFILEINFO *pFixedVersionInfo;
|
||
|
DWORD dwVersionInfoSize;
|
||
|
DWORD dwReturnCode;
|
||
|
|
||
|
dwSize = GetFileVersionInfoSize( const_cast<TCHAR *>(pszFile), &dwHandle);
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
// .txt and .inf, etc files might not have versions
|
||
|
if( dwSize == 0 )
|
||
|
{
|
||
|
dwReturnCode = GetLastError();
|
||
|
hr = E_FAIL;
|
||
|
} else {
|
||
|
LPVOID pVersionInfo= new BYTE [dwSize];
|
||
|
|
||
|
if( NULL == pVersionInfo) {
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
} else {
|
||
|
if( !GetFileVersionInfo( const_cast<TCHAR *>(pszFile), dwHandle, dwSize, pVersionInfo ) ) {
|
||
|
dwReturnCode = GetLastError();
|
||
|
DbgLog((LOG_ERROR, 1, TEXT("Error in GetFileVersionInfo for %s. ec=%d"),
|
||
|
pszFile, dwReturnCode));
|
||
|
hr = E_FAIL;
|
||
|
} else {
|
||
|
if( !VerQueryValue( pVersionInfo,
|
||
|
TEXT("\\"), // we need the root block
|
||
|
(LPVOID *) &pFixedVersionInfo,
|
||
|
(PUINT) &dwVersionInfoSize ) )
|
||
|
{
|
||
|
dwReturnCode = GetLastError();
|
||
|
hr = E_FAIL;
|
||
|
} else {
|
||
|
result.SetVersion( ((UINT64(pFixedVersionInfo->dwFileVersionMS))<<32) + pFixedVersionInfo->dwFileVersionLS);
|
||
|
}
|
||
|
|
||
|
// Structure used to store enumerated languages and code pages.
|
||
|
|
||
|
|
||
|
// Read the list of languages and code pages.
|
||
|
LANGANDCODEPAGE *lpTranslate;
|
||
|
UINT cbTranslate;
|
||
|
|
||
|
if( VerQueryValue(pVersionInfo,
|
||
|
TEXT("\\VarFileInfo\\Translation"),
|
||
|
(LPVOID*)&lpTranslate,
|
||
|
&cbTranslate) )
|
||
|
{
|
||
|
|
||
|
// Read the file description for each language and code page.
|
||
|
|
||
|
for( DWORD i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++ )
|
||
|
{
|
||
|
TCHAR SubBlock[512];
|
||
|
// Retrieve file description for language and code page "i".
|
||
|
TCHAR* lpBuffer;
|
||
|
UINT dwBytes;
|
||
|
wsprintf( SubBlock,
|
||
|
TEXT("\\StringFileInfo\\%04x%04x\\CompanyName"),
|
||
|
lpTranslate[i].wLanguage,
|
||
|
lpTranslate[i].wCodePage);
|
||
|
|
||
|
|
||
|
if( VerQueryValue(pVersionInfo,
|
||
|
SubBlock,
|
||
|
(VOID **)&lpBuffer,
|
||
|
&dwBytes) )
|
||
|
{
|
||
|
result.SetCompanyName( lpBuffer );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] pVersionInfo;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static bool EndsIn( const TCHAR* pStr, const TCHAR* pExtension )
|
||
|
{
|
||
|
int iExtLen = lstrlen( pExtension );
|
||
|
int iStrLen = lstrlen( pStr );
|
||
|
if( iStrLen >= iExtLen ) {
|
||
|
return lstrcmp( &pStr[iStrLen - iExtLen], pExtension ) == 0;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define MAKE_VER_NUM( v1, v2, v3, v4 ) (((UINT64(v1) << 16 | (v2) )<<16 | (v3) ) << 16 | (v4) )
|
||
|
|
||
|
static void StrCpy( TCHAR* pDest, WCHAR* pSrc, int iDestSize )
|
||
|
{
|
||
|
#ifdef UNICODE
|
||
|
lstrcpyn(pDest, pSrc, iDestSize );
|
||
|
#else
|
||
|
WideCharToMultiByte(
|
||
|
CP_ACP,
|
||
|
0, // flags
|
||
|
pSrc, // src
|
||
|
-1, // cch
|
||
|
pDest, // dest
|
||
|
iDestSize, // cb
|
||
|
0, // lpDefaultChar
|
||
|
0); // lpUsedDefaultChar
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static HRESULT GetFilenameFromCLSID( const VARIANT& varFilterClsid, TCHAR szFilename[ MAX_PATH ] )
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
TCHAR szKey[512];
|
||
|
TCHAR szQuery[512];
|
||
|
|
||
|
// Convert BSTR to string and free variant storage
|
||
|
StrCpy( szQuery, varFilterClsid.bstrVal, sizeof( szQuery )/ sizeof( szQuery[0]) );
|
||
|
SysFreeString(varFilterClsid.bstrVal);
|
||
|
|
||
|
// Create key name for reading filename registry
|
||
|
wsprintf(szKey, TEXT("Software\\Classes\\CLSID\\%s\\InprocServer32\0"),
|
||
|
szQuery);
|
||
|
|
||
|
// Variables needed for registry query
|
||
|
HKEY hkeyFilter=0;
|
||
|
DWORD dwSize=MAX_PATH;
|
||
|
int rc=0;
|
||
|
|
||
|
// Open the CLSID key that contains information about the filter
|
||
|
rc = RegOpenKey(HKEY_LOCAL_MACHINE, szKey, &hkeyFilter);
|
||
|
if (rc == ERROR_SUCCESS)
|
||
|
{
|
||
|
rc = RegQueryValueEx(hkeyFilter, NULL, // Read (Default) value
|
||
|
NULL, NULL, (BYTE *)szFilename, &dwSize);
|
||
|
|
||
|
if (rc == ERROR_SUCCESS)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
rc = RegCloseKey(hkeyFilter);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static const TCHAR* FilenameFromPathname( const TCHAR* pStr )
|
||
|
{
|
||
|
const TCHAR* pSlash=0;
|
||
|
const TCHAR* pOrig = pStr;
|
||
|
while( *pStr ) {
|
||
|
if( *pStr == TEXT('\\') ) {
|
||
|
pSlash = pStr;
|
||
|
}
|
||
|
pStr++;
|
||
|
}
|
||
|
if( pSlash ) {
|
||
|
return pSlash +1;
|
||
|
} else {
|
||
|
return pOrig;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool GetFileLength( const TCHAR* pszPathname, ULONGLONG* pullFileSize )
|
||
|
{
|
||
|
WIN32_FIND_DATA wInfo;
|
||
|
|
||
|
HANDLE hInfo = FindFirstFile( pszPathname, &wInfo );
|
||
|
FindClose(hInfo);
|
||
|
if (hInfo!=INVALID_HANDLE_VALUE) {
|
||
|
if( pullFileSize ) {
|
||
|
*pullFileSize = (ULONGLONG(wInfo.nFileSizeHigh) << 32) | wInfo.nFileSizeLow;
|
||
|
}
|
||
|
return true;
|
||
|
} else {
|
||
|
if( pullFileSize ) {
|
||
|
*pullFileSize = 0;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static DWORD CRCFromFile( const TCHAR* pFile )
|
||
|
{
|
||
|
ULONGLONG length;
|
||
|
DWORD dwCRC = 0;
|
||
|
|
||
|
if( GetFileLength( pFile, &length )) {
|
||
|
BYTE* pBuffer = new BYTE[ULONG(length)];
|
||
|
if( pBuffer ) {
|
||
|
HANDLE hFile = ::CreateFile( pFile,
|
||
|
GENERIC_READ, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if( hFile != INVALID_HANDLE_VALUE ) {
|
||
|
DWORD dwActual;
|
||
|
if( ReadFile( hFile, pBuffer, ULONG(length), &dwActual, NULL ) ) {
|
||
|
dwCRC=CRC32( pBuffer, ULONG(length));
|
||
|
}
|
||
|
CloseHandle( hFile );
|
||
|
}
|
||
|
delete [] pBuffer;
|
||
|
}
|
||
|
}
|
||
|
return dwCRC;
|
||
|
}
|
||
|
|
||
|
static HRESULT AddSWFilter( IMoniker* pMon, DVDResult& result )
|
||
|
{
|
||
|
IPropertyBag *pPropBag;
|
||
|
HRESULT hr = pMon->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
|
||
|
if(SUCCEEDED(hr))
|
||
|
{
|
||
|
CLSID filterCLSID;
|
||
|
HRESULT hrGotFilename=E_FAIL;
|
||
|
TCHAR szFilename[ MAX_PATH ];
|
||
|
|
||
|
// open clsid/{filter-clsid} key
|
||
|
VARIANT varbstrClsid;
|
||
|
varbstrClsid.vt = VT_BSTR;
|
||
|
varbstrClsid.bstrVal = 0;
|
||
|
hr = pPropBag->Read(L"CLSID", &varbstrClsid, 0);
|
||
|
if(SUCCEEDED(hr)) {
|
||
|
ASSERT(varbstrClsid.vt == VT_BSTR);
|
||
|
WCHAR *strFilter = varbstrClsid.bstrVal;
|
||
|
|
||
|
if (CLSIDFromString(varbstrClsid.bstrVal, &filterCLSID) == S_OK) {
|
||
|
}
|
||
|
hrGotFilename = GetFilenameFromCLSID( varbstrClsid, szFilename );
|
||
|
|
||
|
SysFreeString(varbstrClsid.bstrVal);
|
||
|
}
|
||
|
|
||
|
pPropBag->Release();
|
||
|
|
||
|
if ( DVD_KSPROXY == filterCLSID || CLSID_AVIDec == filterCLSID ) {
|
||
|
; // ignore this filter if it is the AVI Decompressor or KSProxy
|
||
|
} else {
|
||
|
|
||
|
if( S_OK == hrGotFilename ) {
|
||
|
HRESULT hres = GetFileVersion( szFilename, result );
|
||
|
if( S_OK == hres ) {
|
||
|
result.SetName( FilenameFromPathname( szFilename ));
|
||
|
result.SetFound( true );
|
||
|
result.SetCRC( CRCFromFile( szFilename ));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
DbgLog((LOG_ERROR, 1, TEXT("ERROR: WARNING: BindToStorage failed"))) ;
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT AddHWFilter( IMoniker* pMon, DVDResult& result )
|
||
|
{
|
||
|
IPropertyBag *pPropBag;
|
||
|
HRESULT hr = pMon->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
|
||
|
if(SUCCEEDED(hr))
|
||
|
{
|
||
|
result.SetFound( true );
|
||
|
|
||
|
VARIANT var ;
|
||
|
var.vt = VT_EMPTY ;
|
||
|
hr = pPropBag->Read(L"FriendlyName", &var, 0) ;
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
DbgLog((LOG_TRACE, 5, TEXT("FriendlyName: %S"), var.bstrVal)) ;
|
||
|
|
||
|
//
|
||
|
// We have got a device under the required category. The proxy
|
||
|
// for it is already instantiated. So add to the list of HW
|
||
|
// decoders to be used for building the graph.
|
||
|
//
|
||
|
TCHAR szName[512];
|
||
|
|
||
|
// Convert BSTR to string and free variant storage
|
||
|
StrCpy( szName, var.bstrVal, sizeof( szName )/ sizeof( szName[0]) );
|
||
|
|
||
|
result.SetName( szName );
|
||
|
VariantClear(&var) ;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(SUCCEEDED(hr)) ; // so that we know
|
||
|
result.SetName( TEXT("generic HW") );
|
||
|
}
|
||
|
pPropBag->Release();
|
||
|
} else {
|
||
|
DbgLog((LOG_ERROR, 1, TEXT("ERROR: WARNING: BindToStorage failed"))) ;
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Check if there are any SW DVD Decoders
|
||
|
static HRESULT SWCheck(DVDResult& result)
|
||
|
{
|
||
|
HRESULT hres = S_OK; // result for the function
|
||
|
HRESULT hr; // temporary result...
|
||
|
|
||
|
// Create filter mapper to find software decoders
|
||
|
IFilterMapper2 * pMapper ; // filter mapper object pointer
|
||
|
hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC,
|
||
|
IID_IFilterMapper2, (LPVOID *)&pMapper) ;
|
||
|
if (FAILED(hr) || NULL == pMapper)
|
||
|
{
|
||
|
DbgLog((LOG_ERROR, 0,
|
||
|
TEXT("ERROR: Couldn't create class FilterMapper for DVD SW Dec category (Error 0x%lx)"),
|
||
|
hr)) ;
|
||
|
ASSERT( !"Can't create filtermapper" );
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
|
||
|
const UINT NUM_TYPES = 3; // the number of type/subtype pairs
|
||
|
|
||
|
GUID types[NUM_TYPES * 2];
|
||
|
types[0] = MEDIATYPE_MPEG2_PES;
|
||
|
types[1] = MEDIASUBTYPE_MPEG2_VIDEO;
|
||
|
types[2] = MEDIATYPE_DVD_ENCRYPTED_PACK;
|
||
|
types[3] = MEDIASUBTYPE_MPEG2_VIDEO;
|
||
|
types[4] = MEDIATYPE_Video;
|
||
|
types[5] = MEDIASUBTYPE_MPEG2_VIDEO;
|
||
|
|
||
|
IEnumMoniker *pEnumMon = NULL ;
|
||
|
|
||
|
hr = pMapper->EnumMatchingFilters(&pEnumMon, 0, TRUE, MERIT_DO_NOT_USE+1,
|
||
|
TRUE, NUM_TYPES, types, NULL, NULL, FALSE, TRUE, 0, NULL,
|
||
|
NULL, NULL);
|
||
|
|
||
|
if (FAILED(hr) || NULL == pEnumMon)
|
||
|
{
|
||
|
DbgLog((LOG_ERROR, 1, TEXT("ERROR: No matching filter enum found (Error 0x%lx)"), hr)) ;
|
||
|
pMapper->Release();
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
|
||
|
// now we check if we have a solution.
|
||
|
// we ignore the AVI decompressor and KSProxy CLSID's.
|
||
|
ULONG ul ;
|
||
|
IMoniker *pMon = NULL;
|
||
|
|
||
|
while ( S_OK == pEnumMon->Next(1, &pMon, &ul) && 1 == ul)
|
||
|
{
|
||
|
hres = AddSWFilter( pMon, result );
|
||
|
}
|
||
|
|
||
|
// clean up after ourselves
|
||
|
|
||
|
if (NULL != pEnumMon) {
|
||
|
pEnumMon->Release();
|
||
|
}
|
||
|
if (NULL != pMapper) {
|
||
|
pMapper->Release();
|
||
|
}
|
||
|
if( hres == S_OK && !result.Found()==false ) {
|
||
|
return S_FALSE;
|
||
|
} else {
|
||
|
return hres;
|
||
|
}
|
||
|
} // end of SWCheck
|
||
|
|
||
|
static TCHAR CharUpper( TCHAR c )
|
||
|
{
|
||
|
// see the docs on CharUpper, single char if upper address WORD is NULL
|
||
|
return (TCHAR) CharUpper( (LPTSTR)c );
|
||
|
}
|
||
|
|
||
|
static void Truncate( TCHAR* pStr, const TCHAR* pTruncateAt )
|
||
|
{
|
||
|
int iTruncLen = lstrlen( pTruncateAt );
|
||
|
int iStrLen = lstrlen( pStr );
|
||
|
|
||
|
for( int i=0; i < iStrLen - iTruncLen; i++ ) {
|
||
|
for( int j=0; j < iTruncLen; j++ ) {
|
||
|
if( CharUpper( pStr[i+j] ) != pTruncateAt[j] ) {
|
||
|
goto NextLoop;
|
||
|
}
|
||
|
}
|
||
|
pStr[i+iTruncLen]=TEXT('\0');
|
||
|
break;
|
||
|
NextLoop:
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Determine if there are any MCI DVD devices present
|
||
|
static HRESULT MCICheck( DVDResult& result)
|
||
|
{
|
||
|
TCHAR lpRetString[255];
|
||
|
LPCTSTR lpDefault = TEXT("*!*");
|
||
|
|
||
|
GetPrivateProfileString(
|
||
|
TEXT("mci"),
|
||
|
TEXT("DVDVideo"),
|
||
|
lpDefault,
|
||
|
lpRetString,
|
||
|
255,
|
||
|
TEXT("system.ini") );
|
||
|
|
||
|
if (lstrcmp(lpRetString, lpDefault)) {// if they are not the same
|
||
|
// truncate ret string to *.drv
|
||
|
Truncate( lpRetString, TEXT(".DRV") );
|
||
|
|
||
|
result.SetName( lpRetString );
|
||
|
result.SetFound( true );
|
||
|
|
||
|
// try to hunt down version info
|
||
|
//
|
||
|
// File is in system
|
||
|
//
|
||
|
// Create key name for reading filename registry
|
||
|
TCHAR szPathname[MAX_PATH];
|
||
|
GetSystemDirectory( szPathname, sizeof(szPathname)/sizeof(szPathname[0]));
|
||
|
lstrcat( szPathname, TEXT("\\") );
|
||
|
lstrcat( szPathname, lpRetString );
|
||
|
|
||
|
HRESULT hres = GetFileVersion( szPathname, result );
|
||
|
if( S_OK == hres ) {
|
||
|
result.SetCRC( CRCFromFile( szPathname ));
|
||
|
} else {
|
||
|
GetWindowsDirectory( szPathname, sizeof(szPathname)/sizeof(szPathname[0]));
|
||
|
lstrcat( szPathname, TEXT("\\") );
|
||
|
lstrcat( szPathname, lpRetString );
|
||
|
hres = GetFileVersion( szPathname, result );
|
||
|
if( S_OK == hres ) {
|
||
|
result.SetCRC( CRCFromFile( szPathname ));
|
||
|
} else {
|
||
|
ASSERT(!"Can't find DVD MCI filename");
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
return hres;
|
||
|
} else {
|
||
|
return S_FALSE; // can't really fail this check
|
||
|
}
|
||
|
} // end of MCICheck
|
||
|
|
||
|
// Check if there are any HW DVD Decoders
|
||
|
// we use DevEnum to check for DVDHWDecodersCategory
|
||
|
static HRESULT HWCheck(DVDResult& result)
|
||
|
{
|
||
|
ICreateDevEnum *pCreateDevEnum ;
|
||
|
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_ICreateDevEnum, (void**)&pCreateDevEnum) ;
|
||
|
if (FAILED(hr) || NULL == pCreateDevEnum)
|
||
|
{
|
||
|
DbgLog((LOG_ERROR, 0, TEXT("WARNING: Couldn't create system dev enum (Error 0x%lx)"), hr)) ;
|
||
|
// need to put some better error handling in here
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
|
||
|
IEnumMoniker *pEnumMon ;
|
||
|
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_DVDHWDecodersCategory,
|
||
|
&pEnumMon, 0) ;
|
||
|
pCreateDevEnum->Release() ;
|
||
|
|
||
|
if (S_FALSE == hr )
|
||
|
{
|
||
|
// should indicate that we have no enumerator but not an error because
|
||
|
// this means there are no such devices.
|
||
|
|
||
|
DbgLog((LOG_ERROR, 0,
|
||
|
TEXT("WARNING: Couldn't create class enum for DVD HW Dec category (Error 0x%lx)"),
|
||
|
hr)) ;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
if (FAILED(hr) || NULL == pEnumMon)
|
||
|
{
|
||
|
DbgLog((LOG_ERROR, 0,
|
||
|
TEXT("ERROR: Couldn't create class enum for DVD HW Dec category (Error 0x%lx)"),
|
||
|
hr)) ;
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
|
||
|
hr = pEnumMon->Reset() ;
|
||
|
|
||
|
ULONG ul ;
|
||
|
IMoniker *pMon ;
|
||
|
|
||
|
if (S_OK == pEnumMon->Next(1, &pMon, &ul) && 1 == ul) {
|
||
|
// Always comes back as WDM generic Filter Proxy (ksproxy.ax)
|
||
|
hr = AddHWFilter( pMon, result );
|
||
|
}
|
||
|
|
||
|
if (NULL != pEnumMon) {
|
||
|
pEnumMon->Release();
|
||
|
}
|
||
|
|
||
|
return result.Found() ? S_OK : S_FALSE;
|
||
|
} // end of HWCheck
|
||
|
|
||
|
HRESULT DVDDetect( DVDResult& mci, DVDResult& sw, DVDResult& hw )
|
||
|
// Primary entry point - will determine if any DVD is present
|
||
|
{
|
||
|
HRESULT hres = CoInitialize(NULL);
|
||
|
if( SUCCEEDED( hres )) {
|
||
|
//
|
||
|
// DSHOW HARDWARE CHECK
|
||
|
//
|
||
|
hres = HWCheck(hw);
|
||
|
if( SUCCEEDED( hres)) {
|
||
|
//
|
||
|
// DSHOW SOFTWARE CHECK
|
||
|
//
|
||
|
hres = SWCheck(sw);
|
||
|
if( SUCCEEDED( hres)) {
|
||
|
//
|
||
|
// MCI CHECK
|
||
|
//
|
||
|
hres = MCICheck(mci);
|
||
|
}
|
||
|
}
|
||
|
CoUninitialize();
|
||
|
} else {
|
||
|
ASSERT( !"Can't CoInit" );
|
||
|
}
|
||
|
|
||
|
return hres;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Decoder exe CompanyName version crc
|
||
|
//
|
||
|
// Ravisent 2000 "QIDVDF.AX" "RAVISENT Technologies Inc." 1.2.0.115 0x786cd58f
|
||
|
// Ravisent (ATIPlayer) "QIDVDF.AX" "Quadrant International, Inc." 1.1.0.37 0x41a24b10
|
||
|
// Intervideo 1.31 (WinDVD) "IVIVIDEO.AX" " InterVideo Inc." 1.0.0.1 0xadf3b652 Will have to get version through registry
|
||
|
// Intervideo 2.111 (WinDVD) "IVIVIDEO.AX" " InterVideo Inc." 1.0.0.1 0x42b020f8
|
||
|
// Mediamatics "DVD Express AV Decoder.dll" "Mediamatics, Inc." 5.0.0.15
|
||
|
// 5.0.0.38
|
||
|
// 5.0.0.42
|
||
|
// 5.0.0.45
|
||
|
// 5.0.0.75 0xdf2fa5df
|
||
|
// 5.0.0.97
|
||
|
//
|
||
|
// MGI (SoftDVDMax) "ZVIDFLT.AX" "MGI Software Corp." 1.0.0.1 0x4d91884f
|
||
|
//
|
||
|
// MGI Build 00089 "DVDVideo.ax" "MGI Software Corp." 1.8.6.0 0x6bc2c0d4 can't find setup.ddl in Win2k, causes error in Whistler
|
||
|
// MGI Build 33959 "zvidflt.ax" "MGI Software Corp." 1.0.0.1 0x0482b35b works fine in both win2k and whistler
|
||
|
// MGI karaoke "zvidflt.ax" "MGI Software Corp." 1.0.0.1 0x4d91884f no disk in drive error on whistler, no windows 2000 compatible decoder on win2k
|
||
|
// MGI 4.00.0001 "zvidflt.ax" "MGI Software Corp." 1.0.0.1 0xd6a7abc3 works fin in both Win2k and Whistler
|
||
|
// MGI 5.0DH "zvidflt.ax" "MGI Software Corp." 1.0.0.1 0xd66f9452 works fine in both win2k and whistler
|
||
|
//
|
||
|
// Zoran 3.07.01 "Softpeg.drv" "" 0.0.0.0 0x0 Win2k cant install, whistler says software ot configured to run on your machine
|
||
|
// Zoran 3.25.02 "zvidflt.ax" "Zoran/CompCore Corp." 1.0.0.0 0x1e44e3d6 DVD Authentication error in whistler, no video or error in win2k
|
||
|
// Zoran 3.25.03 "Softpeg.drv" "" 0.0.0.0 0x0 drive not ready or no disk in drive on both win2k and whistler
|
||
|
// Cyberlink PowerDVD 2.50 "clvsd.ax" "CyberLink Corp." 2.0.0.0 0x964ef3b9 Generates errors in all dshow players in both Win2k and Whistler
|
||
|
// Cyberlink PowerDVD 2.5.5 "clvsd.ax "CyberLink Corp." 2.0.0.0 0x964ef3b9 Works fine in Whistler, Win2k there's no video
|
||
|
|
||
|
#define MAKE_VER_NUM( v1, v2, v3, v4 ) (((UINT64(v1) << 16 | (v2) )<<16 | (v3) ) << 16 | (v4) )
|
||
|
|
||
|
static bool AreEquivalent( const char* pStr1, const char* pStr2 )
|
||
|
{
|
||
|
return lstrcmpiA( pStr1, pStr2 ) ==0;
|
||
|
}
|
||
|
|
||
|
static bool AreEquivalent( const WCHAR* pStr1, const WCHAR* pStr2 )
|
||
|
{
|
||
|
return lstrcmpiW( pStr1, pStr2 ) ==0;
|
||
|
}
|
||
|
|
||
|
DecoderVendor DVDResult::GetVendor() const
|
||
|
{
|
||
|
const TCHAR* pName = GetCompanyName();
|
||
|
|
||
|
//TBD: vendor_NEC
|
||
|
|
||
|
if( AreEquivalent( pName, TEXT("Mediamatics, Inc.") )) {
|
||
|
return vendor_MediaMatics;
|
||
|
} else if( AreEquivalent( pName, TEXT(" InterVideo Inc.")) ) {
|
||
|
return vendor_Intervideo;
|
||
|
} else if( AreEquivalent( pName, TEXT("RAVISENT Technologies Inc.")) ||
|
||
|
AreEquivalent( pName, TEXT("Quadrant International, Inc.")) )
|
||
|
{
|
||
|
return vendor_Ravisent;
|
||
|
} else if( AreEquivalent( pName, TEXT("CyberLink Corp.")) ) {
|
||
|
return vendor_CyberLink;
|
||
|
} else if( AreEquivalent( pName, TEXT("MGI Software Corp.")) ||
|
||
|
AreEquivalent( pName, TEXT("Zoran/CompCore Corp.")) )
|
||
|
{
|
||
|
return vendor_MGI;
|
||
|
} else {
|
||
|
return vendor_Unknown;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
//
|
||
|
// only valid AFTER GUI setup and at first boot
|
||
|
//
|
||
|
static bool IsWin9xUpgrade()
|
||
|
{
|
||
|
// The answer file has the answer (system32\$winnt$.inf)
|
||
|
// [data]
|
||
|
// win9xupgrade=yes
|
||
|
TCHAR dir[MAX_PATH];
|
||
|
UINT uiResult = GetWindowsDirectory( dir, sizeof(dir)/sizeof(dir[0]) );
|
||
|
if( uiResult > 0 ) {
|
||
|
lstrcat( dir, TEXT("\\system32\\$winnt$.inf") );
|
||
|
TCHAR buffer[100];
|
||
|
buffer[0]=0;
|
||
|
DWORD dwResult = GetPrivateProfileString(
|
||
|
TEXT("data"), // section name
|
||
|
TEXT("win9xupgrade"), // key name
|
||
|
TEXT(""), // default string
|
||
|
buffer, // destination buffer
|
||
|
sizeof(buffer)/sizeof(buffer[0]), // size of destination buffer in TCHARS
|
||
|
dir );// initialization file name
|
||
|
|
||
|
bool fResult = ( dwResult > 0 ) && AreEquivalent( buffer, TEXT("yes") );
|
||
|
|
||
|
return fResult;
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static bool DoesAspi32Exist( bool fWillBe9xUpgrade )
|
||
|
{
|
||
|
if( fWillBe9xUpgrade ) {
|
||
|
return false;
|
||
|
} else {
|
||
|
TCHAR szPathname[MAX_PATH];
|
||
|
GetWindowsDirectory( szPathname, sizeof(szPathname)/sizeof(szPathname[0]));
|
||
|
//
|
||
|
// NOTE: this will fail under Win9x (since its system\drivers) implying there isn't going
|
||
|
// to be an aspi32 under Whislter (blocked & deleted). So under 9x, we return true
|
||
|
// and under Whistler their apps only survive if its around.
|
||
|
//
|
||
|
lstrcat( szPathname, TEXT("\\system32\\drivers\\aspi32.sys") );
|
||
|
ULONGLONG ullLength;
|
||
|
return GetFileLength( szPathname, &ullLength );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool DVDResult::ShouldUpgrade( bool fWillBe9xUpgrade ) const
|
||
|
{
|
||
|
switch( GetVendor() ) {
|
||
|
case vendor_MediaMatics:
|
||
|
return GetVersion() <= MAKE_VER_NUM( 5,1,0,5 ); // TBD: arbitrary right now for debugging purposes
|
||
|
|
||
|
// return GetVersion() <= MAKE_VER_NUM( 5,0,0,15 );
|
||
|
|
||
|
case vendor_Intervideo:
|
||
|
{
|
||
|
// so far all versions require aspi32
|
||
|
if( GetVersion() <= MAKE_VER_NUM(1,0,0,1 )) {
|
||
|
bool fAspiExists = DoesAspi32Exist(fWillBe9xUpgrade);
|
||
|
if( !fAspiExists ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
// all unknown versions are 1.0.0.1
|
||
|
// We correct the known ones to numbers above that
|
||
|
switch( GetCRC()) {
|
||
|
case 0xadf3b652: // 1.31.0.0
|
||
|
return false;
|
||
|
case 0x42b020f8: // 2.111.0.0
|
||
|
return false;
|
||
|
default:
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DVDResult* DVDDetectBuffer::Detect()
|
||
|
{
|
||
|
if(SUCCEEDED(DVDDetect( mci, sw, hw )))
|
||
|
{
|
||
|
const DVDResult* pResult;
|
||
|
|
||
|
if( sw.Found()) {
|
||
|
return &sw;
|
||
|
} else if( mci.Found()) {
|
||
|
return &mci;
|
||
|
} else {
|
||
|
// hardware, so we don't care about the decoder OR the app for now
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
HRESULT DVDDetectBuffer::DetectAll()
|
||
|
{
|
||
|
return DVDDetect( mci, sw, hw );
|
||
|
}
|
||
|
|
||
|
#define RUN_KEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run")
|
||
|
|
||
|
static bool RegSetRunValue( const TCHAR* pString, const TCHAR* pValue )
|
||
|
{
|
||
|
HKEY hRunKey;
|
||
|
LONG lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RUN_KEY, 0, KEY_SET_VALUE, &hRunKey) ;
|
||
|
if (ERROR_SUCCESS == lRet) {
|
||
|
lRet = RegSetValueEx(hRunKey, pString, 0, REG_SZ, (BYTE *)pValue, sizeof(pValue[0])*(lstrlen(pValue)+1) );
|
||
|
RegCloseKey( hRunKey );
|
||
|
return ( ERROR_SUCCESS == lRet);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool RegRunDeleteValue( const TCHAR* pString )
|
||
|
{
|
||
|
HKEY hRunKey;
|
||
|
LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RUN_KEY, 0, KEY_SET_VALUE, &hRunKey) ;
|
||
|
if (ERROR_SUCCESS == lRet) {
|
||
|
lRet = RegDeleteValue( hRunKey, pString ) ;
|
||
|
// either we nuked it or it didn't exist in the first place, either way its gone
|
||
|
bool fWorked = ( lRet == ERROR_SUCCESS || lRet == ERROR_FILE_NOT_FOUND );
|
||
|
ASSERT( fWorked ) ;
|
||
|
RegCloseKey( hRunKey );
|
||
|
return fWorked;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#define DVDUPGRADE_NAME TEXT("DVDUpgrade")
|
||
|
bool DVDDetectSetupRun::Remove()
|
||
|
{
|
||
|
bool fOk = RegRunDeleteValue( DVDUPGRADE_NAME );
|
||
|
ASSERT(fOk);
|
||
|
return fOk;
|
||
|
}
|
||
|
|
||
|
bool DVDDetectSetupRun::Add()
|
||
|
{
|
||
|
bool fOk;
|
||
|
fOk = RegSetRunValue( DVDUPGRADE_NAME, TEXT("DVDUpgrd.exe /upgrade"));
|
||
|
ASSERT(fOk);
|
||
|
return fOk;
|
||
|
}
|
||
|
|
||
|
extern "C" BOOL
|
||
|
IsDvdPresent (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
DVDDetectBuffer buffer;
|
||
|
const DVDResult* pResult = buffer.Detect();
|
||
|
if( pResult ) {
|
||
|
const DVDResult& result = *pResult;
|
||
|
// at this point the $winnt$.inf doesn't exist, so we must assume
|
||
|
// that we're bing called from the win95upg
|
||
|
const bool fInWin9xUpgrade = true;
|
||
|
return result.ShouldUpgrade(fInWin9xUpgrade);
|
||
|
}
|
||
|
return false;
|
||
|
}
|