xref: /reactos/base/applications/mplay32/mplay32.c (revision 84ccccab)
1 /*
2  * PROJECT:         ReactOS Multimedia Player
3  * FILE:            base/applications/mplay32/mplay32.c
4  * PROGRAMMERS:     Dmitry Chapyshev (dmitry@reactos.org)
5  */
6 
7 #include "mplay32.h"
8 
9 #define IDT_PLAYTIMER 1000
10 
11 #define MAIN_WINDOW_HEIGHT    125
12 #define MAIN_WINDOW_MIN_WIDTH 250
13 #define MAX_MCISTR            256
14 
15 HINSTANCE hInstance = NULL;
16 HWND hTrackBar = NULL;
17 HWND hToolBar = NULL;
18 HWND hTimeDisplay = NULL;
19 HMENU hMainMenu = NULL;
20 
21 TCHAR szAppTitle[256] = _T("");
22 TCHAR szDefaultFilter[MAX_PATH] = _T("");
23 TCHAR szCurrentFile[MAX_PATH] = _T("");
24 LPTSTR szFilter = NULL;
25 
26 WORD wDeviceId = 0;
27 BOOL bRepeat = FALSE;
28 BOOL bIsSingleWindow = FALSE;
29 UINT MaxFilePos = 0;
30 RECT PrevWindowPos;
31 
32 static DWORD GetDeviceMode(HWND hwnd);
33 
34 /* ToolBar Buttons */
35 static const TBBUTTON Buttons[] =
36 {   /* iBitmap,        idCommand,    fsState,         fsStyle,     bReserved[2], dwData, iString */
37     {TBICON_PLAY,      IDC_PLAY,     TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
38     {TBICON_STOP,      IDC_STOP,     TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
39     {TBICON_EJECT,     IDC_EJECT,    TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
40     {15,               0,            TBSTATE_ENABLED, BTNS_SEP,    {0}, 0, 0},
41     {TBICON_BACKWARD,  IDC_BACKWARD, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
42     {TBICON_SEEKBACK,  IDC_SEEKBACK, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
43     {TBICON_SEEKFORW,  IDC_SEEKFORW, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
44     {TBICON_FORWARD,   IDC_FORWARD,  TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
45 //  {TBICON_PAUSE,     IDC_PAUSE,    TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0}
46 };
47 
48 void EnableMenuItems(HWND hwnd)
49 {
50     MCIERROR mciError;
51     MCI_GENERIC_PARMS mciGeneric;
52     MCI_DGV_RECT_PARMS mciVideoRect;
53     MCI_DGV_WINDOW_PARMSW mciVideoWindow;
54 
55     EnableMenuItem(hMainMenu, IDM_CLOSE_FILE, MF_BYCOMMAND | MF_ENABLED);
56 
57     mciError = mciSendCommand(wDeviceId, MCI_CONFIGURE, MCI_TEST, (DWORD_PTR)&mciGeneric);
58     if (mciError == 0)
59     {
60         EnableMenuItem(hMainMenu, IDM_DEVPROPS, MF_BYCOMMAND | MF_ENABLED);
61     }
62 
63     mciVideoWindow.hWnd = hwnd;
64 
65     mciError = mciSendCommand(wDeviceId, MCI_WINDOW, MCI_DGV_WINDOW_HWND | MCI_TEST, (DWORD_PTR)&mciVideoWindow);
66     if (!mciError)
67     {
68         mciError = mciSendCommand(wDeviceId, MCI_WHERE, MCI_DGV_WHERE_SOURCE | MCI_TEST, (DWORD_PTR)&mciVideoRect);
69         if (!mciError)
70         {
71             EnableMenuItem(hMainMenu, IDM_SWITCHVIEW, MF_BYCOMMAND | MF_ENABLED);
72         }
73     }
74 }
75 
76 void DisableMenuItems(void)
77 {
78     EnableMenuItem(hMainMenu, IDM_CLOSE_FILE, MF_BYCOMMAND | MF_GRAYED);
79     EnableMenuItem(hMainMenu, IDM_DEVPROPS, MF_BYCOMMAND | MF_GRAYED);
80     EnableMenuItem(hMainMenu, IDM_SWITCHVIEW, MF_BYCOMMAND | MF_GRAYED);
81 }
82 
83 void ResizeClientArea(HWND hwnd, int nWidth, int nHeight)
84 {
85     RECT rcClientRect;
86     RECT rcWindowRect;
87     POINT ptDifference;
88 
89     GetClientRect(hwnd, &rcClientRect);
90     GetWindowRect(hwnd, &rcWindowRect);
91     ptDifference.x = (rcWindowRect.right - rcWindowRect.left) - rcClientRect.right;
92     ptDifference.y = (rcWindowRect.bottom - rcWindowRect.top) - rcClientRect.bottom;
93     MoveWindow(hwnd, rcWindowRect.left, rcWindowRect.top, nWidth + ptDifference.x, nHeight + ptDifference.y, TRUE);
94 }
95 
96 void UpdateWindowCaption(HWND hwnd)
97 {
98     TCHAR szNewTitle[MAX_PATH + 3 + 256];
99     TCHAR szStatus[128];
100 
101     if (wDeviceId == 0)
102     {
103         SetWindowText(hwnd, szAppTitle);
104         return;
105     }
106 
107     switch (GetDeviceMode(hwnd))
108     {
109         case MCI_MODE_PAUSE:
110         {
111             LoadString(hInstance, IDS_MODE_PAUSE, szStatus, ARRAYSIZE(szStatus));
112             break;
113         }
114 
115         case MCI_MODE_STOP:
116         {
117             LoadString(hInstance, IDS_MODE_STOP, szStatus, ARRAYSIZE(szStatus));
118             break;
119         }
120 
121         case MCI_MODE_PLAY:
122         {
123             LoadString(hInstance, IDS_MODE_PLAY, szStatus, ARRAYSIZE(szStatus));
124             break;
125         }
126 
127         case MCI_MODE_OPEN:
128         {
129             LoadString(hInstance, IDS_MODE_OPEN, szStatus, ARRAYSIZE(szStatus));
130             break;
131         }
132 
133         case MCI_MODE_RECORD:
134         {
135             LoadString(hInstance, IDS_MODE_RECORD, szStatus, ARRAYSIZE(szStatus));
136             break;
137         }
138 
139         case MCI_MODE_SEEK:
140         {
141             LoadString(hInstance, IDS_MODE_SEEK, szStatus, ARRAYSIZE(szStatus));
142             break;
143         }
144 
145         case MCI_MODE_NOT_READY:
146         {
147             LoadString(hInstance, IDS_MODE_NOT_READY, szStatus, ARRAYSIZE(szStatus));
148             break;
149         }
150 
151         default:
152         {
153             LoadString(hInstance, IDS_MODE_UNKNOWN, szStatus, ARRAYSIZE(szStatus));
154         }
155     }
156 
157     StringCbPrintf(szNewTitle, sizeof(szNewTitle), _T("%s - %s (%s)"), szAppTitle, szCurrentFile, szStatus);
158     SetWindowText(hwnd, szNewTitle);
159 }
160 
161 void UpdateTimeDisplay(HWND hwnd)
162 {
163     MCI_STATUS_PARMS mciStatus;
164     TCHAR szTime[MAX_MCISTR];
165     DWORD dwTimeFormat;
166 
167     if (!wDeviceId)
168     {
169         SetWindowText(hwnd, _T(""));
170         return;
171     }
172 
173     mciStatus.dwItem = MCI_STATUS_TIME_FORMAT;
174     mciStatus.dwReturn = 0;
175     mciSendCommand(wDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
176     dwTimeFormat = mciStatus.dwReturn;
177 
178     mciStatus.dwItem = MCI_STATUS_POSITION;
179     mciStatus.dwReturn = 0;
180     mciSendCommand(wDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
181 
182     switch(dwTimeFormat)
183     {
184         case MCI_FORMAT_MILLISECONDS:
185         {
186             int s, m, h;
187 
188             s = (mciStatus.dwReturn / 1000) % 60;
189             m = ((mciStatus.dwReturn / (1000*60)) % 60);
190             h = ((mciStatus.dwReturn / (1000*60*60)) % 24);
191             StringCbPrintf(szTime, sizeof(szTime), _T("%02lu:%02lu:%02lu"), h, m, s);
192             break;
193         }
194 
195         /* The time format is unknown, so use the returned position as is */
196         default:
197         {
198             StringCbPrintf(szTime, sizeof(szTime), _T("%lu"), mciStatus.dwReturn);
199             break;
200         }
201     }
202 
203     SetWindowText(hwnd, szTime);
204 }
205 
206 static VOID
207 ShowLastWin32Error(HWND hwnd)
208 {
209     LPTSTR lpMessageBuffer;
210     DWORD dwError = GetLastError();
211 
212     if (dwError == ERROR_SUCCESS)
213         return;
214 
215     if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
216                        FORMAT_MESSAGE_FROM_SYSTEM |
217                        FORMAT_MESSAGE_IGNORE_INSERTS,
218                        NULL,
219                        dwError,
220                        LANG_USER_DEFAULT,
221                        (LPTSTR)&lpMessageBuffer,
222                        0, NULL))
223     {
224         return;
225     }
226 
227     MessageBox(hwnd, lpMessageBuffer, szAppTitle, MB_OK | MB_ICONERROR);
228     LocalFree(lpMessageBuffer);
229 }
230 
231 static VOID
232 SetImageList(HWND hwnd)
233 {
234     HIMAGELIST hImageList;
235 
236     hImageList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR24, 1, 1);
237     if (!hImageList)
238     {
239         ShowLastWin32Error(hwnd);
240         return;
241     }
242 
243     ImageList_AddMasked(hImageList,
244                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_PLAYICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
245                         RGB(255, 255, 255));
246 
247     ImageList_AddMasked(hImageList,
248                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_STOPICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
249                         RGB(255, 255, 255));
250 
251     ImageList_AddMasked(hImageList,
252                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_EJECTICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
253                         RGB(255, 255, 255));
254 
255     ImageList_AddMasked(hImageList,
256                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_BACKWARDICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
257                         RGB(255, 255, 255));
258 
259     ImageList_AddMasked(hImageList,
260                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_SEEKBACKICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
261                         RGB(255, 255, 255));
262 
263     ImageList_AddMasked(hImageList,
264                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_SEEKFORWICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
265                         RGB(255, 255, 255));
266 
267     ImageList_AddMasked(hImageList,
268                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_FORWARDICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
269                         RGB(255, 255, 255));
270 
271     ImageList_AddMasked(hImageList,
272                         LoadImage(hInstance, MAKEINTRESOURCE(IDB_PAUSEICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR),
273                         RGB(255, 255, 255));
274 
275     ImageList_Destroy((HIMAGELIST)SendMessage(hToolBar,
276                                               TB_SETIMAGELIST,
277                                               0,
278                                               (LPARAM)hImageList));
279 }
280 
281 static VOID
282 ShowMCIError(HWND hwnd, MCIERROR mciError)
283 {
284     TCHAR szErrorMessage[MAX_MCISTR];
285     TCHAR szTempMessage[MAX_MCISTR + 44];
286 
287     if (mciGetErrorString(mciError, szErrorMessage, ARRAYSIZE(szErrorMessage)) == FALSE)
288     {
289         LoadString(hInstance, IDS_DEFAULTMCIERRMSG, szErrorMessage, ARRAYSIZE(szErrorMessage));
290     }
291 
292     StringCbPrintf(szTempMessage, sizeof(szTempMessage), _T("MMSYS%lu: %s"), mciError, szErrorMessage);
293     MessageBox(hwnd, szTempMessage, szAppTitle, MB_OK | MB_ICONEXCLAMATION);
294 }
295 
296 static VOID
297 InitControls(HWND hwnd)
298 {
299     INT NumButtons = ARRAYSIZE(Buttons);
300 
301     InitCommonControls();
302 
303     /* Create trackbar */
304     hTrackBar = CreateWindowEx(0,
305                                TRACKBAR_CLASS,
306                                NULL,
307                                TBS_ENABLESELRANGE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS,
308                                0,
309                                0,
310                                340,
311                                20,
312                                hwnd,
313                                NULL,
314                                hInstance,
315                                NULL);
316     if (!hTrackBar)
317     {
318         ShowLastWin32Error(hwnd);
319         return;
320     }
321 
322     /* Create toolbar */
323     hToolBar = CreateWindowEx(0,
324                               TOOLBARCLASSNAME,
325                               NULL,
326                               WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS |
327                               TBSTYLE_FLAT | CCS_BOTTOM | TBSTYLE_TOOLTIPS,
328                               0,
329                               40,
330                               340,
331                               30,
332                               hwnd,
333                               NULL,
334                               hInstance,
335                               NULL);
336     if (!hToolBar)
337     {
338         ShowLastWin32Error(hwnd);
339         return;
340     }
341 
342     hTimeDisplay = CreateWindowEx(0,
343                                   L"STATIC",
344                                   NULL,
345                                   WS_CHILD | WS_VISIBLE | SS_CENTER | SS_SUNKEN,
346                                   195,
347                                   4,
348                                   135,
349                                   18,
350                                   hToolBar,
351                                   NULL,
352                                   hInstance,
353                                   NULL);
354     if (!hTimeDisplay)
355     {
356         ShowLastWin32Error(hwnd);
357         return;
358     }
359 
360     SetImageList(hwnd);
361     SendMessage(hToolBar, TB_ADDBUTTONS, NumButtons, (LPARAM)Buttons);
362 }
363 
364 static VOID
365 SwitchViewMode(HWND hwnd)
366 {
367     MCIERROR mciError;
368     MCI_DGV_RECT_PARMS mciVideoRect;
369     MCI_DGV_WINDOW_PARMSW mciVideoWindow;
370     RECT rcToolbarRect;
371     RECT rcTempRect;
372 
373     mciVideoWindow.hWnd = hwnd;
374 
375     mciError = mciSendCommand(wDeviceId, MCI_WINDOW, MCI_DGV_WINDOW_HWND | MCI_TEST, (DWORD_PTR)&mciVideoWindow);
376     if (mciError != 0)
377         return;
378 
379     mciError = mciSendCommand(wDeviceId, MCI_WHERE, MCI_DGV_WHERE_SOURCE | MCI_TEST, (DWORD_PTR)&mciVideoRect);
380     if (mciError != 0)
381         return;
382 
383     if (!bIsSingleWindow)
384     {
385         GetWindowRect(hwnd, &PrevWindowPos);
386 
387         SetParent(hTrackBar, hToolBar);
388 
389         mciError = mciSendCommand(wDeviceId, MCI_WHERE, MCI_DGV_WHERE_SOURCE, (DWORD_PTR)&mciVideoRect);
390         if (mciError != 0)
391         {
392             ShowMCIError(hwnd, mciError);
393             return;
394         }
395 
396         GetWindowRect(hToolBar, &rcToolbarRect);
397         ResizeClientArea(hwnd, mciVideoRect.rc.right, mciVideoRect.rc.bottom + (rcToolbarRect.bottom - rcToolbarRect.top));
398 
399         mciError = mciSendCommand(wDeviceId, MCI_WINDOW, MCI_DGV_WINDOW_HWND, (DWORD_PTR)&mciVideoWindow);
400         if (mciError != 0)
401         {
402             ShowMCIError(hwnd, mciError);
403             return;
404         }
405 
406         GetWindowRect(hToolBar, &rcTempRect);
407         MoveWindow(hTrackBar, 180, 0, rcTempRect.right - rcTempRect.left - 322, 25, TRUE);
408         MoveWindow(hTimeDisplay, rcTempRect.right - rcTempRect.left - 140, 4, 135, 18, TRUE);
409 
410         CheckMenuItem(hMainMenu, IDM_SWITCHVIEW, MF_BYCOMMAND | MF_CHECKED);
411         bIsSingleWindow = TRUE;
412     }
413     else
414     {
415         bIsSingleWindow = FALSE;
416         CheckMenuItem(hMainMenu, IDM_SWITCHVIEW, MF_BYCOMMAND | MF_UNCHECKED);
417 
418         mciVideoWindow.hWnd = MCI_DGV_WINDOW_DEFAULT;
419         mciError = mciSendCommand(wDeviceId, MCI_WINDOW, MCI_DGV_WINDOW_HWND, (DWORD_PTR)&mciVideoWindow);
420         if (mciError != 0)
421         {
422             ShowMCIError(hwnd, mciError);
423             return;
424         }
425 
426         SetParent(hTrackBar, hwnd);
427 
428         MoveWindow(hwnd, PrevWindowPos.left, PrevWindowPos.top, PrevWindowPos.right - PrevWindowPos.left, PrevWindowPos.bottom - PrevWindowPos.top, TRUE);
429     }
430 }
431 
432 static DWORD
433 GetNumDevices(VOID)
434 {
435     MCI_SYSINFO_PARMS mciSysInfo;
436     DWORD dwNumDevices = 0;
437 
438     mciSysInfo.dwCallback  = 0;
439     mciSysInfo.lpstrReturn = (LPTSTR)&dwNumDevices;
440     mciSysInfo.dwRetSize   = sizeof(dwNumDevices);
441     mciSysInfo.dwNumber    = 0;
442     mciSysInfo.wDeviceType = MCI_ALL_DEVICE_ID;
443 
444     mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_QUANTITY, (DWORD_PTR)&mciSysInfo);
445 
446     return *(DWORD*)mciSysInfo.lpstrReturn;
447 }
448 
449 static DWORD
450 GetDeviceName(DWORD dwDeviceIndex, LPTSTR lpDeviceName, DWORD dwDeviceNameSize)
451 {
452     MCI_SYSINFO_PARMS mciSysInfo;
453 
454     mciSysInfo.dwCallback  = 0;
455     mciSysInfo.lpstrReturn = lpDeviceName;
456     mciSysInfo.dwRetSize   = dwDeviceNameSize;
457     mciSysInfo.dwNumber    = dwDeviceIndex;
458     mciSysInfo.wDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
459 
460     return mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, MCI_SYSINFO_NAME, (DWORD_PTR)&mciSysInfo);
461 }
462 
463 static DWORD
464 GetDeviceFriendlyName(LPTSTR lpDeviceName, LPTSTR lpFriendlyName, DWORD dwFriendlyNameSize)
465 {
466     MCIERROR mciError;
467     MCI_OPEN_PARMS mciOpen;
468     MCI_INFO_PARMS mciInfo;
469     MCI_GENERIC_PARMS mciGeneric;
470 
471     mciOpen.dwCallback = 0;
472     mciOpen.wDeviceID  = 0;
473     mciOpen.lpstrDeviceType  = lpDeviceName;
474     mciOpen.lpstrElementName = NULL;
475     mciOpen.lpstrAlias = NULL;
476 
477     mciError = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_WAIT, (DWORD_PTR)&mciOpen);
478     if (mciError != 0)
479         return mciError;
480 
481     mciInfo.dwCallback  = 0;
482     mciInfo.lpstrReturn = lpFriendlyName;
483     mciInfo.dwRetSize   = dwFriendlyNameSize;
484 
485     mciError = mciSendCommand(mciOpen.wDeviceID, MCI_INFO, MCI_INFO_PRODUCT, (DWORD_PTR)&mciInfo);
486 
487     mciGeneric.dwCallback = 0;
488     mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&mciGeneric);
489 
490     return mciError;
491 }
492 
493 static BOOL
494 DeviceUsesFiles(LPTSTR lpDeviceName)
495 {
496     MCIERROR mciError;
497     MCI_OPEN_PARMS mciOpen;
498     MCI_GETDEVCAPS_PARMS mciDevCaps;
499     MCI_GENERIC_PARMS mciGeneric;
500 
501     mciOpen.dwCallback = 0;
502     mciOpen.wDeviceID  = 0;
503     mciOpen.lpstrDeviceType  = lpDeviceName;
504     mciOpen.lpstrElementName = NULL;
505     mciOpen.lpstrAlias = NULL;
506 
507     mciError = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_WAIT, (DWORD_PTR)&mciOpen);
508     if (mciError != 0)
509         return FALSE;
510 
511     mciDevCaps.dwCallback  = 0;
512     mciDevCaps.dwReturn = 0;
513     mciDevCaps.dwItem = MCI_GETDEVCAPS_USES_FILES;
514 
515     mciError = mciSendCommand(mciOpen.wDeviceID, MCI_GETDEVCAPS, MCI_WAIT | MCI_GETDEVCAPS_ITEM, (DWORD_PTR)&mciDevCaps);
516     if (mciError != 0)
517         return FALSE;
518 
519     mciGeneric.dwCallback = 0;
520     mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&mciGeneric);
521 
522     return (BOOL)mciDevCaps.dwReturn;
523 }
524 
525 static MCIERROR
526 CloseMciDevice(VOID)
527 {
528     MCIERROR mciError;
529     MCI_GENERIC_PARMS mciGeneric;
530 
531     if (wDeviceId)
532     {
533         mciError = mciSendCommand(wDeviceId, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&mciGeneric);
534         if (mciError != 0) return mciError;
535         wDeviceId = 0;
536     }
537 
538     UpdateTimeDisplay(hTimeDisplay);
539 
540     DisableMenuItems();
541 
542     return 0;
543 }
544 
545 static MCIERROR
546 OpenMciDevice(HWND hwnd, LPTSTR lpType, LPTSTR lpFileName)
547 {
548     MCIERROR mciError;
549     MCI_STATUS_PARMS mciStatus;
550     MCI_OPEN_PARMS mciOpen;
551     DWORD dwFlags = MCI_WAIT;
552     LPTSTR lpStr;
553 
554     if (wDeviceId)
555         CloseMciDevice();
556 
557     mciOpen.lpstrDeviceType = lpType;
558     mciOpen.lpstrElementName = lpFileName;
559     mciOpen.dwCallback = 0;
560     mciOpen.wDeviceID = 0;
561     mciOpen.lpstrAlias = NULL;
562 
563     if (lpType)
564         dwFlags |= MCI_OPEN_TYPE;
565 
566     if (lpFileName)
567         dwFlags |= MCI_OPEN_ELEMENT;
568 
569     mciError = mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD_PTR)&mciOpen);
570     if (mciError != 0)
571         return mciError;
572 
573     mciStatus.dwItem = MCI_STATUS_LENGTH;
574 
575     mciError = mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)&mciStatus);
576     if (mciError != 0)
577         return mciError;
578 
579     SendMessage(hTrackBar, TBM_SETRANGEMIN, (WPARAM)TRUE, (LPARAM)1);
580     SendMessage(hTrackBar, TBM_SETRANGEMAX, (WPARAM)TRUE, (LPARAM)mciStatus.dwReturn);
581     SendMessage(hTrackBar, TBM_SETPAGESIZE, 0, 10);
582     SendMessage(hTrackBar, TBM_SETLINESIZE, 0, 1);
583     SendMessage(hTrackBar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)1);
584 
585     if (mciStatus.dwReturn < 10000)
586     {
587         SendMessage(hTrackBar, TBM_SETTICFREQ, (WPARAM)100, (LPARAM)0);
588     }
589     else if (mciStatus.dwReturn < 100000)
590     {
591         SendMessage(hTrackBar, TBM_SETTICFREQ, (WPARAM)1000, (LPARAM)0);
592     }
593     else if (mciStatus.dwReturn < 1000000)
594     {
595         SendMessage(hTrackBar, TBM_SETTICFREQ, (WPARAM)10000, (LPARAM)0);
596     }
597     else
598     {
599         SendMessage(hTrackBar, TBM_SETTICFREQ, (WPARAM)100000, (LPARAM)0);
600     }
601 
602     MaxFilePos = mciStatus.dwReturn;
603     wDeviceId = mciOpen.wDeviceID;
604 
605     /* NOTE: Everything above this line may be done instead in OpenMediaFile() */
606 
607     if (lpFileName)
608     {
609         lpStr = _tcsrchr(lpFileName, _T('\\'));
610         if (lpStr) // Get only the file name (skip the last path separator)
611             lpStr++;
612         else
613             lpStr = lpFileName;
614     }
615     else
616         lpStr = lpType;
617 
618     StringCbCopy(szCurrentFile, sizeof(szCurrentFile), lpStr);
619 
620     EnableMenuItems(hwnd);
621 
622     UpdateTimeDisplay(hTimeDisplay);
623     UpdateWindowCaption(hwnd);
624 
625     return 0;
626 }
627 
628 static DWORD
629 GetDeviceMode(HWND hwnd)
630 {
631     MCIERROR mciError;
632     MCI_STATUS_PARMS mciStatus;
633 
634     mciStatus.dwItem = MCI_STATUS_MODE;
635     mciError = mciSendCommand(wDeviceId, MCI_STATUS, MCI_WAIT | MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
636     if (mciError != 0)
637     {
638         ShowMCIError(hwnd, mciError);
639         return MCI_MODE_NOT_READY;
640     }
641 
642     return mciStatus.dwReturn;
643 }
644 
645 static VOID
646 StopPlayback(HWND hwnd)
647 {
648     MCIERROR mciError;
649     MCI_GENERIC_PARMS mciGeneric;
650     MCI_SEEK_PARMS mciSeek;
651 
652     if (wDeviceId == 0) return;
653 
654     KillTimer(hwnd, IDT_PLAYTIMER);
655     SendMessage(hTrackBar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)1);
656 
657     mciGeneric.dwCallback = (DWORD_PTR)hwnd;
658     mciError = mciSendCommand(wDeviceId, MCI_STOP, MCI_WAIT, (DWORD_PTR)&mciGeneric);
659     if (mciError != 0)
660     {
661         ShowMCIError(hwnd, mciError);
662         return;
663     }
664 
665     mciSendCommand(wDeviceId, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, (DWORD_PTR)&mciSeek);
666 
667     UpdateTimeDisplay(hTimeDisplay);
668     UpdateWindowCaption(hwnd);
669 
670     SendMessage(hToolBar,
671                 TB_SETCMDID,
672                 0,
673                 IDC_PLAY);
674     SendMessage(hToolBar,
675                 TB_CHANGEBITMAP,
676                 IDC_PLAY,
677                 IDB_PLAYICON - IDB_PLAYICON);
678 }
679 
680 static VOID
681 SeekPlayback(HWND hwnd, DWORD dwNewPos)
682 {
683     MCIERROR mciError;
684     MCI_SEEK_PARMS mciSeek;
685     MCI_PLAY_PARMS mciPlay;
686 
687     if (wDeviceId == 0) return;
688 
689     mciSeek.dwTo = (DWORD_PTR)dwNewPos;
690     mciError = mciSendCommand(wDeviceId, MCI_SEEK, MCI_WAIT | MCI_TO, (DWORD_PTR)&mciSeek);
691     if (mciError != 0)
692     {
693         ShowMCIError(hwnd, mciError);
694     }
695 
696     mciPlay.dwCallback = (DWORD_PTR)hwnd;
697     mciError = mciSendCommand(wDeviceId, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&mciPlay);
698     if (mciError != 0)
699     {
700         ShowMCIError(hwnd, mciError);
701     }
702 }
703 
704 static VOID
705 SeekBackPlayback(HWND hwnd)
706 {
707     MCI_STATUS_PARMS mciStatus;
708     DWORD dwNewPos;
709 
710     if (wDeviceId == 0) return;
711 
712     mciStatus.dwItem = MCI_STATUS_POSITION;
713     mciSendCommand(wDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
714 
715     dwNewPos = mciStatus.dwReturn - 1;
716 
717     if ((UINT)dwNewPos <= 1)
718     {
719         StopPlayback(hwnd);
720     }
721     else
722     {
723         SeekPlayback(hwnd, dwNewPos);
724     }
725 }
726 
727 static VOID
728 SeekForwPlayback(HWND hwnd)
729 {
730     MCI_STATUS_PARMS mciStatus;
731     DWORD dwNewPos;
732 
733     if (wDeviceId == 0) return;
734 
735     mciStatus.dwItem = MCI_STATUS_POSITION;
736     mciSendCommand(wDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
737 
738     dwNewPos = mciStatus.dwReturn + 1;
739 
740     if ((UINT)dwNewPos >= MaxFilePos)
741     {
742         StopPlayback(hwnd);
743     }
744     else
745     {
746         SeekPlayback(hwnd, dwNewPos);
747     }
748 }
749 
750 VOID CALLBACK
751 PlayTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
752 {
753     MCI_STATUS_PARMS mciStatus;
754     DWORD dwPos;
755 
756     if (wDeviceId == 0) KillTimer(hwnd, IDT_PLAYTIMER);
757 
758     mciStatus.dwItem = MCI_STATUS_POSITION;
759     mciSendCommand(wDeviceId, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
760     dwPos = mciStatus.dwReturn;
761 
762     SendMessage(hTrackBar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)dwPos);
763     UpdateTimeDisplay(hTimeDisplay);
764 }
765 
766 static VOID
767 StartPlayback(HWND hwnd)
768 {
769     MCIERROR mciError;
770     MCI_PLAY_PARMS mciPlay;
771     MCI_SEEK_PARMS mciSeek;
772 
773     SetTimer(hwnd, IDT_PLAYTIMER, 100, (TIMERPROC)PlayTimerProc);
774 
775     mciSendCommand(wDeviceId, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, (DWORD_PTR)&mciSeek);
776 
777     mciPlay.dwCallback = (DWORD_PTR)hwnd;
778     mciPlay.dwFrom = 0;
779     mciPlay.dwTo = MaxFilePos;
780 
781     mciError = mciSendCommand(wDeviceId, MCI_PLAY, MCI_NOTIFY | MCI_FROM /*| MCI_TO*/, (DWORD_PTR)&mciPlay);
782     if (mciError != 0)
783     {
784         ShowMCIError(hwnd, mciError);
785         return;
786     }
787 
788     UpdateWindowCaption(hwnd);
789 
790     SendMessage(hToolBar,
791                 TB_SETCMDID,
792                 0,
793                 IDC_PAUSE);
794     SendMessage(hToolBar,
795                 TB_CHANGEBITMAP,
796                 IDC_PAUSE,
797                 IDB_PAUSEICON - IDB_PLAYICON);
798 }
799 
800 static VOID
801 TogglePlaybackState(HWND hwnd)
802 {
803     MCIERROR mciError;
804     MCI_GENERIC_PARMS mciGeneric;
805     ULONG idBmp = IDB_PLAYICON;
806     ULONG idCmd = IDC_PLAY;
807 
808     if (wDeviceId == 0) return;
809 
810     switch (GetDeviceMode(hwnd))
811     {
812         case MCI_MODE_OPEN:
813         case MCI_MODE_STOP:
814         {
815             StartPlayback(hwnd);
816             return;
817         }
818 
819         case MCI_MODE_PLAY:
820         {
821             mciGeneric.dwCallback = (DWORD_PTR)hwnd;
822             mciError = mciSendCommand(wDeviceId, MCI_PAUSE, MCI_WAIT, (DWORD_PTR)&mciGeneric);
823             idBmp = IDB_PLAYICON;
824             idCmd = IDC_PLAY;
825             break;
826         }
827 
828         case MCI_MODE_PAUSE:
829         {
830             mciGeneric.dwCallback = (DWORD_PTR)hwnd;
831             mciError = mciSendCommand(wDeviceId, MCI_RESUME, MCI_WAIT, (DWORD_PTR)&mciGeneric);
832             idBmp = IDB_PAUSEICON;
833             idCmd = IDC_PAUSE;
834             break;
835         }
836 
837         default:
838         {
839             return;
840         }
841     }
842 
843     if (mciError != 0)
844     {
845         ShowMCIError(hwnd, mciError);
846         return;
847     }
848 
849     UpdateWindowCaption(hwnd);
850 
851     SendMessage(hToolBar,
852                 TB_SETCMDID,
853                 0,
854                 idCmd);
855     SendMessage(hToolBar,
856                 TB_CHANGEBITMAP,
857                 idCmd,
858                 idBmp - IDB_PLAYICON);
859 }
860 
861 static VOID
862 ShowDeviceProperties(HWND hwnd)
863 {
864     MCIERROR mciError;
865     MCI_GENERIC_PARMS mciGeneric;
866 
867     mciError = mciSendCommand(wDeviceId, MCI_CONFIGURE, MCI_WAIT, (DWORD_PTR)&mciGeneric);
868     if (mciError != 0)
869     {
870         ShowMCIError(hwnd, mciError);
871     }
872 }
873 
874 static VOID
875 CloseMediaFile(HWND hwnd)
876 {
877     StopPlayback(hwnd);
878 
879     if (bIsSingleWindow)
880         SwitchViewMode(hwnd);
881 
882     CloseMciDevice();
883     UpdateWindowCaption(hwnd);
884 }
885 
886 static VOID
887 OpenMediaFile(HWND hwnd, LPTSTR lpFileName, LPTSTR lpType)
888 {
889     MCIERROR mciError;
890 
891     if (GetFileAttributes(lpFileName) == INVALID_FILE_ATTRIBUTES)
892         return;
893 
894     if (wDeviceId)
895         CloseMediaFile(hwnd);
896 
897     mciError = OpenMciDevice(hwnd, lpType, lpFileName);
898     if (mciError != 0)
899     {
900         ShowMCIError(hwnd, mciError);
901         return;
902     }
903 
904     StartPlayback(hwnd);
905 }
906 
907 static DWORD
908 InsertDeviceMenuItem(HMENU hMenu, UINT uItem, BOOL fByPosition, UINT uItemID, DWORD dwDeviceIndex)
909 {
910     MENUITEMINFO lpmii;
911     MCIERROR mciError;
912     TCHAR szDeviceName[MAX_MCISTR];
913     TCHAR szFriendlyName[MAX_MCISTR];
914 
915     mciError = GetDeviceName(dwDeviceIndex, szDeviceName, sizeof(szDeviceName));
916     if (mciError)
917     {
918         return mciError;
919     }
920 
921     mciError = GetDeviceFriendlyName(szDeviceName, szFriendlyName, sizeof(szFriendlyName));
922     if (mciError)
923     {
924         return mciError;
925     }
926 
927     if (DeviceUsesFiles(szDeviceName))
928     {
929         StringCbCat(szFriendlyName, sizeof(szFriendlyName), _T("..."));
930     }
931 
932     ZeroMemory(&lpmii, sizeof(MENUITEMINFO));
933     lpmii.cbSize = sizeof(lpmii);
934     lpmii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
935     lpmii.wID = uItemID;
936     lpmii.fType = MF_STRING;
937     lpmii.dwTypeData = szFriendlyName;
938     lpmii.dwItemData = dwDeviceIndex;
939     InsertMenuItem(hMenu, uItem, fByPosition, &lpmii);
940 
941     return 0;
942 }
943 
944 static VOID
945 BuildFileFilterAndDeviceMenu(VOID)
946 {
947     TCHAR szDeviceName[MAX_MCISTR];
948     TCHAR szFriendlyName[MAX_MCISTR];
949     TCHAR *szDevice = NULL;
950     static TCHAR szDefaultExtension[] = _T("*.*");
951     TCHAR *szExtensionList = NULL;
952     TCHAR *szExtension = NULL;
953     TCHAR *c = NULL;
954     TCHAR *d = NULL;
955     DWORD dwNumValues;
956     DWORD dwNumDevices;
957     DWORD dwValueNameLen;
958     DWORD dwValueDataSize;
959     DWORD dwMaskLen;
960     DWORD dwFilterSize;
961     DWORD dwDeviceSize;
962     DWORD dwExtensionLen;
963     DWORD dwPosition = 0;
964     DWORD i;
965     DWORD j;
966     UINT uSizeRemain;
967     UINT uMaskRemain;
968     HKEY hKey = NULL;
969 
970     /* Always load the default (all files) filter */
971     LoadString(hInstance, IDS_ALL_TYPES_FILTER, szDefaultFilter, ARRAYSIZE(szDefaultFilter));
972 
973     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI Extensions"), 0, KEY_READ, &hKey) != ERROR_SUCCESS)
974     {
975         goto Failure;
976     }
977 
978     if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwNumValues, &dwValueNameLen, &dwValueDataSize, NULL, NULL) != ERROR_SUCCESS)
979     {
980         goto Failure;
981     }
982 
983     dwMaskLen = ((dwValueNameLen + 3) * dwNumValues) + 1;
984 
985     szExtensionList = malloc(dwMaskLen * sizeof(TCHAR));
986     if (!szExtensionList)
987         goto Failure;
988 
989     dwNumDevices = GetNumDevices();
990 
991     /* Allocate space for every pair of Device and Extension Filter */
992     dwFilterSize = (MAX_MCISTR + (dwMaskLen * 2) + 5) * dwNumDevices;
993 
994     /* Add space for the "All supported" entry */
995     dwFilterSize = (dwFilterSize + (dwMaskLen * 2) + 7) * sizeof(TCHAR) + sizeof(szDefaultFilter);
996 
997     szFilter = malloc(dwFilterSize);
998     if (!szFilter)
999         goto Failure;
1000 
1001     szExtension = malloc((dwValueNameLen + 1) * sizeof(TCHAR));
1002     if (!szExtension)
1003         goto Failure;
1004 
1005     szDevice = malloc(dwValueDataSize + sizeof(TCHAR));
1006     if (!szDevice)
1007         goto Failure;
1008 
1009     ZeroMemory(szFilter, dwFilterSize);
1010 
1011     uSizeRemain = dwFilterSize;
1012     c = szFilter;
1013 
1014     for (j = 1; j <= dwNumDevices; j++)
1015     {
1016         if (GetDeviceName(j, szDeviceName, sizeof(szDeviceName)))
1017         {
1018             continue;
1019         }
1020 
1021         if (GetDeviceFriendlyName(szDeviceName, szFriendlyName, sizeof(szFriendlyName)))
1022         {
1023             continue;
1024         }
1025 
1026         /* Insert a menu item under the "Device" menu for every found MCI device */
1027         InsertDeviceMenuItem(GetSubMenu(hMainMenu, 3), dwPosition, TRUE, IDM_DEVICE_FIRST + dwPosition, j);
1028         dwPosition++;
1029 
1030         /* Copy the default extension list, that may be overwritten after... */
1031         StringCbCopy(szExtensionList, dwMaskLen * sizeof(TCHAR), szDefaultExtension);
1032 
1033         /* Try to determine the real extension list */
1034         uMaskRemain = dwMaskLen * sizeof(TCHAR);
1035         d = szExtensionList;
1036 
1037         for (i = 0; i < dwNumValues; i++)
1038         {
1039             dwExtensionLen = dwValueNameLen + 1;
1040             dwDeviceSize   = dwValueDataSize + sizeof(TCHAR);
1041 
1042             ZeroMemory(szDevice, dwDeviceSize);
1043 
1044             if (RegEnumValue(hKey, i, szExtension, &dwExtensionLen, NULL, NULL, (LPBYTE)szDevice, &dwDeviceSize) == ERROR_SUCCESS)
1045             {
1046                 CharLowerBuff(szDevice, dwDeviceSize / sizeof(TCHAR));
1047                 CharLowerBuff(szDeviceName, ARRAYSIZE(szDeviceName));
1048                 if (_tcscmp(szDeviceName, szDevice) == 0)
1049                 {
1050                      CharLowerBuff(szExtension, dwExtensionLen);
1051                      StringCbPrintfEx(d, uMaskRemain, &d, &uMaskRemain, 0, _T("%s%s%s"), _T("*."), szExtension, _T(";"));
1052                 }
1053             }
1054         }
1055 
1056         /* Remove the last separator */
1057         d--;
1058         uSizeRemain += sizeof(*d);
1059         *d = _T('\0');
1060 
1061         /* Add the description */
1062         StringCbPrintfEx(c, uSizeRemain, &c, &uSizeRemain, 0, _T("%s (%s)"), szFriendlyName, szExtensionList);
1063 
1064         /* Skip one char to separate the description from the filter mask */
1065         c++;
1066         uSizeRemain -= sizeof(*c);
1067 
1068         /* Append the filter mask */
1069         StringCbCopyEx(c, uSizeRemain, szExtensionList, &c, &uSizeRemain, 0);
1070 
1071         /* Skip another char to separate the elements of the filter mask */
1072         c++;
1073         uSizeRemain -= sizeof(*c);
1074     }
1075 
1076     /* Build the full list of supported extensions */
1077     uMaskRemain = dwMaskLen * sizeof(TCHAR);
1078     d = szExtensionList;
1079 
1080     for (i = 0; i < dwNumValues; i++)
1081     {
1082         dwExtensionLen = dwValueNameLen + 1;
1083 
1084         if (RegEnumValue(hKey, i, szExtension, &dwExtensionLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
1085         {
1086             CharLowerBuff(szExtension, dwExtensionLen);
1087             StringCbPrintfEx(d, uMaskRemain, &d, &uMaskRemain, 0, _T("%s%s%s"), _T("*."), szExtension, _T(";"));
1088         }
1089     }
1090 
1091     /* Remove the last separator */
1092     d--;
1093     uSizeRemain += sizeof(*d);
1094     *d = _T('\0');
1095 
1096     /* Add the default (all files) description */
1097     StringCbPrintfEx(c, uSizeRemain, &c, &uSizeRemain, 0, _T("%s (%s)"), szDefaultFilter, szExtensionList);
1098 
1099     /* Skip one char to separate the description from the filter mask */
1100     c++;
1101     uSizeRemain -= sizeof(*c);
1102 
1103     /* Append the filter mask */
1104     StringCbCopyEx(c, uSizeRemain, szExtensionList, &c, &uSizeRemain, 0);
1105 
1106 Cleanup:
1107     if (szExtensionList) free(szExtensionList);
1108     if (szExtension)     free(szExtension);
1109     if (szDevice)        free(szDevice);
1110     RegCloseKey(hKey);
1111 
1112     return;
1113 
1114 Failure:
1115     /* We failed at retrieving the supported files, so use the default filter */
1116     if (szFilter) free(szFilter);
1117     szFilter = szDefaultFilter;
1118 
1119     uSizeRemain = sizeof(szDefaultFilter);
1120     c = szFilter;
1121 
1122     /* Add the default (all files) description */
1123     StringCbPrintfEx(c, uSizeRemain, &c, &uSizeRemain, 0, _T("%s (%s)"), szDefaultFilter, szDefaultExtension);
1124 
1125     /* Skip one char to separate the description from the filter mask */
1126     c++;
1127     uSizeRemain -= sizeof(*c);
1128 
1129     /* Append the filter mask */
1130     StringCbCopyEx(c, uSizeRemain, szDefaultExtension, &c, &uSizeRemain, 0);
1131 
1132     goto Cleanup;
1133 }
1134 
1135 static VOID
1136 CleanupFileFilter(VOID)
1137 {
1138     if (szFilter && szFilter != szDefaultFilter) free(szFilter);
1139 }
1140 
1141 static VOID
1142 OpenFileDialog(HWND hwnd, DWORD dwFilterIndex, LPTSTR lpType)
1143 {
1144     OPENFILENAME OpenFileName;
1145     TCHAR szFile[MAX_PATH + 1] = _T("");
1146     TCHAR szCurrentDir[MAX_PATH];
1147 
1148     ZeroMemory(&OpenFileName, sizeof(OpenFileName));
1149 
1150     if (!GetCurrentDirectory(ARRAYSIZE(szCurrentDir), szCurrentDir))
1151     {
1152         StringCbCopy(szCurrentDir, sizeof(szCurrentDir), _T("c:\\"));
1153     }
1154 
1155     OpenFileName.lStructSize     = sizeof(OpenFileName);
1156     OpenFileName.hwndOwner       = hwnd;
1157     OpenFileName.hInstance       = hInstance;
1158     OpenFileName.lpstrFilter     = szFilter;
1159     OpenFileName.lpstrFile       = szFile;
1160     OpenFileName.nMaxFile        = ARRAYSIZE(szFile);
1161     OpenFileName.lpstrInitialDir = szCurrentDir;
1162     OpenFileName.Flags           = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_SHAREAWARE;
1163     OpenFileName.lpstrDefExt     = _T("\0");
1164     OpenFileName.nFilterIndex = dwFilterIndex;
1165 
1166     if (!GetOpenFileName(&OpenFileName))
1167         return;
1168 
1169     OpenMediaFile(hwnd, OpenFileName.lpstrFile, lpType);
1170 }
1171 
1172 static VOID
1173 HandleDeviceMenuItem(HWND hwnd, UINT uItem)
1174 {
1175     MENUITEMINFO lpmii;
1176     TCHAR szDeviceName[MAX_MCISTR];
1177     MCIERROR mciError;
1178 
1179     ZeroMemory(&lpmii, sizeof(MENUITEMINFO));
1180     lpmii.cbSize = sizeof(lpmii);
1181     lpmii.fMask = MIIM_DATA;
1182     GetMenuItemInfo(hMainMenu, uItem, FALSE, &lpmii);
1183 
1184     mciError = GetDeviceName(lpmii.dwItemData, szDeviceName, sizeof(szDeviceName));
1185     if (mciError)
1186     {
1187         ShowMCIError(hwnd, mciError);
1188         return;
1189     }
1190 
1191     if (DeviceUsesFiles(szDeviceName))
1192     {
1193         OpenFileDialog(hwnd, uItem - IDM_DEVICE_FIRST + 1, szDeviceName);
1194         return;
1195     }
1196 
1197     mciError = OpenMciDevice(hwnd, szDeviceName, NULL);
1198     if (mciError)
1199     {
1200         ShowMCIError(hwnd, mciError);
1201     }
1202 
1203     return;
1204 }
1205 
1206 LRESULT CALLBACK
1207 MainWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
1208 {
1209     switch (Message)
1210     {
1211         case WM_CREATE:
1212         {
1213             InitControls(hwnd);
1214             hMainMenu = GetMenu(hwnd);
1215             break;
1216         }
1217 
1218         case WM_DROPFILES:
1219         {
1220             HDROP drophandle;
1221             TCHAR droppedfile[MAX_PATH];
1222 
1223             drophandle = (HDROP)wParam;
1224             DragQueryFile(drophandle, 0, droppedfile, ARRAYSIZE(droppedfile));
1225             DragFinish(drophandle);
1226             OpenMediaFile(hwnd, droppedfile, NULL);
1227             break;
1228         }
1229 
1230         case MM_MCINOTIFY:
1231         {
1232             if (wParam == MCI_NOTIFY_SUCCESSFUL)
1233             {
1234                 StopPlayback(hwnd);
1235                 if (bRepeat)
1236                 {
1237                     StartPlayback(hwnd);
1238                 }
1239             }
1240             break;
1241         }
1242 
1243         case WM_NOTIFY:
1244         {
1245             LPNMHDR pnmhdr = (LPNMHDR)lParam;
1246 
1247             switch (pnmhdr->code)
1248             {
1249                 case TTN_GETDISPINFO:
1250                 {
1251                     LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lParam;
1252                     UINT idButton = (UINT)lpttt->hdr.idFrom;
1253 
1254                     switch (idButton)
1255                     {
1256                         case IDC_PLAY:
1257                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_PLAY);
1258                             break;
1259                         case IDC_STOP:
1260                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_STOP);
1261                             break;
1262                         case IDC_EJECT:
1263                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_EJECT);
1264                             break;
1265                         case IDC_BACKWARD:
1266                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_BACKWARD);
1267                             break;
1268                         case IDC_SEEKBACK:
1269                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_SEEKBACK);
1270                             break;
1271                         case IDC_SEEKFORW:
1272                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_SEEKFORW);
1273                             break;
1274                         case IDC_FORWARD:
1275                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_FORWARD);
1276                             break;
1277                         case IDC_PAUSE:
1278                             lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_PAUSE);
1279                             break;
1280                     }
1281                     break;
1282                 }
1283             }
1284         }
1285         break;
1286 
1287         case WM_SIZING:
1288         {
1289             LPRECT pRect = (LPRECT)lParam;
1290 
1291             if (!bIsSingleWindow)
1292             {
1293                 if (pRect->right - pRect->left < MAIN_WINDOW_MIN_WIDTH)
1294                     pRect->right = pRect->left + MAIN_WINDOW_MIN_WIDTH;
1295 
1296                 if (pRect->bottom - pRect->top != MAIN_WINDOW_HEIGHT)
1297                     pRect->bottom = pRect->top + MAIN_WINDOW_HEIGHT;
1298             }
1299             return TRUE;
1300         }
1301 
1302         case WM_SIZE:
1303         {
1304             RECT Rect;
1305 
1306             if (hToolBar && hTrackBar)
1307             {
1308                 SendMessage(hToolBar, TB_AUTOSIZE, 0, 0);
1309                 SendMessage(hToolBar, TB_GETITEMRECT, 1, (LPARAM)&Rect);
1310                 MoveWindow(hTimeDisplay, LOWORD(lParam) - 140, 4, 135, 18, TRUE);
1311 
1312                 if (!bIsSingleWindow)
1313                 {
1314                     UINT Size = GetSystemMetrics(SM_CYMENU) + Rect.bottom;
1315                     MoveWindow(hTrackBar, 0, 0, LOWORD(lParam), HIWORD(lParam) - Size, TRUE);
1316                 }
1317                 else
1318                 {
1319                     RECT ToolbarRect;
1320                     MCI_DGV_PUT_PARMS mciPut;
1321 
1322                     MoveWindow(hTrackBar, 180, 0, LOWORD(lParam) - 322, 25, TRUE);
1323 
1324                     GetClientRect(hwnd, &Rect);
1325                     GetClientRect(hToolBar, &ToolbarRect);
1326 
1327                     mciPut.rc.top = 0;
1328                     mciPut.rc.left = 0;
1329                     mciPut.rc.right = Rect.right;
1330                     mciPut.rc.bottom = Rect.bottom - (ToolbarRect.bottom - ToolbarRect.top) - 2;
1331 
1332                     mciSendCommand(wDeviceId, MCI_PUT, MCI_DGV_PUT_DESTINATION | MCI_DGV_RECT | MCI_WAIT, (DWORD_PTR)&mciPut);
1333                 }
1334             }
1335             return 0L;
1336         }
1337 
1338         case WM_HSCROLL:
1339         {
1340             if (hTrackBar == (HWND)lParam)
1341             {
1342                 if (wDeviceId)
1343                 {
1344                     DWORD dwNewPos = (DWORD)SendMessage(hTrackBar, TBM_GETPOS, 0, 0);
1345                     SeekPlayback(hwnd, dwNewPos);
1346                 }
1347                 else
1348                 {
1349                     SendMessage(hTrackBar, TBM_SETPOS, TRUE, 0);
1350                 }
1351             }
1352         }
1353         break;
1354 
1355         case WM_NCLBUTTONDBLCLK:
1356         {
1357             if (wParam == HTCAPTION)
1358             {
1359                 SwitchViewMode(hwnd);
1360             }
1361         }
1362         break;
1363 
1364         case WM_COMMAND:
1365         {
1366             if (LOWORD(wParam) >= IDM_DEVICE_FIRST)
1367             {
1368                 HandleDeviceMenuItem(hwnd, LOWORD(wParam));
1369                 break;
1370             }
1371 
1372             switch (LOWORD(wParam))
1373             {
1374                 case IDC_PLAY:
1375                 case IDC_PAUSE:
1376                 {
1377                     if (wDeviceId)
1378                         TogglePlaybackState(hwnd);
1379                     else
1380                         OpenFileDialog(hwnd, 1, NULL);
1381 
1382                     break;
1383                 }
1384 
1385                 case IDC_STOP:
1386                     StopPlayback(hwnd);
1387                     break;
1388 
1389                 case IDC_EJECT:
1390                     break;
1391 
1392                 case IDC_BACKWARD:
1393                     break;
1394 
1395                 case IDC_SEEKBACK:
1396                     SeekBackPlayback(hwnd);
1397                     break;
1398 
1399                 case IDC_SEEKFORW:
1400                     SeekForwPlayback(hwnd);
1401                     break;
1402 
1403                 case IDC_FORWARD:
1404                     break;
1405 
1406                 case IDM_OPEN_FILE:
1407                     OpenFileDialog(hwnd, 1, NULL);
1408                     return 0;
1409 
1410                 case IDM_CLOSE_FILE:
1411                     CloseMediaFile(hwnd);
1412                     break;
1413 
1414                 case IDM_REPEAT:
1415                 {
1416                     if (!bRepeat)
1417                     {
1418                         CheckMenuItem(hMainMenu, IDM_REPEAT, MF_BYCOMMAND | MF_CHECKED);
1419                         bRepeat = TRUE;
1420                     }
1421                     else
1422                     {
1423                         CheckMenuItem(hMainMenu, IDM_REPEAT, MF_BYCOMMAND | MF_UNCHECKED);
1424                         bRepeat = FALSE;
1425                     }
1426                     break;
1427                 }
1428 
1429                 case IDM_SWITCHVIEW:
1430                     SwitchViewMode(hwnd);
1431                     break;
1432 
1433                 case IDM_DEVPROPS:
1434                     ShowDeviceProperties(hwnd);
1435                     break;
1436 
1437                 case IDM_VOLUMECTL:
1438                     ShellExecute(hwnd, NULL, _T("SNDVOL32.EXE"), NULL, NULL, SW_SHOWNORMAL);
1439                     break;
1440 
1441                 case IDM_ABOUT:
1442                 {
1443                     HICON mplayIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
1444                     ShellAbout(hwnd, szAppTitle, 0, mplayIcon);
1445                     DeleteObject(mplayIcon);
1446                     break;
1447                 }
1448 
1449                 case IDM_EXIT:
1450                     PostMessage(hwnd, WM_CLOSE, 0, 0);
1451                     return 0;
1452             }
1453             break;
1454         }
1455 
1456         case WM_DESTROY:
1457             CloseMediaFile(hwnd);
1458             PostQuitMessage(0);
1459             return 0;
1460     }
1461 
1462     return DefWindowProc(hwnd, Message, wParam, lParam);
1463 }
1464 
1465 INT WINAPI
1466 _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdShow)
1467 {
1468     WNDCLASSEX WndClass = {0};
1469     TCHAR szClassName[] = _T("ROSMPLAY32");
1470     HWND hwnd;
1471     MSG msg;
1472     DWORD dwError;
1473     HANDLE hAccel;
1474 
1475     hInstance = hInst;
1476 
1477     LoadString(hInstance, IDS_APPTITLE, szAppTitle, ARRAYSIZE(szAppTitle));
1478 
1479     WndClass.cbSize            = sizeof(WndClass);
1480     WndClass.lpszClassName     = szClassName;
1481     WndClass.lpfnWndProc       = MainWndProc;
1482     WndClass.hInstance         = hInstance;
1483     WndClass.style             = CS_HREDRAW | CS_VREDRAW;
1484     WndClass.hIcon             = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
1485     WndClass.hCursor           = LoadCursor(NULL, IDC_ARROW);
1486     WndClass.hbrBackground     = (HBRUSH)(COLOR_BTNFACE + 1);
1487     WndClass.lpszMenuName      = MAKEINTRESOURCE(IDR_MAINMENU);
1488 
1489     if (!RegisterClassEx(&WndClass))
1490     {
1491         ShowLastWin32Error(NULL);
1492         return 0;
1493     }
1494 
1495     hwnd = CreateWindow(szClassName,
1496                         szAppTitle,
1497                         WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_OVERLAPPED | WS_CAPTION | WS_CLIPCHILDREN,
1498                         CW_USEDEFAULT,
1499                         CW_USEDEFAULT,
1500                         350,
1501                         MAIN_WINDOW_HEIGHT,
1502                         NULL,
1503                         NULL,
1504                         hInstance,
1505                         NULL);
1506     if (!hwnd)
1507     {
1508         ShowLastWin32Error(NULL);
1509         return 0;
1510     }
1511 
1512     hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(ID_ACCELERATORS));
1513 
1514     BuildFileFilterAndDeviceMenu();
1515 
1516     DragAcceptFiles(hwnd, TRUE);
1517 
1518     DisableMenuItems();
1519 
1520     dwError = SearchPath(NULL, _T("SNDVOL32.EXE"), NULL, 0, NULL, NULL);
1521     if (dwError == 0)
1522     {
1523         EnableMenuItem(hMainMenu, IDM_VOLUMECTL, MF_BYCOMMAND | MF_GRAYED);
1524     }
1525 
1526     /* Show it */
1527     ShowWindow(hwnd, SW_SHOW);
1528     UpdateWindow(hwnd);
1529 
1530     OpenMediaFile(hwnd, lpCmdLine, NULL);
1531 
1532     /* Message Loop */
1533     while (GetMessage(&msg, NULL, 0, 0))
1534     {
1535         if (!TranslateAccelerator(hwnd, hAccel, &msg))
1536         {
1537             TranslateMessage(&msg);
1538             DispatchMessage(&msg);
1539         }
1540     }
1541 
1542     CleanupFileFilter();
1543 
1544     DestroyAcceleratorTable(hAccel);
1545 
1546     return (INT)msg.wParam;
1547 }
1548