home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / JukeboxASF / JukeboxASFDlg.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-09  |  50.9 KB  |  1,844 lines

  1. //------------------------------------------------------------------------------
  2. // File: JukeboxASFDlg.cpp
  3. //
  4. // Desc: DirectShow sample code - implementation of CJukeboxDlg class.
  5. //
  6. // Copyright (c) 1998-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9. #include "stdafx.h"
  10. #include <atlbase.h>
  11. #include "JukeboxASF.h"
  12. #include "JukeboxASFDlg.h"
  13. #include "playvideo.h"
  14. #include "mediatypes.h"
  15.  
  16. #ifdef _DEBUG
  17. #define new DEBUG_NEW
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CAboutDlg dialog used for App About
  24.  
  25. class CAboutDlg : public CDialog
  26. {
  27. public:
  28.     CAboutDlg();
  29.  
  30. // Dialog Data
  31.     //{{AFX_DATA(CAboutDlg)
  32.     enum { IDD = IDD_ABOUTBOX };
  33.     //}}AFX_DATA
  34.  
  35.     // ClassWizard generated virtual function overrides
  36.     //{{AFX_VIRTUAL(CAboutDlg)
  37.     protected:
  38.     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
  39.     //}}AFX_VIRTUAL
  40.  
  41. // Implementation
  42. protected:
  43.     //{{AFX_MSG(CAboutDlg)
  44.     //}}AFX_MSG
  45.     DECLARE_MESSAGE_MAP()
  46. };
  47.  
  48. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  49. {
  50.     //{{AFX_DATA_INIT(CAboutDlg)
  51.     //}}AFX_DATA_INIT
  52. }
  53.  
  54. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  55. {
  56.     CDialog::DoDataExchange(pDX);
  57.     //{{AFX_DATA_MAP(CAboutDlg)
  58.     //}}AFX_DATA_MAP
  59. }
  60.  
  61. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  62.     //{{AFX_MSG_MAP(CAboutDlg)
  63.         // No message handlers
  64.     //}}AFX_MSG_MAP
  65. END_MESSAGE_MAP()
  66.  
  67. /////////////////////////////////////////////////////////////////////////////
  68. // CJukeboxDlg dialog
  69.  
  70. CJukeboxDlg::CJukeboxDlg(CWnd* pParent /*=NULL*/)
  71.     : CDialog(CJukeboxDlg::IDD, pParent)
  72. {
  73.     //{{AFX_DATA_INIT(CJukeboxDlg)
  74.     //}}AFX_DATA_INIT
  75.     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  76.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  77. }
  78.  
  79. void CJukeboxDlg::DoDataExchange(CDataExchange* pDX)
  80. {
  81.     CDialog::DoDataExchange(pDX);
  82.     //{{AFX_DATA_MAP(CJukeboxDlg)
  83.     DDX_Control(pDX, IDC_STATIC_POSITION, m_StrPosition);
  84.     DDX_Control(pDX, IDC_SLIDER, m_Seekbar);
  85.     DDX_Control(pDX, IDC_STATIC_IMAGESIZE, m_StrImageSize);
  86.     DDX_Control(pDX, IDC_STATIC_DURATION, m_StrDuration);
  87.     DDX_Control(pDX, IDC_EDIT_MEDIADIR, m_EditMediaDir);
  88.     DDX_Control(pDX, IDC_SPIN_FILES, m_SpinFiles);
  89.     DDX_Control(pDX, IDC_BUTTON_FRAMESTEP, m_ButtonFrameStep);
  90.     DDX_Control(pDX, IDC_LIST_EVENTS, m_ListEvents);
  91.     DDX_Control(pDX, IDC_CHECK_EVENTS, m_CheckEvents);
  92.     DDX_Control(pDX, IDC_BUTTON_PROPPAGE, m_ButtonProperties);
  93.     DDX_Control(pDX, IDC_STATUS_DIRECTORY, m_StrMediaPath);
  94.     DDX_Control(pDX, IDC_CHECK_MUTE, m_CheckMute);
  95.     DDX_Control(pDX, IDC_BUTTON_STOP, m_ButtonStop);
  96.     DDX_Control(pDX, IDC_BUTTON_PLAY, m_ButtonPlay);
  97.     DDX_Control(pDX, IDC_BUTTON_PAUSE, m_ButtonPause);
  98.     DDX_Control(pDX, IDC_CHECK_PLAYTHROUGH, m_CheckPlaythrough);
  99.     DDX_Control(pDX, IDC_CHECK_LOOP, m_CheckLoop);
  100.     DDX_Control(pDX, IDC_STATIC_FILEDATE, m_StrFileDate);
  101.     DDX_Control(pDX, IDC_STATIC_FILESIZE, m_StrFileSize);
  102.     DDX_Control(pDX, IDC_LIST_PINS_OUTPUT, m_ListPinsOutput);
  103.     DDX_Control(pDX, IDC_LIST_PINS_INPUT, m_ListPinsInput);
  104.     DDX_Control(pDX, IDC_STATIC_FILELIST, m_StrFileList);
  105.     DDX_Control(pDX, IDC_STATUS, m_Status);
  106.     DDX_Control(pDX, IDC_MOVIE_SCREEN, m_Screen);
  107.     DDX_Control(pDX, IDC_LIST_FILTERS, m_ListFilters);
  108.     DDX_Control(pDX, IDC_LIST_FILES, m_ListFiles);
  109.     //}}AFX_DATA_MAP
  110. }
  111.  
  112. BEGIN_MESSAGE_MAP(CJukeboxDlg, CDialog)
  113.     //{{AFX_MSG_MAP(CJukeboxDlg)
  114.     ON_WM_ERASEBKGND()
  115.     ON_WM_SYSCOMMAND()
  116.     ON_WM_PAINT()
  117.     ON_WM_QUERYDRAGICON()
  118.     ON_WM_CLOSE()
  119.     ON_WM_DESTROY()
  120.     ON_LBN_SELCHANGE(IDC_LIST_FILES, OnSelectFile)
  121.     ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnPause)
  122.     ON_BN_CLICKED(IDC_BUTTON_PLAY, OnPlay)
  123.     ON_BN_CLICKED(IDC_BUTTON_STOP, OnStop)
  124.     ON_BN_CLICKED(IDC_CHECK_MUTE, OnCheckMute)
  125.     ON_BN_CLICKED(IDC_CHECK_LOOP, OnCheckLoop)
  126.     ON_BN_CLICKED(IDC_CHECK_PLAYTHROUGH, OnCheckPlaythrough)
  127.     ON_LBN_SELCHANGE(IDC_LIST_FILTERS, OnSelchangeListFilters)
  128.     ON_LBN_DBLCLK(IDC_LIST_FILTERS, OnDblclkListFilters)
  129.     ON_BN_CLICKED(IDC_BUTTON_PROPPAGE, OnButtonProppage)
  130.     ON_BN_CLICKED(IDC_CHECK_EVENTS, OnCheckEvents)
  131.     ON_BN_CLICKED(IDC_BUTTON_FRAMESTEP, OnButtonFramestep)
  132.     ON_BN_CLICKED(IDC_BUTTON_CLEAR_EVENTS, OnButtonClearEvents)
  133.     ON_LBN_DBLCLK(IDC_LIST_FILES, OnDblclkListFiles)
  134.     ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_FILES, OnDeltaposSpinFiles)
  135.     ON_BN_CLICKED(IDC_BUTTON_SET_MEDIADIR, OnButtonSetMediadir)
  136.     ON_WM_TIMER()
  137.     ON_BN_CLICKED(IDC_BUTTON_GRAPHEDIT, OnButtonGraphedit)
  138.     //}}AFX_MSG_MAP
  139. END_MESSAGE_MAP()
  140.  
  141.  
  142. /////////////////////////////////////////////////////////////////////////////
  143. // CJukeboxDlg message handlers
  144.  
  145. void CJukeboxDlg::OnSysCommand(UINT nID, LPARAM lParam)
  146. {
  147.     if ((nID & 0xFFF0) == IDM_ABOUTBOX)
  148.     {
  149.         CAboutDlg dlgAbout;
  150.         dlgAbout.DoModal();
  151.     }
  152.     else
  153.     {
  154.         CDialog::OnSysCommand(nID, lParam);
  155.     }
  156. }
  157.  
  158. // If you add a minimize button to your dialog, you will need the code below
  159. //  to draw the icon.  For MFC applications using the document/view model,
  160. //  this is automatically done for you by the framework.
  161.  
  162. void CJukeboxDlg::OnPaint() 
  163. {
  164.     if (IsIconic())
  165.     {
  166.         CPaintDC dc(this); // device context for painting
  167.  
  168.         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  169.  
  170.         // Center icon in client rectangle
  171.         int cxIcon = GetSystemMetrics(SM_CXICON);
  172.         int cyIcon = GetSystemMetrics(SM_CYICON);
  173.         CRect rect;
  174.         GetClientRect(&rect);
  175.         int x = (rect.Width() - cxIcon + 1) / 2;
  176.         int y = (rect.Height() - cyIcon + 1) / 2;
  177.  
  178.         // Draw the icon
  179.         dc.DrawIcon(x, y, m_hIcon);
  180.     }
  181.     else
  182.     {
  183.         CDialog::OnPaint();
  184.     }
  185. }
  186.  
  187. // The system calls this to obtain the cursor to display while the user drags
  188. //  the minimized window.
  189. HCURSOR CJukeboxDlg::OnQueryDragIcon()
  190. {
  191.     return (HCURSOR) m_hIcon;
  192. }
  193.  
  194.  
  195. /////////////////////////////////////////////////////////////////////////////
  196. // CJukeboxDlg DirectShow code and message handlers
  197.  
  198.  
  199. BOOL CJukeboxDlg::OnInitDialog()
  200. {
  201.     CDialog::OnInitDialog();
  202.  
  203.     // Add "About..." menu item to system menu.
  204.  
  205.     // IDM_ABOUTBOX must be in the system command range.
  206.     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  207.     ASSERT(IDM_ABOUTBOX < 0xF000);
  208.  
  209.     CMenu* pSysMenu = GetSystemMenu(FALSE);
  210.     if (pSysMenu != NULL)
  211.     {
  212.         CString strAboutMenu;
  213.         strAboutMenu.LoadString(IDS_ABOUTBOX);
  214.         if (!strAboutMenu.IsEmpty())
  215.         {
  216.             pSysMenu->AppendMenu(MF_SEPARATOR);
  217.             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  218.         }
  219.     }
  220.  
  221.     // Set the icon for this dialog.  The framework does this automatically
  222.     //  when the application's main window is not a dialog
  223.     SetIcon(m_hIcon, TRUE);            // Set big icon
  224.     SetIcon(m_hIcon, FALSE);        // Set small icon
  225.     
  226.     // Initialize COM
  227.     CoInitialize(NULL);
  228.     
  229.     // Initialize DirectShow and query for needed interfaces
  230.     HRESULT hr = InitDirectShow();
  231.     if(FAILED(hr))
  232.     {
  233.         RetailOutput(TEXT("Failed to initialize DirectShow!  hr=0x%x\r\n"), hr);
  234.         return FALSE;
  235.     }
  236.  
  237.     // IMPORTANT
  238.     // Since we're embedding video in a child window of a dialog,
  239.     // we must set the WS_CLIPCHILDREN style to prevent the bounding
  240.     // rectangle from drawing over our video frames.
  241.     //
  242.     // Neglecting to set this style can lead to situations when the video
  243.     // is erased and replaced with black (or the default color of the 
  244.     // bounding rectangle).
  245.     m_Screen.ModifyStyle(0, WS_CLIPCHILDREN);
  246.  
  247.     // Propagate the files list and select the first item
  248.     InitMediaDirectory();
  249.    
  250.     // Initialize seeking trackbar range
  251.     m_Seekbar.SetRange(0, 100, TRUE);
  252.     m_Seekbar.SetTicFreq(10);
  253.     
  254.     return TRUE;  // return TRUE  unless you set the focus to a control
  255. }
  256.  
  257.  
  258. void CJukeboxDlg::InitMediaDirectory(void)
  259. {
  260.     // Fill the media file list, starting with the directory passed
  261.     // on the command line.  If no directory is passed, then read the
  262.     // default media path for the DirectX SDK.
  263.     TCHAR szDir[MAX_PATH];
  264.     LONG lResult=0;
  265.  
  266.     if (theApp.m_lpCmdLine[0] == L'\0')
  267.     {
  268.         lResult = GetDXMediaPath(szDir);
  269.  
  270.         // If the DirectX SDK is not installed, use the Windows media
  271.         // directory instead.
  272.         if (lResult != 0)
  273.         {
  274.             GetWindowsDirectory(szDir, MAX_PATH);
  275.             _tcscat(szDir, _T("\\Media\\") );
  276.         }
  277.     }
  278.     else
  279.         _tcscpy(szDir, theApp.m_lpCmdLine);
  280.  
  281.     TCHAR szPathMsg[MAX_PATH];
  282.     wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szDir);
  283.     m_StrMediaPath.SetWindowText(szPathMsg);
  284.  
  285.     // Save current directory name
  286.     wsprintf(m_szCurrentDir, TEXT("%s\0"), szDir);
  287.  
  288.     m_EditMediaDir.SetLimitText(MAX_PATH);
  289.     m_EditMediaDir.SetWindowText(szDir);
  290.  
  291.     FillFileList(szDir);
  292. }
  293.  
  294.  
  295. LONG CJukeboxDlg::GetDXMediaPath(TCHAR *szPath)
  296. {
  297.     HKEY  hKey;
  298.     DWORD dwType, dwSize = MAX_PATH;
  299.  
  300.     // Open the appropriate registry key
  301.     LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  302.                                 _T("Software\\Microsoft\\DirectX SDK"),
  303.                                 0, KEY_READ, &hKey );
  304.     if( ERROR_SUCCESS != lResult )
  305.         return -1;
  306.  
  307.     lResult = RegQueryValueEx( hKey, _T("DX81SDK Samples Path"), NULL,
  308.                               &dwType, (BYTE*)szPath, &dwSize );
  309.     RegCloseKey( hKey );
  310.  
  311.     if( ERROR_SUCCESS != lResult )
  312.         return -1;
  313.  
  314.     _tcscat( szPath, _T("\\Media\\") );
  315.     return 0;
  316. }
  317.  
  318.  
  319. void CJukeboxDlg::FillFileList(LPTSTR pszRootDir)
  320. {
  321.     UINT attr = 0;
  322.  
  323.     m_ListFiles.ResetContent();
  324.  
  325.     ::SetCurrentDirectory(pszRootDir);
  326.     Say(TEXT("Building file list..."));
  327.  
  328.     // Add all of our known supported media types to the file list.
  329.     // Add files of each type in order.
  330.     for (int i=0; i < NUM_MEDIA_TYPES; i++)
  331.     {
  332.         m_ListFiles.Dir(attr, TypeInfo[i].pszType);
  333.     }
  334.     Say(TEXT("File list complete."));
  335.  
  336.     // Update list box title with number of items added
  337.     int nItems  = m_ListFiles.GetCount();
  338.     TCHAR szTitle[64];
  339.     wsprintf(szTitle, TEXT("Media files (%d found)"), nItems);
  340.     m_StrFileList.SetWindowText(szTitle);
  341.     
  342.     // Automatically select the first file in the list once
  343.     // the dialog is displayed.
  344.     PostMessage(WM_FIRSTFILE, 0, 0L);
  345.     m_nCurrentFileSelection = -1;     // No selection yet
  346. }
  347.  
  348.  
  349. void CJukeboxDlg::OnClose() 
  350. {
  351.     // Release DirectShow interfaces
  352.     StopMedia();
  353.     FreeDirectShow();
  354.  
  355.     // Release COM
  356.     CoUninitialize();
  357.  
  358.     CDialog::OnClose();
  359. }
  360.  
  361. void CJukeboxDlg::OnDestroy() 
  362. {
  363.     FreeDirectShow();    
  364.  
  365.     CDialog::OnDestroy();
  366. }
  367.  
  368.  
  369. void CJukeboxDlg::OnSelectFile() 
  370. {
  371.     HRESULT hr;
  372.     TCHAR szFilename[MAX_PATH];
  373.  
  374.     // If this is the currently selected file, do nothing
  375.     int nItem = m_ListFiles.GetCurSel();
  376.     if (nItem == m_nCurrentFileSelection)
  377.         return;
  378.  
  379.     // Remember the current selection to speed double-click processing
  380.     m_nCurrentFileSelection = nItem;
  381.  
  382.     // Read file name from list box
  383.     m_ListFiles.GetText(nItem, szFilename);
  384.  
  385.     // Remember current play state to restart playback
  386.     int nCurrentState = g_psCurrent;
  387.  
  388.     // First release any existing interfaces
  389.     ResetDirectShow();
  390.  
  391.     // Clear filter/pin/event information listboxes
  392.     m_ListFilters.ResetContent();
  393.     m_ListPinsInput.ResetContent();
  394.     m_ListPinsOutput.ResetContent();
  395.     m_ListEvents.ResetContent();
  396.  
  397.     // Load the selected media file
  398.     hr = PrepareMedia(szFilename);
  399.     if (FAILED(hr))
  400.     {
  401.         // Error - disable play button and give feedback
  402.         Say(TEXT("File failed to render!"));
  403.         m_ButtonPlay.EnableWindow(FALSE);
  404.         MessageBeep(0);
  405.         return;
  406.     }
  407.     else
  408.     {
  409.         m_ButtonPlay.EnableWindow(TRUE);
  410.     }
  411.  
  412.     // Display useful information about this file
  413.     DisplayFileInfo(szFilename);
  414.     DisplayImageInfo();
  415.     DisplayFileDuration();
  416.  
  417.     // Set up the seeking trackbar and read capabilities
  418.     ConfigureSeekbar();
  419.  
  420.     // Enumerate and display filters in graph
  421.     hr = EnumFilters();
  422.  
  423.     // Select the first filter in the list to display pin info
  424.     m_ListFilters.SetCurSel(0);
  425.     OnSelchangeListFilters();
  426.  
  427.     // See if the renderer supports frame stepping on this file.
  428.     // Enable/disable frame stepping button accordingly
  429.     m_ButtonFrameStep.EnableWindow(CanStep());
  430.  
  431.     // If the user has asked to mute audio then we
  432.     // need to mute this new clip before continuing.
  433.     if (g_bGlobalMute)
  434.         MuteAudio();
  435.  
  436.     // If we were running when the user changed selection,
  437.     // start running the newly selected clip
  438.     if (nCurrentState == State_Running)
  439.     {
  440.         OnPlay();
  441.     }
  442.     else
  443.     {
  444.         // Cue the first video frame
  445.         OnStop();
  446.     }
  447. }
  448.  
  449.  
  450. BOOL CJukeboxDlg::IsWindowsMediaFile(LPTSTR lpszFile)
  451. {
  452.     if (_tcsstr(lpszFile, TEXT(".asf")) ||
  453.         _tcsstr(lpszFile, TEXT(".ASF")) ||
  454.         _tcsstr(lpszFile, TEXT(".wma")) ||
  455.         _tcsstr(lpszFile, TEXT(".WMA")) ||
  456.         _tcsstr(lpszFile, TEXT(".wmv")) ||
  457.         _tcsstr(lpszFile, TEXT(".WMV")))
  458.         return TRUE;
  459.     else
  460.         return FALSE;
  461. }
  462.  
  463. HRESULT CJukeboxDlg::PrepareMedia(LPTSTR lpszMovie)
  464. {
  465.     USES_CONVERSION;
  466.     HRESULT hr = S_OK;
  467.  
  468.     Say(TEXT("Loading..."));
  469.  
  470.     // Is this a Windows Media file (ASF, WMA, WMV)?  If so, use the new
  471.     // ASF Reader filter, which is faster and much better at seeking than
  472.     // the default ASF Reader filter used by default with RenderFile.
  473.     if (IsWindowsMediaFile(lpszMovie))
  474.     {
  475.         hr = RenderWMFile(T2W(lpszMovie));
  476.         if (FAILED(hr)) {
  477.             RetailOutput(TEXT("*** Failed(%08lx) to Render WM File(%s)!\r\n"),
  478.                      hr, lpszMovie);
  479.             return hr;
  480.         }
  481.     }
  482.     else
  483.     {
  484.         // Allow DirectShow to create the FilterGraph for this media file
  485.         hr = pGB->RenderFile(T2W(lpszMovie), NULL);
  486.         if (FAILED(hr)) {
  487.             RetailOutput(TEXT("*** Failed(%08lx) in RenderFile(%s)!\r\n"),
  488.                      hr, lpszMovie);
  489.             return hr;
  490.         }
  491.     }
  492.  
  493.     // Set the message drain of the video window to point to our main
  494.     // application window.  If this is an audio-only or MIDI file, 
  495.     // then put_MessageDrain will fail.
  496.     hr = pVW->put_MessageDrain((OAHWND) m_hWnd);
  497.     if (FAILED(hr))
  498.     {
  499.         g_bAudioOnly = TRUE;
  500.     }
  501.  
  502.     // Have the graph signal event via window callbacks
  503.     hr = pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
  504.  
  505.     // Configure the video window
  506.     if (!g_bAudioOnly)
  507.     {
  508.         // We'll manually set the video to be visible
  509.         hr = pVW->put_Visible(OAFALSE);
  510.  
  511.         hr = pVW->put_WindowStyle(WS_CHILD);
  512.         hr = pVW->put_Owner((OAHWND) m_Screen.GetSafeHwnd());
  513.  
  514.         // Place video window within the bounding rectangle
  515.         CenterVideo();
  516.  
  517.         // Make the video window visible within the screen window.
  518.         // If this is an audio-only file, then there won't be a video interface.
  519.         hr = pVW->put_Visible(OATRUE);
  520.         hr = pVW->SetWindowForeground(-1);
  521.     }
  522.  
  523.     Say(TEXT("Ready"));
  524.     return hr;
  525. }
  526.  
  527. HRESULT CJukeboxDlg::RenderWMFile(LPCWSTR wFile)
  528. {
  529.     HRESULT hr=S_OK;
  530.     IFileSourceFilter *pFS=NULL;
  531.     IBaseFilter *pReader=NULL;
  532.  
  533.     // Load the improved ASF reader filter by CLSID
  534.     hr = CreateFilter(CLSID_WMAsfReader, &pReader);
  535.     if(FAILED(hr))
  536.     {
  537.         RetailOutput(TEXT("Failed to create WMAsfWriter filter!  hr=0x%x\n"), hr);
  538.         return hr;
  539.     }
  540.  
  541.     // Add the ASF reader filter to the graph.  For ASF/WMV/WMA content,
  542.     // this filter is NOT the default and must be added explicitly.
  543.     hr = pGB->AddFilter(pReader, L"ASF Reader");
  544.     if(FAILED(hr))
  545.     {
  546.         RetailOutput(TEXT("Failed to add ASF reader filter to graph!  hr=0x%x\n"), hr);
  547.         return hr;
  548.     }
  549.  
  550.     // Create the key provider that will be used to unlock the WM SDK
  551.     JIF(AddKeyProvider(pGB));
  552.  
  553.     // Set its source filename
  554.     JIF(pReader->QueryInterface(IID_IFileSourceFilter, (void **) &pFS));
  555.     JIF(pFS->Load(wFile, NULL));
  556.     pFS->Release();
  557.  
  558.     // Render the output pins of the ASF reader to build the
  559.     // remainder of the graph automatically
  560.     JIF(RenderOutputPins(pGB, pReader));
  561.  
  562.     // Since the graph is built and the filters are added to the graph,
  563.     // the WM ASF reader interface can be released.
  564.     pReader->Release();
  565.  
  566. CLEANUP:
  567.     return hr;
  568. }
  569.  
  570. HRESULT CJukeboxDlg::CreateFilter(REFCLSID clsid, IBaseFilter **ppFilter)
  571. {
  572.     HRESULT hr;
  573.  
  574.     hr = CoCreateInstance(clsid,
  575.         NULL,
  576.         CLSCTX_INPROC_SERVER,
  577.         IID_IBaseFilter,
  578.         (void **) ppFilter);
  579.  
  580.     if(FAILED(hr))
  581.     {
  582.         RetailOutput(TEXT("CreateFilter: Failed to create filter!  hr=0x%x\n"), hr);
  583.         *ppFilter = NULL;
  584.         return hr;
  585.     }
  586.  
  587.     return S_OK;
  588. }
  589.  
  590.  
  591. HRESULT CJukeboxDlg::RenderOutputPins(IGraphBuilder *pGB, IBaseFilter *pFilter)
  592. {
  593.     HRESULT            hr = S_OK;
  594.     IEnumPins *        pEnumPin = NULL;
  595.     IPin *            pConnectedPin = NULL, * pPin;
  596.     PIN_DIRECTION    PinDirection;
  597.     ULONG            ulFetched;
  598.  
  599.     // Enumerate all pins on the filter
  600.     hr = pFilter->EnumPins( &pEnumPin );
  601.  
  602.     if(SUCCEEDED(hr))
  603.     {
  604.         // Step through every pin, looking for the output pins
  605.         while (S_OK == (hr = pEnumPin->Next( 1L, &pPin, &ulFetched)))
  606.         {
  607.             // Is this pin connected?  We're not interested in connected pins.
  608.             hr = pPin->ConnectedTo(&pConnectedPin);
  609.             if (pConnectedPin)
  610.             {
  611.                 pConnectedPin->Release();
  612.                 pConnectedPin = NULL;
  613.             }
  614.  
  615.             // If this pin is not connected, render it.
  616.             if (VFW_E_NOT_CONNECTED == hr)
  617.             {
  618.                 hr = pPin->QueryDirection( &PinDirection );
  619.                 if ( ( S_OK == hr ) && ( PinDirection == PINDIR_OUTPUT ) )
  620.                 {
  621.                     hr = pGB->Render(pPin);
  622.                 }
  623.             }
  624.             pPin->Release();
  625.  
  626.             // If there was an error, stop enumerating
  627.             if (FAILED(hr))                      
  628.                 break;
  629.         }
  630.     }
  631.  
  632.     // Release pin enumerator
  633.     pEnumPin->Release();
  634.     return hr;
  635. }
  636.  
  637.  
  638. HRESULT CJukeboxDlg::AddKeyProvider(IGraphBuilder *pGraph)
  639. {
  640.     HRESULT hr;
  641.  
  642.     // Instantiate the key provider class, and AddRef it
  643.     // so that COM doesn't try to free our static object.
  644.     prov.AddRef();  // Don't let COM try to free our static object.
  645.  
  646.     // Give the graph an IObjectWithSite pointer to us for callbacks & QueryService.
  647.     IObjectWithSite* pObjectWithSite = NULL;
  648.  
  649.     hr = pGraph->QueryInterface(IID_IObjectWithSite, (void**)&pObjectWithSite);
  650.     if (SUCCEEDED(hr))
  651.     {
  652.         // Use the IObjectWithSite pointer to specify our key provider object.
  653.         // The filter graph manager will use this pointer to call
  654.         // QueryService to do the unlocking.
  655.         // If the unlocking succeeds, then we can build our graph.
  656.             
  657.         hr = pObjectWithSite->SetSite((IUnknown *) (IServiceProvider *) &prov);
  658.         pObjectWithSite->Release();
  659.     }
  660.  
  661.     return hr;
  662. }
  663.  
  664. //
  665. //  Displays a text string in a status line near the bottom of the dialog
  666. //
  667. void CJukeboxDlg::Say(LPTSTR szText)
  668. {
  669.     m_Status.SetWindowText(szText);
  670. }
  671.  
  672. void CJukeboxDlg::OnPause() 
  673. {  
  674.     if (g_psCurrent == State_Paused)
  675.     {
  676.         RunMedia();
  677.         StartSeekTimer();
  678.         Say(TEXT("Running"));
  679.     }
  680.     else
  681.     {
  682.         StopSeekTimer();
  683.         PauseMedia();
  684.         Say(TEXT("PAUSED"));
  685.     }
  686. }
  687.  
  688. void CJukeboxDlg::OnPlay() 
  689. {
  690.       RunMedia();
  691.     StartSeekTimer();
  692.     Say(TEXT("Running"));
  693. }
  694.  
  695. void CJukeboxDlg::ShowState()
  696. {
  697.     HRESULT hr;
  698.  
  699.     OAFilterState fs;
  700.     hr = pMC->GetState(500, &fs);
  701.     if (FAILED(hr))
  702.     {
  703.         RetailOutput(TEXT("Failed to read graph state!  hr=0x%x\r\n"), hr);
  704.         return;
  705.     }
  706.  
  707.     // Show debug output for current media state
  708.     switch (fs)
  709.     {
  710.         case State_Stopped:
  711.             RetailOutput(TEXT("State_Stopped\r\n"));
  712.             break;
  713.         case State_Paused:
  714.             RetailOutput(TEXT("State_Paused\r\n"));
  715.             break;
  716.         case State_Running:
  717.             RetailOutput(TEXT("State_Running\r\n"));
  718.             break;
  719.     }
  720. }
  721.  
  722. void CJukeboxDlg::OnStop() 
  723. {
  724.     HRESULT hr;
  725.  
  726.     // Stop playback immediately with IMediaControl::Stop().
  727.     StopSeekTimer();
  728.     StopMedia();
  729.  
  730.     // Wait for the stop to propagate to all filters
  731.     OAFilterState fs;
  732.     hr = pMC->GetState(500, &fs);
  733.     if (FAILED(hr))
  734.     {
  735.         RetailOutput(TEXT("Failed to read graph state!  hr=0x%x\r\n"), hr);
  736.     }
  737.  
  738.     // Reset to beginning of media clip
  739.     LONGLONG pos=0;
  740.     hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  741.                            NULL, AM_SEEKING_NoPositioning);
  742.     if (FAILED(hr))
  743.     {
  744.         RetailOutput(TEXT("Failed to seek to beginning of media!  hr=0x%x\r\n"), hr);
  745.     }
  746.   
  747.     // Display the first frame of the media clip, if it contains video.
  748.     // StopWhenReady() pauses all filters internally (which allows the video
  749.     // renderer to queue and display the first video frame), after which
  750.     // it sets the filters to the stopped state.  This enables easy preview
  751.     // of the video's poster frame.
  752.     hr = pMC->StopWhenReady();
  753.     if (FAILED(hr))
  754.     {
  755.         RetailOutput(TEXT("Failed in StopWhenReady!  hr=0x%x\r\n"), hr);
  756.     }
  757.  
  758.     Say(TEXT("Stopped"));
  759.  
  760.     // Reset slider bar and position label back to zero
  761.     ReadMediaPosition();
  762. }
  763.  
  764. HRESULT CJukeboxDlg::InitDirectShow(void)
  765. {
  766.     HRESULT hr = S_OK;
  767.  
  768.     g_bAudioOnly = FALSE;
  769.  
  770.     // Zero interfaces (sanity check)
  771.     pVW = NULL;
  772.     pBV = NULL;
  773.  
  774.     JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGB));
  775.     JIF(pGB->QueryInterface(IID_IMediaControl,  (void **)&pMC));
  776.     JIF(pGB->QueryInterface(IID_IMediaSeeking,  (void **)&pMS));
  777.     JIF(pGB->QueryInterface(IID_IBasicVideo,    (void **)&pBV));
  778.     JIF(pGB->QueryInterface(IID_IVideoWindow,   (void **)&pVW));
  779.     JIF(pGB->QueryInterface(IID_IMediaEventEx,  (void **)&pME));
  780.  
  781.     return S_OK;
  782.  
  783. CLEANUP:
  784.     FreeDirectShow();
  785.     return(hr);
  786. }
  787.  
  788. HRESULT CJukeboxDlg::FreeDirectShow(void)
  789. {
  790.     HRESULT hr=S_OK;
  791.  
  792.     StopSeekTimer();
  793.     StopMedia();
  794.  
  795.     // Disable event callbacks
  796.     if (pME)
  797.         hr = pME->SetNotifyWindow((OAHWND)NULL, 0, 0);
  798.  
  799.     // Hide video window and remove owner.  This is not necessary here,
  800.     // since we are about to destroy the filter graph, but it is included
  801.     // for demonstration purposes.  Remember to hide the video window and
  802.     // clear its owner when destroying a window that plays video.
  803.     if(pVW)
  804.     {
  805.         hr = pVW->put_Visible(OAFALSE);
  806.         hr = pVW->put_Owner(NULL);
  807.     }
  808.  
  809.     SAFE_RELEASE(pMC);
  810.     SAFE_RELEASE(pMS);
  811.     SAFE_RELEASE(pVW);
  812.     SAFE_RELEASE(pBV);
  813.     SAFE_RELEASE(pME);
  814.     SAFE_RELEASE(pGB);
  815.  
  816.     return hr;
  817. }
  818.  
  819. void CJukeboxDlg::ResetDirectShow(void)
  820. {
  821.     // Destroy the current filter graph its filters.
  822.     FreeDirectShow();
  823.  
  824.     // Reinitialize graph builder and query for interfaces
  825.     InitDirectShow();
  826. }
  827.  
  828. void CJukeboxDlg::CenterVideo(void)
  829. {
  830.     LONG width, height;
  831.     HRESULT hr;
  832.  
  833.     if ((g_bAudioOnly) || (!pVW))
  834.         return;
  835.  
  836.     // Read coordinates of video container window
  837.     RECT rc;
  838.     m_Screen.GetClientRect(&rc);
  839.     width =  rc.right - rc.left;
  840.     height = rc.bottom - rc.top;
  841.  
  842.     // Ignore the video's original size and stretch to fit bounding rectangle
  843.     hr = pVW->SetWindowPosition(rc.left, rc.top, width, height);
  844.     if (FAILED(hr))
  845.     {
  846.         RetailOutput(TEXT("Failed to set window position!  hr=0x%x\r\n"), hr);
  847.         return;
  848.     }
  849. }
  850.  
  851.  
  852. LRESULT CJukeboxDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  853. {
  854.     // Field notifications from the DirectShow filter graph manager
  855.     // and those posted by the application
  856.     switch (message)
  857.     {
  858.         case WM_GRAPHNOTIFY:
  859.             HandleGraphEvent();
  860.             break;
  861.  
  862.         case WM_HSCROLL:
  863.             HandleTrackbar(LOWORD(wParam));
  864.             break;
  865.  
  866.         case WM_PLAYFILE:
  867.             PlaySelectedFile();
  868.             break;
  869.  
  870.         case WM_NEXTFILE:
  871.             PlayNextFile();
  872.             break;
  873.  
  874.         case WM_PREVIOUSFILE:
  875.             PlayPreviousFile();
  876.             break;
  877.  
  878.         case WM_FIRSTFILE:
  879.             // Select the first item in the list
  880.             m_ListFiles.SetCurSel(0);
  881.             OnSelectFile();
  882.             break;
  883.     }
  884.  
  885.     // Pass along this message to the video window, which exists as a child
  886.     // of the m_Screen window.  This method should be used by windows that 
  887.     // make a renderer window a child window. It forwards significant messages 
  888.     // to the child window that the child window would not otherwise receive. 
  889.     if (pVW)
  890.     {
  891.         pVW->NotifyOwnerMessage((LONG_PTR) m_hWnd, message, wParam, lParam);
  892.     }
  893.  
  894.     return CDialog::WindowProc(message, wParam, lParam);
  895. }
  896.  
  897.  
  898. HRESULT CJukeboxDlg::HandleGraphEvent(void)
  899. {
  900.     LONG evCode, evParam1, evParam2;
  901.     HRESULT hr=S_OK;
  902.  
  903.     // Since we may have a scenario where we're shutting down the application,
  904.     // but events are still being generated, make sure that the event
  905.     // interface is still valid before using it.  It's possible that
  906.     // the interface could be freed during shutdown but later referenced in
  907.     // this callback before the app completely exits.
  908.     if (!pME)
  909.         return S_OK;
  910.     
  911.     while(SUCCEEDED(pME->GetEvent(&evCode, (LONG_PTR *) &evParam1, 
  912.                     (LONG_PTR *) &evParam2, 0)))
  913.     {
  914.         // Spin through the events
  915.         hr = pME->FreeEventParams(evCode, evParam1, evParam2);
  916.  
  917.         if(EC_COMPLETE == evCode)
  918.         {
  919.             // If looping, reset to beginning and continue playing
  920.             if (g_bLooping)
  921.             {
  922.                 LONGLONG pos=0;
  923.  
  924.                 // Reset to first frame of movie
  925.                 hr = pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
  926.                                        NULL, AM_SEEKING_NoPositioning);
  927.                 if (FAILED(hr))
  928.                 {
  929.                     // Some custom filters (like the Windows CE MIDI filter) 
  930.                     // may not implement seeking interfaces (IMediaSeeking)
  931.                     // to allow seeking to the start.  In that case, just stop 
  932.                     // and restart for the same effect.  This should not be
  933.                     // necessary in most cases.
  934.                     StopMedia();
  935.                     RunMedia();
  936.                 }
  937.             }
  938.             else if (g_bPlayThrough)
  939.             {
  940.                 // Tell the app to select the next file in the list
  941.                 PostMessage(WM_NEXTFILE, 0, 0);
  942.             }
  943.             else
  944.             {
  945.                 // Stop playback and display first frame of movie
  946.                 OnStop();
  947.             }
  948.         }
  949.  
  950.         //  If requested, display DirectShow events received
  951.         if (g_bDisplayEvents)
  952.         {
  953.             DisplayECEvent(evCode, evParam1, evParam2);
  954.         }
  955.     }
  956.  
  957.     return hr;
  958. }
  959.  
  960. void CJukeboxDlg::OnCheckMute() 
  961. {
  962.     // Remember global mute status for next file.  When you destroy a
  963.     // filtergraph, you lose all of its audio settings.  Therefore, when
  964.     // we create the next graph, we will mute the audio before running
  965.     // the graph if this global variable is set.
  966.     g_bGlobalMute ^= 1; 
  967.  
  968.     if (g_bGlobalMute)
  969.         MuteAudio();
  970.     else
  971.         ResumeAudio();
  972. }
  973.  
  974. void CJukeboxDlg::OnCheckLoop() 
  975. {
  976.     g_bLooping ^= 1;
  977.  
  978.     // Looping and play-through are mutually exclusive
  979.     if ((g_bLooping) && (g_bPlayThrough))
  980.     {
  981.         // Disable play-through and uncheck button
  982.         g_bPlayThrough = 0;
  983.         m_CheckPlaythrough.SetCheck(0);
  984.     }
  985. }
  986.  
  987. void CJukeboxDlg::OnCheckPlaythrough() 
  988. {
  989.     g_bPlayThrough ^= 1;    
  990.  
  991.     // Looping and play-through are mutually exclusive
  992.     if ((g_bPlayThrough) && (g_bLooping) )
  993.     {
  994.         // Disable play-through and uncheck button
  995.         g_bLooping = 0;
  996.         m_CheckLoop.SetCheck(0);
  997.     }
  998. }
  999.  
  1000. void CJukeboxDlg::OnCheckEvents() 
  1001. {
  1002.     g_bDisplayEvents ^= 1;    
  1003. }
  1004.  
  1005. void CJukeboxDlg::OnButtonClearEvents() 
  1006. {
  1007.     m_ListEvents.ResetContent();
  1008. }
  1009.  
  1010. void CJukeboxDlg::OnButtonSetMediadir() 
  1011. {
  1012.     // Make sure that we're not playing media
  1013.     OnStop();
  1014.  
  1015.     // Read the string in the media directory edit box.
  1016.     TCHAR szEditPath[MAX_PATH];
  1017.     DWORD dwAttr;
  1018.  
  1019.     m_EditMediaDir.GetWindowText(szEditPath, MAX_PATH);
  1020.  
  1021.     // Is this a valid directory name?
  1022.     dwAttr = GetFileAttributes(szEditPath);
  1023.     if ((dwAttr == (DWORD) -1) || (! (dwAttr & FILE_ATTRIBUTE_DIRECTORY)))
  1024.     {
  1025.         MessageBox(TEXT("Please enter a valid directory name."), TEXT("Media error"));
  1026.         return;
  1027.     }
  1028.  
  1029.     // User has specified a valid media directory.  
  1030.     // Update the current path string.
  1031.     TCHAR szPathMsg[MAX_PATH];
  1032.     wsprintf(szPathMsg, TEXT("Media directory: %s\0"), szEditPath);
  1033.     m_StrMediaPath.SetWindowText(szPathMsg);
  1034.  
  1035.     // Save current directory name.  Append the trailing '\' to match
  1036.     // the string created by GetDXSDKMediaPath() (if not present)
  1037.     int nLength = _tcslen(szEditPath);
  1038.     if (szEditPath[nLength - 1] != TEXT('\\'))
  1039.         wsprintf(m_szCurrentDir, TEXT("%s\\\0"), szEditPath);
  1040.  
  1041.     // Propagate the files list and select the first item
  1042.     FillFileList(szEditPath);
  1043. }
  1044.  
  1045.  
  1046. void CJukeboxDlg::OnDblclkListFiles() 
  1047. {
  1048.     // Because it might take time to render the file and display
  1049.     // its first frame, it's better to post a message that tells
  1050.     // the app to play the selected file when ready.
  1051.     PostMessage(WM_PLAYFILE, 0, 0L);
  1052. }
  1053.  
  1054. void CJukeboxDlg::PlaySelectedFile() 
  1055. {
  1056.     OnPlay();
  1057. }
  1058.  
  1059. void CJukeboxDlg::OnDeltaposSpinFiles(NMHDR* pNMHDR, LRESULT* pResult) 
  1060. {
  1061.     NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
  1062.  
  1063.     if (pNMUpDown->iDelta > 0)
  1064.         PostMessage(WM_NEXTFILE, 0, 0L);
  1065.     else
  1066.         PostMessage(WM_PREVIOUSFILE, 0, 0L);
  1067.  
  1068.     *pResult = 0;
  1069. }
  1070.  
  1071. void CJukeboxDlg::PlayNextFile(void)
  1072. {
  1073.     int nItems  = m_ListFiles.GetCount();
  1074.  
  1075.     // Return if the list is empty
  1076.     if (!nItems)
  1077.         return;
  1078.         
  1079.     int nCurSel = m_ListFiles.GetCurSel();
  1080.     int nNewSel = (nCurSel + 1) % nItems;
  1081.  
  1082.     // Select the next item in the list, wrapping to top if needed
  1083.     m_ListFiles.SetCurSel(nNewSel);
  1084.     OnSelectFile();
  1085. }
  1086.  
  1087.  
  1088. void CJukeboxDlg::PlayPreviousFile(void)
  1089. {
  1090.     int nItems  = m_ListFiles.GetCount();
  1091.  
  1092.     // Return if the list is empty
  1093.     if (!nItems)
  1094.         return;
  1095.         
  1096.     int nCurSel = m_ListFiles.GetCurSel();
  1097.     int nNewSel = nCurSel - 1;
  1098.  
  1099.     // If moved off top of list, select last item in list
  1100.     if (nNewSel < 0)
  1101.         nNewSel = nItems - 1;
  1102.  
  1103.     // Select the next item in the list, wrapping to top if needed
  1104.     m_ListFiles.SetCurSel(nNewSel);
  1105.     OnSelectFile();
  1106. }
  1107.  
  1108.  
  1109. HRESULT CJukeboxDlg::EnumFilters (void) 
  1110. {
  1111.     HRESULT hr;
  1112.     IEnumFilters *pEnum = NULL;
  1113.     IBaseFilter *pFilter = NULL;
  1114.     ULONG cFetched;
  1115.  
  1116.     // Clear filters list box
  1117.     m_ListFilters.ResetContent();
  1118.     
  1119.     // Get filter enumerator
  1120.     hr = pGB->EnumFilters(&pEnum);
  1121.     if (FAILED(hr))
  1122.     {
  1123.         m_ListFilters.AddString(TEXT("<ERROR>"));
  1124.         return hr;
  1125.     }
  1126.  
  1127.     // Enumerate all filters in the graph
  1128.     while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
  1129.     {
  1130.         FILTER_INFO FilterInfo;
  1131.         TCHAR szName[256];
  1132.         
  1133.         hr = pFilter->QueryFilterInfo(&FilterInfo);
  1134.         if (FAILED(hr))
  1135.         {
  1136.             m_ListFilters.AddString(TEXT("<ERROR>"));
  1137.         }
  1138.         else
  1139.         {
  1140.             // Add the filter name to the filters listbox
  1141.             USES_CONVERSION;
  1142.  
  1143.             lstrcpy(szName, W2T(FilterInfo.achName));
  1144.             m_ListFilters.AddString(szName);
  1145.  
  1146.             FilterInfo.pGraph->Release();
  1147.         }       
  1148.         pFilter->Release();
  1149.     }
  1150.     pEnum->Release();
  1151.  
  1152.     return hr;
  1153. }
  1154.  
  1155.  
  1156. //
  1157. // The GraphBuilder interface provides a FindFilterByName() method,
  1158. // which provides similar functionality to the method below.
  1159. // This local method is provided for educational purposes.
  1160. //
  1161. IBaseFilter *CJukeboxDlg::FindFilterFromName(LPTSTR szNameToFind)
  1162. {
  1163.     USES_CONVERSION;
  1164.  
  1165.     HRESULT hr;
  1166.     IEnumFilters *pEnum = NULL;
  1167.     IBaseFilter *pFilter = NULL;
  1168.     ULONG cFetched;
  1169.     BOOL bFound = FALSE;
  1170.  
  1171.     // Get filter enumerator
  1172.     hr = pGB->EnumFilters(&pEnum);
  1173.     if (FAILED(hr))
  1174.         return NULL;
  1175.  
  1176.     // Enumerate all filters in the graph
  1177.     while((pEnum->Next(1, &pFilter, &cFetched) == S_OK) && (!bFound))
  1178.     {
  1179.         FILTER_INFO FilterInfo;
  1180.         TCHAR szName[256];
  1181.         
  1182.         hr = pFilter->QueryFilterInfo(&FilterInfo);
  1183.         if (FAILED(hr))
  1184.         {
  1185.             pFilter->Release();
  1186.             pEnum->Release();
  1187.             return NULL;
  1188.         }
  1189.  
  1190.         // Compare this filter's name with the one we want
  1191.         lstrcpy(szName, W2T(FilterInfo.achName));
  1192.         if (! lstrcmp(szName, szNameToFind))
  1193.         {
  1194.             bFound = TRUE;
  1195.         }
  1196.  
  1197.         FilterInfo.pGraph->Release();
  1198.  
  1199.         // If we found the right filter, don't release its interface.
  1200.         // The caller will use it and release it later.
  1201.         if (!bFound)
  1202.             pFilter->Release();
  1203.         else
  1204.             break;
  1205.     }
  1206.     pEnum->Release();
  1207.  
  1208.     return (bFound ? pFilter : NULL);
  1209. }
  1210.  
  1211.  
  1212. HRESULT CJukeboxDlg::EnumPins(IBaseFilter *pFilter, PIN_DIRECTION PinDir,
  1213.                               CListBox& Listbox)
  1214. {
  1215.     HRESULT hr;
  1216.     IEnumPins  *pEnum = NULL;
  1217.     IPin *pPin = NULL;
  1218.  
  1219.     // Clear the specified listbox (input or output)
  1220.     Listbox.ResetContent();
  1221.  
  1222.     // Get pin enumerator
  1223.     hr = pFilter->EnumPins(&pEnum);
  1224.     if (FAILED(hr))
  1225.     {
  1226.         Listbox.AddString(TEXT("<ERROR>"));
  1227.         return hr;
  1228.     }
  1229.  
  1230.     // Enumerate all pins on this filter
  1231.     while(pEnum->Next(1, &pPin, 0) == S_OK)
  1232.     {
  1233.         PIN_DIRECTION PinDirThis;
  1234.  
  1235.         hr = pPin->QueryDirection(&PinDirThis);
  1236.         if (FAILED(hr))
  1237.         {
  1238.             Listbox.AddString(TEXT("<ERROR>"));
  1239.             pPin->Release();
  1240.             continue;
  1241.         }
  1242.  
  1243.         // Does the pin's direction match the requested direction?
  1244.         if (PinDir == PinDirThis)
  1245.         {
  1246.             PIN_INFO pininfo={0};
  1247.  
  1248.             // Direction matches, so add pin name to listbox
  1249.             hr = pPin->QueryPinInfo(&pininfo);
  1250.             if (SUCCEEDED(hr))
  1251.             {
  1252.                 CString str(pininfo.achName);
  1253.                 Listbox.AddString(str);
  1254.             }
  1255.  
  1256.             // The pininfo structure contains a reference to an IBaseFilter,
  1257.             // so you must release its reference to prevent resource a leak.
  1258.             pininfo.pFilter->Release();
  1259.         }
  1260.         pPin->Release();
  1261.     }
  1262.     pEnum->Release();
  1263.  
  1264.     return hr;
  1265. }
  1266.  
  1267.  
  1268. void CJukeboxDlg::OnSelchangeListFilters() 
  1269. {
  1270.     HRESULT hr;
  1271.     IBaseFilter *pFilter = NULL;
  1272.     TCHAR szNameToFind[128];
  1273.  
  1274.     // Read the current filter name from the list box
  1275.     int nCurSel = m_ListFilters.GetCurSel();
  1276.     m_ListFilters.GetText(nCurSel, szNameToFind);
  1277.  
  1278.     // Read the current list box name and find it in the graph
  1279.     pFilter = FindFilterFromName(szNameToFind);
  1280.     if (!pFilter)
  1281.         return;
  1282.  
  1283.     // Now that we have filter information, enumerate pins by direction
  1284.     // and add their names to the appropriate listboxes
  1285.     hr = EnumPins(pFilter, PINDIR_INPUT,  m_ListPinsInput);
  1286.     hr = EnumPins(pFilter, PINDIR_OUTPUT, m_ListPinsOutput);
  1287.  
  1288.     // Find out if this filter supports a property page
  1289.     if (SupportsPropertyPage(pFilter))
  1290.         m_ButtonProperties.EnableWindow(TRUE);
  1291.     else
  1292.         m_ButtonProperties.EnableWindow(FALSE);
  1293.     
  1294.     // Must release the filter interface returned from FindFilterByName()
  1295.     pFilter->Release();
  1296. }
  1297.  
  1298.  
  1299. BOOL CJukeboxDlg::DisplayImageInfo(void)
  1300. {
  1301.     HRESULT hr;
  1302.     long lWidth, lHeight;
  1303.     
  1304.     // If this file has no video component, clear the text field
  1305.     if ((!pBV) || (g_bAudioOnly))
  1306.     {
  1307.         m_StrImageSize.SetWindowText(TEXT("\0"));
  1308.         return FALSE;
  1309.     }
  1310.  
  1311.     hr = pBV->GetVideoSize(&lWidth, &lHeight);
  1312.     if (SUCCEEDED(hr))
  1313.     {
  1314.         TCHAR szSize[64];
  1315.         wsprintf(szSize, TEXT("Image size: %d x %d\0"), lWidth, lHeight);
  1316.         m_StrImageSize.SetWindowText(szSize);
  1317.     }
  1318.  
  1319.     return TRUE;
  1320. }
  1321.  
  1322.  
  1323. BOOL CJukeboxDlg::DisplayFileInfo(LPTSTR szFile)
  1324. {
  1325.     HANDLE hFile;
  1326.     LONGLONG llSize=0;
  1327.     DWORD dwSizeLow=0, dwSizeHigh=0;
  1328.     TCHAR szScrap[64];
  1329.  
  1330.     // Open the specified file to read size and date information
  1331.     hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
  1332.                        (DWORD) 0, NULL);
  1333.  
  1334.     if (hFile == INVALID_HANDLE_VALUE)
  1335.     {
  1336.         RetailOutput(TEXT("*** Failed(0x%x) to open file (to read size)!\r\n"),
  1337.                      GetLastError());
  1338.         return FALSE;
  1339.     }
  1340.  
  1341.     dwSizeLow = GetFileSize(hFile, &dwSizeHigh);
  1342.     if ((dwSizeLow == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
  1343.     {
  1344.         RetailOutput(TEXT("*** Error(0x%x) reading file size!\r\n"),
  1345.                      GetLastError());
  1346.         CloseHandle(hFile);
  1347.         return FALSE;
  1348.     }
  1349.  
  1350.     // Large files will also use the upper DWORD to report size.
  1351.     // Add them together for the true size if necessary.
  1352.     if (dwSizeHigh)
  1353.         llSize = (dwSizeHigh << 16) + dwSizeLow;
  1354.     else
  1355.         llSize = dwSizeLow;
  1356.  
  1357.     // Read date information
  1358.     BY_HANDLE_FILE_INFORMATION fi;
  1359.     if (GetFileInformationByHandle(hFile, &fi))
  1360.     {
  1361.         CTime time(fi.ftLastWriteTime);
  1362.  
  1363.         wsprintf(szScrap, TEXT("File date: %02d/%02d/%d\0"), 
  1364.                  time.GetMonth(), time.GetDay(), time.GetYear());
  1365.         m_StrFileDate.SetWindowText(szScrap);
  1366.     }
  1367.  
  1368.     CloseHandle(hFile);
  1369.  
  1370.     // Update size/date windows
  1371.     wsprintf(szScrap, TEXT("Size: %d bytes\0"), dwSizeLow);
  1372.     m_StrFileSize.SetWindowText(szScrap);
  1373.  
  1374.     return TRUE;
  1375. }
  1376.  
  1377.  
  1378. HRESULT CJukeboxDlg::DisplayFileDuration(void)
  1379. {
  1380.     HRESULT hr;
  1381.  
  1382.     if (!pMS)
  1383.         return E_NOINTERFACE;
  1384.  
  1385.     // Initialize the display in case we can't read the duration
  1386.     m_StrDuration.SetWindowText(TEXT("<00:00.000>"));
  1387.  
  1388.     // Is media time supported for this file?
  1389.     if (S_OK != pMS->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME))
  1390.         return E_NOINTERFACE;
  1391.  
  1392.     // Read the time format to restore later
  1393.     GUID guidOriginalFormat;
  1394.     hr = pMS->GetTimeFormat(&guidOriginalFormat);
  1395.     if (FAILED(hr))
  1396.         return hr;
  1397.  
  1398.     // Ensure media time format for easy display
  1399.     hr = pMS->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
  1400.     if (FAILED(hr))
  1401.         return hr;
  1402.  
  1403.     // Read the file's duration
  1404.     LONGLONG llDuration;
  1405.     hr = pMS->GetDuration(&llDuration);
  1406.     if (FAILED(hr))
  1407.         return hr;
  1408.  
  1409.     // Return to the original format
  1410.     if (guidOriginalFormat != TIME_FORMAT_MEDIA_TIME)
  1411.     {
  1412.         hr = pMS->SetTimeFormat(&guidOriginalFormat);
  1413.         if (FAILED(hr))
  1414.             return hr;
  1415.     }
  1416.  
  1417.     // Convert the LONGLONG duration into human-readable format
  1418.     unsigned long nTotalMS = (unsigned long) llDuration / 10000; // 100ns -> ms
  1419.     int nMS = nTotalMS % 1000;
  1420.     int nSeconds = nTotalMS / 1000;
  1421.     int nMinutes = nSeconds / 60;
  1422.     nSeconds %= 60;
  1423.  
  1424.     // Update the display
  1425.     TCHAR szDuration[24];
  1426.     wsprintf(szDuration, _T("%02dm:%02d.%03ds\0"), nMinutes, nSeconds, nMS);
  1427.     m_StrDuration.SetWindowText(szDuration);
  1428.  
  1429.     return hr;
  1430. }
  1431.  
  1432.  
  1433. BOOL CJukeboxDlg::SupportsPropertyPage(IBaseFilter *pFilter) 
  1434. {
  1435.     HRESULT hr;
  1436.     ISpecifyPropertyPages *pSpecify;
  1437.  
  1438.     // Discover if this filter contains a property page
  1439.     hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
  1440.     if (SUCCEEDED(hr)) 
  1441.     {
  1442.         pSpecify->Release();
  1443.         return TRUE;
  1444.     }
  1445.     else
  1446.         return FALSE;
  1447. }
  1448.  
  1449. void CJukeboxDlg::OnButtonProppage() 
  1450. {
  1451.     HRESULT hr;
  1452.     IBaseFilter *pFilter = NULL;
  1453.     TCHAR szNameToFind[128];
  1454.     ISpecifyPropertyPages *pSpecify;
  1455.  
  1456.     // Read the current filter name from the list box
  1457.     int nCurSel = m_ListFilters.GetCurSel();
  1458.     m_ListFilters.GetText(nCurSel, szNameToFind);
  1459.  
  1460.     // Read the current list box name and find it in the graph
  1461.     pFilter = FindFilterFromName(szNameToFind);
  1462.     if (!pFilter)
  1463.         return;
  1464.  
  1465.     // Discover if this filter contains a property page
  1466.     hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
  1467.     if (SUCCEEDED(hr)) 
  1468.     {
  1469.         do 
  1470.         {
  1471.             FILTER_INFO FilterInfo;
  1472.             hr = pFilter->QueryFilterInfo(&FilterInfo);
  1473.             if (FAILED(hr))
  1474.                 break;
  1475.  
  1476.             CAUUID caGUID;
  1477.             hr = pSpecify->GetPages(&caGUID);
  1478.             if (FAILED(hr))
  1479.                 break;
  1480.  
  1481.             pSpecify->Release();
  1482.         
  1483.             // Display the filter's property page
  1484.             OleCreatePropertyFrame(
  1485.                 m_hWnd,                 // Parent window
  1486.                 0,                      // x (Reserved)
  1487.                 0,                      // y (Reserved)
  1488.                 FilterInfo.achName,     // Caption for the dialog box
  1489.                 1,                      // Number of filters
  1490.                 (IUnknown **)&pFilter,  // Pointer to the filter 
  1491.                 caGUID.cElems,          // Number of property pages
  1492.                 caGUID.pElems,          // Pointer to property page CLSIDs
  1493.                 0,                      // Locale identifier
  1494.                 0,                      // Reserved
  1495.                 NULL                    // Reserved
  1496.             );
  1497.             CoTaskMemFree(caGUID.pElems);
  1498.             FilterInfo.pGraph->Release(); 
  1499.  
  1500.         } while(0);
  1501.     }
  1502.  
  1503.     pFilter->Release();
  1504. }
  1505.  
  1506. void CJukeboxDlg::OnDblclkListFilters() 
  1507. {
  1508.     OnButtonProppage();
  1509. }
  1510.  
  1511.  
  1512. void CJukeboxDlg::DisplayECEvent(long lEventCode, long lParam1, long lParam2)
  1513. {
  1514.     static TCHAR szMsg[256];
  1515.     BOOL bMatch = TRUE;
  1516.  
  1517. #define HANDLE_EC(c)                              \
  1518.     case c:                                       \
  1519.         wsprintf(szMsg, TEXT("%s\0"), TEXT(#c));  \
  1520.         break;
  1521.  
  1522.     switch (lEventCode)
  1523.     {
  1524.         HANDLE_EC(EC_ACTIVATE);
  1525.         HANDLE_EC(EC_BUFFERING_DATA);
  1526.         HANDLE_EC(EC_CLOCK_CHANGED);
  1527.         HANDLE_EC(EC_COMPLETE);
  1528.         HANDLE_EC(EC_DEVICE_LOST);
  1529.         HANDLE_EC(EC_DISPLAY_CHANGED);
  1530.         HANDLE_EC(EC_END_OF_SEGMENT);
  1531.         HANDLE_EC(EC_ERROR_STILLPLAYING);
  1532.         HANDLE_EC(EC_ERRORABORT);
  1533.         HANDLE_EC(EC_EXTDEVICE_MODE_CHANGE);
  1534.         HANDLE_EC(EC_FULLSCREEN_LOST);
  1535.         HANDLE_EC(EC_GRAPH_CHANGED);
  1536.         HANDLE_EC(EC_LENGTH_CHANGED);
  1537.         HANDLE_EC(EC_NEED_RESTART);
  1538.         HANDLE_EC(EC_NOTIFY_WINDOW);
  1539.         HANDLE_EC(EC_OLE_EVENT);
  1540.         HANDLE_EC(EC_OPENING_FILE);
  1541.         HANDLE_EC(EC_PALETTE_CHANGED);
  1542.         HANDLE_EC(EC_PAUSED);
  1543.         HANDLE_EC(EC_QUALITY_CHANGE);
  1544.         HANDLE_EC(EC_REPAINT);
  1545.         HANDLE_EC(EC_SEGMENT_STARTED);
  1546.         HANDLE_EC(EC_SHUTTING_DOWN);
  1547.         HANDLE_EC(EC_SNDDEV_IN_ERROR);
  1548.         HANDLE_EC(EC_SNDDEV_OUT_ERROR);
  1549.         HANDLE_EC(EC_STARVATION);
  1550.         HANDLE_EC(EC_STEP_COMPLETE);
  1551.         HANDLE_EC(EC_STREAM_CONTROL_STARTED);
  1552.         HANDLE_EC(EC_STREAM_CONTROL_STOPPED);
  1553.         HANDLE_EC(EC_STREAM_ERROR_STILLPLAYING);
  1554.         HANDLE_EC(EC_STREAM_ERROR_STOPPED);
  1555.         HANDLE_EC(EC_TIMECODE_AVAILABLE);
  1556.         HANDLE_EC(EC_USERABORT);
  1557.         HANDLE_EC(EC_VIDEO_SIZE_CHANGED);
  1558.         HANDLE_EC(EC_WINDOW_DESTROYED);
  1559.  
  1560.     default:
  1561.         bMatch = FALSE;
  1562.         RetailOutput(TEXT("  Received unknown event code (0x%x)\r\n"), lEventCode);
  1563.         break;
  1564.     }
  1565.  
  1566.     // If a recognized event was found, add its name to the events list box
  1567.     if (bMatch)
  1568.         m_ListEvents.AddString(szMsg);
  1569. }
  1570.  
  1571.  
  1572. void CJukeboxDlg::OnButtonFramestep() 
  1573. {
  1574.     StepFrame();
  1575. }
  1576.  
  1577.  
  1578. //
  1579. // Some hardware decoders and video renderers support stepping media
  1580. // frame by frame with the IVideoFrameStep interface.  See the interface
  1581. // documentation for more details on frame stepping.
  1582. //
  1583. BOOL CJukeboxDlg::CanStep(void)
  1584. {
  1585.     HRESULT hr;
  1586.     IVideoFrameStep* pFS;
  1587.  
  1588.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
  1589.     if (FAILED(hr))
  1590.         return FALSE;
  1591.  
  1592.     // Check if this decoder can step
  1593.     hr = pFS->CanStep(0L, NULL); 
  1594.  
  1595.     pFS->Release();
  1596.  
  1597.     if (hr == S_OK)
  1598.         return TRUE;
  1599.     else
  1600.         return FALSE;
  1601. }
  1602.  
  1603. HRESULT CJukeboxDlg::StepFrame(void)
  1604. {
  1605.     // Get the Frame Stepping Interface
  1606.     HRESULT hr;
  1607.     IVideoFrameStep* pFS;
  1608.  
  1609.     hr = pGB->QueryInterface(__uuidof(IVideoFrameStep), (PVOID *)&pFS);
  1610.     if (FAILED(hr))
  1611.         return hr;
  1612.  
  1613.     // The graph must be paused for frame stepping to work
  1614.     if (g_psCurrent != State_Paused)
  1615.         OnPause();
  1616.  
  1617.     // Step one frame
  1618.     hr = pFS->Step(1, NULL); 
  1619.     pFS->Release();
  1620.  
  1621.     // Since the media position has changed slightly, update the
  1622.     // slider bar and position label.
  1623.     ReadMediaPosition();
  1624.     return hr;
  1625. }
  1626.  
  1627.  
  1628. void CJukeboxDlg::ConfigureSeekbar()
  1629. {
  1630.     // Disable seekbar for new file and reset tracker/position label
  1631.     m_Seekbar.SetPos(0);
  1632.     m_StrPosition.SetWindowText(TEXT("Position: 00m:00s\0"));
  1633.     g_rtTotalTime=0;
  1634.  
  1635.     // If we can't read the file's duration, disable the seek bar
  1636.     if (pMS && SUCCEEDED(pMS->GetDuration(&g_rtTotalTime)))
  1637.         m_Seekbar.EnableWindow(TRUE);
  1638.     else
  1639.         m_Seekbar.EnableWindow(FALSE);
  1640. }
  1641.  
  1642. void CJukeboxDlg::StartSeekTimer() 
  1643. {
  1644.     // Cancel any pending timer event
  1645.     StopSeekTimer();
  1646.  
  1647.     // Create a new timer
  1648.     g_wTimerID = SetTimer(TIMERID, TICKLEN, NULL);
  1649. }
  1650.  
  1651. void CJukeboxDlg::StopSeekTimer() 
  1652. {
  1653.     // Cancel the timer
  1654.     if(g_wTimerID)        
  1655.     {                
  1656.         KillTimer(g_wTimerID);
  1657.         g_wTimerID = 0;
  1658.     }
  1659. }
  1660.  
  1661. void CJukeboxDlg::OnTimer(UINT nIDEvent) 
  1662. {
  1663.     ReadMediaPosition();
  1664.  
  1665.     CDialog::OnTimer(nIDEvent);
  1666. }
  1667.  
  1668. void CJukeboxDlg::ReadMediaPosition()
  1669. {
  1670.     HRESULT hr;
  1671.     REFERENCE_TIME rtNow;
  1672.  
  1673.     // Read the current stream position
  1674.     hr = pMS->GetCurrentPosition(&rtNow);
  1675.     if (FAILED(hr))
  1676.         return;
  1677.  
  1678.     // Convert position into a percentage value and update slider position
  1679.     if (g_rtTotalTime != 0)
  1680.     {
  1681.         long lTick = (long)((rtNow * 100) / g_rtTotalTime);
  1682.         m_Seekbar.SetPos(lTick);
  1683.     }
  1684.     else
  1685.         m_Seekbar.SetPos(0);
  1686.     
  1687.     // Update the 'current position' string on the main dialog
  1688.     UpdatePosition(rtNow);
  1689. }
  1690.  
  1691. void CJukeboxDlg::UpdatePosition(REFERENCE_TIME rtNow) 
  1692. {
  1693.     HRESULT hr;
  1694.  
  1695.     // If no reference time was passed in, read the current position
  1696.     if (rtNow == 0)
  1697.     {
  1698.         // Read the current stream position
  1699.         hr = pMS->GetCurrentPosition(&rtNow);
  1700.         if (FAILED(hr))
  1701.             return;
  1702.     }
  1703.  
  1704.     // Convert the LONGLONG duration into human-readable format
  1705.     unsigned long nTotalMS = (unsigned long) rtNow / 10000; // 100ns -> ms
  1706.     int nSeconds = nTotalMS / 1000;
  1707.     int nMinutes = nSeconds / 60;
  1708.     nSeconds %= 60;
  1709.  
  1710.     // Update the display
  1711.     TCHAR szPosition[24], szCurrentString[24];
  1712.     wsprintf(szPosition, _T("Position: %02dm:%02ds\0"), nMinutes, nSeconds);
  1713.  
  1714.     // Read current string and compare to the new string.  To prevent flicker,
  1715.     // don't update this label unless the string has changed.
  1716.     m_StrPosition.GetWindowText(szCurrentString, 24);
  1717.  
  1718.     if (_tcscmp(szCurrentString, szPosition))
  1719.         m_StrPosition.SetWindowText(szPosition);
  1720. }
  1721.  
  1722. void CJukeboxDlg::HandleTrackbar(WPARAM wReq)
  1723. {
  1724.     HRESULT hr;
  1725.     static OAFilterState state;
  1726.     static BOOL bStartOfScroll = TRUE;
  1727.  
  1728.     // If the file is not seekable, the trackbar is disabled. 
  1729.     DWORD dwPosition = m_Seekbar.GetPos();
  1730.  
  1731.     // Pause when the scroll action begins.
  1732.     if (bStartOfScroll) 
  1733.     {       
  1734.         hr = pMC->GetState(10, &state);
  1735.         bStartOfScroll = FALSE;
  1736.         hr = pMC->Pause();
  1737.     }
  1738.     
  1739.     // Update the position continuously.
  1740.     REFERENCE_TIME rtNew = (g_rtTotalTime * dwPosition) / 100;
  1741.  
  1742.     hr = pMS->SetPositions(&rtNew, AM_SEEKING_AbsolutePositioning,
  1743.                            NULL,   AM_SEEKING_NoPositioning);
  1744.  
  1745.     // Restore the state at the end.
  1746.     if (wReq == TB_ENDTRACK)
  1747.     {
  1748.         if (state == State_Stopped)
  1749.             hr = pMC->Stop();
  1750.         else if (state == State_Running) 
  1751.             hr = pMC->Run();
  1752.  
  1753.         bStartOfScroll = TRUE;
  1754.     }
  1755.  
  1756.     // Update the 'current position' string on the main dialog.
  1757.     UpdatePosition(rtNew);
  1758. }
  1759.  
  1760. LONG CJukeboxDlg::GetGraphEditPath(TCHAR *szPath)
  1761. {
  1762.     HKEY  hKey;
  1763.     DWORD dwType, dwSize = MAX_PATH;
  1764.  
  1765.     // Open the appropriate registry key
  1766.     LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1767.                                 _T("Software\\Microsoft\\Shared Tools\\Graphedit"),
  1768.                                 0, KEY_READ, &hKey );
  1769.     if( ERROR_SUCCESS != lResult )
  1770.         return -1;
  1771.  
  1772.     // Read the full path (including .exe name) for the GraphEdit tool
  1773.     lResult = RegQueryValueEx( hKey, _T("Path"), NULL,
  1774.                               &dwType, (BYTE*)szPath, &dwSize );
  1775.     RegCloseKey( hKey );
  1776.  
  1777.     if( ERROR_SUCCESS != lResult )
  1778.         return -1;
  1779.  
  1780.     return 0;
  1781. }
  1782.  
  1783. void CJukeboxDlg::OnButtonGraphedit() 
  1784. {
  1785.     TCHAR szFilename[128], szFile[MAX_PATH];
  1786.  
  1787.     // Read file name from list box
  1788.     int nItem = m_ListFiles.GetCurSel();
  1789.     m_ListFiles.GetText(nItem, szFilename);
  1790.  
  1791.     // Build the full file name with path.  The path will already have
  1792.     // a '\' appended to the end.
  1793.     wsprintf(szFile, TEXT("%s%s\0"), m_szCurrentDir, szFilename);
  1794.  
  1795.     // Launch GraphEdit for the selected file
  1796.     //    
  1797.     // First look for a registry key containing its full path
  1798.     TCHAR szPath[MAX_PATH];
  1799.  
  1800.     LONG lResult = GetGraphEditPath(szPath);
  1801.  
  1802.     // If the DirectX SDK is not installed, just look for GraphEdit
  1803.     // anywhere in the system path.
  1804.     if (lResult != 0)
  1805.     {
  1806.         wsprintf(szPath, TEXT("%s\0"), TEXT("graphedt\0"));
  1807.     }
  1808.  
  1809.     // Lauch GraphEdit using either the full tool path or just its name                               
  1810.     HINSTANCE rc = ShellExecute(m_hWnd, TEXT("open\0"), szPath, 
  1811.                                 szFile, NULL, SW_SHOWNORMAL);
  1812.  
  1813.     if (rc < (HINSTANCE) 32)
  1814.     {
  1815.         // Failed to start the app
  1816.         if ((rc == (HINSTANCE) ERROR_FILE_NOT_FOUND) || 
  1817.             (rc == (HINSTANCE) ERROR_PATH_NOT_FOUND))
  1818.         {
  1819.             MessageBox(TEXT("Couldn't find the GraphEdit application.\r\n\r\n")
  1820.                        TEXT("Please copy graphedt.exe to a directory on your path."), 
  1821.                        TEXT("Can't find GraphEdit"));   
  1822.         }
  1823.     }
  1824. }
  1825.  
  1826. BOOL CJukeboxDlg::OnEraseBkgnd(CDC *pDC)
  1827. {
  1828.     // Intercept background erasing for the movie window, since the
  1829.     // video renderer will keep the screen painted.  Without this code,
  1830.     // your video window might get painted over with gray (the default
  1831.     // background brush) when it is obscured by another window and redrawn.
  1832.     CRect rc;
  1833.  
  1834.     // Get the bounding rectangle for the movie screen
  1835.     m_Screen.GetWindowRect(&rc);
  1836.     ScreenToClient(&rc);
  1837.  
  1838.     // Exclude the clipping region occupied by our movie screen
  1839.     pDC->ExcludeClipRect(&rc);
  1840.     
  1841.     // Erase the remainder of the dialog as usual
  1842.     return CDialog::OnEraseBkgnd(pDC);
  1843. }
  1844.