ntdll: Make wine_build a hidden symbol.
[wine.git] / dlls / mmdevapi / main.c
blob358c1697a236d6873fe0b766da167f4124b5d648
1 /*
2 * Copyright 2009 Maarten Lankhorst
3 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
22 #define COBJMACROS
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
27 #include "ole2.h"
28 #include "olectl.h"
29 #include "rpcproxy.h"
30 #include "propsys.h"
31 #include "propkeydef.h"
32 #include "mmdeviceapi.h"
33 #include "mmsystem.h"
34 #include "dsound.h"
35 #include "audioclient.h"
36 #include "endpointvolume.h"
37 #include "audiopolicy.h"
38 #include "devpkey.h"
39 #include "winreg.h"
40 #include "spatialaudioclient.h"
42 #include "mmdevapi.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
47 DriverFuncs drvs;
49 const WCHAR drv_keyW[] = L"Software\\Wine\\Drivers";
51 static const char *get_priority_string(int prio)
53 switch(prio){
54 case Priority_Unavailable:
55 return "Unavailable";
56 case Priority_Low:
57 return "Low";
58 case Priority_Neutral:
59 return "Neutral";
60 case Priority_Preferred:
61 return "Preferred";
63 return "Invalid";
66 static BOOL load_driver(const WCHAR *name, DriverFuncs *driver)
68 WCHAR driver_module[264];
70 lstrcpyW(driver_module, L"wine");
71 lstrcatW(driver_module, name);
72 lstrcatW(driver_module, L".drv");
74 TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module));
76 driver->module = LoadLibraryW(driver_module);
77 if(!driver->module){
78 TRACE("Unable to load %s: %u\n", wine_dbgstr_w(driver_module),
79 GetLastError());
80 return FALSE;
83 #define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\
84 if(!driver->p##n) { FreeLibrary(driver->module); return FALSE; } } while(0)
85 LDFC(GetPriority);
86 LDFC(GetEndpointIDs);
87 LDFC(GetAudioEndpoint);
88 LDFC(GetAudioSessionManager);
89 #undef LDFC
91 /* optional - do not fail if not found */
92 driver->pGetPropValue = (void*)GetProcAddress(driver->module, "GetPropValue");
94 driver->priority = driver->pGetPriority();
95 lstrcpyW(driver->module_name, driver_module);
97 TRACE("Successfully loaded %s with priority %s\n",
98 wine_dbgstr_w(driver_module), get_priority_string(driver->priority));
100 return TRUE;
103 static BOOL WINAPI init_driver(INIT_ONCE *once, void *param, void **context)
105 static WCHAR default_list[] = L"pulse,alsa,oss,coreaudio,android";
106 DriverFuncs driver;
107 HKEY key;
108 WCHAR reg_list[256], *p, *next, *driver_list = default_list;
110 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
111 DWORD size = sizeof(reg_list);
113 if(RegQueryValueExW(key, L"Audio", 0, NULL, (BYTE*)reg_list, &size) == ERROR_SUCCESS){
114 if(reg_list[0] == '\0'){
115 TRACE("User explicitly chose no driver\n");
116 RegCloseKey(key);
117 return TRUE;
120 driver_list = reg_list;
123 RegCloseKey(key);
126 TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list));
127 for(next = p = driver_list; next; p = next + 1){
128 next = wcschr(p, ',');
129 if(next)
130 *next = '\0';
132 driver.priority = Priority_Unavailable;
133 if(load_driver(p, &driver)){
134 if(driver.priority == Priority_Unavailable)
135 FreeLibrary(driver.module);
136 else if(!drvs.module || driver.priority > drvs.priority){
137 TRACE("Selecting driver %s with priority %s\n",
138 wine_dbgstr_w(p), get_priority_string(driver.priority));
139 if(drvs.module)
140 FreeLibrary(drvs.module);
141 drvs = driver;
142 }else
143 FreeLibrary(driver.module);
144 }else
145 TRACE("Failed to load driver %s\n", wine_dbgstr_w(p));
147 if(next)
148 *next = ',';
151 if (drvs.module != 0){
152 load_devices_from_reg();
153 load_driver_devices(eRender);
154 load_driver_devices(eCapture);
157 return drvs.module != 0;
160 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
162 TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
164 switch (fdwReason)
166 case DLL_PROCESS_ATTACH:
167 DisableThreadLibraryCalls(hinstDLL);
168 break;
169 case DLL_PROCESS_DETACH:
170 if(lpvReserved)
171 break;
172 MMDevEnum_Free();
173 break;
176 return TRUE;
179 typedef HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
181 typedef struct {
182 IClassFactory IClassFactory_iface;
183 REFCLSID rclsid;
184 FnCreateInstance pfnCreateInstance;
185 } IClassFactoryImpl;
187 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
189 return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
192 static HRESULT WINAPI
193 MMCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj)
195 IClassFactoryImpl *This = impl_from_IClassFactory(iface);
196 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
197 if (ppobj == NULL)
198 return E_POINTER;
199 if (IsEqualIID(riid, &IID_IUnknown) ||
200 IsEqualIID(riid, &IID_IClassFactory))
202 *ppobj = iface;
203 IClassFactory_AddRef(iface);
204 return S_OK;
206 *ppobj = NULL;
207 return E_NOINTERFACE;
210 static ULONG WINAPI MMCF_AddRef(LPCLASSFACTORY iface)
212 return 2;
215 static ULONG WINAPI MMCF_Release(LPCLASSFACTORY iface)
217 /* static class, won't be freed */
218 return 1;
221 static HRESULT WINAPI MMCF_CreateInstance(
222 LPCLASSFACTORY iface,
223 LPUNKNOWN pOuter,
224 REFIID riid,
225 LPVOID *ppobj)
227 IClassFactoryImpl *This = impl_from_IClassFactory(iface);
228 TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj);
230 if (pOuter)
231 return CLASS_E_NOAGGREGATION;
233 if (ppobj == NULL) {
234 WARN("invalid parameter\n");
235 return E_POINTER;
237 *ppobj = NULL;
238 return This->pfnCreateInstance(riid, ppobj);
241 static HRESULT WINAPI MMCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
243 IClassFactoryImpl *This = impl_from_IClassFactory(iface);
244 FIXME("(%p, %d) stub!\n", This, dolock);
245 return S_OK;
248 static const IClassFactoryVtbl MMCF_Vtbl = {
249 MMCF_QueryInterface,
250 MMCF_AddRef,
251 MMCF_Release,
252 MMCF_CreateInstance,
253 MMCF_LockServer
256 static IClassFactoryImpl MMDEVAPI_CF[] = {
257 { { &MMCF_Vtbl }, &CLSID_MMDeviceEnumerator, (FnCreateInstance)MMDevEnum_Create }
260 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
262 static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
263 unsigned int i = 0;
264 TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
266 if(!InitOnceExecuteOnce(&init_once, init_driver, NULL, NULL)) {
267 ERR("Driver initialization failed\n");
268 return E_FAIL;
271 if (ppv == NULL) {
272 WARN("invalid parameter\n");
273 return E_INVALIDARG;
276 *ppv = NULL;
278 if (!IsEqualIID(riid, &IID_IClassFactory) &&
279 !IsEqualIID(riid, &IID_IUnknown)) {
280 WARN("no interface for %s\n", debugstr_guid(riid));
281 return E_NOINTERFACE;
284 for (i = 0; i < ARRAY_SIZE(MMDEVAPI_CF); ++i)
286 if (IsEqualGUID(rclsid, MMDEVAPI_CF[i].rclsid)) {
287 IClassFactory_AddRef(&MMDEVAPI_CF[i].IClassFactory_iface);
288 *ppv = &MMDEVAPI_CF[i];
289 return S_OK;
293 WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid),
294 debugstr_guid(riid), ppv);
295 return CLASS_E_CLASSNOTAVAILABLE;
298 struct activate_async_op {
299 IActivateAudioInterfaceAsyncOperation IActivateAudioInterfaceAsyncOperation_iface;
300 LONG ref;
302 IActivateAudioInterfaceCompletionHandler *callback;
303 HRESULT result_hr;
304 IUnknown *result_iface;
307 static struct activate_async_op *impl_from_IActivateAudioInterfaceAsyncOperation(IActivateAudioInterfaceAsyncOperation *iface)
309 return CONTAINING_RECORD(iface, struct activate_async_op, IActivateAudioInterfaceAsyncOperation_iface);
312 static HRESULT WINAPI activate_async_op_QueryInterface(IActivateAudioInterfaceAsyncOperation *iface,
313 REFIID riid, void **ppv)
315 struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
317 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
319 if (!ppv)
320 return E_POINTER;
322 if (IsEqualIID(riid, &IID_IUnknown) ||
323 IsEqualIID(riid, &IID_IActivateAudioInterfaceAsyncOperation)) {
324 *ppv = &This->IActivateAudioInterfaceAsyncOperation_iface;
325 } else {
326 *ppv = NULL;
327 return E_NOINTERFACE;
330 IUnknown_AddRef((IUnknown*)*ppv);
331 return S_OK;
334 static ULONG WINAPI activate_async_op_AddRef(IActivateAudioInterfaceAsyncOperation *iface)
336 struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
337 LONG ref = InterlockedIncrement(&This->ref);
338 TRACE("(%p) refcount now %i\n", This, ref);
339 return ref;
342 static ULONG WINAPI activate_async_op_Release(IActivateAudioInterfaceAsyncOperation *iface)
344 struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
345 LONG ref = InterlockedDecrement(&This->ref);
346 TRACE("(%p) refcount now %i\n", This, ref);
347 if (!ref) {
348 if(This->result_iface)
349 IUnknown_Release(This->result_iface);
350 IActivateAudioInterfaceCompletionHandler_Release(This->callback);
351 HeapFree(GetProcessHeap(), 0, This);
353 return ref;
356 static HRESULT WINAPI activate_async_op_GetActivateResult(IActivateAudioInterfaceAsyncOperation *iface,
357 HRESULT *result_hr, IUnknown **result_iface)
359 struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
361 TRACE("(%p)->(%p, %p)\n", This, result_hr, result_iface);
363 *result_hr = This->result_hr;
365 if(This->result_hr == S_OK){
366 *result_iface = This->result_iface;
367 IUnknown_AddRef(*result_iface);
370 return S_OK;
373 static IActivateAudioInterfaceAsyncOperationVtbl IActivateAudioInterfaceAsyncOperation_vtbl = {
374 activate_async_op_QueryInterface,
375 activate_async_op_AddRef,
376 activate_async_op_Release,
377 activate_async_op_GetActivateResult,
380 static DWORD WINAPI activate_async_threadproc(void *user)
382 struct activate_async_op *op = user;
384 IActivateAudioInterfaceCompletionHandler_ActivateCompleted(op->callback, &op->IActivateAudioInterfaceAsyncOperation_iface);
386 IActivateAudioInterfaceAsyncOperation_Release(&op->IActivateAudioInterfaceAsyncOperation_iface);
388 return 0;
391 static HRESULT get_mmdevice_by_activatepath(const WCHAR *path, IMMDevice **mmdev)
393 IMMDeviceEnumerator *devenum;
394 HRESULT hr;
396 static const WCHAR DEVINTERFACE_AUDIO_RENDER_WSTR[] = L"{E6327CAD-DCEC-4949-AE8A-991E976A79D2}";
397 static const WCHAR DEVINTERFACE_AUDIO_CAPTURE_WSTR[] = L"{2EEF81BE-33FA-4800-9670-1CD474972C3F}";
398 static const WCHAR MMDEV_PATH_PREFIX[] = L"\\\\?\\SWD#MMDEVAPI#";
400 hr = MMDevEnum_Create(&IID_IMMDeviceEnumerator, (void**)&devenum);
401 if (FAILED(hr)) {
402 WARN("Failed to create MMDeviceEnumerator: %08x\n", hr);
403 return hr;
406 if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_RENDER_WSTR)){
407 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eRender, eMultimedia, mmdev);
408 } else if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_CAPTURE_WSTR)){
409 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eCapture, eMultimedia, mmdev);
410 } else if (!memcmp(path, MMDEV_PATH_PREFIX, sizeof(MMDEV_PATH_PREFIX) - sizeof(WCHAR))) {
411 WCHAR device_id[56]; /* == strlen("{0.0.1.00000000}.{fd47d9cc-4218-4135-9ce2-0c195c87405b}") + 1 */
413 lstrcpynW(device_id, path + (ARRAY_SIZE(MMDEV_PATH_PREFIX) - 1), ARRAY_SIZE(device_id));
415 hr = IMMDeviceEnumerator_GetDevice(devenum, device_id, mmdev);
416 } else {
417 FIXME("Unrecognized device id format: %s\n", debugstr_w(path));
418 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
421 if (FAILED(hr)) {
422 WARN("Failed to get requested device (%s): %08x\n", debugstr_w(path), hr);
423 *mmdev = NULL;
424 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
427 IMMDeviceEnumerator_Release(devenum);
429 return hr;
432 /***********************************************************************
433 * ActivateAudioInterfaceAsync (MMDEVAPI.17)
435 HRESULT WINAPI ActivateAudioInterfaceAsync(const WCHAR *path, REFIID riid,
436 PROPVARIANT *params, IActivateAudioInterfaceCompletionHandler *done_handler,
437 IActivateAudioInterfaceAsyncOperation **op_out)
439 struct activate_async_op *op;
440 HANDLE ht;
441 IMMDevice *mmdev;
443 TRACE("(%s, %s, %p, %p, %p)\n", debugstr_w(path), debugstr_guid(riid),
444 params, done_handler, op_out);
446 op = HeapAlloc(GetProcessHeap(), 0, sizeof(*op));
447 if (!op)
448 return E_OUTOFMEMORY;
450 op->ref = 2; /* returned ref and threadproc ref */
451 op->IActivateAudioInterfaceAsyncOperation_iface.lpVtbl = &IActivateAudioInterfaceAsyncOperation_vtbl;
452 op->callback = done_handler;
453 IActivateAudioInterfaceCompletionHandler_AddRef(done_handler);
455 op->result_hr = get_mmdevice_by_activatepath(path, &mmdev);
456 if (SUCCEEDED(op->result_hr)) {
457 op->result_hr = IMMDevice_Activate(mmdev, riid, CLSCTX_INPROC_SERVER, params, (void**)&op->result_iface);
458 IMMDevice_Release(mmdev);
459 }else
460 op->result_iface = NULL;
462 ht = CreateThread(NULL, 0, &activate_async_threadproc, op, 0, NULL);
463 CloseHandle(ht);
465 *op_out = &op->IActivateAudioInterfaceAsyncOperation_iface;
467 return S_OK;