| /* | |
| * Copyright 2009 Maarten Lankhorst | |
| * Copyright 2011 Andrew Eikum for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdarg.h> | |
| #define COBJMACROS | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "wine/library.h" | |
| #include "ole2.h" | |
| #include "olectl.h" | |
| #include "rpcproxy.h" | |
| #include "propsys.h" | |
| #include "propkeydef.h" | |
| #include "mmdeviceapi.h" | |
| #include "mmsystem.h" | |
| #include "dsound.h" | |
| #include "audioclient.h" | |
| #include "endpointvolume.h" | |
| #include "audiopolicy.h" | |
| #include "devpkey.h" | |
| #include "winreg.h" | |
| #include "mmdevapi.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi); | |
| static HINSTANCE instance; | |
| DriverFuncs drvs; | |
| const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\', | |
| 'W','i','n','e','\\','D','r','i','v','e','r','s',0}; | |
| static const char *get_priority_string(int prio) | |
| { | |
| switch(prio){ | |
| case Priority_Unavailable: | |
| return "Unavailable"; | |
| case Priority_Low: | |
| return "Low"; | |
| case Priority_Neutral: | |
| return "Neutral"; | |
| case Priority_Preferred: | |
| return "Preferred"; | |
| } | |
| return "Invalid"; | |
| } | |
| static BOOL load_driver(const WCHAR *name, DriverFuncs *driver) | |
| { | |
| WCHAR driver_module[264]; | |
| static const WCHAR wineW[] = {'w','i','n','e',0}; | |
| static const WCHAR dotdrvW[] = {'.','d','r','v',0}; | |
| lstrcpyW(driver_module, wineW); | |
| lstrcatW(driver_module, name); | |
| lstrcatW(driver_module, dotdrvW); | |
| TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module)); | |
| driver->module = LoadLibraryW(driver_module); | |
| if(!driver->module){ | |
| TRACE("Unable to load %s: %u\n", wine_dbgstr_w(driver_module), | |
| GetLastError()); | |
| return FALSE; | |
| } | |
| #define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\ | |
| if(!driver->p##n) { FreeLibrary(driver->module); return FALSE; } } while(0) | |
| LDFC(GetPriority); | |
| LDFC(GetEndpointIDs); | |
| LDFC(GetAudioEndpoint); | |
| LDFC(GetAudioSessionManager); | |
| #undef LDFC | |
| /* optional - do not fail if not found */ | |
| driver->pGetPropValue = (void*)GetProcAddress(driver->module, "GetPropValue"); | |
| driver->priority = driver->pGetPriority(); | |
| lstrcpyW(driver->module_name, driver_module); | |
| TRACE("Successfully loaded %s with priority %s\n", | |
| wine_dbgstr_w(driver_module), get_priority_string(driver->priority)); | |
| return TRUE; | |
| } | |
| static BOOL WINAPI init_driver(INIT_ONCE *once, void *param, void **context) | |
| { | |
| static const WCHAR drv_value[] = {'A','u','d','i','o',0}; | |
| static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',', | |
| 'c','o','r','e','a','u','d','i','o',',','a','n','d','r','o','i','d',0}; | |
| DriverFuncs driver; | |
| HKEY key; | |
| WCHAR reg_list[256], *p, *next, *driver_list = default_list; | |
| if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){ | |
| DWORD size = sizeof(reg_list); | |
| if(RegQueryValueExW(key, drv_value, 0, NULL, (BYTE*)reg_list, | |
| &size) == ERROR_SUCCESS){ | |
| if(reg_list[0] == '\0'){ | |
| TRACE("User explicitly chose no driver\n"); | |
| RegCloseKey(key); | |
| return TRUE; | |
| } | |
| driver_list = reg_list; | |
| } | |
| RegCloseKey(key); | |
| } | |
| TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list)); | |
| for(next = p = driver_list; next; p = next + 1){ | |
| next = strchrW(p, ','); | |
| if(next) | |
| *next = '\0'; | |
| driver.priority = Priority_Unavailable; | |
| if(load_driver(p, &driver)){ | |
| if(driver.priority == Priority_Unavailable) | |
| FreeLibrary(driver.module); | |
| else if(!drvs.module || driver.priority > drvs.priority){ | |
| TRACE("Selecting driver %s with priority %s\n", | |
| wine_dbgstr_w(p), get_priority_string(driver.priority)); | |
| if(drvs.module) | |
| FreeLibrary(drvs.module); | |
| drvs = driver; | |
| }else | |
| FreeLibrary(driver.module); | |
| }else | |
| TRACE("Failed to load driver %s\n", wine_dbgstr_w(p)); | |
| if(next) | |
| *next = ','; | |
| } | |
| return drvs.module != 0; | |
| } | |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
| { | |
| TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); | |
| switch (fdwReason) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| instance = hinstDLL; | |
| DisableThreadLibraryCalls(hinstDLL); | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| if(lpvReserved) | |
| break; | |
| MMDevEnum_Free(); | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| HRESULT WINAPI DllCanUnloadNow(void) | |
| { | |
| return S_FALSE; | |
| } | |
| typedef HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj); | |
| typedef struct { | |
| IClassFactory IClassFactory_iface; | |
| REFCLSID rclsid; | |
| FnCreateInstance pfnCreateInstance; | |
| } IClassFactoryImpl; | |
| static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface) | |
| { | |
| return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface); | |
| } | |
| static HRESULT WINAPI | |
| MMCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj) | |
| { | |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); | |
| TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); | |
| if (ppobj == NULL) | |
| return E_POINTER; | |
| if (IsEqualIID(riid, &IID_IUnknown) || | |
| IsEqualIID(riid, &IID_IClassFactory)) | |
| { | |
| *ppobj = iface; | |
| IClassFactory_AddRef(iface); | |
| return S_OK; | |
| } | |
| *ppobj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI MMCF_AddRef(LPCLASSFACTORY iface) | |
| { | |
| return 2; | |
| } | |
| static ULONG WINAPI MMCF_Release(LPCLASSFACTORY iface) | |
| { | |
| /* static class, won't be freed */ | |
| return 1; | |
| } | |
| static HRESULT WINAPI MMCF_CreateInstance( | |
| LPCLASSFACTORY iface, | |
| LPUNKNOWN pOuter, | |
| REFIID riid, | |
| LPVOID *ppobj) | |
| { | |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); | |
| TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj); | |
| if (pOuter) | |
| return CLASS_E_NOAGGREGATION; | |
| if (ppobj == NULL) { | |
| WARN("invalid parameter\n"); | |
| return E_POINTER; | |
| } | |
| *ppobj = NULL; | |
| return This->pfnCreateInstance(riid, ppobj); | |
| } | |
| static HRESULT WINAPI MMCF_LockServer(LPCLASSFACTORY iface, BOOL dolock) | |
| { | |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); | |
| FIXME("(%p, %d) stub!\n", This, dolock); | |
| return S_OK; | |
| } | |
| static const IClassFactoryVtbl MMCF_Vtbl = { | |
| MMCF_QueryInterface, | |
| MMCF_AddRef, | |
| MMCF_Release, | |
| MMCF_CreateInstance, | |
| MMCF_LockServer | |
| }; | |
| static IClassFactoryImpl MMDEVAPI_CF[] = { | |
| { { &MMCF_Vtbl }, &CLSID_MMDeviceEnumerator, (FnCreateInstance)MMDevEnum_Create } | |
| }; | |
| HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) | |
| { | |
| static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; | |
| unsigned int i = 0; | |
| TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); | |
| if(!InitOnceExecuteOnce(&init_once, init_driver, NULL, NULL)) { | |
| ERR("Driver initialization failed\n"); | |
| return E_FAIL; | |
| } | |
| if (ppv == NULL) { | |
| WARN("invalid parameter\n"); | |
| return E_INVALIDARG; | |
| } | |
| *ppv = NULL; | |
| if (!IsEqualIID(riid, &IID_IClassFactory) && | |
| !IsEqualIID(riid, &IID_IUnknown)) { | |
| WARN("no interface for %s\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| for (i = 0; i < sizeof(MMDEVAPI_CF)/sizeof(MMDEVAPI_CF[0]); ++i) | |
| { | |
| if (IsEqualGUID(rclsid, MMDEVAPI_CF[i].rclsid)) { | |
| IClassFactory_AddRef(&MMDEVAPI_CF[i].IClassFactory_iface); | |
| *ppv = &MMDEVAPI_CF[i]; | |
| return S_OK; | |
| } | |
| } | |
| WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid), | |
| debugstr_guid(riid), ppv); | |
| return CLASS_E_CLASSNOTAVAILABLE; | |
| } | |
| /*********************************************************************** | |
| * DllRegisterServer (MMDEVAPI.@) | |
| */ | |
| HRESULT WINAPI DllRegisterServer(void) | |
| { | |
| return __wine_register_resources( instance ); | |
| } | |
| /*********************************************************************** | |
| * DllUnregisterServer (MMDEVAPI.@) | |
| */ | |
| HRESULT WINAPI DllUnregisterServer(void) | |
| { | |
| return __wine_unregister_resources( instance ); | |
| } |