msvcp90: Fixed _Getcat return type.
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Most thread locking is complete. There may be a few race
22  * conditions still lurking.
23  *
24  * TODO:
25  *      Implement SetCooperativeLevel properly (need to address focus issues)
26  *      Implement DirectSound3DBuffers (stubs in place)
27  *      Use hardware 3D support if available
28  *      Add critical section locking inside Release and AddRef methods
29  *      Handle static buffers - put those in hardware, non-static not in hardware
30  *      Hardware DuplicateSoundBuffer
31  *      Proper volume calculation for 3d buffers
32  *      Remove DS_HEL_FRAGS and use mixer fragment length for it
33  */
34
35 #include <stdarg.h>
36
37 #define COBJMACROS
38 #define NONAMELESSSTRUCT
39 #define NONAMELESSUNION
40 #include "windef.h"
41 #include "winbase.h"
42 #include "winuser.h"
43 #include "winnls.h"
44 #include "winreg.h"
45 #include "mmsystem.h"
46 #include "winternl.h"
47 #include "mmddk.h"
48 #include "wine/debug.h"
49 #include "dsound.h"
50 #include "dsconf.h"
51 #include "ks.h"
52 #include "rpcproxy.h"
53 #include "rpc.h"
54 #include "rpcndr.h"
55 #include "unknwn.h"
56 #include "oleidl.h"
57 #include "shobjidl.h"
58
59 #include "initguid.h"
60 #include "ksmedia.h"
61 #include "propkey.h"
62 #include "devpkey.h"
63
64 #include "dsound_private.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
67
68 struct list DSOUND_renderers = LIST_INIT(DSOUND_renderers);
69 CRITICAL_SECTION DSOUND_renderers_lock;
70 static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug =
71 {
72     0, 0, &DSOUND_renderers_lock,
73     { &DSOUND_renderers_lock_debug.ProcessLocksList, &DSOUND_renderers_lock_debug.ProcessLocksList },
74       0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_renderers_lock") }
75 };
76 CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 };
77
78 struct list DSOUND_capturers = LIST_INIT(DSOUND_capturers);
79 CRITICAL_SECTION DSOUND_capturers_lock;
80 static CRITICAL_SECTION_DEBUG DSOUND_capturers_lock_debug =
81 {
82     0, 0, &DSOUND_capturers_lock,
83     { &DSOUND_capturers_lock_debug.ProcessLocksList, &DSOUND_capturers_lock_debug.ProcessLocksList },
84       0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_capturers_lock") }
85 };
86 CRITICAL_SECTION DSOUND_capturers_lock = { &DSOUND_capturers_lock_debug, -1, 0, 0, 0, 0 };
87
88 GUID                    DSOUND_renderer_guids[MAXWAVEDRIVERS];
89 GUID                    DSOUND_capture_guids[MAXWAVEDRIVERS];
90
91 WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };
92
93 /* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
94 int ds_hel_buflen = 32768 * 2;
95 int ds_snd_queue_max = 10;
96 int ds_default_sample_rate = 44100;
97 int ds_default_bits_per_sample = 16;
98 static HINSTANCE instance;
99
100 /*
101  * Get a config key from either the app-specific or the default config
102  */
103
104 static inline DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
105                                     char *buffer, DWORD size )
106 {
107     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
108     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
109     return ERROR_FILE_NOT_FOUND;
110 }
111
112
113 /*
114  * Setup the dsound options.
115  */
116
117 void setup_dsound_options(void)
118 {
119     char buffer[MAX_PATH+16];
120     HKEY hkey, appkey = 0;
121     DWORD len;
122
123     buffer[MAX_PATH]='\0';
124
125     /* @@ Wine registry key: HKCU\Software\Wine\DirectSound */
126     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectSound", &hkey )) hkey = 0;
127
128     len = GetModuleFileNameA( 0, buffer, MAX_PATH );
129     if (len && len < MAX_PATH)
130     {
131         HKEY tmpkey;
132         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectSound */
133         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
134         {
135             char *p, *appname = buffer;
136             if ((p = strrchr( appname, '/' ))) appname = p + 1;
137             if ((p = strrchr( appname, '\\' ))) appname = p + 1;
138             strcat( appname, "\\DirectSound" );
139             TRACE("appname = [%s]\n", appname);
140             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
141             RegCloseKey( tmpkey );
142         }
143     }
144
145     /* get options */
146
147     if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH ))
148         ds_hel_buflen = atoi(buffer);
149
150     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
151         ds_snd_queue_max = atoi(buffer);
152
153
154     if (!get_config_key( hkey, appkey, "DefaultSampleRate", buffer, MAX_PATH ))
155         ds_default_sample_rate = atoi(buffer);
156
157     if (!get_config_key( hkey, appkey, "DefaultBitsPerSample", buffer, MAX_PATH ))
158         ds_default_bits_per_sample = atoi(buffer);
159
160     if (appkey) RegCloseKey( appkey );
161     if (hkey) RegCloseKey( hkey );
162
163     TRACE("ds_hel_buflen = %d\n", ds_hel_buflen);
164     TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
165     TRACE("ds_default_sample_rate = %d\n", ds_default_sample_rate);
166     TRACE("ds_default_bits_per_sample = %d\n", ds_default_bits_per_sample);
167 }
168
169 static const char * get_device_id(LPCGUID pGuid)
170 {
171     if (IsEqualGUID(&DSDEVID_DefaultPlayback, pGuid))
172         return "DSDEVID_DefaultPlayback";
173     else if (IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuid))
174         return "DSDEVID_DefaultVoicePlayback";
175     else if (IsEqualGUID(&DSDEVID_DefaultCapture, pGuid))
176         return "DSDEVID_DefaultCapture";
177     else if (IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuid))
178         return "DSDEVID_DefaultVoiceCapture";
179     return debugstr_guid(pGuid);
180 }
181
182 static HRESULT get_mmdevenum(IMMDeviceEnumerator **devenum)
183 {
184     HRESULT hr, init_hr;
185
186     init_hr = CoInitialize(NULL);
187
188     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
189             CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)devenum);
190     if(FAILED(hr)){
191         CoUninitialize();
192         *devenum = NULL;
193         ERR("CoCreateInstance failed: %08x\n", hr);
194         return hr;
195     }
196
197     return init_hr;
198 }
199
200 static void release_mmdevenum(IMMDeviceEnumerator *devenum, HRESULT init_hr)
201 {
202     IMMDeviceEnumerator_Release(devenum);
203     if(SUCCEEDED(init_hr))
204         CoUninitialize();
205 }
206
207 static HRESULT get_mmdevice_guid(IMMDevice *device, IPropertyStore *ps,
208         GUID *guid)
209 {
210     PROPVARIANT pv;
211     HRESULT hr;
212
213     if(!ps){
214         hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
215         if(FAILED(hr)){
216             WARN("OpenPropertyStore failed: %08x\n", hr);
217             return hr;
218         }
219     }else
220         IPropertyStore_AddRef(ps);
221
222     PropVariantInit(&pv);
223
224     hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv);
225     if(FAILED(hr)){
226         IPropertyStore_Release(ps);
227         WARN("GetValue(GUID) failed: %08x\n", hr);
228         return hr;
229     }
230
231     CLSIDFromString(pv.u.pwszVal, guid);
232
233     PropVariantClear(&pv);
234     IPropertyStore_Release(ps);
235
236     return S_OK;
237 }
238
239 /***************************************************************************
240  * GetDeviceID  [DSOUND.9]
241  *
242  * Retrieves unique identifier of default device specified
243  *
244  * PARAMS
245  *    pGuidSrc  [I] Address of device GUID.
246  *    pGuidDest [O] Address to receive unique device GUID.
247  *
248  * RETURNS
249  *    Success: DS_OK
250  *    Failure: DSERR_INVALIDPARAM
251  *
252  * NOTES
253  *    pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
254  *    DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or
255  *    DSDEVID_DefaultVoiceCapture.
256  *    Returns pGuidSrc if pGuidSrc is a valid device or the device
257  *    GUID for the specified constants.
258  */
259 HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
260 {
261     IMMDeviceEnumerator *devenum;
262     EDataFlow flow = (EDataFlow)-1;
263     ERole role = (ERole)-1;
264     HRESULT hr, init_hr;
265
266     TRACE("(%s,%p)\n", get_device_id(pGuidSrc),pGuidDest);
267
268     if(!pGuidSrc || !pGuidDest)
269         return DSERR_INVALIDPARAM;
270
271     init_hr = get_mmdevenum(&devenum);
272     if(!devenum)
273         return init_hr;
274
275     if(IsEqualGUID(&DSDEVID_DefaultPlayback, pGuidSrc)){
276         role = eMultimedia;
277         flow = eRender;
278     }else if(IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuidSrc)){
279         role = eCommunications;
280         flow = eRender;
281     }else if(IsEqualGUID(&DSDEVID_DefaultCapture, pGuidSrc)){
282         role = eMultimedia;
283         flow = eCapture;
284     }else if(IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuidSrc)){
285         role = eCommunications;
286         flow = eCapture;
287     }
288
289     if(role != (ERole)-1 && flow != (EDataFlow)-1){
290         IMMDevice *device;
291
292         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
293                 flow, role, &device);
294         if(FAILED(hr)){
295             WARN("GetDefaultAudioEndpoint failed: %08x\n", hr);
296             release_mmdevenum(devenum, init_hr);
297             return DSERR_NODRIVER;
298         }
299
300         hr = get_mmdevice_guid(device, NULL, pGuidDest);
301         IMMDevice_Release(device);
302
303         release_mmdevenum(devenum, init_hr);
304
305         return (hr == S_OK) ? DS_OK : hr;
306     }
307
308     release_mmdevenum(devenum, init_hr);
309
310     *pGuidDest = *pGuidSrc;
311
312     return DS_OK;
313 }
314
315 struct morecontext
316 {
317     LPDSENUMCALLBACKA callA;
318     LPVOID data;
319 };
320
321 static BOOL CALLBACK a_to_w_callback(LPGUID guid, LPCWSTR descW, LPCWSTR modW, LPVOID data)
322 {
323     struct morecontext *context = data;
324     char descA[MAXPNAMELEN], modA[MAXPNAMELEN];
325
326     WideCharToMultiByte(CP_ACP, 0, descW, -1, descA, sizeof(descA), NULL, NULL);
327     WideCharToMultiByte(CP_ACP, 0, modW, -1, modA, sizeof(modA), NULL, NULL);
328
329     return context->callA(guid, descA, modA, context->data);
330 }
331
332 /***************************************************************************
333  * DirectSoundEnumerateA [DSOUND.2]
334  *
335  * Enumerate all DirectSound drivers installed in the system
336  *
337  * PARAMS
338  *    lpDSEnumCallback  [I] Address of callback function.
339  *    lpContext         [I] Address of user defined context passed to callback function.
340  *
341  * RETURNS
342  *    Success: DS_OK
343  *    Failure: DSERR_INVALIDPARAM
344  */
345 HRESULT WINAPI DirectSoundEnumerateA(
346     LPDSENUMCALLBACKA lpDSEnumCallback,
347     LPVOID lpContext)
348 {
349     struct morecontext context;
350
351     if (lpDSEnumCallback == NULL) {
352         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
353         return DSERR_INVALIDPARAM;
354     }
355
356     context.callA = lpDSEnumCallback;
357     context.data = lpContext;
358
359     return DirectSoundEnumerateW(a_to_w_callback, &context);
360 }
361
362 HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device)
363 {
364     IMMDeviceEnumerator *devenum;
365     IMMDeviceCollection *coll;
366     UINT count, i;
367     HRESULT hr, init_hr;
368
369     init_hr = get_mmdevenum(&devenum);
370     if(!devenum)
371         return init_hr;
372
373     hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
374             DEVICE_STATE_ACTIVE, &coll);
375     if(FAILED(hr)){
376         WARN("EnumAudioEndpoints failed: %08x\n", hr);
377         release_mmdevenum(devenum, init_hr);
378         return hr;
379     }
380
381     hr = IMMDeviceCollection_GetCount(coll, &count);
382     if(FAILED(hr)){
383         IMMDeviceCollection_Release(coll);
384         release_mmdevenum(devenum, init_hr);
385         WARN("GetCount failed: %08x\n", hr);
386         return hr;
387     }
388
389     for(i = 0; i < count; ++i){
390         GUID guid;
391
392         hr = IMMDeviceCollection_Item(coll, i, device);
393         if(FAILED(hr))
394             continue;
395
396         hr = get_mmdevice_guid(*device, NULL, &guid);
397         if(FAILED(hr)){
398             IMMDevice_Release(*device);
399             continue;
400         }
401
402         if(IsEqualGUID(&guid, tgt)){
403             IMMDeviceCollection_Release(coll);
404             release_mmdevenum(devenum, init_hr);
405             return DS_OK;
406         }
407
408         IMMDevice_Release(*device);
409     }
410
411     WARN("No device with GUID %s found!\n", wine_dbgstr_guid(tgt));
412
413     IMMDeviceCollection_Release(coll);
414     release_mmdevenum(devenum, init_hr);
415
416     return DSERR_INVALIDPARAM;
417 }
418
419 static BOOL send_device(IMMDevice *device, GUID *guid,
420         LPDSENUMCALLBACKW cb, void *user)
421 {
422     IPropertyStore *ps;
423     PROPVARIANT pv;
424     BOOL keep_going;
425     HRESULT hr;
426
427     PropVariantInit(&pv);
428
429     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
430     if(FAILED(hr)){
431         WARN("OpenPropertyStore failed: %08x\n", hr);
432         return TRUE;
433     }
434
435     hr = get_mmdevice_guid(device, ps, guid);
436     if(FAILED(hr)){
437         IPropertyStore_Release(ps);
438         return TRUE;
439     }
440
441     hr = IPropertyStore_GetValue(ps,
442             (const PROPERTYKEY *)&DEVPKEY_Device_FriendlyName, &pv);
443     if(FAILED(hr)){
444         IPropertyStore_Release(ps);
445         WARN("GetValue(FriendlyName) failed: %08x\n", hr);
446         return TRUE;
447     }
448
449     TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(guid),
450             wine_dbgstr_w(pv.u.pwszVal));
451
452     keep_going = cb(guid, pv.u.pwszVal, wine_vxd_drv, user);
453
454     PropVariantClear(&pv);
455     IPropertyStore_Release(ps);
456
457     return keep_going;
458 }
459
460 /* S_FALSE means the callback returned FALSE at some point
461  * S_OK means the callback always returned TRUE */
462 HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids,
463         LPDSENUMCALLBACKW cb, void *user)
464 {
465     IMMDeviceEnumerator *devenum;
466     IMMDeviceCollection *coll;
467     IMMDevice *defdev = NULL;
468     UINT count, i, n;
469     BOOL keep_going;
470     HRESULT hr, init_hr;
471
472     static const WCHAR primary_desc[] = {'P','r','i','m','a','r','y',' ',
473         'S','o','u','n','d',' ','D','r','i','v','e','r',0};
474     static const WCHAR empty_drv[] = {0};
475
476     init_hr = get_mmdevenum(&devenum);
477     if(!devenum)
478         return init_hr;
479
480     hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
481             DEVICE_STATE_ACTIVE, &coll);
482     if(FAILED(hr)){
483         release_mmdevenum(devenum, init_hr);
484         WARN("EnumAudioEndpoints failed: %08x\n", hr);
485         return DS_OK;
486     }
487
488     hr = IMMDeviceCollection_GetCount(coll, &count);
489     if(FAILED(hr)){
490         IMMDeviceCollection_Release(coll);
491         release_mmdevenum(devenum, init_hr);
492         WARN("GetCount failed: %08x\n", hr);
493         return DS_OK;
494     }
495
496     if(count == 0){
497         release_mmdevenum(devenum, init_hr);
498         return DS_OK;
499     }
500
501     TRACE("Calling back with NULL (%s)\n", wine_dbgstr_w(primary_desc));
502     keep_going = cb(NULL, primary_desc, empty_drv, user);
503
504     /* always send the default device first */
505     if(keep_going){
506         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flow,
507                 eMultimedia, &defdev);
508         if(FAILED(hr)){
509             defdev = NULL;
510             n = 0;
511         }else{
512             keep_going = send_device(defdev, &guids[0], cb, user);
513             n = 1;
514         }
515     }
516
517     for(i = 0; keep_going && i < count; ++i){
518         IMMDevice *device;
519
520         hr = IMMDeviceCollection_Item(coll, i, &device);
521         if(FAILED(hr)){
522             WARN("Item failed: %08x\n", hr);
523             continue;
524         }
525
526         if(device != defdev){
527             send_device(device, &guids[n], cb, user);
528             ++n;
529         }
530
531         IMMDevice_Release(device);
532     }
533
534     if(defdev)
535         IMMDevice_Release(defdev);
536     IMMDeviceCollection_Release(coll);
537
538     release_mmdevenum(devenum, init_hr);
539
540     return (keep_going == TRUE) ? S_OK : S_FALSE;
541 }
542
543 /***************************************************************************
544  * DirectSoundEnumerateW [DSOUND.3]
545  *
546  * Enumerate all DirectSound drivers installed in the system
547  *
548  * PARAMS
549  *    lpDSEnumCallback  [I] Address of callback function.
550  *    lpContext         [I] Address of user defined context passed to callback function.
551  *
552  * RETURNS
553  *    Success: DS_OK
554  *    Failure: DSERR_INVALIDPARAM
555  */
556 HRESULT WINAPI DirectSoundEnumerateW(
557         LPDSENUMCALLBACKW lpDSEnumCallback,
558         LPVOID lpContext )
559 {
560     HRESULT hr;
561
562     TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext);
563
564     if (lpDSEnumCallback == NULL) {
565         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
566         return DSERR_INVALIDPARAM;
567     }
568
569     setup_dsound_options();
570
571     hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids,
572             lpDSEnumCallback, lpContext);
573     return SUCCEEDED(hr) ? DS_OK : hr;
574 }
575
576 /***************************************************************************
577  * DirectSoundCaptureEnumerateA [DSOUND.7]
578  *
579  * Enumerate all DirectSound drivers installed in the system.
580  *
581  * PARAMS
582  *    lpDSEnumCallback  [I] Address of callback function.
583  *    lpContext         [I] Address of user defined context passed to callback function.
584  *
585  * RETURNS
586  *    Success: DS_OK
587  *    Failure: DSERR_INVALIDPARAM
588  */
589 HRESULT WINAPI DirectSoundCaptureEnumerateA(
590     LPDSENUMCALLBACKA lpDSEnumCallback,
591     LPVOID lpContext)
592 {
593     struct morecontext context;
594
595     if (lpDSEnumCallback == NULL) {
596         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
597         return DSERR_INVALIDPARAM;
598     }
599
600     context.callA = lpDSEnumCallback;
601     context.data = lpContext;
602
603     return DirectSoundCaptureEnumerateW(a_to_w_callback, &context);
604 }
605
606 /***************************************************************************
607  * DirectSoundCaptureEnumerateW [DSOUND.8]
608  *
609  * Enumerate all DirectSound drivers installed in the system.
610  *
611  * PARAMS
612  *    lpDSEnumCallback  [I] Address of callback function.
613  *    lpContext         [I] Address of user defined context passed to callback function.
614  *
615  * RETURNS
616  *    Success: DS_OK
617  *    Failure: DSERR_INVALIDPARAM
618  */
619 HRESULT WINAPI
620 DirectSoundCaptureEnumerateW(
621     LPDSENUMCALLBACKW lpDSEnumCallback,
622     LPVOID lpContext)
623 {
624     HRESULT hr;
625
626     TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
627
628     if (lpDSEnumCallback == NULL) {
629         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
630         return DSERR_INVALIDPARAM;
631     }
632
633     setup_dsound_options();
634
635     hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids,
636             lpDSEnumCallback, lpContext);
637     return SUCCEEDED(hr) ? DS_OK : hr;
638 }
639
640 /*******************************************************************************
641  * DirectSound ClassFactory
642  */
643
644 typedef  HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
645  
646 typedef struct {
647     IClassFactory IClassFactory_iface;
648     REFCLSID rclsid;
649     FnCreateInstance pfnCreateInstance;
650 } IClassFactoryImpl;
651
652 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
653 {
654     return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
655 }
656
657 static HRESULT WINAPI
658 DSCF_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppobj)
659 {
660     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
661     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
662     if (ppobj == NULL)
663         return E_POINTER;
664     if (IsEqualIID(riid, &IID_IUnknown) ||
665         IsEqualIID(riid, &IID_IClassFactory))
666     {
667         *ppobj = iface;
668         IClassFactory_AddRef(iface);
669         return S_OK;
670     }
671     *ppobj = NULL;
672     return E_NOINTERFACE;
673 }
674
675 static ULONG WINAPI DSCF_AddRef(LPCLASSFACTORY iface)
676 {
677     return 2;
678 }
679
680 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface)
681 {
682     /* static class, won't be freed */
683     return 1;
684 }
685
686 static HRESULT WINAPI DSCF_CreateInstance(
687     LPCLASSFACTORY iface,
688     LPUNKNOWN pOuter,
689     REFIID riid,
690     LPVOID *ppobj)
691 {
692     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
693     TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj);
694
695     if (pOuter)
696         return CLASS_E_NOAGGREGATION;
697
698     if (ppobj == NULL) {
699         WARN("invalid parameter\n");
700         return DSERR_INVALIDPARAM;
701     }
702     *ppobj = NULL;
703     return This->pfnCreateInstance(riid, ppobj);
704 }
705  
706 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
707 {
708     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
709     FIXME("(%p, %d) stub!\n", This, dolock);
710     return S_OK;
711 }
712
713 static const IClassFactoryVtbl DSCF_Vtbl = {
714     DSCF_QueryInterface,
715     DSCF_AddRef,
716     DSCF_Release,
717     DSCF_CreateInstance,
718     DSCF_LockServer
719 };
720
721 static IClassFactoryImpl DSOUND_CF[] = {
722     { { &DSCF_Vtbl }, &CLSID_DirectSound, DSOUND_Create },
723     { { &DSCF_Vtbl }, &CLSID_DirectSound8, DSOUND_Create8 },
724     { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture, DSOUND_CaptureCreate },
725     { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture8, DSOUND_CaptureCreate8 },
726     { { &DSCF_Vtbl }, &CLSID_DirectSoundFullDuplex, DSOUND_FullDuplexCreate },
727     { { &DSCF_Vtbl }, &CLSID_DirectSoundPrivate, IKsPrivatePropertySetImpl_Create },
728     { { NULL }, NULL, NULL }
729 };
730
731 /*******************************************************************************
732  * DllGetClassObject [DSOUND.@]
733  * Retrieves class object from a DLL object
734  *
735  * NOTES
736  *    Docs say returns STDAPI
737  *
738  * PARAMS
739  *    rclsid [I] CLSID for the class object
740  *    riid   [I] Reference to identifier of interface for class object
741  *    ppv    [O] Address of variable to receive interface pointer for riid
742  *
743  * RETURNS
744  *    Success: S_OK
745  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
746  *             E_UNEXPECTED
747  */
748 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
749 {
750     int i = 0;
751     TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
752
753     if (ppv == NULL) {
754         WARN("invalid parameter\n");
755         return E_INVALIDARG;
756     }
757
758     *ppv = NULL;
759
760     if (!IsEqualIID(riid, &IID_IClassFactory) &&
761         !IsEqualIID(riid, &IID_IUnknown)) {
762         WARN("no interface for %s\n", debugstr_guid(riid));
763         return E_NOINTERFACE;
764     }
765
766     while (NULL != DSOUND_CF[i].rclsid) {
767         if (IsEqualGUID(rclsid, DSOUND_CF[i].rclsid)) {
768             DSCF_AddRef(&DSOUND_CF[i].IClassFactory_iface);
769             *ppv = &DSOUND_CF[i];
770             return S_OK;
771         }
772         i++;
773     }
774
775     WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid),
776          debugstr_guid(riid), ppv);
777     return CLASS_E_CLASSNOTAVAILABLE;
778 }
779
780
781 /*******************************************************************************
782  * DllCanUnloadNow [DSOUND.4]
783  * Determines whether the DLL is in use.
784  *
785  * RETURNS
786  *    Can unload now: S_OK
787  *    Cannot unload now (the DLL is still active): S_FALSE
788  */
789 HRESULT WINAPI DllCanUnloadNow(void)
790 {
791     return S_FALSE;
792 }
793
794 #define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)      \
795         guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2;               \
796         guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3;     \
797         guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6;     \
798         guid.Data4[6] = b7; guid.Data4[7] = b8;
799
800 /***********************************************************************
801  *           DllMain (DSOUND.init)
802  */
803 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
804 {
805     TRACE("(%p %d %p)\n", hInstDLL, fdwReason, lpvReserved);
806
807     switch (fdwReason) {
808     case DLL_PROCESS_ATTACH:
809         TRACE("DLL_PROCESS_ATTACH\n");
810         instance = hInstDLL;
811         DisableThreadLibraryCalls(hInstDLL);
812         /* Increase refcount on dsound by 1 */
813         GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL);
814         break;
815     case DLL_PROCESS_DETACH:
816         TRACE("DLL_PROCESS_DETACH\n");
817         DeleteCriticalSection(&DSOUND_renderers_lock);
818         DeleteCriticalSection(&DSOUND_capturers_lock);
819         break;
820     default:
821         TRACE("UNKNOWN REASON\n");
822         break;
823     }
824     return TRUE;
825 }
826
827 /***********************************************************************
828  *              DllRegisterServer (DSOUND.@)
829  */
830 HRESULT WINAPI DllRegisterServer(void)
831 {
832     return __wine_register_resources( instance );
833 }
834
835 /***********************************************************************
836  *              DllUnregisterServer (DSOUND.@)
837  */
838 HRESULT WINAPI DllUnregisterServer(void)
839 {
840     return __wine_unregister_resources( instance );
841 }