Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

729 lines
20 KiB

  1. //@@@@AUTOBLOCK+============================================================;
  2. //
  3. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. // PURPOSE.
  7. //
  8. // File: medialoc.cpp
  9. //
  10. // Copyright (c) Microsoft Corporation. All Rights Reserved.
  11. //
  12. //@@@@AUTOBLOCK-============================================================;
  13. // MediaLocator.cpp : Implementation of CMediaLocator
  14. #include "stdafx.h"
  15. #include <qeditint.h>
  16. #include <qedit.h>
  17. #include "..\util\dexmisc.h"
  18. #include "MediaLoc.h"
  19. #define STRSAFE_NO_DEPRECATE
  20. #include <strsafe.h>
  21. #define MAX_FILTER_STRING 1024
  22. const long DEFAULT_DIRECTORIES = 8;
  23. const TCHAR * gszRegistryLoc = TEXT("Software\\Microsoft\\ActiveMovie\\MediaLocator");
  24. CMediaLocator::CMediaLocator( )
  25. {
  26. m_bUseLocal = FALSE;
  27. HKEY hKey = NULL;
  28. // create the key just to make sure it's there
  29. //
  30. long Result = RegCreateKeyEx(
  31. HKEY_CURRENT_USER, // key
  32. gszRegistryLoc, // sub key
  33. 0, // reserved
  34. NULL, // lpClass
  35. 0, // options
  36. KEY_EXECUTE | KEY_QUERY_VALUE,
  37. NULL, // default ACL
  38. &hKey,
  39. NULL ); // disp
  40. if( Result == ERROR_SUCCESS )
  41. {
  42. // go find out if we're supposed to look locally
  43. //
  44. DWORD Size = sizeof( long );
  45. DWORD Type = REG_DWORD;
  46. long UseLocal = 0;
  47. Result = RegQueryValueEx(
  48. hKey,
  49. TEXT("UseLocal"),
  50. 0, // reserved
  51. &Type,
  52. (BYTE*) &UseLocal,
  53. &Size );
  54. if( Result == ERROR_SUCCESS )
  55. {
  56. m_bUseLocal = UseLocal;
  57. }
  58. RegCloseKey( hKey );
  59. }
  60. }
  61. /////////////////////////////////////////////////////////////////////////////
  62. // FindMediaFile - try to find the media file using some cacheing mechanism
  63. // Use the registry to hold the caching directories. A return value of S_FALSE
  64. // means the file was replaced with another, a return code of E_FAIL means
  65. // the file couldn't be found anywhere.
  66. /////////////////////////////////////////////////////////////////////////////
  67. //
  68. HRESULT CMediaLocator::FindMediaFile
  69. ( BSTR Input, BSTR FilterString, BSTR * pOutput, long ValidateFlags )
  70. {
  71. CheckPointer( pOutput, E_POINTER );
  72. HRESULT hr;
  73. // don't validate the incoming filename exists, since it very well may
  74. // not (that's the point of this class). We *should* be validating the
  75. // file name isn't a printer or something, but this is better left AFTER
  76. // finding the name, than before
  77. hr = ValidateFilenameIsntNULL( Input);
  78. if( FAILED( hr ) ) return hr;
  79. BOOL UseLocal = ( ( ValidateFlags & SFN_VALIDATEF_USELOCAL ) == SFN_VALIDATEF_USELOCAL );
  80. BOOL WantUI = ( ( ValidateFlags & SFN_VALIDATEF_POPUP ) == SFN_VALIDATEF_POPUP );
  81. BOOL WarnReplace = ( ( ValidateFlags & SFN_VALIDATEF_TELLME ) == SFN_VALIDATEF_TELLME );
  82. BOOL DontFind = ( ( ValidateFlags & SFN_VALIDATEF_NOFIND ) == SFN_VALIDATEF_NOFIND );
  83. UseLocal |= m_bUseLocal ;
  84. // reset this now
  85. //
  86. *pOutput = NULL;
  87. // !!! what if the incoming file is not on a disk, like
  88. // 1) On the web
  89. // 2) On external hardware!
  90. USES_CONVERSION;
  91. TCHAR * tInput = W2T( Input );
  92. BOOL FoundFileAsSpecified = FALSE;
  93. HANDLE hcf = CreateFile(
  94. tInput,
  95. GENERIC_READ, // the point is, we're about to see if we can read it. Don't use "0"
  96. FILE_SHARE_READ, // share mode
  97. NULL, // security
  98. OPEN_EXISTING, // creation disposition
  99. 0, // flags
  100. NULL );
  101. if( INVALID_HANDLE_VALUE != hcf )
  102. {
  103. FoundFileAsSpecified = TRUE;
  104. CloseHandle( hcf );
  105. }
  106. // if we found the file where the user asked and they didn't specify use local
  107. // then return
  108. //
  109. if( FoundFileAsSpecified )
  110. {
  111. if( !UseLocal )
  112. {
  113. hr = ValidateFilename( Input, MAX_PATH, FALSE );
  114. ASSERT( !FAILED( hr ) );
  115. return hr;
  116. }
  117. else
  118. {
  119. // they specified use local and it is local
  120. //
  121. // !!! this isn't good enough.
  122. if( tInput[0] != '\\' || tInput[1] != '\\' )
  123. {
  124. hr = ValidateFilename( Input, MAX_PATH, FALSE );
  125. ASSERT( !FAILED( hr ) );
  126. return hr;
  127. }
  128. }
  129. }
  130. // cut up the filename into little bits
  131. //
  132. TCHAR Drive[_MAX_DRIVE];
  133. TCHAR Dir[_MAX_DIR];
  134. TCHAR Fname[_MAX_FNAME];
  135. TCHAR Ext[_MAX_EXT];
  136. _tsplitpath( tInput, Drive, Dir, Fname, Ext );
  137. TCHAR tNewFileName[_MAX_PATH];
  138. // can't find nothing
  139. //
  140. if( wcslen( Input ) == 0 ) // safe
  141. {
  142. return E_INVALIDARG;
  143. }
  144. // where did we look last?
  145. //
  146. HKEY hKey = NULL;
  147. long Result = RegOpenKeyEx(
  148. HKEY_CURRENT_USER,
  149. gszRegistryLoc ,
  150. 0, // reserved options
  151. KEY_EXECUTE | KEY_SET_VALUE | KEY_QUERY_VALUE,
  152. &hKey );
  153. if( Result != ERROR_SUCCESS )
  154. {
  155. return MAKE_HRESULT( 1, 4, Result );
  156. }
  157. // find out how many cached directories to look in
  158. //
  159. long DirectoryCount = DEFAULT_DIRECTORIES;
  160. DWORD Size = sizeof( long );
  161. DWORD Type = REG_DWORD;
  162. Result = RegQueryValueEx(
  163. hKey,
  164. TEXT("Directories"),
  165. 0, // reserved
  166. &Type,
  167. (BYTE*) &DirectoryCount,
  168. &Size );
  169. if( Result != ERROR_SUCCESS )
  170. {
  171. // if we don't have a count, default to something
  172. //
  173. DirectoryCount = DEFAULT_DIRECTORIES;
  174. }
  175. // bound this to something reasonable
  176. if( DirectoryCount > 32 )
  177. {
  178. DirectoryCount = 32;
  179. }
  180. if( DirectoryCount < 1 )
  181. {
  182. DirectoryCount = 1;
  183. }
  184. while( !DontFind )
  185. {
  186. // look in each directory
  187. //
  188. bool foundit = false;
  189. for( long i = 0 ; i < DirectoryCount ; i++ )
  190. {
  191. TCHAR ValueName[256];
  192. wsprintf( ValueName, TEXT("Directory%2.2ld"), i ); // safe, i can't be > 99
  193. TCHAR DirectoryName[256];
  194. Size = sizeof(DirectoryName);
  195. Type = REG_SZ;
  196. Result = RegQueryValueEx(
  197. hKey,
  198. ValueName,
  199. 0, // reserved
  200. &Type,
  201. (BYTE*) DirectoryName,
  202. &Size );
  203. if( Result != ERROR_SUCCESS )
  204. {
  205. // didn't find it, must not exist, do the next one
  206. //
  207. continue;
  208. }
  209. // found a directory
  210. // build up a new filename
  211. //
  212. size_t Remaining = _MAX_PATH;
  213. TCHAR * tContinue = NULL;
  214. hr = StringCchCopyEx( tNewFileName, Remaining, DirectoryName, &tContinue, &Remaining, 0 );
  215. if( FAILED( hr ) )
  216. {
  217. continue;
  218. }
  219. hr = StringCchCopyEx( tContinue, Remaining, Fname, &tContinue, &Remaining, 0 );
  220. if( FAILED( hr ) )
  221. {
  222. continue;
  223. }
  224. hr = StringCchCopyEx( tContinue, Remaining, Ext, NULL, NULL, 0 );
  225. if( FAILED( hr ) )
  226. {
  227. continue;
  228. }
  229. // if UseLocal is set, and this directory is on the net, then
  230. // ignore it
  231. //
  232. if( UseLocal )
  233. {
  234. // !!! ugly
  235. if( tNewFileName[0] == '\\' && tNewFileName[1] == '\\' )
  236. {
  237. continue;
  238. }
  239. }
  240. HANDLE h = CreateFile(
  241. tNewFileName,
  242. GENERIC_READ, // access
  243. FILE_SHARE_READ, // share mode
  244. NULL, // security
  245. OPEN_EXISTING, // creation disposition
  246. 0, // flags
  247. NULL );
  248. if( INVALID_HANDLE_VALUE == h )
  249. {
  250. // didn't find it, continue
  251. //
  252. continue;
  253. }
  254. // found the directory it was in, break;
  255. //
  256. CloseHandle( h );
  257. foundit = true;
  258. break;
  259. }
  260. // found it!
  261. //
  262. if( foundit )
  263. {
  264. // validate it first
  265. //
  266. hr = ValidateFilename( T2W( tNewFileName ), MAX_PATH, FALSE );
  267. ASSERT( !FAILED( hr ) );
  268. if( SUCCEEDED( hr ) )
  269. {
  270. AddOneToDirectoryCache( hKey, i );
  271. *pOutput = SysAllocString( T2W( tNewFileName ) );
  272. hr = *pOutput ? S_FALSE : E_OUTOFMEMORY;
  273. if( WarnReplace )
  274. {
  275. ShowWarnReplace( Input, *pOutput );
  276. }
  277. }
  278. RegCloseKey( hKey );
  279. hKey = NULL;
  280. return hr;
  281. }
  282. // we didn't find it. :-(
  283. // if we got here, we found it where it was supposed to be, but
  284. // we tried to look for it locally instead. Return we found it.
  285. //
  286. if( FoundFileAsSpecified )
  287. {
  288. hr = ValidateFilename( Input, MAX_PATH, FALSE );
  289. ASSERT( !FAILED( hr ) );
  290. RegCloseKey( hKey );
  291. return hr;
  292. }
  293. if( !UseLocal )
  294. {
  295. break; // out of while loop
  296. }
  297. UseLocal = FALSE;
  298. } // while 1 ( UseLocal will break us out )
  299. // it's REALLY not around!
  300. // if we don't want UI, just signal we couldn't find it
  301. //
  302. if( !WantUI )
  303. {
  304. // we return S_FALSE to signify the file was replaced, and
  305. // a failure code to signify we couldn't find it period.
  306. //
  307. RegCloseKey( hKey );
  308. hKey = NULL;
  309. return E_FAIL;
  310. }
  311. // bring up a UI and try to go find it
  312. //
  313. OPENFILENAME ofn;
  314. ZeroMemory( &ofn, sizeof( ofn ) );
  315. // we need to find two double-nulls in a row if they really specified a
  316. // filter string
  317. TCHAR * tFilter = NULL;
  318. TCHAR ttFilter[MAX_FILTER_STRING];
  319. if( !FAILED( ValidateFilenameIsntNULL( FilterString ) ) )
  320. {
  321. size_t FilterLen = 0;
  322. HRESULT hrLen = StringCchLength( FilterString, MAX_FILTER_STRING, &FilterLen );
  323. if( FilterLen < 2 )
  324. {
  325. return E_INVALIDARG;
  326. }
  327. if( FAILED( hrLen ) )
  328. {
  329. return E_INVALIDARG;
  330. }
  331. // look for two nulls
  332. //
  333. for( int i = 0 ; i < MAX_FILTER_STRING - 1 ; i++ )
  334. {
  335. // found it
  336. //
  337. if( FilterString[i] == 0 && FilterString[i+1] == 0 )
  338. {
  339. break;
  340. }
  341. }
  342. if( i >= MAX_FILTER_STRING - 1 )
  343. {
  344. return E_INVALIDARG;
  345. }
  346. #ifndef UNICODE
  347. // copy it to a shorty string, with two nulls
  348. //
  349. WideCharToMultiByte( CP_ACP, 0, FilterString, i + 2, ttFilter, MAX_FILTER_STRING, NULL, NULL );
  350. #else if
  351. // need to copy both the extra zero's as well, or the filter string will fail
  352. CopyMemory(ttFilter, FilterString, 2*(i+2) );
  353. #endif
  354. // point to it
  355. //
  356. tFilter = ttFilter;
  357. }
  358. // we shouldn't make the return path any longer than max_path either, I think.
  359. // so this code is good.
  360. TCHAR tReturnName[_MAX_PATH];
  361. TCHAR * tEnd = NULL;
  362. size_t tRemaining = 0;
  363. hr = StringCchCopyEx( tReturnName, _MAX_PATH, Fname, &tEnd, &tRemaining, 0 );
  364. if( FAILED( hr ) )
  365. {
  366. return hr;
  367. }
  368. hr = StringCchCopyEx( tEnd, tRemaining, Ext, NULL, NULL, 0 );
  369. if( FAILED( hr ) )
  370. {
  371. return hr;
  372. }
  373. // fashion a title so the user knows what file to find
  374. //
  375. HINSTANCE hinst = _Module.GetModuleInstance( );
  376. TCHAR tErrorMsg[256];
  377. int ReadIn = LoadString( hinst, IDS_CANNOT_FIND_FILE, tErrorMsg, 256 /* characters */ );
  378. TCHAR tTitle[_MAX_PATH + 256]; // it's okay if it's a bit too big
  379. if( ReadIn )
  380. {
  381. tRemaining = _MAX_PATH + 256;
  382. StringCchCopyEx( tTitle, tRemaining, tErrorMsg, &tEnd, &tRemaining, 0 );
  383. StringCchCopyEx( tEnd, tRemaining, tReturnName, NULL, NULL, 0 ); // doesn't matter if it truncates
  384. ofn.lpstrTitle = tTitle;
  385. }
  386. else
  387. {
  388. ReadIn = GetLastError( );
  389. }
  390. ofn.lStructSize = sizeof( ofn );
  391. ofn.hwndOwner = NULL;
  392. ofn.hInstance = _Module.GetModuleInstance( );
  393. ofn.lpstrFilter = tFilter;
  394. ofn.lpstrFile = tReturnName;
  395. ofn.nMaxFile = _MAX_PATH;
  396. ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_LONGNAMES;
  397. long r = GetOpenFileName( &ofn );
  398. if( r == 0 )
  399. {
  400. // not a very good error code, is it?
  401. //
  402. RegCloseKey( hKey );
  403. hKey = NULL;
  404. return E_FAIL;
  405. }
  406. _tsplitpath( ofn.lpstrFile, Drive, Dir, Fname, Ext );
  407. // create a string that is the directory which was found
  408. TCHAR ReplacementPath[_MAX_DRIVE+_MAX_DIR];
  409. StringCchCopy( ReplacementPath, _MAX_DRIVE+_MAX_DIR, Drive ); // safe, Drive can't be over max_drive!
  410. StringCchCat( ReplacementPath, _MAX_DIR, Dir ); // safe
  411. long i = GetLeastUsedDirectory( hKey, DirectoryCount );
  412. ReplaceDirectoryPath( hKey, i, ReplacementPath );
  413. *pOutput = SysAllocString( T2W( ofn.lpstrFile ) );
  414. hr = *pOutput ? S_FALSE : E_OUTOFMEMORY;
  415. RegCloseKey( hKey );
  416. hKey = NULL;
  417. return hr;
  418. }
  419. // sets registry value, called from FindMediaFile
  420. void CMediaLocator::AddOneToDirectoryCache( HKEY hKey, int WhichDirectory )
  421. {
  422. DWORD Size = sizeof( long );
  423. DWORD Type = REG_DWORD;
  424. long UsageCount = 0;
  425. TCHAR ValueName[25];
  426. wsprintf( ValueName, TEXT("Dir%2.2ldUses"), WhichDirectory ); // safe
  427. long Result = RegQueryValueEx(
  428. hKey,
  429. ValueName,
  430. 0, // reserved
  431. &Type,
  432. (BYTE*) &UsageCount,
  433. &Size );
  434. if( Result != ERROR_SUCCESS )
  435. {
  436. return;
  437. }
  438. UsageCount++;
  439. RegSetValueEx(
  440. hKey,
  441. ValueName,
  442. 0, // reserverd
  443. REG_DWORD,
  444. (BYTE*) &UsageCount,
  445. sizeof( UsageCount ) );
  446. }
  447. int CMediaLocator::GetLeastUsedDirectory( HKEY hKey, int DirectoryCount )
  448. {
  449. long Min = -1;
  450. long WhichDir = 0;
  451. long i;
  452. for( i = 0 ; i < DirectoryCount ; i++ )
  453. {
  454. TCHAR ValueName[25];
  455. wsprintf( ValueName, TEXT("Dir%2.2ldUses"), i ); // safe
  456. DWORD Size = sizeof( long );
  457. DWORD Type = REG_DWORD;
  458. long UsageCount = 0;
  459. long Result = RegQueryValueEx(
  460. hKey,
  461. ValueName,
  462. 0, // reserved
  463. &Type,
  464. (BYTE*) &UsageCount,
  465. &Size );
  466. if( Result != ERROR_SUCCESS )
  467. {
  468. // since this key didn't exist yet, it's certainly not
  469. // used, so we can return "i".
  470. //
  471. return i;
  472. }
  473. if( i == 0 )
  474. {
  475. Min = UsageCount;
  476. }
  477. if( UsageCount < Min )
  478. {
  479. Min = UsageCount;
  480. WhichDir = i;
  481. }
  482. } // for
  483. return WhichDir;
  484. }
  485. // sets registry value
  486. void CMediaLocator::ReplaceDirectoryPath( HKEY hKey, int WhichDirectory, TCHAR * Path )
  487. {
  488. // don't stick it in the registry if it's too big
  489. //
  490. if( _tcslen( Path ) > _MAX_PATH )
  491. {
  492. return;
  493. }
  494. TCHAR ValueName[25];
  495. wsprintf( ValueName, TEXT("Directory%2.2ld"), WhichDirectory ); // safe
  496. DWORD Size = sizeof(TCHAR) * ( _tcslen(Path) + 1 ); // safe
  497. RegSetValueEx(
  498. hKey,
  499. ValueName,
  500. 0, // reserved
  501. REG_SZ,
  502. (BYTE*) Path,
  503. Size );
  504. // doesn't matter if it bombs
  505. long UsageCount = 0;
  506. wsprintf( ValueName, TEXT("Dir%2.2ldUses"), WhichDirectory ); // safe
  507. RegSetValueEx(
  508. hKey,
  509. ValueName,
  510. 0, // reserverd
  511. REG_DWORD,
  512. (BYTE*) &UsageCount,
  513. sizeof( UsageCount ) );
  514. // doesn't matter if it bombs
  515. }
  516. STDMETHODIMP CMediaLocator::AddFoundLocation( BSTR Dir )
  517. {
  518. // where did we look last?
  519. //
  520. HKEY hKey = NULL;
  521. long Result = RegOpenKeyEx(
  522. HKEY_CURRENT_USER,
  523. gszRegistryLoc ,
  524. 0, // reserved options
  525. KEY_READ | KEY_WRITE, // access
  526. &hKey );
  527. if( Result != ERROR_SUCCESS )
  528. {
  529. return MAKE_HRESULT( 1, 4, Result );
  530. }
  531. // find out how many cached directories to look in
  532. //
  533. long DirectoryCount = DEFAULT_DIRECTORIES;
  534. DWORD Size = sizeof( long );
  535. DWORD Type = REG_DWORD;
  536. Result = RegQueryValueEx(
  537. hKey,
  538. TEXT("Directories"),
  539. 0, // reserved
  540. &Type,
  541. (BYTE*) &DirectoryCount,
  542. &Size );
  543. if( Result != ERROR_SUCCESS )
  544. {
  545. // if we don't have a count, default to something
  546. //
  547. DirectoryCount = DEFAULT_DIRECTORIES;
  548. }
  549. // bound it
  550. if( ( DirectoryCount < 1 ) || ( DirectoryCount > 32 ) )
  551. {
  552. DirectoryCount = DEFAULT_DIRECTORIES;
  553. }
  554. USES_CONVERSION;
  555. TCHAR * tDir = W2T( Dir );
  556. long i = GetLeastUsedDirectory( hKey, DirectoryCount );
  557. if( ( i < 0 ) || ( i > 31 ) )
  558. {
  559. RegCloseKey( hKey );
  560. return NOERROR;
  561. }
  562. ReplaceDirectoryPath( hKey, i, tDir );
  563. RegCloseKey( hKey );
  564. return NOERROR;
  565. }
  566. void CMediaLocator::ShowWarnReplace( TCHAR * pOriginal, TCHAR * pReplaced )
  567. {
  568. CAutoLock Lock( &m_Lock );
  569. HINSTANCE h = _Module.GetModuleInstance( );
  570. // these will (correctly) truncate the incoming strings if they're too long
  571. // so nothing needs done here, we can even ignore the return codes
  572. // from the below
  573. StringCchCopy( CMediaLocator::szShowWarnOriginal, _MAX_PATH, pOriginal );
  574. StringCchCopy( CMediaLocator::szShowWarnReplaced, _MAX_PATH, pReplaced );
  575. HWND hDlg = CreateDialog( h, MAKEINTRESOURCE( IDD_MEDLOC_DIALOG ), NULL, DlgProc );
  576. if( !hDlg )
  577. {
  578. return;
  579. }
  580. static int cx = 0;
  581. static int cy = 0;
  582. cx += 20;
  583. cy += 15;
  584. if( cx > 600 ) cx -= 600;
  585. if( cy > 400 ) cy -= 400;
  586. ShowWindow( hDlg, SW_SHOW );
  587. SetWindowPos( hDlg, NULL, cx, cy, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
  588. USES_CONVERSION;
  589. TCHAR * tOriginal = W2T( CMediaLocator::szShowWarnOriginal );
  590. TCHAR * tReplaced = W2T( CMediaLocator::szShowWarnReplaced );
  591. SetDlgItemText( hDlg, IDC_ORIGINAL, tOriginal );
  592. SetDlgItemText( hDlg, IDC_FOUND, tReplaced );
  593. DWORD ThreadId = 0;
  594. HANDLE NewThread = CreateThread( NULL, 0, ThreadProc, (LPVOID) hDlg, 0, &ThreadId );
  595. if( !NewThread )
  596. {
  597. EndDialog( hDlg, TRUE );
  598. }
  599. else
  600. {
  601. SetThreadPriority( NewThread, THREAD_PRIORITY_BELOW_NORMAL );
  602. }
  603. // by the time this dialog call gets back, it will have used the values
  604. // stored in the static vars and they are free to be used again
  605. }
  606. TCHAR CMediaLocator::szShowWarnOriginal[_MAX_PATH];
  607. TCHAR CMediaLocator::szShowWarnReplaced[_MAX_PATH];
  608. INT_PTR CALLBACK CMediaLocator::DlgProc( HWND h, UINT i, WPARAM w, LPARAM l )
  609. {
  610. switch( i )
  611. {
  612. case WM_INITDIALOG:
  613. {
  614. return TRUE;
  615. }
  616. }
  617. return FALSE;
  618. }
  619. DWORD WINAPI CMediaLocator::ThreadProc( LPVOID lpParam )
  620. {
  621. Sleep( 3000 );
  622. EndDialog( (HWND) lpParam, TRUE );
  623. return 0;
  624. }