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