ReactOS 0.4.15-dev-7918-g2a2556c
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
26typedef union {
35
36extern 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__)
50static 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
73static DWORD MSF_Add(DWORD d1, DWORD d2)
74{
75 WORD c, m, s, f;
76 f = MCI_MSF_FRAME(d1) + MCI_MSF_FRAME(d2);
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
86static MCIERROR ok_open = 0; /* MCIERR_CANNOT_LOAD_DRIVER */
87
88/* TODO show that shareable flag is not what Wine implements. */
89
90static 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
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
157 parm.status.dwReturn = 0xFEEDABAD;
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
168 parm.status.dwReturn = 0xFEEDABAD;
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
180 ok(!err, "STATUS mode: %s\n", dbg_mcierr(err));
181 switch(parm.status.dwReturn) {
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
218 parm.status.dwReturn = 0;
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. */
323 parm.status.dwTrack = 1;
324 parm.status.dwReturn = 0xFEEDABAD;
326 ok(!err, "STATUS type track 1: %s\n", dbg_mcierr(err));
328 "unknown track type %lx\n", parm.status.dwReturn);
329
330 if (parm.status.dwReturn == MCI_CDA_TRACK_OTHER) {
331 /* Find an audio 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;
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
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
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",
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 */
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 */
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 */
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));
548 test_notification(hwnd, "STOP final", 0);
549}
550
552{
553 MCIDEVICEID wDeviceID;
554 MCI_PARMS_UNION parm;
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 */
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] = '\\';
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
597{
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);
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}
#define broken(x)
Definition: _sntprintf.h:21
int strcmp(const char *String1, const char *String2)
Definition: utclib.c:469
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
#define trace
Definition: atltest.h:70
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
#define ok_(x1, x2)
Definition: atltest.h:61
#define msg(x)
Definition: auth_time.c:54
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define CDFRAMES_PERSEC
Definition: mcicda.c:44
DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
Definition: mci.c:2289
DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet, UINT uRetLen, HWND hwndCallback)
Definition: mci.c:1586
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
unsigned short WORD
Definition: ntddk_ex.h:93
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLdouble s
Definition: gl.h:2039
const GLubyte * c
Definition: glext.h:8905
GLfloat f
Definition: glext.h:7540
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
const GLfloat * m
Definition: glext.h:10848
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
_Check_return_ int __cdecl atoi(_In_z_ const char *_Str)
#define f
Definition: ke_i.h:83
#define c
Definition: ke_i.h:80
#define trace_(file, line,...)
Definition: kmt_test.h:223
#define MCI_SAVE
Definition: mmsystem.h:661
#define MCIERR_FLAGS_NOT_COMPATIBLE
Definition: mmsystem.h:593
#define MCI_OPEN_SHAREABLE
Definition: mmsystem.h:734
#define MCI_NOTIFY_SUPERSEDED
Definition: mmsystem.h:726
#define MCI_NOTIFY
Definition: mmsystem.h:729
#define MCI_TO
Definition: mmsystem.h:732
#define MCI_CDA_TRACK_OTHER
Definition: mmsystem.h:818
#define MCI_STATUS_POSITION
Definition: mmsystem.h:745
#define MCI_MAKE_TMSF(t, m, s, f)
Definition: mmsystem.h:720
#define MCI_GETDEVCAPS
Definition: mmsystem.h:654
#define MCI_OPEN_ELEMENT_ID
Definition: mmsystem.h:737
#define MCI_RESUME
Definition: mmsystem.h:675
#define MCI_OPEN_ELEMENT
Definition: mmsystem.h:735
#define MCIERR_HARDWARE
Definition: mmsystem.h:572
#define MCI_MAKE_MSF(m, s, f)
Definition: mmsystem.h:715
#define MCIERR_CANNOT_LOAD_DRIVER
Definition: mmsystem.h:576
#define MCI_GETDEVCAPS_DEVICE_TYPE
Definition: mmsystem.h:762
#define MCI_TRACK
Definition: mmsystem.h:733
#define MCI_STATUS
Definition: mmsystem.h:662
#define MCI_MSF_MINUTE(t)
Definition: mmsystem.h:712
#define MCI_OPEN_TYPE_ID
Definition: mmsystem.h:738
#define MCIERR_NO_IDENTITY
Definition: mmsystem.h:643
#define MCIERR_UNRECOGNIZED_COMMAND
Definition: mmsystem.h:571
#define MCI_ALL_DEVICE_ID
Definition: mmsystem.h:679
#define MCI_MODE_OPEN
Definition: mmsystem.h:700
#define MCI_CDA_TRACK_AUDIO
Definition: mmsystem.h:817
DWORD MCIERROR
Definition: mmsystem.h:958
#define MCI_STOP
Definition: mmsystem.h:651
#define MCI_CLOSE
Definition: mmsystem.h:647
#define MCI_SEEK
Definition: mmsystem.h:650
#define MCIERR_INVALID_FILE
Definition: mmsystem.h:604
#define MCIERR_MISSING_PARAMETER
Definition: mmsystem.h:583
#define MCI_NOTIFY_ABORTED
Definition: mmsystem.h:727
#define MCIERR_OUTOFRANGE
Definition: mmsystem.h:592
#define MCI_OPEN
Definition: mmsystem.h:646
#define MCI_STATUS_NUMBER_OF_TRACKS
Definition: mmsystem.h:746
UINT MCIDEVICEID
Definition: mmsystem.h:959
#define MCI_GETDEVCAPS_ITEM
Definition: mmsystem.h:758
#define MCI_STATUS_LENGTH
Definition: mmsystem.h:744
#define MCI_STATUS_MODE
Definition: mmsystem.h:747
#define MCI_MSF_FRAME(t)
Definition: mmsystem.h:714
#define MCI_MODE_STOP
Definition: mmsystem.h:695
#define MCIERR_MUST_USE_SHAREABLE
Definition: mmsystem.h:599
#define MCI_NOTIFY_SUCCESSFUL
Definition: mmsystem.h:725
#define MCI_FORMAT_MSF
Definition: mmsystem.h:703
#define MCI_MSF_SECOND(t)
Definition: mmsystem.h:713
#define MCI_FROM
Definition: mmsystem.h:731
#define MCI_CDA_STATUS_TYPE_TRACK
Definition: mmsystem.h:816
#define MCI_MODE_NOT_READY
Definition: mmsystem.h:694
#define MM_MCINOTIFY
Definition: mmsystem.h:55
#define MCI_STATUS_MEDIA_PRESENT
Definition: mmsystem.h:748
#define MCI_OPEN_TYPE
Definition: mmsystem.h:739
#define MCIERR_UNSUPPORTED_FUNCTION
Definition: mmsystem.h:584
#define MCI_PLAY
Definition: mmsystem.h:649
#define MCI_STATUS_TIME_FORMAT
Definition: mmsystem.h:749
#define MCI_DEVTYPE_CD_AUDIO
Definition: mmsystem.h:683
#define MCI_STEP
Definition: mmsystem.h:657
#define MCI_RECORD
Definition: mmsystem.h:658
#define MCI_STATUS_ITEM
Definition: mmsystem.h:742
#define MCI_OPEN_ALIAS
Definition: mmsystem.h:736
static void test_play(void)
Definition: animate.c:129
#define todo_wine
Definition: custom.c:79
const char * dbg_mcierr(MCIERROR err)
Definition: mci.c:43
static DWORD MSF_Add(DWORD d1, DWORD d2)
Definition: mcicda.c:73
static BOOL spurious_message(LPMSG msg)
Definition: mcicda.c:38
static void test_notification_dbg(HWND hwnd, const char *command, WPARAM type, int line)
Definition: mcicda.c:50
static MCIERROR ok_open
Definition: mcicda.c:86
#define test_notification(hwnd, command, type)
Definition: mcicda.c:49
static void test_openclose(HWND hwnd)
Definition: mcicda.c:551
#define WS_POPUP
Definition: pedump.c:616
#define err(...)
int winetest_interactive
int seek(void *fd, ulong off, int mode)
Definition: pe.c:51
#define memset(x, y, z)
Definition: compat.h:39
Definition: parser.c:49
Definition: ps.c:97
DWORD_PTR dwCallback
Definition: mmsystem.h:1517
DWORD_PTR dwReturn
Definition: mmsystem.h:1567
MCIDEVICEID wDeviceID
Definition: mmsystem.h:1668
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:790
#define DWORD_PTR
Definition: treelist.c:76
TW_UINT32 TW_UINT16 TW_UINT16 MSG
Definition: twain.h:1829
uint32_t DWORD_PTR
Definition: typedefs.h:65
MCI_SEEK_PARMS seek
Definition: mci.c:39
MCI_PLAY_PARMS play
Definition: mcicda.c:30
MCI_GETDEVCAPS_PARMS caps
Definition: mci.c:37
MCI_OPEN_PARMSA open
Definition: mcicda.c:29
MCI_SAVE_PARMSA save
Definition: mcicda.c:32
MCI_GENERIC_PARMS gen
Definition: mci.c:40
MCI_WAVE_OPEN_PARMSA open
Definition: mci.c:36
MCI_STATUS_PARMS status
Definition: mci.c:34
_In_ LONG _In_ HWND hwnd
Definition: winddi.h:4023
UINT_PTR WPARAM
Definition: windef.h:207
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)
DWORD WINAPI MsgWaitForMultipleObjects(_In_ DWORD nCount, _In_reads_opt_(nCount) CONST HANDLE *pHandles, _In_ BOOL fWaitAll, _In_ DWORD dwMilliseconds, _In_ DWORD dwWakeMask)
#define PM_REMOVE
Definition: winuser.h:1196
#define QS_POSTMESSAGE
Definition: winuser.h:877
BOOL WINAPI PeekMessageA(_Out_ LPMSG, _In_opt_ HWND, _In_ UINT, _In_ UINT, _In_ UINT)
BOOL WINAPI DestroyWindow(_In_ HWND)
const char * LPCSTR
Definition: xmlstorage.h:183