VIDCLIP.CPP
//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE. 
// 
// Copyright  1994-1996  Microsoft Corporation.  All Rights Reserved. 
// 
// PROGRAM: VidClip.c - Stolen from Generic.C 
// 
// PURPOSE: Illustrates the 'minimum' functionality of a well-behaved Win32 application.. 
// 
// PLATFORMS:  Windows 95, Windows NT 4.0 and up 
// 
// FUNCTIONS: 
//    WinMain() - calls initialization function, processes message loop 
//    InitApplication() - Initializes window data nd registers window 
//    InitInstance() -saves instance handle and creates main window 
//    WindProc() Processes messages 
//    About() - Process menssages for "About" dialog box 
//    CenterWindow() -  Centers one window over another 
// 
// SPECIAL INSTRUCTIONS: N/A 
// 
#include <vcproj.h> 
 
#define APPNAME "VidClip" 
 
#include <strmif.h> 
#include <uuids.h> 
#include <amstream.h> 
#include <initguid.h> 
#include <ddrawex.h> 
 
#define _WIN32_WINNT 0x0400 
#define _ATL_APARTMENT_THREADED 
 
 
#include "atlbase.h" 
CComModule _Module; 
#include "atlcom.h" 
 
#include <atlimpl.cpp> 
 
 
 
// Global Variables: 
 
HINSTANCE hInst;      // current instance 
char szAppName[100];  // Name of the app 
char szTitle[100];    // The title bar text 
IDirectDraw *g_pDD = NULL; 
 
TCHAR g_szStart[100]; 
TCHAR g_szEnd[100]; 
TCHAR g_szAll[100]; 
 
const DDPIXELFORMAT g_aPixelFormats[] = 
{ 
///    {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_PALETTEINDEXED8, 0, 8, 0, 0, 0, 0}, 
    {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00007C00, 0x000003E0, 0x0000001F, 0}, 
    {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0}, 
    {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0} 
}; 
CDocument g_Document; 
 
// Foward declarations of functions included in this code module: 
 
BOOL InitApplication(HINSTANCE); 
BOOL InitInstance(HINSTANCE, int); 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); 
LPTSTR   GetStringRes (int id); 
HRESULT CreateWriterStream(LPOLESTR szOutputFileName, 
                           LPOLESTR szVideoCodec, 
                           DDSURFACEDESC& ddsdVideoFormat, 
                           LPOLESTR szAudioCodec, 
                           WAVEFORMATEX& wfexAudioFormat, 
                           IMultiMediaStream **ppMMStream); 
void DoMakeMovie(HINSTANCE, HWND, CDocument *); 
 
 
// 
//  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) 
// 
//  PURPOSE: Entry point for the application. 
// 
//  COMMENTS: 
// 
// This function initializes the application and processes the 
// message loop. 
// 
int APIENTRY WinMain(HINSTANCE hInstance, 
                     HINSTANCE hPrevInstance, 
                     LPSTR     lpCmdLine, 
                     int       nCmdShow) 
{ 
   MSG msg; 
   HACCEL hAccelTable; 
 
   CoInitialize(NULL); 
   // Initialize global strings 
   lstrcpy (szAppName, APPNAME); 
   LoadString (hInstance, IDS_APP_TITLE, szTitle, 100); 
   LoadString (hInstance, IDS_START, g_szStart, sizeof(g_szStart)); 
   LoadString (hInstance, IDS_END,   g_szEnd, sizeof(g_szEnd)); 
   LoadString (hInstance, IDS_ALL,   g_szAll, sizeof(g_szAll)); 
 
   if (!hPrevInstance) { 
      // Perform instance initialization: 
      if (!InitApplication(hInstance)) { 
         return (FALSE); 
      } 
   } 
 
   // Perform application initialization: 
   if (!InitInstance(hInstance, nCmdShow)) { 
      return (FALSE); 
   } 
 
   hAccelTable = LoadAccelerators (hInstance, szAppName); 
 
   // Main message loop: 
   while (GetMessage(&msg, NULL, 0, 0)) { 
      if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) { 
         TranslateMessage(&msg); 
         DispatchMessage(&msg); 
      } 
   } 
 
   CoUninitialize(); 
   return (msg.wParam); 
 
   lpCmdLine; // This will prevent 'unused formal parameter' warnings 
} 
 
 
// 
//  FUNCTION: InitApplication(HANDLE) 
// 
//  PURPOSE: Initializes window data and registers window class 
// 
//  COMMENTS: 
// 
//       In this function, we initialize a window class by filling out a data 
//       structure of type WNDCLASS. 
// 
BOOL InitApplication(HINSTANCE hInstance) 
{ 
    WNDCLASSEX  wcex; 
    HWND        hwnd; 
 
    // Win32 will always set hPrevInstance to NULL, so lets check 
    // things a little closer. This is because we only want a single 
    // version of this app to run at a time 
    hwnd = FindWindow (szAppName, szTitle); 
    if (hwnd) { 
        // We found another version of ourself. Lets defer to it: 
        if (IsIconic(hwnd)) { 
            ShowWindow(hwnd, SW_RESTORE); 
        } 
        SetForegroundWindow (hwnd); 
 
        // If this app actually had any functionality, we would 
        // also want to communicate any action that our 'twin' 
        // should now perform based on how the user tried to 
        // execute us. 
        return FALSE; 
    } else { 
        // Fill in window class structure with parameters that describe 
        // the main window. 
 
         // Added elements for Windows 95: 
        wcex.cbSize = sizeof(WNDCLASSEX); 
 
        wcex.style          = CS_HREDRAW | CS_VREDRAW; 
        wcex.lpfnWndProc    = (WNDPROC)WndProc; 
        wcex.cbClsExtra     = 0; 
        wcex.cbWndExtra     = 0; 
        wcex.hInstance      = hInstance; 
        wcex.hIcon          = LoadIcon (hInstance, szAppName); 
        wcex.hIconSm        = LoadIcon(hInstance, "SMALL"); 
        wcex.hCursor        = LoadCursor(NULL, IDC_ARROW); 
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); 
 
        wcex.lpszMenuName  = szAppName; 
        wcex.lpszClassName = szAppName; 
 
        // Register the window class and return success/failure code. 
        return RegisterClassEx(&wcex); 
    } 
} 
 
 
BOOL AddListViewItem(HWND hLV, TCHAR * pszItem, void * pThingie) 
{ 
    LV_ITEM lvi; 
    lvi.mask= LVIF_TEXT | LVIF_PARAM; 
    lvi.iItem= 0x7FFF; 
    lvi.iSubItem= 0; 
    lvi.pszText= pszItem; 
    lvi.lParam= (LPARAM)pThingie; 
    lvi.cchTextMax  = 0; 
    ListView_InsertItem(hLV, &lvi); 
    return TRUE; 
} 
 
 
// 
//   FUNCTION: InitInstance(HANDLE, int) 
// 
//   PURPOSE: Saves instance handle and creates main window 
// 
//   COMMENTS: 
// 
//        In this function, we save the instance handle in a global variable and 
//        create and display the main program window. 
// 
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 
{ 
   HWND hWnd; 
 
   hInst = hInstance; // Store instance handle in our global variable 
 
   hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
      NULL, NULL, hInstance, NULL); 
 
   if (!hWnd) { 
      return (FALSE); 
   } 
 
   InitCommonControls(); 
   /* 
   HWND hListView = CreateWindowEx(0, WC_LISTVIEW, "Fred", WS_CHILDWINDOW | WS_CLIPSIBLINGS | LVS_REPORT, 
                        0, 0, 0, 0, hWnd, (HMENU)1, hInstance, NULL); 
 
   InitListView(hListView); 
   AddListViewItem(hListView, "This is a test", NULL); 
   AddListViewItem(hListView, "And another test", NULL); 
   AddListViewItem(hListView, "And yet another test", NULL); 
   */ 
   g_Document.Initialize(hWnd); 
 
   ShowWindow(hWnd, nCmdShow); 
   UpdateWindow(hWnd); 
 
   MessageBox (GetFocus(), GetStringRes(IDS_LIMITEDFUNCTION), szAppName, MB_OK); 
 
   //ShowWindow(hListView, nCmdShow); 
   //UpdateWindow(hListView); 
 
   // BUGBUG -- There is still a bug in ddstream with 8bpp palettes & ddreaw 
   // Make sure to make this work with a NULL g_pDD!!! 
   CComPtr <IDirectDrawFactory> pFactory; 
   HRESULT hr = CoCreateInstance(CLSID_DirectDrawFactory, NULL, CLSCTX_INPROC_SERVER, 
                                IID_IDirectDrawFactory, (void **)&pFactory); 
   if (SUCCEEDED(hr)) { 
        hr = pFactory->CreateDirectDraw(NULL, NULL, DDSCL_NORMAL,0, NULL, &g_pDD); 
   } 
   return (TRUE); 
} 
 
 
 
void UpdateMenus(HWND hWnd, CDocument * pDoc) 
{ 
    BOOL bEnableSave = FALSE; 
    BOOL bEnableEdit = FALSE; 
    if (pDoc->m_ClipList.NumClips() > 0) { 
        bEnableSave = (pDoc->m_TargetFileName != NULL); 
        bEnableEdit = (pDoc->m_ClipList.CurSelClipIndex() >= 0); 
    } 
    HMENU hMenu = GetMenu(hWnd); 
    UINT uEditSetting = bEnableEdit ? MF_ENABLED : MF_GRAYED; 
    UINT uSaveSetting = bEnableSave ? MF_ENABLED : MF_GRAYED; 
    EnableMenuItem(hMenu, IDM_VIDEO_EDITCLIP, uEditSetting); 
    EnableMenuItem(hMenu, IDM_VIDEO_DELETECLIP, uEditSetting); 
    EnableMenuItem(hMenu, IDM_VIDEO_MAKEMOVIE, uSaveSetting); 
    EnableMenuItem(hMenu, IDM_SAVE, uSaveSetting); 
    EnableMenuItem(hMenu, IDM_SAVEAS, uSaveSetting); 
} 
 
 
 
// 
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG) 
// 
//  PURPOSE:  Processes messages for the main window. 
// 
//  MESSAGES: 
// 
// WM_COMMAND - process the application menu 
// WM_PAINT - Paint the main window 
// WM_DESTROY - post a quit message and return 
//    WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes 
//    WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate 
//    WM_NCRBUTTONUP - User has clicked the right button on the application's system menu 
// 
// 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
   int wmId, wmEvent; 
   PAINTSTRUCT ps; 
   HDC hdc; 
   POINT pnt; 
   HMENU hMenu; 
   BOOL bGotHelp; 
   CDocument *pDoc = &g_Document; 
 
   switch (message) { 
 
      case WM_COMMAND: 
         wmId    = LOWORD(wParam); // Remember, these are... 
         wmEvent = HIWORD(wParam); // ...different for Win32! 
 
         //Parse the menu selections: 
         switch (wmId) { 
 
            case IDM_ABOUT: 
               DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About); 
               break; 
 
            case IDM_SETTINGS: 
                DoSettingsDialog(hInst, hWnd, pDoc); 
                UpdateMenus(hWnd, pDoc); 
                break; 
 
            case IDM_EXIT: 
               DestroyWindow (hWnd); 
               break; 
 
            case IDM_HELPTOPICS: // Only called in Windows 95 
               bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_FINDER,(DWORD)0); 
               if (!bGotHelp) 
               { 
                  MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), 
                              szAppName, MB_OK|MB_ICONHAND); 
               } 
               break; 
 
            case IDM_HELPCONTENTS: // Not called in Windows 95 
               bGotHelp = WinHelp (hWnd, APPNAME".HLP", HELP_CONTENTS,(DWORD)0); 
               if (!bGotHelp) 
               { 
                  MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), 
                              szAppName, MB_OK|MB_ICONHAND); 
               } 
               break; 
 
            case IDM_HELPSEARCH: // Not called in Windows 95 
               if (!WinHelp(hWnd, APPNAME".HLP", HELP_PARTIALKEY, 
                           (DWORD)(LPSTR)"")) 
               { 
                  MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), 
                              szAppName, MB_OK|MB_ICONHAND); 
               } 
               break; 
 
            case IDM_HELPHELP: // Not called in Windows 95 
               if(!WinHelp(hWnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) 
               { 
                  MessageBox (GetFocus(), GetStringRes(IDS_NO_HELP), 
                              szAppName, MB_OK|MB_ICONHAND); 
               } 
               break; 
 
            case IDM_VIDEO_ADDCLIP: 
                pDoc->NewClip(); 
                break; 
 
            case IDM_VIDEO_EDITCLIP: 
                pDoc->EditClip(); 
                break; 
 
            case IDM_VIDEO_DELETECLIP: 
                pDoc->DeleteClip(); 
                break; 
 
            case IDM_VIDEO_MAKEMOVIE: 
                DoMakeMovie(hInst, hWnd, pDoc); 
                break; 
 
            // Here are all the other possible menu options, 
            // all of these are currently disabled: 
            case IDM_NEW: 
                pDoc->ResetContents(); 
                break; 
 
            case IDM_OPEN: 
                return pDoc->OpenFile(); 
 
            case IDM_SAVE: 
            case IDM_SAVEAS: 
                return pDoc->SaveAsFile(wmId == IDM_SAVEAS); 
 
            case IDM_UNDO: 
            case IDM_CUT: 
            case IDM_COPY: 
            case IDM_PASTE: 
            case IDM_LINK: 
            case IDM_LINKS: 
 
            default: 
               return (DefWindowProc(hWnd, message, wParam, lParam)); 
         } 
         break; 
 
      case WM_NCRBUTTONUP: // RightClick on windows non-client area... 
         if (SendMessage(hWnd, WM_NCHITTEST, 0, lParam) == HTSYSMENU) 
         { 
            // The user has clicked the right button on the applications 
            // 'System Menu'. Here is where you would alter the default 
            // system menu to reflect your application. Notice how the 
            // explorer deals with this. For this app, we aren't doing 
            // anything 
            return (DefWindowProc(hWnd, message, wParam, lParam)); 
         } else { 
            // Nothing we are interested in, allow default handling... 
            return (DefWindowProc(hWnd, message, wParam, lParam)); 
         } 
            break; 
 
        case WM_RBUTTONDOWN: // RightClick in windows client area... 
            pnt.x = LOWORD(lParam); 
            pnt.y = HIWORD(lParam); 
            ClientToScreen(hWnd, (LPPOINT) &pnt); 
      // This is where you would determine the appropriate 'context' 
      // menu to bring up. Since this app has no real functionality, 
      // we will just bring up the 'Help' menu: 
            hMenu = GetSubMenu (GetMenu (hWnd), 2); 
            if (hMenu) { 
                TrackPopupMenu (hMenu, 0, pnt.x, pnt.y, 0, hWnd, NULL); 
            } else { 
            // Couldn't find the menu... 
                MessageBeep(0); 
            } 
            break; 
 
 
 
      case WM_PAINT: 
         hdc = BeginPaint (hWnd, &ps); 
         // Add any drawing code here... 
         EndPaint (hWnd, &ps); 
         break; 
 
      case WM_DESTROY: 
         // Tell WinHelp we don't need it any more... 
               WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0); 
         PostQuitMessage(0); 
         break; 
 
      case WM_SIZE: 
          pDoc->m_ClipList.SetSize(LOWORD(lParam), HIWORD(lParam)); 
          break; 
 
      case WM_NOTIFY: { 
          LPNMHDR pnmhdr = (LPNMHDR)lParam; 
          if (pnmhdr->hwndFrom == pDoc->m_ClipList.m_hLV) { 
              if (pnmhdr->code == NM_DBLCLK) { 
                pDoc->EditClip(); 
              } 
              UpdateMenus(hWnd, pDoc); 
          } 
          } 
          break; 
 
      case WM_ACTIVATE: 
          UpdateMenus(hWnd, pDoc); 
          // Fall through... 
 
      default: 
         return (DefWindowProc(hWnd, message, wParam, lParam)); 
   } 
   return (0); 
} 
 
// 
//  FUNCTION: About(HWND, unsigned, WORD, LONG) 
// 
//  PURPOSE:  Processes messages for "About" dialog box 
//       This version allows greater flexibility over the contents of the 'About' box, 
//       by pulling out values from the 'Version' resource. 
// 
//  MESSAGES: 
// 
// WM_INITDIALOG - initialize dialog box 
// WM_COMMAND    - Input received 
// 
// 
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
   static  HFONT hfontDlg;    // Font for dialog text 
   static   HFONT hFinePrint; // Font for 'fine print' in dialog 
   DWORD   dwVerInfoSize;     // Size of version information block 
   LPSTR   lpVersion;         // String pointer to 'version' text 
   DWORD   dwVerHnd=0;        // An 'ignored' parameter, always '0' 
   UINT    uVersionLen; 
   WORD    wRootLen; 
   BOOL    bRetCode; 
   int     i; 
   char    szFullPath[256]; 
   char    szResult[256]; 
   char    szGetName[256]; 
   DWORD dwVersion; 
   char  szVersion[40]; 
   DWORD dwResult; 
 
   switch (message) { 
        case WM_INITDIALOG: 
         ShowWindow (hDlg, SW_HIDE); 
 
         if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE) 
         { 
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0, 
                                  VARIABLE_PITCH | FF_DONTCARE, ""); 
            hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0, 
                                    VARIABLE_PITCH | FF_DONTCARE, ""); 
         } 
         else 
         { 
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                                  VARIABLE_PITCH | FF_SWISS, ""); 
            hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
                                    VARIABLE_PITCH | FF_SWISS, ""); 
         } 
 
         CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER)); 
         GetModuleFileName (hInst, szFullPath, sizeof(szFullPath)); 
 
         // Now lets dive in and pull out the version information: 
         dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd); 
         if (dwVerInfoSize) { 
            LPSTR   lpstrVffInfo; 
            HANDLE  hMem; 
            hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize); 
            lpstrVffInfo  = (char *)GlobalLock(hMem); 
            GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo); 
            // The below 'hex' value looks a little confusing, but 
            // essentially what it is, is the hexidecimal representation 
            // of a couple different values that represent the language 
            // and character set that we are wanting string values for. 
            // 040904E4 is a very common one, because it means: 
            //   US English, Windows MultiLingual characterset 
            // Or to pull it all apart: 
            // 04------        = SUBLANG_ENGLISH_USA 
            // --09----        = LANG_ENGLISH 
            // --11----        = LANG_JAPANESE 
            // ----04E4 = 1252 = Codepage for Windows:Multilingual 
 
            lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG)); 
 
            wRootLen = lstrlen(szGetName); // Save this position 
 
            // Set the title of the dialog: 
            lstrcat (szGetName, "ProductName"); 
            bRetCode = VerQueryValue((LPVOID)lpstrVffInfo, 
               (LPSTR)szGetName, 
               (LPVOID *)&lpVersion, 
               (UINT *)&uVersionLen); 
 
            // Notice order of version and string... 
            if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE) 
            { 
               lstrcpy(szResult, lpVersion); 
               lstrcat(szResult, "    o [ W        "); 
            } 
            else 
            { 
               lstrcpy(szResult, "About "); 
               lstrcat(szResult, lpVersion); 
            } 
 
            // ----------------------------------------------------- 
 
            SetWindowText (hDlg, szResult); 
 
            // Walk through the dialog items that we want to replace: 
            for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) { 
               GetDlgItemText(hDlg, i, szResult, sizeof(szResult)); 
               szGetName[wRootLen] = (char)0; 
               lstrcat (szGetName, szResult); 
               uVersionLen   = 0; 
               lpVersion     = NULL; 
               bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo, 
                  (LPSTR)szGetName, 
                  (LPVOID *)&lpVersion, 
                  (UINT *)&uVersionLen); 
 
               if ( bRetCode && uVersionLen && lpVersion) { 
               // Replace dialog item text with version info 
                  lstrcpy(szResult, lpVersion); 
                  SetDlgItemText(hDlg, i, szResult); 
               } 
               else 
               { 
                  dwResult = GetLastError(); 
 
                  wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult); 
                  SetDlgItemText (hDlg, i, szResult); 
               } 
               SendMessage (GetDlgItem (hDlg, i), WM_SETFONT, 
                  (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg), 
                  TRUE); 
            } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) 
 
 
            GlobalUnlock(hMem); 
            GlobalFree(hMem); 
 
         } else { 
            // No version information available. 
         } // if (dwVerInfoSize) 
 
            SendMessage (GetDlgItem (hDlg, IDC_LABEL), WM_SETFONT, 
            (WPARAM)hfontDlg,(LPARAM)TRUE); 
 
         // We are  using GetVersion rather then GetVersionEx 
         // because earlier versions of Windows NT and Win32s 
         // didn't include GetVersionEx: 
         dwVersion = GetVersion(); 
 
         if (dwVersion < 0x80000000) { 
            // Windows NT 
            wsprintf (szVersion, "Microsoft Windows NT %u.%u (Build: %u)", 
               (DWORD)(LOBYTE(LOWORD(dwVersion))), 
               (DWORD)(HIBYTE(LOWORD(dwVersion))), 
                    (DWORD)(HIWORD(dwVersion)) ); 
         } else if (LOBYTE(LOWORD(dwVersion))<4) { 
            // Win32s 
                wsprintf (szVersion, "Microsoft Win32s %u.%u (Build: %u)", 
               (DWORD)(LOBYTE(LOWORD(dwVersion))), 
               (DWORD)(HIBYTE(LOWORD(dwVersion))), 
                    (DWORD)(HIWORD(dwVersion) & ~0x8000) ); 
         } else { 
            // Windows 95 
                wsprintf (szVersion, "Microsoft Windows 95 %u.%u", 
                    (DWORD)(LOBYTE(LOWORD(dwVersion))), 
                    (DWORD)(HIBYTE(LOWORD(dwVersion))) ); 
         } 
 
          SetWindowText (GetDlgItem(hDlg, IDC_OSVERSION), szVersion); 
         ShowWindow (hDlg, SW_SHOW); 
         return (TRUE); 
 
      case WM_COMMAND: 
         if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { 
            EndDialog(hDlg, TRUE); 
            DeleteObject (hfontDlg); 
            DeleteObject (hFinePrint); 
            return (TRUE); 
         } 
         break; 
   } 
 
    return FALSE; 
} 
 
// 
//   FUNCTION: CenterWindow(HWND, HWND) 
// 
//   PURPOSE: Centers one window over another. 
// 
//   COMMENTS: 
// 
//        In this function, we save the instance handle in a global variable and 
//        create and display the main program window. 
// 
//       This functionwill center one window over another ensuring that 
//    the placement of the window is within the 'working area', meaning 
//    that it is both within the display limits of the screen, and not 
//    obscured by the tray or other framing elements of the desktop. 
BOOL CenterWindow (HWND hwndChild, HWND hwndParent) 
{ 
   RECT    rChild, rParent, rWorkArea; 
   int     wChild, hChild, wParent, hParent; 
   int     xNew, yNew; 
   BOOL  bResult; 
 
   // Get the Height and Width of the child window 
   GetWindowRect (hwndChild, &rChild); 
   wChild = rChild.right - rChild.left; 
   hChild = rChild.bottom - rChild.top; 
 
   // Get the Height and Width of the parent window 
   GetWindowRect (hwndParent, &rParent); 
   wParent = rParent.right - rParent.left; 
   hParent = rParent.bottom - rParent.top; 
 
   // Get the limits of the 'workarea' 
   bResult = SystemParametersInfo( 
      SPI_GETWORKAREA,  // system parameter to query or set 
      sizeof(RECT), 
      &rWorkArea, 
      0); 
   if (!bResult) { 
      rWorkArea.left = rWorkArea.top = 0; 
      rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); 
      rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); 
   } 
 
   // Calculate new X position, then adjust for workarea 
   xNew = rParent.left + ((wParent - wChild) /2); 
   if (xNew < rWorkArea.left) { 
      xNew = rWorkArea.left; 
   } else if ((xNew+wChild) > rWorkArea.right) { 
      xNew = rWorkArea.right - wChild; 
   } 
 
   // Calculate new Y position, then adjust for workarea 
   yNew = rParent.top  + ((hParent - hChild) /2); 
   if (yNew < rWorkArea.top) { 
      yNew = rWorkArea.top; 
   } else if ((yNew+hChild) > rWorkArea.bottom) { 
      yNew = rWorkArea.bottom - hChild; 
   } 
 
   // Set it, and return 
   return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); 
} 
 
 
//--------------------------------------------------------------------------- 
// 
// FUNCTION:    GetStringRes (int id INPUT ONLY) 
// 
// COMMENTS:    Load the resource string with the ID given, and return a 
//              pointer to it.  Notice that the buffer is common memory so 
//              the string must be used before this call is made a second time. 
// 
//--------------------------------------------------------------------------- 
 
LPTSTR   GetStringRes (int id) 
{ 
  static TCHAR buffer[MAX_PATH]; 
 
  buffer[0]=0; 
  LoadString (GetModuleHandle (NULL), id, buffer, MAX_PATH); 
  return buffer; 
} 
 
 
 
//////////////////////////////////////////////////////////////////// 
 
typedef HRESULT (STDAPICALLTYPE * PFNSAMPLECALLBACK) (IStreamSample *pSource, 
                                                      IStreamSample *pDest, 
                                                      void * pvContext); 
 
#define MAX_COPY_STREAMS 5 
 
class CopyPair { 
public: 
    CComPtr<IStreamSample> pSource; 
    CComPtr<IStreamSample> pDest; 
    MSPID PurposeId; 
    PFNSAMPLECALLBACK pCallback; 
    void * pCallbackContext; 
    HRESULT hrLastStatus; 
    bool    bReading; 
    bool    bGotFirstSample; 
    STREAM_TIME stActualStart; 
    STREAM_TIME stStartBias; 
    STREAM_TIME stLastEndTime; 
}; 
 
 
 
class CCopyEngine 
{ 
public: 
    CCopyEngine(IMultiMediaStream *pDestMMStream); 
    ~CCopyEngine(); 
 
    HRESULT InitStream(REFMSPID PurposeId, 
                       PFNSAMPLECALLBACK pCallback = NULL, 
                       void * pContext = NULL, 
                       bool bSharedFormat = false); 
    HRESULT CopyStreamData(IMultiMediaStream *pSourceMMStream, 
                           STREAM_TIME stStart, STREAM_TIME stEnd); 
 
private: 
    CComPtr<IMultiMediaStream>  m_pDestMMStream; 
    CopyPair        m_aPair[MAX_COPY_STREAMS]; 
    HANDLE          m_aEvent[MAX_COPY_STREAMS]; 
    int             m_cNumPairs; 
    bool            m_bStarted; 
}; 
 
/////// 
 
CCopyEngine::CCopyEngine(IMultiMediaStream *pDestMMStream) : 
    m_cNumPairs(0), 
    m_pDestMMStream(pDestMMStream), 
    m_bStarted(false) 
    {}; 
 
HRESULT CCopyEngine::InitStream(REFMSPID PurposeId, 
                                PFNSAMPLECALLBACK pCallback, void * pContext, 
                                bool bSharedFormat) 
{ 
    HRESULT hr = E_FAIL;    // Assume it won't work. 
    if (m_cNumPairs < MAX_COPY_STREAMS) { 
        CComPtr <IMediaStream> pDest; 
        if (m_pDestMMStream->GetMediaStream(PurposeId, &pDest) == NOERROR) { 
            CComPtr<IStreamSample> pDestSample; 
            hr = pDest->AllocateSample(0, &pDestSample); 
            if (SUCCEEDED(hr)) { 
                m_aEvent[m_cNumPairs] = CreateEvent(NULL, TRUE, FALSE, NULL); 
                if (!m_aEvent[m_cNumPairs]) { 
                    hr = E_OUTOFMEMORY; 
                } else { 
                    m_aPair[m_cNumPairs].pDest = pDestSample; 
                    m_aPair[m_cNumPairs].pCallback = pCallback; 
                    m_aPair[m_cNumPairs].pCallbackContext = pContext; 
                    m_aPair[m_cNumPairs].PurposeId = PurposeId; 
                    m_aPair[m_cNumPairs].stStartBias = 0; 
                    m_cNumPairs++; 
                } 
            } 
        } 
    } 
    return hr; 
} 
 
 
HRESULT CCopyEngine::CopyStreamData(IMultiMediaStream *pSourceMMStream, 
                                    STREAM_TIME stClipStart, STREAM_TIME stClipEnd) 
{ 
    HRESULT hr; 
    if (m_cNumPairs == 0) { 
        return S_FALSE; 
    } 
    int i; 
    for (i = 0; i < m_cNumPairs; i++) { 
        m_aPair[i].bGotFirstSample = false; 
        CComPtr <IMediaStream> pSource; 
        if (pSourceMMStream->GetMediaStream(m_aPair[i].PurposeId, &pSource) == NOERROR) { 
            m_aPair[i].pSource = NULL; 
            hr = pSource->AllocateSample(0, &m_aPair[i].pSource); 
        } else { 
            hr = E_FAIL; 
        } 
        if (FAILED(hr)) { 
            return hr; 
        } 
    } 
    pSourceMMStream->SetState(STREAMSTATE_RUN); 
    if (stClipStart) { 
        pSourceMMStream->Seek(stClipStart); 
    } 
    for (i = 0; i < m_cNumPairs; i++) { 
        m_aPair[i].hrLastStatus = NOERROR; 
        m_aPair[i].bReading = true; 
        m_aPair[i].pSource->Update(0, m_aEvent[i], NULL, 0); 
    } 
    if (!m_bStarted) { 
        m_pDestMMStream->SetState(STREAMSTATE_RUN); 
        m_bStarted = true; 
    } 
    int NumRunning = m_cNumPairs; 
    while (NumRunning > 0) { 
        DWORD dwWaitRet = WaitForMultipleObjects(m_cNumPairs, m_aEvent, FALSE, INFINITE); 
        if (dwWaitRet >= WAIT_OBJECT_0 && dwWaitRet < WAIT_OBJECT_0 + m_cNumPairs) { 
            int iCompleted = dwWaitRet - WAIT_OBJECT_0; 
            CopyPair *pPair = &m_aPair[iCompleted]; 
            IStreamSample *pDone = pPair->bReading ? pPair->pSource : pPair->pDest; 
            pPair->hrLastStatus = pDone->CompletionStatus(0, 0); 
if (pPair->hrLastStatus == NOERROR) { 
                if (pPair->bReading) { 
                    STREAM_TIME stStart, stStop; 
                    pPair->pSource->GetSampleTimes(&stStart, &stStop, NULL); 
                    if (stClipEnd > 0 && stStart > stClipEnd) { 
                        if (pPair->bGotFirstSample) { 
                            pPair->stStartBias += pPair->stLastEndTime - pPair->stActualStart; 
                        } 
                        NumRunning--; 
                        ResetEvent(m_aEvent[iCompleted]); 
                    } else { 
                        if (pPair->pCallback) { 
                            pPair->pCallback(pPair->pSource, pPair->pDest, pPair->pCallbackContext); 
                        } 
                        if (!pPair->bGotFirstSample) { 
                            pPair->stActualStart = stStart; 
                            pPair->bGotFirstSample = true; 
                        } 
                        pPair->stLastEndTime = stStop; 
                        stStart += pPair->stStartBias - pPair->stActualStart; 
                        stStop += pPair->stStartBias - pPair->stActualStart; 
                        pPair->pDest->SetSampleTimes(&stStart, &stStop); 
                        pPair->bReading = false; 
                        pPair->pDest->Update(0, m_aEvent[iCompleted], NULL, 0); 
                    } 
                } else { 
                    pPair->pSource->Update(0, m_aEvent[iCompleted], NULL, 0); 
                    pPair->bReading = true; 
                } 
            } else { 
                if (pPair->bGotFirstSample) { 
                    pPair->stStartBias += pPair->stLastEndTime - pPair->stActualStart; 
                } 
                ResetEvent(m_aEvent[iCompleted]); 
                NumRunning--; 
            } 
        } 
    } 
    pSourceMMStream->SetState(STREAMSTATE_STOP); 
    for (i = 0; i < m_cNumPairs; i++) { 
        m_aPair[i].pSource = NULL;  // Release the source sample 
    } 
    return NOERROR; 
} 
 
 
CCopyEngine::~CCopyEngine() 
{ 
    int i; 
    for (i = 0; i < m_cNumPairs; i++) { 
        CloseHandle(m_aEvent[i]); 
        if (m_bStarted && m_aPair[i].pDest) { 
            CComPtr<IMediaStream> pMS; 
            m_aPair[i].pDest->GetMediaStream(&pMS); 
            pMS->SendEndOfStream(0); 
        } 
    } 
    if (m_bStarted) { 
        m_pDestMMStream->SetState(STREAMSTATE_STOP); 
    } 
 
} 
 
 
 
 
/////////////////////////////////////////////////////////////////// 
 
void ErrorMessage(int StringId) 
{ 
    MessageBox (GetFocus(), GetStringRes(StringId), szAppName, MB_OK|MB_ICONHAND); 
} 
 
#define CHECK_ERROR(x, idFailMsg) if (FAILED(hr = (x))) { if (idFailMsg) ErrorMessage(idFailMsg); goto Exit; } 
 
#define RECTHEIGHT(r) ((r).bottom - (r).top) 
#define RECTWIDTH(r)  ((r).right - (r).left) 
 
HRESULT STDAPICALLTYPE VideoCallback(IStreamSample *pSource, IStreamSample *pDest, 
                                     void * pvhWnd) 
{ 
    CComQIPtr <IDirectDrawStreamSample, &IID_IDirectDrawStreamSample> pSrcSample(pSource); 
    CComPtr <IDirectDrawSurface> pSrcSurface; 
    RECT rectSrc; 
    if (SUCCEEDED(pSrcSample->GetSurface(&pSrcSurface, &rectSrc))) { 
        HDC hdcSurface; 
        if (SUCCEEDED(pSrcSurface->GetDC(&hdcSurface))) { 
            CComQIPtr <IDirectDrawStreamSample, &IID_IDirectDrawStreamSample> pDestSample(pDest); 
            CComPtr <IDirectDrawSurface> pDestSurface; 
            RECT rectDest; 
            HRESULT hr = pDestSample->GetSurface(&pDestSurface, &rectDest); 
 
            HDC hdcDest = GetDC((HWND)pvhWnd); 
            BOOL fred = StretchBlt(hdcDest, 0, 0, rectDest.right-rectDest.left, rectDest.bottom-rectDest.top, 
                       hdcSurface, rectSrc.left, rectSrc.top, RECTWIDTH(rectSrc), RECTHEIGHT(rectSrc), SRCCOPY); 
            ReleaseDC((HWND)pvhWnd, hdcDest); 
 
            hr = pDestSurface->GetDC(&hdcDest); 
            StretchBlt(hdcDest, 0, 0, rectDest.right-rectDest.left, rectDest.bottom-rectDest.top, 
                       hdcSurface, rectSrc.left, rectSrc.top, RECTWIDTH(rectSrc), RECTHEIGHT(rectSrc), SRCCOPY); 
 
            TextOut(hdcDest, 20, 20, "Easy to do effects!", 19); 
 
            pDestSurface->ReleaseDC(hdcDest); 
 
        } 
 
        //  Release this ALWAYS to bypass NT4.0 DDraw bug 
        pSrcSurface->ReleaseDC(hdcSurface); 
    } 
    return NOERROR; 
} 
 
 
 
HRESULT OpenReadMMStream(LPOLESTR pszFileName, DDSURFACEDESC & ddsd, IMultiMediaStream **ppMMStream) 
{ 
    *ppMMStream = NULL; 
    CComPtr <IAMMultiMediaStream> pAMStream; 
    CComPtr <IMediaStream> pVideoStream; 
    CComQIPtr<IDirectDrawMediaStream, &IID_IDirectDrawMediaStream> pDDStream; 
    HRESULT hr; 
 
    CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, 
 IID_IAMMultiMediaStream, (void **)&pAMStream), IDS_UNABLETOINITREAD); 
    CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_READ, 0, NULL), IDS_UNABLETOINITREAD); 
    CHECK_ERROR(pAMStream->AddMediaStream(g_pDD, &MSPID_PrimaryVideo, 0, &pVideoStream), IDS_UNABLETOINITREAD); 
    pDDStream = pVideoStream; 
 
    CHECK_ERROR(pDDStream->SetFormat(&ddsd, NULL), IDS_UNABLETOINITREAD); 
 //   CHECK_ERROR(pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL)); 
 
    CHECK_ERROR(pAMStream->OpenFile(pszFileName, AMMSF_NOCLOCK), IDS_UNABLETOOPENREAD); 
 
    *ppMMStream = pAMStream; 
    pAMStream->AddRef(); 
 
Exit: 
    return hr; 
} 
 
 
 
void DoMakeMovie(HINSTANCE hInst, HWND hWndPreview, CDocument * pDocument) 
{ 
    if (!pDocument->m_TargetFileName) { 
        ErrorMessage(IDS_NOFILENAME); 
        return; 
    } 
    if (pDocument->m_ClipList.NumClips() == 0) { 
        ErrorMessage(IDS_NOCLIPS); 
        return; 
    } 
 
    DDSURFACEDESC ddsd; 
    ddsd.dwSize = sizeof(ddsd); 
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; 
    ddsd.dwHeight = pDocument->m_Height; 
    ddsd.dwWidth  = pDocument->m_Width; 
    memcpy(&ddsd.ddpfPixelFormat, &g_aPixelFormats[pDocument->m_PixelDepth], sizeof(ddsd.ddpfPixelFormat)); 
 
    WAVEFORMATEX wfex; 
 
    ShowWindow(pDocument->m_ClipList.m_hLV, SW_HIDE); 
 
    CComPtr <IMultiMediaStream> pWriterStream; 
    if (SUCCEEDED(CreateWriterStream(pDocument->m_TargetFileName,   // If this fails, it will display an appropriate message 
                       pDocument->m_VideoCodecDisplayName, 
                       ddsd, 
                       pDocument->m_AudioCodecDisplayName, 
                       wfex, 
                       &pWriterStream))) { 
        CCopyEngine Engine(pWriterStream); 
        Engine.InitStream(MSPID_PrimaryVideo, VideoCallback, hWndPreview, false); 
        int NumClips = pDocument->m_ClipList.NumClips(); 
        for (int i = 0; i < NumClips; i++) { 
            CClip *pClip = pDocument->m_ClipList.GetClip(i); 
            CComPtr <IMultiMediaStream> pReadStream; 
            ddsd.dwFlags = DDSD_PIXELFORMAT;    // Only set pixel format on source streams 
            if (SUCCEEDED(OpenReadMMStream(pClip->m_FileName, ddsd, &pReadStream))) { 
                Engine.CopyStreamData(pReadStream, pClip->m_stStart, pClip->m_stEnd); 
            } 
        } 
    } 
 
    ShowWindow(pDocument->m_ClipList.m_hLV, SW_SHOW); 
} 
 
 
 
HRESULT AddAndRenderCompressor(LPOLESTR pszCodec, 
                               LPOLESTR pszFilterName, 
                               ICaptureGraphBuilder *pBuilder, 
                               IMediaStream *pStream, 
                               IBaseFilter *pMuxFilter) 
{ 
    HRESULT hr; 
    CComPtr <IMoniker> pDeviceMoniker; 
    CComPtr <IBindCtx> pBindCtx; 
    CComPtr <IBaseFilter> pCodecFilter; 
    CComPtr <IGraphBuilder> pFilterGraph; 
    if (pszCodec) { 
        unsigned long ccEaten; 
        CHECK_ERROR(CreateBindCtx(0, &pBindCtx), IDS_NOCOMPRESSOR); 
        CHECK_ERROR(MkParseDisplayName(pBindCtx, pszCodec, &ccEaten, &pDeviceMoniker), IDS_NOCOMPRESSOR); 
        CHECK_ERROR(pDeviceMoniker->BindToObject(pBindCtx, NULL, IID_IBaseFilter, (void **)&pCodecFilter), IDS_NOCOMPRESSOR); 
        CHECK_ERROR(pBuilder->GetFiltergraph(&pFilterGraph), IDS_INTERNALERROR); 
        CHECK_ERROR(pFilterGraph->AddFilter(pCodecFilter, pszFilterName), IDS_INTERNALERROR); 
        hr = pBuilder->RenderStream(NULL, pStream, pCodecFilter, pMuxFilter); 
        if (FAILED(hr)) { 
            ErrorMessage(IDS_CANTCOMPRESS); 
        } 
    } else { 
        hr = pBuilder->RenderStream(NULL, pStream, NULL, pMuxFilter); 
        if (FAILED(hr)) { 
            ErrorMessage(IDS_CANTCONNECTTOMUX); 
        } 
    } 
Exit: 
    return hr; 
} 
 
 
HRESULT CreateWriterStream(LPOLESTR szOutputFileName, 
                           LPOLESTR szVideoCodec, 
                           DDSURFACEDESC& ddsdVideoFormat, 
                           LPOLESTR szAudioCodec, 
                           WAVEFORMATEX& wfexAudioFormat, 
                           IMultiMediaStream **ppMMStream) 
{ 
    *ppMMStream = NULL; 
    CComPtr <IAMMultiMediaStream> pAMStream; 
    CComPtr <IMediaStream> pVideoStream; 
    CComPtr <IMediaStream> pAudioStream; 
    CComPtr <ICaptureGraphBuilder> pBuilder; 
    CComPtr <IGraphBuilder> pFilterGraph; 
    CComPtr <IFileSinkFilter> pFileSinkWriter; 
    CComPtr <IBaseFilter> pMuxFilter; 
    CComQIPtr <IDirectDrawMediaStream, &IID_IDirectDrawMediaStream> pDDStream; 
    CComQIPtr <IAudioMediaStream, &IID_IAudioMediaStream> pAudioSpecificStream; 
    HRESULT hr; 
 
    CHECK_ERROR(CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, 
 IID_IAMMultiMediaStream, (void **)&pAMStream), IDS_UNABLETOINITWRITE); 
    CHECK_ERROR(pAMStream->Initialize(STREAMTYPE_WRITE, 0, NULL), IDS_UNABLETOINITWRITE); 
 
    CHECK_ERROR(pAMStream->AddMediaStream(g_pDD, &MSPID_PrimaryVideo, 0, &pVideoStream), IDS_UNABLETOINITWRITE); 
  //  CHECK_ERROR(pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, &pAudioStream)); 
 
    pDDStream = pVideoStream; 
   // pAudioSpecificStream = pAudioStream; 
 
    CHECK_ERROR(pDDStream->SetFormat(&ddsdVideoFormat, NULL), IDS_CANTSETFORMAT); 
 /// BUGBUG!!!   CHECK_ERROR(pAudioSpecificStream->SetFormat(&wfexAudioFormat)); 
 
    CHECK_ERROR(CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER, 
                                 IID_ICaptureGraphBuilder, (void **)&pBuilder), IDS_UNABLETOINITWRITE); 
 
    CHECK_ERROR(pAMStream->GetFilterGraph(&pFilterGraph), IDS_INTERNALERROR); 
    CHECK_ERROR(pBuilder->SetFiltergraph(pFilterGraph), IDS_INTERNALERROR); 
 
    CHECK_ERROR(pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, szOutputFileName, &pMuxFilter, &pFileSinkWriter), IDS_UNABLETOSETOUTPUTNAME); 
 
    CHECK_ERROR(AddAndRenderCompressor(szVideoCodec, L"Video compressor", pBuilder, pVideoStream, pMuxFilter), 0); // This function displays its own message 
///    CHECK_ERROR(AddAndRenderCompressor(szAudioCodec, L"Audio compressor", pBuilder, pAudioStream, pMuxFilter)); 
 
    *ppMMStream = pAMStream; 
    (*ppMMStream)->AddRef(); 
 
Exit: 
    return hr; 
}