ReactOS  0.4.14-dev-583-g2a1ba2c
mcicda.c
Go to the documentation of this file.
1 /*
2  * Test MCI CD-ROM access
3  *
4  * Copyright 2010 Jörg Höhle
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdio.h>
22 #include "windows.h"
23 #include "mmsystem.h"
24 #include "wine/test.h"
25 
26 typedef union {
35 
36 extern const char* dbg_mcierr(MCIERROR err); /* from mci.c */
37 
39 {
40  /* WM_DEVICECHANGE 0x0219 appears randomly */
41  if(msg->message != MM_MCINOTIFY) {
42  trace("skipping spurious message %04x\n",msg->message);
43  return TRUE;
44  }
45  return FALSE;
46 }
47 
48 /* A single ok() in each code path allows us to prefix this with todo_wine */
49 #define test_notification(hwnd, command, type) test_notification_dbg(hwnd, command, type, __LINE__)
50 static void test_notification_dbg(HWND hwnd, const char* command, WPARAM type, int line)
51 { /* Use type 0 as meaning no message */
52  MSG msg;
53  BOOL seen;
54  do { seen = PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE); }
55  while(seen && spurious_message(&msg));
56  if(type && !seen) {
57  /* We observe transient delayed notification, mostly on native.
58  * Notification is not always present right when mciSend returns. */
59  trace_(__FILE__,line)("Waiting for delayed notification from %s\n", command);
62  }
63  if(!seen)
64  ok_(__FILE__,line)(type==0, "Expect message %04lx from %s\n", type, command);
65  else if(msg.hwnd != hwnd)
66  ok_(__FILE__,line)(msg.hwnd == hwnd, "Didn't get the handle to our test window\n");
67  else if(msg.message != MM_MCINOTIFY)
68  ok_(__FILE__,line)(msg.message == MM_MCINOTIFY, "got %04x instead of MM_MCINOTIFY from command %s\n", msg.message, command);
69  else ok_(__FILE__,line)(msg.wParam == type, "got %04lx instead of MCI_NOTIFY_xyz %04lx from command %s\n", msg.wParam, type, command);
70 }
71 
72 #define CDFRAMES_PERSEC 75
73 static DWORD MSF_Add(DWORD d1, DWORD d2)
74 {
75  WORD c, m, s, f;
76  f = MCI_MSF_FRAME(d1) + MCI_MSF_FRAME(d2);
77  c = f / CDFRAMES_PERSEC;
78  f = f % CDFRAMES_PERSEC;
79  s = MCI_MSF_SECOND(d1) + MCI_MSF_SECOND(d2) + c;
80  c = s / 60;
81  s = s % 60;
82  m = MCI_MSF_MINUTE(d1) + MCI_MSF_MINUTE(d2) + c; /* may be > 60 */
83  return MCI_MAKE_MSF(m,s,f);
84 }
85 
86 static MCIERROR ok_open = 0; /* MCIERR_CANNOT_LOAD_DRIVER */
87 
88 /* TODO show that shareable flag is not what Wine implements. */
89 
90 static void test_play(HWND hwnd)
91 {
92  MCIDEVICEID wDeviceID;
93  MCI_PARMS_UNION parm;
94  MCIERROR err, ok_hw;
95  DWORD numtracks, track, duration;
97  char buf[1024];
98  memset(buf, 0, sizeof(buf));
99  parm.gen.dwCallback = (DWORD_PTR)hwnd; /* once to rule them all */
100 
101  err = mciSendStringA("open cdaudio alias c notify shareable", buf, sizeof(buf), hwnd);
103  "mci open cdaudio notify returned %s\n", dbg_mcierr(err));
104  ok_open = err;
105  test_notification(hwnd, "open alias notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
106  /* Native returns MUST_USE_SHAREABLE when there's trouble with the hardware
107  * (e.g. unreadable disk) or when Media Player already has the device open,
108  * yet adding that flag does not help get past this error. */
109 
110  if(err) {
111  skip("Cannot open any cdaudio device, %s.\n", dbg_mcierr(err));
112  return;
113  }
114  wDeviceID = atoi(buf);
115  ok(!strcmp(buf,"1"), "mci open deviceId: %s, expected 1\n", buf);
116  /* Win9X-ME may start the MCI and media player upon insertion of a CD. */
117 
118  err = mciSendStringA("sysinfo all name 1 open", buf, sizeof(buf), NULL);
119  ok(!err,"sysinfo all name 1 returned %s\n", dbg_mcierr(err));
120  if(!err && wDeviceID != 1) trace("Device '%s' is open.\n", buf);
121 
122  err = mciSendStringA("capability c has video notify", buf, sizeof(buf), hwnd);
123  ok(!err, "capability video: %s\n", dbg_mcierr(err));
124  if(!err) ok(!strcmp(buf, "false"), "capability video is %s\n", buf);
125  test_notification(hwnd, "capability notify", MCI_NOTIFY_SUCCESSFUL);
126 
127  err = mciSendStringA("capability c can play", buf, sizeof(buf), hwnd);
128  ok(!err, "capability video: %s\n", dbg_mcierr(err));
129  if(!err) ok(!strcmp(buf, "true"), "capability play is %s\n", buf);
130 
131  err = mciSendStringA("capability c", buf, sizeof(buf), NULL);
132  ok(err == MCIERR_MISSING_PARAMETER, "capability nokeyword: %s\n", dbg_mcierr(err));
133 
134  parm.caps.dwItem = 0x4001;
135  parm.caps.dwReturn = 0xFEEDABAD;
137  ok(err == MCIERR_UNSUPPORTED_FUNCTION, "GETDEVCAPS %x: %s\n", parm.caps.dwItem, dbg_mcierr(err));
138 
139  parm.caps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
141  ok(!err, "GETDEVCAPS device type: %s\n", dbg_mcierr(err));
142  if(!err) ok( parm.caps.dwReturn == MCI_DEVTYPE_CD_AUDIO, "getdevcaps device type: %u\n", parm.caps.dwReturn);
143 
144  err = mciSendCommandA(wDeviceID, MCI_RECORD, 0, (DWORD_PTR)&parm);
145  ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_RECORD: %s\n", dbg_mcierr(err));
146 
147  /* Wine's MCI_MapMsgAtoW crashes on MCI_SAVE without parm->lpfilename */
148  parm.save.lpfilename = "foo";
149  err = mciSendCommandA(wDeviceID, MCI_SAVE, 0, (DWORD_PTR)&parm);
150  ok(err == MCIERR_UNSUPPORTED_FUNCTION, "MCI_SAVE: %s\n", dbg_mcierr(err));
151 
152  /* commands from the core set are UNSUPPORTED, others UNRECOGNIZED */
153  err = mciSendCommandA(wDeviceID, MCI_STEP, 0, (DWORD_PTR)&parm);
154  ok(err == MCIERR_UNRECOGNIZED_COMMAND, "MCI_STEP: %s\n", dbg_mcierr(err));
155 
156  parm.status.dwItem = MCI_STATUS_TIME_FORMAT;
157  parm.status.dwReturn = 0xFEEDABAD;
158  err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
159  ok(!err, "STATUS time format: %s\n", dbg_mcierr(err));
160  if(!err) ok(parm.status.dwReturn == MCI_FORMAT_MSF, "status time default format: %ld\n", parm.status.dwReturn);
161 
162  /* "CD-Audio" */
163  err = mciSendStringA("info c product wait notify", buf, sizeof(buf), hwnd);
164  ok(!err, "info product: %s\n", dbg_mcierr(err));
165  test_notification(hwnd, "info notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
166 
167  parm.status.dwItem = MCI_STATUS_MEDIA_PRESENT;
168  parm.status.dwReturn = 0xFEEDABAD;
169  err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
170  ok(err || parm.status.dwReturn == TRUE || parm.status.dwReturn == FALSE,
171  "STATUS media present: %s\n", dbg_mcierr(err));
172 
173  if (parm.status.dwReturn != TRUE) {
174  skip("No CD-ROM in drive.\n");
175  return;
176  }
177 
178  parm.status.dwItem = MCI_STATUS_MODE;
179  err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
180  ok(!err, "STATUS mode: %s\n", dbg_mcierr(err));
181  switch(parm.status.dwReturn) {
182  case MCI_MODE_NOT_READY:
183  skip("CD-ROM mode not ready (DVD in drive?)\n");
184  return;
185  case MCI_MODE_OPEN: /* should not happen with MEDIA_PRESENT */
186  skip("CD-ROM drive is open\n");
187  /* set door closed may not work. */
188  return;
189  default: /* play/record/seek/pause */
190  ok(parm.status.dwReturn==MCI_MODE_STOP, "STATUS mode is %lx\n", parm.status.dwReturn);
191  /* fall through */
192  case MCI_MODE_STOP: /* normal */
193  break;
194  }
195 
196  /* Initial mode is "stopped" with a CD in drive */
197  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
198  ok(!err, "status mode: %s\n", dbg_mcierr(err));
199  if(!err) ok(!strcmp(buf, "stopped"), "status mode is initially %s\n", buf);
200 
201  err = mciSendStringA("status c ready", buf, sizeof(buf), hwnd);
202  ok(!err, "status ready: %s\n", dbg_mcierr(err));
203  if(!err) ok(!strcmp(buf, "true"), "status ready with media is %s\n", buf);
204 
205  err = mciSendStringA("info c product identity", buf, sizeof(buf), hwnd);
206  ok(!err, "info 2flags: %s\n", dbg_mcierr(err)); /* not MCIERR_FLAGS_NOT_COMPATIBLE */
207  /* Precedence rule p>u>i verified experimentally, not tested here. */
208 
209  err = mciSendStringA("info c identity", buf, sizeof(buf), hwnd);
210  ok(!err || err == MCIERR_HARDWARE, "info identity: %s\n", dbg_mcierr(err));
211  /* a blank disk causes MCIERR_HARDWARE and other commands to fail likewise. */
212  ok_hw = err;
213 
214  err = mciSendStringA("info c upc", buf, sizeof(buf), hwnd);
215  ok(err == ok_hw || err == MCIERR_NO_IDENTITY, "info upc: %s\n", dbg_mcierr(err));
216 
217  parm.status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
218  parm.status.dwReturn = 0;
219  err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
220  ok(err == ok_hw, "STATUS number of tracks: %s\n", dbg_mcierr(err));
221  numtracks = parm.status.dwReturn;
222  /* cf. MAXIMUM_NUMBER_TRACKS */
223  ok(0 < numtracks && numtracks <= 99, "number of tracks=%ld\n", parm.status.dwReturn);
224 
225  err = mciSendStringA("status c length", buf, sizeof(buf), hwnd);
226  ok(err == ok_hw, "status length: %s\n", dbg_mcierr(err));
227  if(!err) trace("CD length %s\n", buf);
228 
229  if(err) { /* MCIERR_HARDWARE when given a blank disk */
230  skip("status length %s (blank disk?)\n", dbg_mcierr(err));
231  return;
232  }
233 
234  /* Linux leaves the drive at some random position,
235  * native initialises to the start position below. */
236  err = mciSendStringA("status c position", buf, sizeof(buf), hwnd);
237  ok(!err, "status position: %s\n", dbg_mcierr(err));
238  if(!err) todo_wine ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"),
239  "status position initially %s\n", buf);
240  /* 2 seconds is the initial position even with data tracks. */
241 
242  err = mciSendStringA("status c position start notify", buf, sizeof(buf), hwnd);
243  ok(err == ok_hw, "status position start: %s\n", dbg_mcierr(err));
244  if(!err) ok(!strcmp(buf, "00:02:00") || !strcmp(buf, "00:02:33") || !strcmp(buf, "00:03:00"),
245  "status position start %s\n", buf);
246  test_notification(hwnd, "status notify", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
247 
248  err = mciSendStringA("status c position start track 1 notify", buf, sizeof(buf), hwnd);
249  ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start: %s\n", dbg_mcierr(err));
250  test_notification(hwnd, "status 2flags", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
251 
252  err = mciSendStringA("play c from 00:02:00 to 00:01:00 notify", buf, sizeof(buf), hwnd);
253  ok(err == MCIERR_OUTOFRANGE, "play 2s to 1s: %s\n", dbg_mcierr(err));
254  test_notification(hwnd, "play 2s to 1s", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
255 
256  err = mciSendStringA("resume c", buf, sizeof(buf), hwnd);
258  "resume without play: %s\n", dbg_mcierr(err)); /* not NONAPPLICABLE_FUNCTION */
259  /* vmware with a .iso (data-only) yields no error on NT/w2k */
260 
261  err = mciSendStringA("seek c wait", buf, sizeof(buf), hwnd);
262  ok(err == MCIERR_MISSING_PARAMETER, "seek noflag: %s\n", dbg_mcierr(err));
263 
264  err = mciSendStringA("seek c to start to end", buf, sizeof(buf), hwnd);
265  ok(err == MCIERR_FLAGS_NOT_COMPATIBLE || broken(!err), "seek to start+end: %s\n", dbg_mcierr(err));
266  /* Win9x only errors out with Seek to start to <position> */
267 
268  /* set Wine to a defined position before play */
269  err = mciSendStringA("seek c to start notify", buf, sizeof(buf), hwnd);
270  ok(!err, "seek to start: %s\n", dbg_mcierr(err));
271  test_notification(hwnd, "seek to start", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
272  /* Win9X Status position / current track then sometimes report the end position / track! */
273 
274  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
275  ok(!err, "status mode: %s\n", dbg_mcierr(err));
276  if(!err) ok(!strcmp(buf, "stopped"), "status mode after seek is %s\n", buf);
277 
278  /* MCICDA ignores MCI_SET_VIDEO */
279  err = mciSendStringA("set c video on", buf, sizeof(buf), hwnd);
280  ok(!err, "set video: %s\n", dbg_mcierr(err));
281 
282  /* One xp machine ignored SET_AUDIO, one w2k and one w7 machine honoured it
283  * and simultaneously toggled the mute button in the mixer control panel.
284  * Or does it only depend on the HW, not the OS?
285  * Some vmware machines return MCIERR_HARDWARE. */
286  err = mciSendStringA("set c audio all on", buf, sizeof(buf), hwnd);
287  ok(!err || err == MCIERR_HARDWARE, "set audio: %s\n", dbg_mcierr(err));
288 
289  err = mciSendStringA("set c time format ms", buf, sizeof(buf), hwnd);
290  ok(!err, "set time format ms: %s\n", dbg_mcierr(err));
291 
292  memset(buf, 0, sizeof(buf));
293  err = mciSendStringA("status c position start", buf, sizeof(buf), hwnd);
294  ok(!err, "status position start (ms): %s\n", dbg_mcierr(err));
295  duration = atoi(buf);
296  if(!err) ok(duration > 2000, "status position initially %sms\n", buf);
297  /* 00:02:00 corresponds to 2001 ms, 02:33 -> 2441 etc. */
298 
299  err = mciSendStringA("status c position start track 1", buf, sizeof(buf), hwnd);
300  ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "status position start+track: %s\n", dbg_mcierr(err));
301 
302  err = mciSendStringA("status c notify wait", buf, sizeof(buf), hwnd);
303  ok(err == MCIERR_MISSING_PARAMETER, "status noflag: %s\n", dbg_mcierr(err));
304 
305  err = mciSendStringA("status c length track 1", buf, sizeof(buf), hwnd);
306  ok(!err, "status length (ms): %s\n", dbg_mcierr(err));
307  if(!err) {
308  trace("track #1 length %sms\n", buf);
309  duration = atoi(buf);
310  } else duration = 2001; /* for the position test below */
311 
312  if (0) { /* causes some native systems to return Seek and Play with MCIERR_HARDWARE */
313  /* depending on capability can eject only? */
314  err = mciSendStringA("set c door closed notify", buf, sizeof(buf), hwnd);
315  ok(!err, "set door closed: %s\n", dbg_mcierr(err));
316  test_notification(hwnd, "door closed", err ? 0 : MCI_NOTIFY_SUCCESSFUL);
317  }
318  /* Changing the disk while the MCI device is open causes the Status
319  * command to report stale data. Native obviously caches the TOC. */
320 
321  /* status type track is localised, strcmp("audio|other") may fail. */
322  parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
323  parm.status.dwTrack = 1;
324  parm.status.dwReturn = 0xFEEDABAD;
326  ok(!err, "STATUS type track 1: %s\n", dbg_mcierr(err));
327  ok(parm.status.dwReturn==MCI_CDA_TRACK_OTHER || parm.status.dwReturn==MCI_CDA_TRACK_AUDIO,
328  "unknown track type %lx\n", parm.status.dwReturn);
329 
330  if (parm.status.dwReturn == MCI_CDA_TRACK_OTHER) {
331  /* Find an audio track */
332  parm.status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
333  parm.status.dwTrack = numtracks;
334  parm.status.dwReturn = 0xFEEDABAD;
336  ok(!err, "STATUS type track %u: %s\n", numtracks, dbg_mcierr(err));
337  ok(parm.status.dwReturn == MCI_CDA_TRACK_OTHER || parm.status.dwReturn == MCI_CDA_TRACK_AUDIO,
338  "unknown track type %lx\n", parm.status.dwReturn);
339  track = (!err && parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) ? numtracks : 0;
340 
341  /* Seek to start (above) skips over data tracks
342  * In case of a data only CD, it seeks to the end of disk, however
343  * another Status position a few seconds later yields MCIERR_HARDWARE. */
344  parm.status.dwItem = MCI_STATUS_POSITION;
345  parm.status.dwReturn = 2000;
346  err = mciSendCommandA(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&parm);
347  ok(!err || broken(err == MCIERR_HARDWARE), "STATUS position: %s\n", dbg_mcierr(err));
348 
349  if(!err && track) ok(parm.status.dwReturn > duration,
350  "Seek did not skip data tracks, position %lums\n", parm.status.dwReturn);
351  /* dwReturn > start + length(#1) may fail because of small position report fluctuation.
352  * On some native systems, status position fluctuates around the target position;
353  * Successive calls return varying positions! */
354 
355  err = mciSendStringA("set c time format msf", buf, sizeof(buf), hwnd);
356  ok(!err, "set time format msf: %s\n", dbg_mcierr(err));
357 
358  parm.status.dwItem = MCI_STATUS_LENGTH;
359  parm.status.dwTrack = 1;
360  parm.status.dwReturn = 0xFEEDABAD;
362  ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
363  duration = parm.status.dwReturn;
364  trace("track #1 length: %02um:%02us:%02uframes\n",
365  MCI_MSF_MINUTE(duration), MCI_MSF_SECOND(duration), MCI_MSF_FRAME(duration));
366  ok(duration>>24==0, "CD length high bits %08X\n", duration);
367 
368  /* TODO only with mixed CDs? */
369  /* play track 1 to length silently works with data tracks */
370  parm.play.dwFrom = MCI_MAKE_MSF(0,2,0);
371  parm.play.dwTo = duration; /* omitting 2 seconds from end */
372  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD_PTR)&parm);
373  ok(!err, "PLAY data to %08X: %s\n", duration, dbg_mcierr(err));
374 
375  Sleep(1500*factor); /* Time to spin up, hopefully less than track length */
376 
377  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
378  ok(!err, "status mode: %s\n", dbg_mcierr(err));
379  if(!err) ok(!strcmp(buf, "stopped"), "status mode on data is %s\n", buf);
380  } else if (parm.status.dwReturn == MCI_CDA_TRACK_AUDIO) {
381  skip("Got no mixed data+audio CD.\n");
382  track = 1;
383  } else track = 0;
384 
385  if (!track) {
386  skip("Found no audio track.\n");
387  return;
388  }
389 
390  err = mciSendStringA("set c time format msf", buf, sizeof(buf), hwnd);
391  ok(!err, "set time format msf: %s\n", dbg_mcierr(err));
392 
393  parm.status.dwItem = MCI_STATUS_LENGTH;
394  parm.status.dwTrack = numtracks;
395  parm.status.dwReturn = 0xFEEDABAD;
397  ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
398  duration = parm.status.dwReturn;
399  trace("last track length: %02um:%02us:%02uframes\n",
400  MCI_MSF_MINUTE(duration), MCI_MSF_SECOND(duration), MCI_MSF_FRAME(duration));
401  ok(duration>>24==0, "CD length high bits %08X\n", duration);
402 
403  parm.status.dwItem = MCI_STATUS_POSITION;
404  /* dwTrack is still set */
406  ok(!err, "STATUS position start track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
407  trace("last track position: %02um:%02us:%02uframes\n",
408  MCI_MSF_MINUTE(parm.status.dwReturn), MCI_MSF_SECOND(parm.status.dwReturn), MCI_MSF_FRAME(parm.status.dwReturn));
409 
410  /* Seek to position + length always works, esp.
411  * for the last track it's NOT the position of the lead-out. */
412  parm.seek.dwTo = MSF_Add(parm.status.dwReturn, duration);
413  err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
414  ok(!err, "SEEK to %08X position last + length: %s\n", parm.seek.dwTo, dbg_mcierr(err));
415 
416  parm.seek.dwTo = MSF_Add(parm.seek.dwTo, MCI_MAKE_MSF(0,0,1));
417  err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
418  ok(err == MCIERR_OUTOFRANGE, "SEEK past %08X position last + length: %s\n", parm.seek.dwTo, dbg_mcierr(err));
419 
420  err = mciSendStringA("set c time format tmsf", buf, sizeof(buf), hwnd);
421  ok(!err, "set time format tmsf: %s\n", dbg_mcierr(err));
422 
423  parm.play.dwFrom = track;
424  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD_PTR)&parm);
425  ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
426 
427  if(err) {
428  skip("Cannot manage to play track %u.\n", track);
429  return;
430  }
431 
432  Sleep(1800*factor); /* Time to spin up, hopefully less than track length */
433 
434  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
435  ok(!err, "status mode: %s\n", dbg_mcierr(err));
436  if(!err) ok(!strcmp(buf, "playing"), "status mode during play is %s\n", buf);
437 
438  err = mciSendStringA("pause c", buf, sizeof(buf), hwnd);
439  ok(!err, "pause: %s\n", dbg_mcierr(err));
440 
441  test_notification(hwnd, "pause should abort notification", MCI_NOTIFY_ABORTED);
442 
443  /* Native returns stopped when paused,
444  * yet the Stop command is different as it would disallow Resume. */
445  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
446  ok(!err, "status mode: %s\n", dbg_mcierr(err));
447  if(!err) todo_wine ok(!strcmp(buf, "stopped"), "status mode while paused is %s\n", buf);
448 
449  err = mciSendCommandA(wDeviceID, MCI_RESUME, 0, 0);
450  ok(!err || /* Win9x */ err == MCIERR_UNSUPPORTED_FUNCTION,
451  "RESUME without parms: %s\n", dbg_mcierr(err));
452 
453  Sleep(1300*factor);
454 
455  /* Native continues to play without interruption */
456  err = mciSendCommandA(wDeviceID, MCI_PLAY, 0, 0);
457  todo_wine ok(!err, "PLAY without parms: %s\n", dbg_mcierr(err));
458 
459  parm.play.dwFrom = MCI_MAKE_TMSF(numtracks,0,1,0);
460  parm.play.dwTo = 1;
461  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD_PTR)&parm);
462  ok(err == MCIERR_OUTOFRANGE, "PLAY: %s\n", dbg_mcierr(err));
463 
464  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
465  ok(!err, "status mode: %s\n", dbg_mcierr(err));
466  if(!err) ok(!strcmp(buf, "playing"), "status mode after play is %s\n", buf);
467 
468  err = mciSendCommandA(wDeviceID, MCI_STOP, MCI_NOTIFY, (DWORD_PTR)&parm);
469  ok(!err, "STOP notify: %s\n", dbg_mcierr(err));
471  test_notification(hwnd, "STOP #1", 0);
472 
473  parm.play.dwFrom = track;
474  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD_PTR)&parm);
475  ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
476 
477  Sleep(1600*factor);
478 
479  parm.seek.dwTo = 1; /* not <track>, to test position below */
480  err = mciSendCommandA(wDeviceID, MCI_SEEK, MCI_TO, (DWORD_PTR)&parm);
481  ok(!err, "SEEK to %u notify: %s\n", track, dbg_mcierr(err));
482  /* Note that native's Status position / current track may move the head
483  * and reflect the new position only seconds after issuing the command. */
484 
485  /* Seek stops */
486  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
487  ok(!err, "status mode: %s\n", dbg_mcierr(err));
488  if(!err) ok(!strcmp(buf, "stopped"), "status mode after play is %s\n", buf);
489 
490  test_notification(hwnd, "Seek aborts Play", MCI_NOTIFY_ABORTED);
491  test_notification(hwnd, "Seek", 0);
492 
493  parm.play.dwFrom = track;
494  parm.play.dwTo = MCI_MAKE_TMSF(track,0,0,21); /* 21 frames, subsecond */
495  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)&parm);
496  ok(!err, "PLAY from %u notify: %s\n", track, dbg_mcierr(err));
497 
498  Sleep(2200*factor);
499 
500  err = mciSendStringA("status c mode", buf, sizeof(buf), hwnd);
501  ok(!err, "status mode: %s\n", dbg_mcierr(err));
502  if(!err) ok(!strcmp(buf, "stopped") || broken(!strcmp(buf, "playing")), "status mode after play is %s\n", buf);
503  if(!err && !strcmp(buf, "playing")) trace("status playing after sleep\n");
504 
505  /* Playing to end asynchronously sends no notification! */
506  test_notification(hwnd, "PLAY to end", 0);
507 
508  err = mciSendStringA("status c mode notify", buf, sizeof(buf), hwnd);
509  ok(!err, "status mode: %s\n", dbg_mcierr(err));
510  if(!err) ok(!strcmp(buf, "stopped") || broken(!strcmp(buf, "playing")), "status mode after play is %s\n", buf);
511  if(!err && !strcmp(buf, "playing")) trace("status still playing\n");
512  /* Some systems report playing even after Sleep(3900ms) yet the successful
513  * notification tests (not ABORTED) indicates they are finished. */
514 
515  test_notification(hwnd, "dangling from PLAY", MCI_NOTIFY_SUPERSEDED);
517 
518  err = mciSendStringA("stop c", buf, sizeof(buf), hwnd);
519  ok(!err, "stop: %s\n", dbg_mcierr(err));
520 
521  test_notification(hwnd, "PLAY to end", 0);
522 
523  /* length as MSF despite set time format TMSF */
524  parm.status.dwItem = MCI_STATUS_LENGTH;
525  parm.status.dwTrack = numtracks;
526  parm.status.dwReturn = 0xFEEDABAD;
528  ok(!err, "STATUS length track %u: %s\n", parm.status.dwTrack, dbg_mcierr(err));
529  ok(duration == parm.status.dwReturn, "length MSF<>TMSF %08lX\n", parm.status.dwReturn);
530 
531  /* Play from position start to start+length always works. */
532  /* TODO? also play it using MSF */
533  parm.play.dwFrom = numtracks;
534  parm.play.dwTo = (duration << 8) | numtracks; /* as TMSF */
535  err = mciSendCommandA(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)&parm);
536  ok(!err, "PLAY (TMSF) from %08X to %08X: %s\n", parm.play.dwFrom, parm.play.dwTo, dbg_mcierr(err));
537 
538  Sleep(1400*factor);
539 
540  err = mciSendStringA("status c current track", buf, sizeof(buf), hwnd);
541  ok(!err, "status track: %s\n", dbg_mcierr(err));
542  if(!err) todo_wine ok(numtracks == atoi(buf), "status current track gave %s, expected %u\n", buf, numtracks);
543  /* fails in Wine because SEEK is independent on IOCTL_CDROM_RAW_READ */
544 
545  err = mciSendCommandA(wDeviceID, MCI_STOP, 0, 0);
546  ok(!err, "STOP: %s\n", dbg_mcierr(err));
547  test_notification(hwnd, "STOP aborts", MCI_NOTIFY_ABORTED);
548  test_notification(hwnd, "STOP final", 0);
549 }
550 
552 {
553  MCIDEVICEID wDeviceID;
554  MCI_PARMS_UNION parm;
555  MCIERROR err;
556  char drive[] = {'a',':','\\','X','\0'};
558  /* todo_wine Every open below should yield this same error. */
559  skip("CD-ROM device likely not installed or disabled.\n");
560  return;
561  }
562 
563  /* Bug in native since NT: After OPEN "c" without MCI_OPEN_ALIAS fails with
564  * MCIERR_DEVICE_OPEN, any subsequent OPEN fails with EXTENSION_NOT_FOUND! */
565  parm.open.lpstrAlias = "x"; /* with alias, OPEN "c" behaves normally */
567  parm.open.lpstrElementName = drive;
568  for ( ; strlen(drive); drive[strlen(drive)-1] = 0)
569  for (drive[0] = 'a'; drive[0] <= 'z'; drive[0]++) {
572  ok(!err || err == MCIERR_INVALID_FILE, "OPEN %s type: %s\n", drive, dbg_mcierr(err));
573  /* open X:\ fails in Win9x/NT. Only open X: works everywhere. */
574  if(!err) {
575  wDeviceID = parm.open.wDeviceID;
576  trace("ok with %s\n", drive);
577  err = mciSendCommandA(wDeviceID, MCI_CLOSE, 0, 0);
578  ok(!err,"mciCommand close returned %s\n", dbg_mcierr(err));
579  }
580  }
581  drive[0] = '\\';
583  MCI_OPEN_SHAREABLE, (DWORD_PTR)&parm);
584  ok(err == MCIERR_INVALID_FILE, "OPEN %s type: %s\n", drive, dbg_mcierr(err));
585  if(!err) mciSendCommandA(parm.open.wDeviceID, MCI_CLOSE, 0, 0);
586 
587  if (0) {
588  parm.open.lpstrElementName = (LPCSTR)0xDEADBEEF;
591  todo_wine ok(err == MCIERR_FLAGS_NOT_COMPATIBLE, "OPEN elt_ID: %s\n", dbg_mcierr(err));
592  if(!err) mciSendCommandA(parm.open.wDeviceID, MCI_CLOSE, 0, 0);
593  }
594 }
595 
596 START_TEST(mcicda)
597 {
598  MCIERROR err;
599  HWND hwnd;
600  hwnd = CreateWindowExA(0, "static", "mcicda test", WS_POPUP, 0,0,100,100,
601  0, 0, 0, NULL);
602  test_notification(hwnd, "-prior to tests-", 0);
603  test_play(hwnd);
606  todo_wine ok(!err || broken(err == MCIERR_HARDWARE /* blank CD or testbot without CD-ROM */),
607  "STOP all returned %s\n", dbg_mcierr(err));
608  err = mciSendStringA("close all", NULL, 0, hwnd);
609  ok(!err, "final close all returned %s\n", dbg_mcierr(err));
610  test_notification(hwnd, "-tests complete-", 0);
612 }
const DOCKBAR PVOID HWND HWND * hwnd
Definition: tooldock.h:22
#define trace_(file, line,...)
Definition: kmt_test.h:221
#define MCI_MODE_STOP
Definition: mmsystem.h:695
#define MCIERR_INVALID_FILE
Definition: mmsystem.h:604
#define TRUE
Definition: types.h:120
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:790
#define MCIERR_CANNOT_LOAD_DRIVER
Definition: mmsystem.h:576
#define MCI_STATUS_ITEM
Definition: mmsystem.h:742
#define DWORD_PTR
Definition: treelist.c:76
#define MCI_MSF_FRAME(t)
Definition: mmsystem.h:714
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint factor
Definition: glfuncs.h:178
#define MCI_STATUS_NUMBER_OF_TRACKS
Definition: mmsystem.h:746
TW_UINT32 TW_UINT16 TW_UINT16 MSG
Definition: twain.h:1827
#define MCIERR_UNSUPPORTED_FUNCTION
Definition: mmsystem.h:584
#define MCI_CDA_TRACK_AUDIO
Definition: mmsystem.h:817
#define MCI_STEP
Definition: mmsystem.h:657
MCI_OPEN_PARMSA open
Definition: mcicda.c:29
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
MCI_GETDEVCAPS_PARMS caps
Definition: mci.c:37
#define MCI_PLAY
Definition: mmsystem.h:649
#define MCI_RESUME
Definition: mmsystem.h:675
static void test_notification_dbg(HWND hwnd, const char *command, WPARAM type, int line)
Definition: mcicda.c:50
UINT_PTR WPARAM
Definition: windef.h:207
int winetest_interactive
BOOL WINAPI PeekMessageA(_Out_ LPMSG, _In_opt_ HWND, _In_ UINT, _In_ UINT, _In_ UINT)
#define MCI_STATUS_POSITION
Definition: mmsystem.h:745
BOOL WINAPI DestroyWindow(_In_ HWND)
#define MCI_CDA_TRACK_OTHER
Definition: mmsystem.h:818
#define MCI_SAVE
Definition: mmsystem.h:661
#define MCI_NOTIFY_ABORTED
Definition: mmsystem.h:727
const GLfloat * m
Definition: glext.h:10848
static DWORD MSF_Add(DWORD d1, DWORD d2)
Definition: mcicda.c:73
#define MCI_STOP
Definition: mmsystem.h:651
MCI_PLAY_PARMS play
Definition: mcicda.c:30
#define MCI_OPEN_ELEMENT_ID
Definition: mmsystem.h:737
#define MCIERR_FLAGS_NOT_COMPATIBLE
Definition: mmsystem.h:593
unsigned int BOOL
Definition: ntddk_ex.h:94
#define MCI_RECORD
Definition: mmsystem.h:658
static void test_play(HWND hwnd)
Definition: mcicda.c:90
#define MCI_OPEN_TYPE_ID
Definition: mmsystem.h:738
#define MCI_CDA_STATUS_TYPE_TRACK
Definition: mmsystem.h:816
#define MCI_GETDEVCAPS_DEVICE_TYPE
Definition: mmsystem.h:762
smooth NULL
Definition: ftsmooth.c:416
#define MCI_MSF_MINUTE(t)
Definition: mmsystem.h:712
Definition: parser.c:48
#define MCI_ALL_DEVICE_ID
Definition: mmsystem.h:679
#define MCI_OPEN
Definition: mmsystem.h:646
const char * LPCSTR
Definition: xmlstorage.h:183
#define MCIERR_MISSING_PARAMETER
Definition: mmsystem.h:583
DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet, UINT uRetLen, HWND hwndCallback)
Definition: mci.c:1405
MCI_WAVE_OPEN_PARMSA open
Definition: mci.c:36
#define MCI_STATUS
Definition: mmsystem.h:662
#define MCI_MSF_SECOND(t)
Definition: mmsystem.h:713
GLfloat f
Definition: glext.h:7540
#define trace
Definition: atltest.h:70
#define MCI_STATUS_LENGTH
Definition: mmsystem.h:744
#define MCI_OPEN_TYPE
Definition: mmsystem.h:739
#define MCI_OPEN_SHAREABLE
Definition: mmsystem.h:734
const GLubyte * c
Definition: glext.h:8905
unsigned short WORD
Definition: ntddk_ex.h:93
START_TEST(mcicda)
Definition: mcicda.c:596
unsigned long DWORD
Definition: ntddk_ex.h:95
static BOOL spurious_message(LPMSG msg)
Definition: mcicda.c:38
#define MCI_STATUS_MODE
Definition: mmsystem.h:747
#define MCI_NOTIFY_SUCCESSFUL
Definition: mmsystem.h:725
DWORD MCIERROR
Definition: mmsystem.h:958
DWORD WINAPI MsgWaitForMultipleObjects(_In_ DWORD nCount, _In_reads_opt_(nCount) CONST HANDLE *pHandles, _In_ BOOL fWaitAll, _In_ DWORD dwMilliseconds, _In_ DWORD dwWakeMask)
int seek(void *fd, ulong off, int mode)
Definition: pe.c:51
#define todo_wine
Definition: test.h:163
#define MCI_DEVTYPE_CD_AUDIO
Definition: mmsystem.h:683
GLdouble s
Definition: gl.h:2039
#define MCIERR_OUTOFRANGE
Definition: mmsystem.h:592
#define MCIERR_NO_IDENTITY
Definition: mmsystem.h:643
#define MCI_GETDEVCAPS_ITEM
Definition: mmsystem.h:758
#define err(...)
#define MCI_OPEN_ALIAS
Definition: mmsystem.h:736
uint32_t DWORD_PTR
Definition: typedefs.h:63
#define CDFRAMES_PERSEC
Definition: mcicda.c:72
#define broken(x)
Definition: _sntprintf.h:21
#define MCI_FORMAT_MSF
Definition: mmsystem.h:703
static MCIERROR ok_open
Definition: mcicda.c:86
MCI_SAVE_PARMSA save
Definition: mcicda.c:32
static char drive[2]
Definition: batch.c:28
#define MCI_MODE_NOT_READY
Definition: mmsystem.h:694
#define MCI_NOTIFY_SUPERSEDED
Definition: mmsystem.h:726
static void test_openclose(HWND hwnd)
Definition: mcicda.c:551
#define MCI_MAKE_TMSF(t, m, s, f)
Definition: mmsystem.h:720
DWORD_PTR dwCallback
Definition: mmsystem.h:1517
#define MM_MCINOTIFY
Definition: mmsystem.h:55
#define f
Definition: ke_i.h:83
#define ok(value,...)
Definition: atltest.h:57
#define MCI_OPEN_ELEMENT
Definition: mmsystem.h:735
#define QS_POSTMESSAGE
Definition: winuser.h:888
#define WS_POPUP
Definition: pedump.c:616
#define MCI_MAKE_MSF(m, s, f)
Definition: mmsystem.h:715
#define MCI_CLOSE
Definition: mmsystem.h:647
#define MCI_GETDEVCAPS
Definition: mmsystem.h:654
#define MCI_TRACK
Definition: mmsystem.h:733
UINT MCIDEVICEID
Definition: mmsystem.h:959
MCI_GENERIC_PARMS gen
Definition: mci.c:40
#define skip(...)
Definition: atltest.h:64
int command(const char *fmt,...)
Definition: ftp.c:266
#define msg(x)
Definition: auth_time.c:54
_Check_return_ int __cdecl atoi(_In_z_ const char *_Str)
const char * dbg_mcierr(MCIERROR err)
Definition: mci.c:43
#define MCIERR_UNRECOGNIZED_COMMAND
Definition: mmsystem.h:571
#define MCIERR_HARDWARE
Definition: mmsystem.h:572
HWND WINAPI CreateWindowExA(_In_ DWORD dwExStyle, _In_opt_ LPCSTR lpClassName, _In_opt_ LPCSTR lpWindowName, _In_ DWORD dwStyle, _In_ int X, _In_ int Y, _In_ int nWidth, _In_ int nHeight, _In_opt_ HWND hWndParent, _In_opt_ HMENU hMenu, _In_opt_ HINSTANCE hInstance, _In_opt_ LPVOID lpParam)
#define MCI_MODE_OPEN
Definition: mmsystem.h:700
#define c
Definition: ke_i.h:80
#define MCI_FROM
Definition: mmsystem.h:731
#define test_notification(hwnd, command, type)
Definition: mcicda.c:49
DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
Definition: mci.c:2122
int strcmp(const char *String1, const char *String2)
Definition: utclib.c:469
#define MCIERR_MUST_USE_SHAREABLE
Definition: mmsystem.h:599
#define PM_REMOVE
Definition: winuser.h:1182
#define MCI_STATUS_TIME_FORMAT
Definition: mmsystem.h:749
#define MCI_NOTIFY
Definition: mmsystem.h:729
#define MCI_STATUS_MEDIA_PRESENT
Definition: mmsystem.h:748
#define memset(x, y, z)
Definition: compat.h:39
static SERVICE_STATUS status
Definition: service.c:31
#define MCI_SEEK
Definition: mmsystem.h:650
#define MCI_TO
Definition: mmsystem.h:732
#define ok_(x1, x2)
Definition: atltest.h:61